/*
 * @Author       : stark1898y 1658608470@qq.com
 * @Date         : 2024-09-04 13:33:49
 * @LastEditors: mbw && 1600520629@qq.com
 * @LastEditTime: 2025-01-17 10:22:34
 * @FilePath: \JT-DT-YD4N02A_RTT_MRS-NT26K\bsp\src\at_device_nt26k.c
 * @Description  :
 *
 * Copyright (c) 2024 by yzy, All Rights Reserved.
 */
/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-12-13     qiyongzhong  first version
 */
// #include <stdio.h>
#include <string.h>
#include "bsp_flash.h"
#include <at_device_nt26k.h>
#include "bsp_nt26k.h"
#include "user_sys.h"
#include "bsp_rtc.h"
#include "bsp_led.h"
#include "bsp_rng.h"
#define LOG_TAG "at.dev.nt26k"
#include <at_log.h>

#include "pin.h"

#if IOT_MODULE_SWITCH == 1

#define NT26K_POWER_OFF RT_FALSE
#define NT26K_POWER_ON RT_TRUE
#define NT26K_POWER_ON_TIME 3
#define NT26K_POWER_OFF_TIME 4
#define AT_CLIENT_RECV_BUFF_LEN 512
#define AT_DEFAULT_TIMEOUT 5000
#ifdef AT_DEVICE_USING_NT26K
#define NT26K_WAIT_CONNECT_TIME 5000
#define NT26K_THREAD_STACK_SIZE 4096
#define NT26K_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 2)

char data_buf[AT_CLIENT_RECV_BUFF_LEN] = {0};

nt26k_sys_info nt26k = {0};
struct rt_completion nt26k_init_complate;
volatile rt_uint8_t socket_id = 0;

// 将本地时间转换为对应时区时间
void Time_Zone_Conversion (TsRtcDateTime *timeInfo)
{
    // 根据时区差异进行转换，这里时区差为8小时，没有分钟差异。
    timeInfo->hour += RT_LIBC_DEFAULT_TIMEZONE;  // 小时部分加8小时
    if (timeInfo->hour >= 24)
    {                                            // 如果超过24小时，则天数加1，小时数减24。
        timeInfo->day++;
        timeInfo->hour -= 24;
        if (timeInfo->day > 365)
        {
            timeInfo->year += 1;
        }
    }
}

rt_err_t Nt26k_Reset (struct at_device *device)
{
    LOG_I ("NT26K Reset");
    rt_pin_mode (NT26K_RST_PIN, PIN_MODE_OUTPUT);
    rt_pin_write (NT26K_RST_PIN, PIN_LOW);
    rt_thread_mdelay (10);
    rt_pin_write (NT26K_RST_PIN, PIN_HIGH);
    rt_thread_mdelay (50);
    rt_pin_write (NT26K_RST_PIN, PIN_LOW);

    return RT_EOK;
}

static int nt26k_power_on (struct at_device *device)
{
    struct at_device_nt26k *nt26k = RT_NULL;

    nt26k = (struct at_device_nt26k *)device->user_data;

    rt_pin_write (nt26k->pwr_en_pin, PIN_LOW);
    rt_thread_mdelay (10);
    rt_pin_write (nt26k->pwr_en_pin, PIN_HIGH);
    
    return device->class->device_ops->control (device, AT_DEVICE_CTRL_WAKEUP, RT_NULL);
}

static rt_err_t nt26k_power_off (struct at_device *device)
{
    struct at_device_nt26k *nt26k = RT_NULL;
    nt26k = (struct at_device_nt26k *)device->user_data;

    device->class->device_ops->control (device, AT_DEVICE_RF_CLOSE, RT_NULL);

    rt_pin_write (nt26k->pwr_en_pin, PIN_LOW);

    return RT_EOK;
}

static rt_err_t nt26k_Close_Rf (struct at_device *device)
{
    at_response_t resp = at_create_resp (64, 0, rt_tick_from_millisecond (60000));
    if (resp == RT_NULL)
    {
        LOG_D ("no memory for resp create.");
        at_delete_resp (resp);
        return (-RT_ERROR);
    }
    if (at_obj_exec_cmd (device->client, resp, "AT+QPOWD=1") == RT_EOK)
    {
        at_delete_resp (resp);
        return RT_EOK;
    }

    return (-RT_ERROR);
}
#ifdef TEST_ENABLE
void TEST_Nt26k_Reset()
{
    Nt26k_Reset (RT_NULL);
}

MSH_CMD_EXPORT (TEST_Nt26k_Reset, "test_nt26k_reset");
#endif

static int nt26k_sleep (struct at_device *device)
{
    at_response_t resp = RT_NULL;
    struct at_device_nt26k *nt26k = RT_NULL;

    nt26k = (struct at_device_nt26k *)device->user_data;
    if (!nt26k->power_status)  // power off
    {
        return (RT_EOK);
    }
    if (nt26k->sleep_status)  // is sleep status
    {
        return (RT_EOK);
    }

    resp = at_create_resp (64, 0, rt_tick_from_millisecond (300));
    if (resp == RT_NULL)
    {
        LOG_D ("no memory for resp create.");
        at_delete_resp (resp);
        return (-RT_ERROR);
    }

    /* enable sleep mode */ 
    // Sleep1
    if (at_obj_exec_cmd (device->client, resp, "AT+ECPMUCFG=1,2") != RT_EOK)

    {
        LOG_D ("enable sleep fail.\"AT+ECPMUCFG=1,2\" execute fail.");
        at_delete_resp (resp);
        return (-RT_ERROR);
    }

    /* enable PSM mode */
    if (at_obj_exec_cmd (device->client, resp, "AT+CPSMS=1,,,,\"00100010\"") != RT_EOK)

    {
        LOG_D ("enable sleep fail.\"AT+CPSMS=1,,,,\"00100010\" execute fail.");
        at_delete_resp (resp);
        return (-RT_ERROR);
    }

    nt26k->sleep_status = RT_TRUE;

    at_delete_resp (resp);
    return (RT_EOK);
}

static int nt26k_wakeup (struct at_device *device)
{
    struct at_device_nt26k *nt26k = RT_NULL;

    nt26k = (struct at_device_nt26k *)device->user_data;
    if (nt26k->pwr_key_pin != -1)
    {
        rt_pin_write (nt26k->pwr_key_pin, PIN_LOW);
        rt_thread_mdelay (100);
        rt_pin_write (nt26k->pwr_key_pin, PIN_HIGH);
        nt26k->sleep_status = RT_TRUE;
    }
    return (RT_EOK);
}

static void urc_tcp_recv (struct at_client *client, const char *data, rt_size_t size)
{
    int len = 0;
    rt_uint16_t crc16 = 0;
    rt_uint16_t rec_crc16 = 0;
    char crc16_buf[6] = {0};
    rt_uint8_t recv_byte_buf[AT_CLIENT_RECV_BUFF_LEN] = {0};

    rt_memset (data_buf, 0, sizeof (data_buf));

    if (sscanf (data, "+QIURC: \"recv\",%*d,%d,%s", &len, data_buf) == 2)
    {
        HexStrToBytes (data_buf, recv_byte_buf, len * 2);
        LOG_D ("nt26k recv data: %s", data_buf);
        crc16 = crc1021 (data_buf, len * 2 - 10);  // 去掉帧尾三个字节和2个字节的校验值，字符长度为10

        rt_strncpy (crc16_buf, data_buf + len * 2 - 10, 4);
        LOG_D ("nt26k crc16: %04s", crc16_buf);
        rec_crc16 = strtol (crc16_buf, NULL, 16);
        if (crc16 != rec_crc16)  // 看下数据接收的是否正确
        {
            LOG_E ("nt26k recv data error {crc16 [%x]!= rec_crc16[%x]}", crc16, rec_crc16);
        }
        else
        {
            /*比较数组的长度和结构体的长度是否一致，如果不一致则数据解析错误，如果一致复制数组值到结构体中*/
            if (len == sizeof (struct Nt26kRecvData))
            {
                rt_memset (nt26k_ops.recv, 0, sizeof (struct Nt26kRecvData));        // 清空结构体
                rt_memcpy (nt26k_ops.recv, recv_byte_buf, sizeof (struct Nt26kRecvData));
                if (nt26k_ops.recv->recv_data.event_type != INSTRUCTION_HEART_BEAT)  // 加这个的原因就是如果有指令下发，会出现两个信号量，指令会执行两次，所以排除心跳包才释放信号量
                {
                    rt_sem_release (nt26k_recv_sem);
                }
                else
                {
                    // 接收到的是心跳包
                    rt_sem_release (nt26k_recv_heart_sem);
                }
            }
            else
            {
                LOG_E ("nt26k recv data error {len [%d]!= sizeof(struct Nt26kRecvData)[%d]}", len, sizeof (struct Nt26kRecvData));
            }
        }
    }
    rt_memset (recv_byte_buf, 0, sizeof (recv_byte_buf));
}

static void urc_device_reset (struct at_client *client, const char *data, rt_size_t size)
{
    LOG_D ("device reset");
    nt26k_conncet_tcp_flag = 0;
}

static void urc_tcp_disconnect (struct at_client *client, const char *data, rt_size_t size)
{
    size_t id;

    if (sscanf (data, "+QIURC: \"closed\",%d", &id) == 1)
    {
        if (id == socket_id)
        {
            LOG_W ("socket %d disconnect", id);
            nt26k_conncet_tcp_flag = 0;
            rt_sem_release (nt26k_disconnect_sem);
        }
    }
}

static const struct at_urc urc_table[] = {
    {"+QIURC: \"recv\"",   "\r\n", urc_tcp_recv      },
    {"boot.rom",           "\r\n", urc_device_reset  }, // 这个是软件复位时，
    {"Lierda",             "\r\n", urc_device_reset  }, // 这个时硬件复位时回复
    {"+QIURC: \"closed\"", "\r\n", urc_tcp_disconnect}, // tcp连接断开
};

void show_resp_info (at_response_t resp)
{
    RT_ASSERT (resp);
    /* Print response line buffer */
    const char *line_buffer = RT_NULL;

    for (rt_size_t line_num = 1; line_num <= resp->line_counts; line_num++)
    {
        if ((line_buffer = at_resp_get_line (resp, line_num)) != RT_NULL)
            LOG_I ("line %d buffer : %s", line_num, line_buffer);
        else
            LOG_I ("Parse line buffer error!");
    }
    return;
}

int at_device_nt26k_disconnect_tcp (struct at_device *device)
{
#define NT26K_CLOSE_REP_TIME (5 * AT_DEFAULT_TIMEOUT)

    at_response_t resp = at_create_resp (AT_CLIENT_RECV_BUFF_LEN, 0, NT26K_CLOSE_REP_TIME);
    if (resp == RT_NULL)
    {
        LOG_E ("No memory for response structure!");
        at_delete_resp (resp);
        return -RT_ENOMEM;
    }
    if (at_obj_exec_cmd (device->client, resp, TCP_CLOSE_SOCKET, socket_id) != RT_EOK)
    {
        at_delete_resp (resp);
        return -RT_ERROR;
    }
    nt26k_conncet_tcp_flag = 0;
    at_delete_resp (resp);
    return RT_EOK;
}

static int at_device_nt26k_connect_tcp (struct at_device *device)
{

    if (nt26k_conncet_tcp_flag)
        return RT_EOK;
    ASSERT (device);
    int err_code = 0;
    at_response_t resp = at_create_resp (64, 4, AT_DEFAULT_TIMEOUT);
    if (resp == RT_NULL)
    {
        LOG_E ("No memory for response structure!");
        at_delete_resp (resp);
        return -RT_ENOMEM;
    }
    if (Flash_Get_Sever_Data (&sever_info) != RT_EOK)
    {
        LOG_E ("Get Sever Data Failed");
        at_delete_resp (resp);
        return RT_ERROR;
    }
    if (at_obj_exec_cmd (device->client, resp, TCP_CONNECT_CMD, sever_info.server_url, sever_info.server_port) == RT_EOK)
    {
        if (at_resp_parse_line_args_by_kw (resp, "+QIOPEN:", "+QIOPEN:%d,%d", &socket_id, &err_code) > 0)
        {
            if (err_code == 0)
            {
                LOG_D ("nt26k connect to tcp server success");
                nt26k_conncet_tcp_flag = 1;
                at_delete_resp (resp);
                return RT_EOK;
            }
            else
            {
                LOG_E ("nt26k connect to tcp server failed. error code: %d", err_code);
                if (err_code == 570)
                {
                    nt26k_disconnect_pdp_flag = 1;
                }
            }
        }
    }
    at_delete_resp (resp);
    return RT_ERROR;
}

int at_send_data (struct at_device *device, const char *data, rt_size_t size)
{
#define NT26K_SEND_RESP_TIME (3000)
    const char *line_buffer = RT_NULL;
    rt_uint8_t retry = 0;
    at_response_t resp = at_create_resp (AT_CLIENT_RECV_BUFF_LEN / 4, 2, NT26K_SEND_RESP_TIME);
    rt_uint8_t max_retry_count = (rt_uint8_t)Flash_Get_SysCfg (kIotRetryId);
    if (resp == RT_NULL)
    {
        LOG_E ("no memory for nt26k device(%s) response structure.", device->name);
        at_delete_resp (resp);
        return -RT_ERROR;
    }

    while (retry < max_retry_count)
    {
        rt_thread_mdelay (IMEI_Delay());  // 获取一个随机延时
        if (at_obj_exec_cmd (device->client, resp, TCP_SEND_DATA, socket_id, data) == RT_EOK)
        {
            if ((line_buffer = at_resp_get_line (resp, 2)) != RT_NULL)
            {
                if (rt_strstr (line_buffer, "SEND OK") != RT_NULL)
                {
                    LOG_D ("send data success, socket_id: %d", socket_id);
                    at_delete_resp (resp);
                    return RT_EOK;
                }
            }
        }
        retry++;
        rt_thread_mdelay (1000);
    }

    at_delete_resp (resp);
    return -RT_ERROR;
}

int nt26k_check_link_status (struct at_device *device)
{
    at_response_t resp = RT_NULL;
    int result = -RT_ERROR;

    resp = at_create_resp (64, 0, rt_tick_from_millisecond (300));
    if (resp == RT_NULL)
    {
        LOG_D ("no memory for resp create.");
        at_delete_resp (resp);
        return (-RT_ERROR);
    }

    result = -RT_ERROR;
    if (at_obj_exec_cmd (device->client, resp, "AT+CEREG?") == RT_EOK)
    {
        int link_stat = 0;
        if (at_resp_parse_line_args_by_kw (resp, "+CEREG:", "+CEREG: %*d,%d", &link_stat) > 0)
        {
            if (link_stat == 1 || link_stat == 5)
            {
                result = RT_EOK;
            }
        }
    }

    at_delete_resp (resp);

    return (result);
}

/* =============================  nt26k network interface operations ============================= */
/* set nt26k network interface device status and address information */
int nt26k_netdev_set_info (struct netdev *netdev)
{
#define NT26K_INFO_RESP_SIZE 128
#define NT26K_INFO_RESP_TIMO rt_tick_from_millisecond (1000)

    int result = RT_EOK;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;

    RT_ASSERT (netdev);

    device = at_device_get_by_name (AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL)
    {
        LOG_E ("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    /* set network interface device status */
    netdev_low_level_set_status (netdev, RT_TRUE);
    netdev_low_level_set_link_status (netdev, RT_TRUE);
    netdev_low_level_set_dhcp_status (netdev, RT_TRUE);

    resp = at_create_resp (NT26K_INFO_RESP_SIZE, 0, NT26K_INFO_RESP_TIMO);
    if (resp == RT_NULL)
    {
        LOG_E ("no memory for resp create.");
        at_delete_resp (resp);
        result = -RT_ENOMEM;
        goto __exit;
    }
    device->class->device_ops->control (device, AT_DEVICE_CTRL_NET_CONN, RT_NULL);  // 打开连接
__exit:
    if (resp)
    {
        at_delete_resp (resp);
    }

    return result;
}

/**
 * @brief  check nt26k network interface device status
 *
 * @param  netdev: nt26k network interface device
 */
static void nt26k_check_link_status_entry (void *parameter)
{
#define NT26K_LINK_DELAY_TIME (10 * 60 * RT_TICK_PER_SECOND)

#define NT26K_LINK_STATUS_OK 1

    int link_status;
    struct at_device *device = RT_NULL;
    struct netdev *netdev = (struct netdev *)parameter;

    device = at_device_get_by_name (AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL)
    {
        LOG_E ("get device(%s) failed.", netdev->name);
        return;
    }

    while (1)
    {
        link_status = nt26k_check_link_status (device);
        if (link_status < 0)
        {
            rt_thread_mdelay (NT26K_LINK_DELAY_TIME);
            continue;
        }
        /* check the network interface device link status  */
        if ((NT26K_LINK_STATUS_OK == link_status) != netdev_is_link_up (netdev))
        {
            netdev_low_level_set_link_status (netdev, (NT26K_LINK_STATUS_OK == link_status));
        }

        rt_thread_delay (NT26K_LINK_DELAY_TIME);
    }
}

int nt26k_netdev_check_link_status (struct netdev *netdev)
{
#define NT26K_LINK_THREAD_TICK 20
#define NT26K_LINK_THREAD_STACK_SIZE (1024 + 512)
#define NT26K_LINK_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX - 2)

    rt_thread_t tid;
    char tname[RT_NAME_MAX] = {0};

    RT_ASSERT (netdev);

    rt_snprintf (tname, RT_NAME_MAX, "%s", netdev->name);

    /* create nt26k link status polling thread  */
    tid = rt_thread_create (tname, nt26k_check_link_status_entry, (void *)netdev,
                            NT26K_LINK_THREAD_STACK_SIZE, NT26K_LINK_THREAD_PRIORITY, NT26K_LINK_THREAD_TICK);
    if (tid != RT_NULL)
    {
        rt_thread_startup (tid);
    }

    return RT_EOK;
}

static int nt26k_net_init (struct at_device *device);

static int nt26k_netdev_set_up (struct netdev *netdev)
{
    struct at_device *device = RT_NULL;

    device = at_device_get_by_name (AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    LOG_D("netdev->name:%s",netdev->name);
    if (device == RT_NULL)
    {
        LOG_E ("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    if (device->is_init == RT_FALSE)
    {
        nt26k_net_init (device);
        device->is_init = RT_TRUE;

        netdev_low_level_set_status (netdev, RT_TRUE);
        LOG_D ("network interface device(%s) set up status.", netdev->name);
    }

    return RT_EOK;
}

static int nt26k_netdev_set_down (struct netdev *netdev)
{
    struct at_device *device = RT_NULL;

    device = at_device_get_by_name (AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL)
    {
        LOG_E ("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    if (device->is_init == RT_TRUE)
    {
        nt26k_power_off (device);
        device->is_init = RT_FALSE;

        netdev_low_level_set_status (netdev, RT_FALSE);
        LOG_D ("network interface device(%s) set down status.", netdev->name);
    }

    return RT_EOK;
}

static int nt26k_netdev_set_dns_server (struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server)
{
#define NT26K_DNS_RESP_LEN 64
#define NT26K_DNS_RESP_TIMEO rt_tick_from_millisecond (300)

    int result = RT_EOK;
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;

    RT_ASSERT (netdev);
    RT_ASSERT (dns_server);

    device = at_device_get_by_name (AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL)
    {
        LOG_E ("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    resp = at_create_resp (NT26K_DNS_RESP_LEN, 0, NT26K_DNS_RESP_TIMEO);
    if (resp == RT_NULL)
    {
        LOG_D ("no memory for resp create.");
        at_delete_resp (resp);
        result = -RT_ENOMEM;
        goto __exit;
    }

    /* send "AT+QIDNSCFG=<pri_dns>[,<sec_dns>]" commond to set dns servers */
    if (at_obj_exec_cmd (device->client, resp, "AT+QIDNSCFG=%d,%s",
                         dns_num, inet_ntoa (*dns_server)) != RT_EOK)
    {
        result = -RT_ERROR;
        goto __exit;
    }

    netdev_low_level_set_dns_server (netdev, dns_num, dns_server);

__exit:
    if (resp)
    {
        at_delete_resp (resp);
    }

    return result;
}

#ifdef NETDEV_USING_PING
static int nt26k_netdev_ping (struct netdev *netdev, const char *host, size_t data_len,
                              uint32_t timeout, struct netdev_ping_resp *ping_resp
#if RT_VER_NUM >= 0x50100
                              ,
                              rt_bool_t is_bind
#endif
)
{
#define NT26K_PING_RESP_SIZE 128
#define NT26K_PING_IP_SIZE 16
#define NT26K_PING_TIMEO (NT26K_CON_REP_TIME)

    rt_err_t result = RT_EOK;
    int response = -1, recv_data_len, ping_time, ttl;
    char ip_addr[NT26K_PING_IP_SIZE] = {0};
    at_response_t resp = RT_NULL;
    struct at_device *device = RT_NULL;

#if RT_VER_NUM >= 0x50100
    RT_UNUSED (is_bind);
#endif

    RT_ASSERT (netdev);
    RT_ASSERT (host);
    RT_ASSERT (ping_resp);

    device = at_device_get_by_name (AT_DEVICE_NAMETYPE_NETDEV, netdev->name);
    if (device == RT_NULL)
    {
        LOG_E ("get device(%s) failed.", netdev->name);
        return -RT_ERROR;
    }

    resp = at_create_resp (NT26K_PING_RESP_SIZE, 4, NT26K_PING_TIMEO);
    if (resp == RT_NULL)
    {
        LOG_E ("no memory for resp create");
        at_delete_resp (resp);
        return -RT_ENOMEM;
    }

    /* send "AT+QPING=<contextID>"<host>"[,[<timeout>][,<pingnum>]]" commond to send ping request */
    if (at_obj_exec_cmd (device->client, resp, "AT+QPING=1,\"%s\",%d,1", host, timeout / RT_TICK_PER_SECOND) < 0)
    {
        result = -RT_ERROR;
        goto __exit;
    }

    at_resp_parse_line_args_by_kw (resp, "+QPING:", "+QPING:%d", &response);
    /* Received the ping response from the server */
    if (response == 0)
    {
        if (at_resp_parse_line_args_by_kw (resp, "+QPING:", "+QPING:%d,\"%[^\"]\",%d,%d,%d",
                                           &response, ip_addr, &recv_data_len, &ping_time, &ttl) <= 0)
        {
            result = -RT_ERROR;
            goto __exit;
        }
    }

    /* prase response number */
    switch (response)
    {
    case 0:
        inet_aton (ip_addr, &(ping_resp->ip_addr));
        ping_resp->data_len = recv_data_len;
        ping_resp->ticks = ping_time;
        ping_resp->ttl = ttl;
        result = RT_EOK;
        break;
    case 569:
        result = -RT_ETIMEOUT;
        break;
    default:
        result = -RT_ERROR;
        break;
    }

__exit:
    if (resp)
    {
        at_delete_resp (resp);
    }

    return result;
}
#endif /* NETDEV_USING_PING */

const struct netdev_ops nt26k_netdev_ops =
    {
        nt26k_netdev_set_up,
        nt26k_netdev_set_down,

        RT_NULL,
        nt26k_netdev_set_dns_server,
        RT_NULL,

#ifdef NETDEV_USING_PING
        nt26k_netdev_ping,
#endif
        RT_NULL,
};

static struct netdev *nt26k_netdev_add (const char *netdev_name)
{
#define ETHERNET_MTU 1380
#define HWADDR_LEN 8
    struct netdev *netdev = RT_NULL;

    netdev = netdev_get_by_name (netdev_name);
    if (netdev != RT_NULL)
    {
        return (netdev);
    }

    netdev = (struct netdev *)rt_calloc (1, sizeof (struct netdev));
    if (netdev == RT_NULL)
    {
        LOG_E ("no memory for netdev create.");
        return RT_NULL;
    }

    netdev->mtu = ETHERNET_MTU;
    netdev->ops = &nt26k_netdev_ops;
    netdev->hwaddr_len = HWADDR_LEN;

#ifdef SAL_USING_AT
    extern int sal_at_netdev_set_pf_info (struct netdev * netdev);
    /* set the network interface socket/netdb operations */
    sal_at_netdev_set_pf_info (netdev);
#endif

    netdev_register (netdev, netdev_name, RT_NULL);

    return netdev;
}

/* =============================  nt26k device operations ============================= */

/* initialize for nt26k */
static void nt26k_init_thread_entry (void *parameter)
{
#define INIT_RETRY 5
#define CPIN_RETRY 20
#define CSQ_RETRY 30
#define CEREG_RETRY 60
#define IPADDR_RETRY 10

    int i;
    rt_err_t result = RT_EOK;
    at_response_t resp = RT_NULL;
    struct at_device *device = (struct at_device *)parameter;
    struct at_client *client = device->client;

    resp = at_create_resp (128, 0, rt_tick_from_millisecond (300));
    if (resp == RT_NULL)
    {
        LOG_E ("no memory for resp create.");
        at_delete_resp (resp);
        return;
    }

    LOG_D ("start init %s device.", device->name);

    while (1)
    {
        /* power on the nt26k device */
        device->class->device_ops->control (device, AT_DEVICE_CTRL_POWER_ON, RT_NULL);
        rt_thread_mdelay (100);
        /* wait nt26k startup finish, send AT every 500ms, if recei
        ve OK, SYNC success*/
        if (at_client_obj_wait_connect (client, NT26K_WAIT_CONNECT_TIME))
        {
            result = -RT_ETIMEOUT;
            goto __exit;
        }
        /* disable echo */
        if (at_obj_exec_cmd (device->client, resp, "ATE0") != RT_EOK)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        /* get module version */
        if (at_obj_exec_cmd (device->client, resp, "AT+CGMR") != RT_EOK)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        for (i = 0; i < (int)resp->line_counts - 1; i++)
        {
            LOG_D ("%s", at_resp_get_line (resp, i + 1));
        }
        /*AT+QICFG="dataformat"  设置收发模式*/
        if (at_obj_exec_cmd (device->client, resp, "AT+QICFG=\"dataformat\",%d,%d", AT_NSONMI_MODE_DEFAULT, AT_NSONMI_MODE_DEFAULT) != RT_EOK)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        /*设置显示模式*/
        if (at_obj_exec_cmd (device->client, resp, "AT+QICFG=\"viewmode\",1") != RT_EOK)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        /*设置保活信息*/
        if (at_obj_exec_cmd (device->client, resp, "AT+QICFG=\"tcp/keepalive\",1,10000,100,10") != RT_EOK)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        if (at_obj_exec_cmd (device->client, resp, "AT+QICFG=\"passiveclosed\",1") != RT_EOK)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        /* check SIM card */
        for (i = 0; i < CPIN_RETRY; i++)
        {
            if (at_obj_exec_cmd (device->client, resp, "AT+CPIN?") == RT_EOK)
            {
                if (at_resp_get_line_by_kw (resp, "+CPIN: READY") != RT_NULL)
                    break;
            }
            rt_thread_mdelay (1000);
        }
        if (i == CPIN_RETRY)
        {
            LOG_E ("%s device SIM card detection failed.", device->name);
            result = -RT_ERROR;
            goto __exit;
        }
        /* set etwork interface device hardware address(IMEI) */
        if ((*(rt_uint16_t *)FLASH_IOT_IMEI_ADDR == 0xE339) || (*(rt_uint16_t *)FLASH_IOT_IMEI_ADDR == 0x39E3))  // 未写入
        {
#define NT26K_NETDEV_HWADDR_LEN 8
#define NT26K_IMEI_LEN 15

            char imei[NT26K_IMEI_LEN] = {0};
            /* send "AT+CGSN" commond to get device IMEI */
            if (at_obj_exec_cmd (device->client, resp, "AT+CGSN") != RT_EOK)
            {
                result = -RT_ERROR;
                goto __exit;
            }
            if (at_resp_parse_line_args_by_kw (resp, "86", "%s", imei) <= 0)
            {
                LOG_E ("%s device prase \"AT+CGSN=1\" cmd error.", device->name);
                result = -RT_ERROR;
                goto __exit;
            }
            LOG_D ("%s device IMEI number: %s", device->name, imei);

            rt_memcpy (nt26k.imei, imei, NT26K_IMEI_LEN);
            Flash_Sys_Cfg (kIotImeiId, nt26k.imei, NT26K_IMEI_LEN);
        }
        else
        {
            rt_memset((char *)nt26k.imei, 0, NT26K_IMEI_LEN);
            Get_IotImei((char *)&nt26k.imei[0], NT26K_IMEI_LEN);
        }
        rt_thread_mdelay (10);
        /* set network interface device hardware iccid  */
        if ((*(rt_uint16_t *)FLASH_IOT_ICCID_ADDR == 0xE339) || (*(rt_uint16_t *)FLASH_IOT_ICCID_ADDR == 0x39E3))  // 未写入
        {

#define NT26K_ICCID_LEN 20

            char iccid[NT26K_ICCID_LEN] = {0};

            /* send "AT+ECICCID" commond to get device iccid */
            if (at_obj_exec_cmd (device->client, resp, "AT+QCCID") != RT_EOK)
            {
                result = -RT_ERROR;
                goto __exit;
            }
            if (at_resp_parse_line_args_by_kw (resp, "+QCCID:", "+QCCID:%s", iccid) <= 0)
            {
                LOG_E ("%s device prase \"AT+ECICCID\" cmd error.", device->name);
                result = -RT_ERROR;
                goto __exit;
            }

            LOG_D ("%s device iccid number: %s", device->name, iccid);

            rt_memcpy (nt26k.iccid, iccid, NT26K_ICCID_LEN);
            Flash_Sys_Cfg (kIotIccidId, nt26k.iccid, NT26K_ICCID_LEN);
        }
        else
        {
            rt_memset((char *)nt26k.iccid, 0, NT26K_ICCID_LEN);
            Get_IotIccid((char *)&nt26k.iccid[0], NT26K_ICCID_LEN);
        }
       rt_thread_mdelay (10);
        /* set network interface device hardware imsi  */
        if ((*(rt_uint16_t *)FLASH_IOT_IMSI_ADDR == 0xE339) || (*(rt_uint16_t *)FLASH_IOT_IMSI_ADDR == 0x39E3))  // 未写入
        {

#define NT26K_IMSI_LEN 15

            char imsi[NT26K_IMSI_LEN] = {0};

            /* send "AT+CIMI" commond to get device imsi */
            if (at_obj_exec_cmd (device->client, resp, "AT+CIMI") != RT_EOK)
            {
                result = -RT_ERROR;
                goto __exit;
            }
            if (at_resp_parse_line_args_by_kw (resp, "46", "%s", imsi) <= 0)
            {
                LOG_E ("%s device prase \"AT+CIMI\" cmd error.", device->name);
                result = -RT_ERROR;
                goto __exit;
            }

            LOG_D ("%s device imsi number: %s", device->name, imsi);

            rt_memcpy (nt26k.imsi, imsi, NT26K_IMSI_LEN);
            Flash_Sys_Cfg (kIotImsiId, nt26k.imsi, NT26K_IMSI_LEN);
        }
        else
        {
            //已经写入了，将其赋值给结构体
            rt_memset((char *)nt26k.imsi, 0, NT26K_IMSI_LEN);
            Get_IotImei((char *)&nt26k.imsi[0], NT26K_IMSI_LEN);

        }
       rt_thread_mdelay (10);
        /* check signal strength */
        for (i = 0; i < CSQ_RETRY; i++)
        {

         if (at_obj_exec_cmd(device->client, resp, "AT+CSQ") == RT_EOK)
         {
             int signal_strength = 0, err_rate = 0;

         if (at_resp_parse_line_args_by_kw(resp, "+CSQ:", "+CSQ: %d,%d", &signal_strength, &err_rate) > 0)
         {
             if ((signal_strength != 99) && (signal_strength != 0))
             {
                 LOG_D("%s device signal strength: %d, channel bit err_rate: %d",
                       device->name, signal_strength, err_rate);
                 nt26k.rssi = signal_strength;
                 break;
             }
         }
        }
        rt_thread_mdelay(1000);
        }
        if (i == CSQ_RETRY)
        {
        LOG_E("%s device signal strength check failed", device->name);
        result = -RT_ERROR;
        goto __exit;
        }

        /* check the GPRS network is registered */
        for (i = 0; i < CEREG_RETRY; i++)
        {

            if (at_obj_exec_cmd (device->client, resp, "AT+CEREG?") == RT_EOK)//60s内注册CS业务
            {
                int link_stat = 0;

                if (at_resp_parse_line_args_by_kw (resp, "+CEREG:", "+CEREG: %*d,%d", &link_stat) > 0)
                {
                    if ((link_stat == 1) || (link_stat == 5))
                    {
                        LOG_D ("%s device GPRS is registered", device->name);
                        break;
                    }
                }

            }
            rt_thread_mdelay (1000);
        }
         if (i == CEREG_RETRY)
         {
             result = -RT_ERROR;
             goto __exit;
         }
        // for (i = 0; i < CEREG_RETRY; i++)
        // {

        //     if (at_obj_exec_cmd (device->client, resp, "AT+QICSGP=1,1,\"cmnet\",\"\",\"\",1") == RT_EOK)
        //     {
        //         break;
        //     }
        //     rt_thread_mdelay (20000);
        // }
        // if (i == CEREG_RETRY)
        // {
        //     result = -RT_ERROR;
        //     goto __exit;
        // }
        for (i = 0; i < CEREG_RETRY; i++)
        {
            if (at_obj_exec_cmd (device->client, resp, "AT+CGACT=1,1") == RT_EOK)
            {
                LOG_D ("pdp激活成功");
                break;
            }
            else {
               // AT+CGACT=0,1 //去激活 PDP
                 if (at_obj_exec_cmd (device->client, resp, "AT+CGACT=0,1") == RT_EOK)
                 {
                     LOG_D ("pdp去激活成功");

                 }
            }
            rt_thread_mdelay (5000);
        }
        if (i == CEREG_RETRY)
        {
            result = -RT_ERROR;
            goto __exit;
        }
        if (at_obj_exec_cmd (device->client, resp, "AT+CCLK?") == RT_EOK)
        {
            TsRtcDateTime rtc_dt;
            int year, mounth, days, hous, min, sec;
            /*+CCLK:24/11/12,06:08:19+32*/
            if (at_resp_parse_line_args_by_kw (resp, "+CCLK:", "+CCLK: \"%d/%d/%d,%d:%d:%d+32\"", &year, &mounth, &days, &hous, &min, &sec) > 0)
            {
                rtc_dt.year = (2000 + year);
                rtc_dt.month = mounth;
                rtc_dt.day = days;
                rtc_dt.hour = hous;  // 此时为零区时间，需要转化为东八区
                rtc_dt.minute = min;
                rtc_dt.second = sec;
                Time_Zone_Conversion (&rtc_dt);  // 时区设置
                rtc_dt.week = RTC_GetWeek (rtc_dt.year, rtc_dt.month, rtc_dt.day);
                RTC_SetTime (rtc_dt.year, rtc_dt.month, rtc_dt.day,
                             rtc_dt.hour, rtc_dt.minute, rtc_dt.second);  // 设置时间
                LOG_I ("RTC时间: %04d-%02d-%02d %02d:%02d:%02d \n",
                       rtc_dt.year, rtc_dt.month, rtc_dt.day, rtc_dt.hour, rtc_dt.minute, rtc_dt.second);
                result = RT_EOK;
            }
        }
        /* initialize successfully  */
        result = RT_EOK;
        break;
        
__exit:
        if (result != RT_EOK)
        {
            /* power off the nt26k device */
            nt26k_power_off(device);
            rt_thread_mdelay (1000);
            LOG_I ("%s device initialize retry...", device->name);
        }
    }

    if (resp)
    {
        at_delete_resp (resp);
    }

    if (result == RT_EOK)
    {
        /* set network interface device status and address information */
        nt26k_netdev_set_info (device->netdev);
        /* check and create link staus sync thread  */
        nt26k_netdev_check_link_status (device->netdev);
        LOG_I ("%s device network initialize success.", device->name);
        rt_completion_done (&nt26k_init_complate);  // 通知初始化完成
    }
    else
    {
        LOG_E ("%s device network initialize failed(%d).", device->name, result);
    }

}

/* nt26k device network initialize */
static int nt26k_net_init (struct at_device *device)
{
#ifdef AT_DEVICE_NT26K_INIT_ASYN
    rt_thread_t tid;
    /* 初始化完成量对象 */
    rt_completion_init (&nt26k_init_complate);

    tid = rt_thread_create ("nt26k_net", nt26k_init_thread_entry, (void *)device,
                            NT26K_THREAD_STACK_SIZE, NT26K_THREAD_PRIORITY, 20);
    if (tid)
    {
        rt_thread_startup (tid);
    }
    else
    {
        LOG_E ("create %s device init thread failed.", device->name);
        return -RT_ERROR;
    }
#else
    nt26k_init_thread_entry (device);
#endif /* AT_DEVICE_NT26K_INIT_ASYN */

    return RT_EOK;
}

// 去除字符串中的 \r 和 \n
void remove_crlf (char *str)
{
    char *p = str;
    while (*p)
    {
        if (*p == '\r' || *p == '\n')
        {
            memmove (p, p + 1, strlen (p));
        }
        else
        {
            p++;
        }
    }
}

rt_err_t Nt26k_Get_Signal_Info (struct at_device *device)
{

#define RETRY 20

    rt_err_t result = RT_EOK;
    at_response_t resp = RT_NULL;
    rt_uint8_t i;
    RT_ASSERT (device);
    resp = at_create_resp (1024, 4, rt_tick_from_millisecond (2000));
    if (resp == RT_NULL)
    {
        LOG_E ("no memory for resp create.");
        at_delete_resp (resp);
        return RT_ERROR;
    }
    /* check signal strength */
    for (i = 0; i < RETRY; i++)
    {

        if (at_obj_exec_cmd (device->client, resp, "AT+CSQ") == RT_EOK)
        {
            int signal_strength = 0, err_rate = 0;

            if (at_resp_parse_line_args_by_kw (resp, "+CSQ:", "+CSQ: %d,%d", &signal_strength, &err_rate) > 0)
            {
                if ((signal_strength != 99) && (signal_strength != 0))
                {
                    LOG_D ("%s device signal strength: %d, channel bit err_rate: %d",
                           device->name, signal_strength, err_rate);
                    nt26k.rssi = signal_strength;
                    break;
                }
            }
        }
        rt_thread_mdelay (1000);
    }
    if (i == RETRY)
    {
        LOG_E ("%s device signal strength check failed", device->name);
        result = -RT_ERROR;
    }
    /* check signal strength */

    //+CESQ:99,99,255,255,28,44

    for (i = 0; i < RETRY; i++)
    {
        if (at_obj_exec_cmd (device->client, resp, "AT+CESQ") == RT_EOK)
        {
            rt_uint8_t rsrp = 0, rsrq = 0;

            if (at_resp_parse_line_args_by_kw (resp, "+CESQ:", "+CESQ:%*[^,],%*[^,],%*[^,],%*[^,],%d,%d", &rsrq, &rsrp) > 0)
            {
                if ((rsrp <= 97) && (rsrp > 0) && ((rsrq <= 34) && (rsrq > 0)))
                {
                    LOG_D ("%s device signal rsrp: %d, rsrq: %d",
                           device->name, rsrp, rsrq);
                    nt26k.rsrp = rsrp;
                    nt26k.rsrq = rsrq;
                    break;
                }
            }
        }
        rt_thread_mdelay (1000);
    }
    if (i == RETRY)
    {
        LOG_E ("%s device signal data failed", device->name);
        result = -RT_ERROR;
    }
    // /* get signal snr, pci */
    // for (int i = 0; i < RETRY; i++)
    // {
    //     if (at_obj_exec_cmd(device->client, resp, "AT+NUESTATS") == RT_EOK)
    //     {
    //         const char *key1 = "SNR:";
    //         const char *key2 = "PCI:";
    //         int pci = 0, snr = 0;
    //         // show_resp_info(resp);

    //  // 获取第 2 行的响应
    //  const char *line = at_resp_get_line(resp, 2);

    //  if (line != NULL)
    //  {
    //      snr = Extract_Value(line, key1);
    //      pci = Extract_Value(line, key2);

    //  if (snr != 0 && pci != 0)
    //  {
    //      nt26k.pci = pci;
    //      nt26k.snr = snr;
    //      LOG_D("pci:%d,snr:%d", nt26k.pci, nt26k.snr);
    //      break;
    //  }
    //  else
    //  {
    //      LOG_D("Extracted values are invalid: SNR=%d, PCI=%d", snr, pci);
    //  }
    // }
    // else
    // {
    //  LOG_D("Failed to get line 2 from response");
    // }
    // }
    // else
    // {
    // LOG_D("Failed to execute AT command");
    // }
    // rt_thread_mdelay(2000);
    // }
    // if (i == RETRY)
    // {
    // LOG_E("%s device signal data failed", device->name);
    // result = -RT_ERROR;
    // }

    at_delete_resp (resp);
    return result;
}

/* nt26k device network interface attach
 *   - set network interface device link status
 */
static int nt26k_init (struct at_device *device)
{
    struct at_device_nt26k *nt26k = RT_NULL;

    RT_ASSERT (device);

    nt26k = (struct at_device_nt26k *)device->user_data;
    nt26k->power_status = RT_FALSE;  // default power is off.
    nt26k->sleep_status = RT_FALSE;  // default sleep is disabled.

    /* initialize AT client */
#if RT_VER_NUM >= 0x50100
    at_client_init (nt26k->client_name, nt26k->recv_line_num, nt26k->recv_line_num);
#else
    at_client_init (nt26k->client_name, nt26k->recv_line_num);
#endif

    device->client = at_client_get (nt26k->client_name);
    if (device->client == RT_NULL)
    {
        LOG_E ("get AT client(%s) failed.", nt26k->client_name);
        return -RT_ERROR;
    }

    /* register URC data execution function  */
    at_obj_set_urc_table (device->client, urc_table, sizeof (urc_table) / sizeof (urc_table[0]));

#ifdef AT_USING_SOCKET
    nt26k_socket_init (device);
#endif

    /* add nt26k device to the netdev list */
    device->netdev = nt26k_netdev_add (nt26k->device_name);
    if (device->netdev == RT_NULL)
    {
        LOG_E ("add netdev(%s) failed.", nt26k->device_name);
        return -RT_ERROR;
    }

    /* initialize nt26k pin configuration */
    if (nt26k->pwr_en_pin != -1)
    {
        rt_pin_mode (nt26k->pwr_en_pin, PIN_MODE_OUTPUT);
    }
    if (nt26k->rst_pin != -1)
    {
        rt_pin_mode (nt26k->rst_pin, PIN_MODE_OUTPUT);
    }
    if (nt26k->pwr_key_pin != -1)
    {
        rt_pin_mode (nt26k->pwr_key_pin, PIN_MODE_OUTPUT);
        device->class->device_ops->control (device, AT_DEVICE_CTRL_WAKEUP, RT_NULL);
    }
    /* initialize nt26k device network */
    return nt26k_netdev_set_up (device->netdev);
}

static int nt26k_deinit (struct at_device *device)
{
    RT_ASSERT (device);

    return nt26k_netdev_set_down (device->netdev);
}

static int nt26k_control (struct at_device *device, int cmd, void *arg)
{
    int result = -RT_ERROR;

    RT_ASSERT (device);

    switch (cmd)
    {
    case AT_DEVICE_CTRL_SLEEP:
        result = nt26k_sleep (device);
        break;
    case AT_DEVICE_CTRL_WAKEUP:
        result = nt26k_wakeup (device);
        break;
    case AT_DEVICE_CTRL_NET_CONN:
        result = at_device_nt26k_connect_tcp (device);
        break;
    case AT_DEVICE_CTRL_NET_DISCONN:
        result = at_device_nt26k_disconnect_tcp (device);  // 直接删除Socket
        break;
    case AT_DEVICE_CTRL_GET_SIGNAL:
        result = Nt26k_Get_Signal_Info (device);
        break;
    case AT_DEVICE_CTRL_RESET:
        result = Nt26k_Reset (device);
        break;
    case AT_DEVICE_CTRL_POWER_ON:
        result = nt26k_power_on (device);
        break;
    case AT_DEVICE_CTRL_POWER_OFF:
        result = nt26k_power_off (device);
        break;
    case AT_DEVICE_RF_CLOSE:
        result = nt26k_Close_Rf(device);
        break;
    case AT_DEVICE_CTRL_LOW_POWER:
    case AT_DEVICE_CTRL_SET_WIFI_INFO:
    case AT_DEVICE_CTRL_GET_GPS:
    case AT_DEVICE_CTRL_GET_VER:
        LOG_W ("not support the control command(%d).", cmd);
        break;
    default:
        LOG_E ("input error control command(%d).", cmd);
        break;
    }

    return result;
}

int Time_Calibration (struct at_device *device)
{
    at_response_t resp = RT_NULL;
    RT_ASSERT (device);
    resp = at_create_resp (64, 2, rt_tick_from_millisecond (2000));
    if (resp == RT_NULL)
    {
        LOG_E ("no memory for resp create.");
        at_delete_resp (resp);
        return RT_ERROR;
    }
    if (at_obj_exec_cmd (device->client, resp, "AT+CCLK?") == RT_EOK)
    {
        TsRtcDateTime rtc_dt;
        ;  // 网络时间
        int year, mounth, days, hous, min, sec;
        /*+CCLK:24/11/12,06:08:19+32*/
        if (at_resp_parse_line_args_by_kw (resp, "+CCLK:", "+CCLK: \"%d/%d/%d,%d:%d:%d+32\"", &year, &mounth, &days, &hous, &min, &sec) > 0)
        {
            rtc_dt.year = (2000 + year);
            rtc_dt.month = mounth;
            rtc_dt.day = days;
            rtc_dt.hour = hous;  // 此时为零区时间，需要转化为东八区
            rtc_dt.minute = min;
            rtc_dt.second = sec;
            Time_Zone_Conversion (&rtc_dt);  // 时区设置
            rtc_dt.week = RTC_GetWeek (rtc_dt.year, rtc_dt.month, rtc_dt.day);
            RTC_SetTime (rtc_dt.year, rtc_dt.month, rtc_dt.day,
                         rtc_dt.hour, rtc_dt.minute, rtc_dt.second);  // 设置时间
            LOG_I ("RTC时间: %04d-%02d-%02d %02d:%02d:%02d \n",
                   rtc_dt.year, rtc_dt.month, rtc_dt.day, rtc_dt.hour, rtc_dt.minute, rtc_dt.second);
            at_delete_resp (resp);
            return RT_EOK;
        }
    }
    at_delete_resp (resp);
    return RT_ERROR;
}

const struct at_device_ops nt26k_device_ops =
    {
        nt26k_init,
        nt26k_deinit,
        nt26k_control,
};

static int nt26k_device_class_register (void)
{
    struct at_device_class *class = RT_NULL;

    class = (struct at_device_class *)rt_calloc (1, sizeof (struct at_device_class));
    if (class == RT_NULL)
    {
        LOG_E ("no memory for device class create.");
        rt_free (class);
        return -RT_ENOMEM;
    }
    class->device_ops = &nt26k_device_ops;

    return at_device_class_register (class, AT_DEVICE_CLASS_NT26K);
}

INIT_DEVICE_EXPORT(nt26k_device_class_register);
#endif  //! IOT_MODULE_SWITCH

#endif  /* AT_DEVICE_USING_NT26K */
