/*****************************************************************************
 * file    lib/process/ca_collect.c  
 * author  YuLiang
 * version 1.0.0
 * date    28-Nov-2023
 * brief   This file provides all the collect related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2023 LandPower</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of LandPower nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef CFG_DEV_TYPE_LAND_CA
/* 标准 C 库头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <linux/serial.h>
#include <asm-generic/ioctls.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/msg.h>
#include <arpa/inet.h>
#include <termios.h>
#include <ifaddrs.h>

/* 私有头文件 */
#include "cmd.h"
#include "hwgpio.h"

#include "ca_collect.h"
#include "ca_db.h"
#include "ca_dbg.h"
#include "ca_land.h"
#include "ca_mqtt.h"

/* Private define ------------------------------------------------------------*/
#define CA_COLL_USART_NAME "ttymxc4"
#define CA_COLL_GPIO_RS485_TX_EN 13
#define CA_COLL_GPIO_RS485_TX_EN1 15
#define CA_COLL_GPIO_4G_PW_EN 78
#define CA_COLL_GPIO_4G_RST 70
#define CA_COLL_LOOP_INR 3
#define CA_COLL_BUF_LEN 1512
#define CA_COLL_CRC32_LEN 4
#define CA_COLL_ERR_CNT_MAX 9
#define CA_COLL_SEND_ERR_MAX 3
#define CA_COLL_SHIQUCHA (28800)
#define CA_COLL_CABLE_FILE "cableMonitor.bak"
#define CA_COLL_4G_FILE_MAX 256
#define CA_COLL_4G_MODE_MAX 128
#define CA_COLL_APN_NAME "3gnet"
#define CA_COLL_LAND_IP "192.168.1.20"
#define CA_COLL_LAND_PORT 9456

#define CA_COLL_RS485_TX_EN(_v_) \
    do { \
        gpio_val_set(ca_coll_ctrl.gpio_rs485_tx_en, _v_); \
        gpio_val_set(ca_coll_ctrl.gpio_rs485_tx_en1, _v_); \
    } while(0)

#define CA_COLL_4G_PW_EN(_v_) \
    do { \
        gpio_val_set(ca_coll_ctrl.gpio_4g_pw, _v_); \
    } while(0)

#define CA_COLL_4G_RST(_v_) \
    do { \
        gpio_val_set(ca_coll_ctrl.gpio_4g_rst, _v_); \
    } while(0)


/* Private typedef -----------------------------------------------------------*/
/* 用于命令行模式节点注册配置保存函数 */
typedef int ca_coll_rs485_cmd_save_config_f(vty_t*, uint8_t);

/* 端口节点配置优先级 */
typedef enum
{
    CA_COLL_RS485_CMD_PRI = 0,
    CA_COLL_RS485_CMD_PRI_COUNT
} CA_COLL_RS485_CMD_PRI_E;

/* 命令类型. */
enum CA_COLL_CMD_TYPE
{
    CA_COLL_CT_REQUEST = 1,             // 通用请求
    CA_COLL_CT_REPLY = 2,               // 通用回复
    CA_COLL_CT_PRV_REQUEST = 128,       // 私有请求
    CA_COLL_CT_PRV_REPLY = 129,         // 私有回复
};

/* 共有命令字. */
enum CA_COLL_CMD
{
    CA_COLL_C_DEV_INFO = 1,             // 获取设备信息
    CA_COLL_C_DEV_INFO_SET = 2,         // 设置设备信息
    CA_COLL_C_RESET = 3,                // 重启
    CA_COLL_C_DEFAULT = 4,              // 回复默认配置
    CA_COLL_C_UPDATE_APP = 5,           // 升级 APP
    CA_COLL_C_UPDATE_IAP = 6,           // 升级 IAP
    CA_COLL_C_DEV_CONFIG = 7,           // 获取设备配置
    CA_COLL_C_DEV_CONFIG_SET = 8,       // 设置设备配置
    CA_COLL_C_UPDATE_APP_RT = 9,        // 升级 APP 结果回复
    CA_COLL_C_KEEPALIVE = 10            // 保活
};

/* 私有命令字. */
enum CA_COLL_CM_CMD
{
    CA_COLL_PRV_ADJ_INFO = 1,           // 校准信息获取
    CA_COLL_PRV_REAL_DATA = 2,          // 实时数据获取
    CA_COLL_PRV_POWER_FRE = 3,          // 工频数据获取
    CA_COLL_PRV_ADJ_INFO_SET = 4,       // 校准信息设置
    CA_COLL_PRV_ADJ_AUTO = 5,           // 自动校准
    CA_COLL_PRV_WAVE = 6,               // 高频数据获取
    CA_COLL_PRV_DEV_STATE = 7,          // 设备状态获取
    CA_COLL_PRV_WAVE_COL = 8,           // 手动高频采样
    CA_COLL_PRV_WAVE_CAL = 9,           // 高频系数计算
    CA_COLL_PRV_WAVE_MAX = 10,          // 高频采样最大值
    CA_COLL_PRV_LOG = 11,               // log 获取
    CA_COLL_PRV_HISTORY_DATA = 12,      // 历史数据获取
    CA_COLL_PRV_ADJ_MANUAL = 13,        // 手动校准
    CA_COLL_PRV_CSG_CONFIG = 14,        // 南网配置获取
    CA_COLL_PRV_CSG_CONFIG_SET = 15     // 南网配置下发
};

/* 4G 拨号文件枚举 */
enum CA_COLL_4G_FILE_E
{      
    CA_COLL_4G_FILE_PPP_CONN = 0,       // 本地连接脚本
    CA_COLL_4G_FILE_PPP_CONN_ETC,       // 实际使用的连接脚本
    CA_COLL_4G_FILE_CNT
};

/* 协议头 */
typedef struct
{
    uint16_t len;                       // 报文总长度, 包括报文头, 数据段和报文尾总长度
    uint8_t dev_type_m;                 // 设备主设备号: 有效值 (1-240), 255 表示广播设备号, 表示接收设备无需在意设备号, 其他值暂时预留
    uint8_t dev_type_s;                 // 设备次设备号: 有效值 (1-240), 255 表示广播设备号, 表示接收设备无需在意设备号, 其他值暂时预留
    uint32_t dev_id;                    // 设备 id: 有效值 (1-0xEFFFFFFF), 0xFFFFFFFF 表示广播设备 id, 其他值暂时预留
    uint8_t cmd_type;                   // 命令类型: 有效值(1-240), (1-120) 为共有命令类型, (121-240) 为私有命令类型, 其他值暂时预留
    uint8_t cmd;                        // 命令: 有效值(1-240), 其他值暂时预留
    uint16_t pkt_id;                    // 报文 id, 设备启动后的第一条命令为 1, 依次累加
    uint8_t reserve[8];                 // 预留位
} ca_coll_proto_head_t;

/* 连续报文头 */
typedef struct
{
    uint32_t index;                     // 报文索引
    uint32_t len;                       // 报文长度
} ca_coll_proto_mul_head_t;

/* 联络报文 */
typedef struct
{
    uint32_t utc;                       // UTC 时间戳
    uint16_t interval;                  // 环流采集间隔, 单位: m
    uint8_t wave_force;                 // 强制高频采样: 0 - 否 1 - 是
    uint8_t wave_interval;              // 高频采样间隔, 单位: h
    uint16_t threshold;                 // 工频采样阈值, 单位: A
    uint16_t wave_threshold;            // 高频采样阈值, 单位: mv
    uint8_t is_updata;                  // 是否升级设备 APP: 0 - 否 1 - 是
    uint8_t main_cable;                 // 主缆 ID (0-6) 默认 5
    uint8_t normal_sleep;               // 正常模式是否休眠: 0 - 否 1 - 是默认 0
    uint8_t is_voltage_col;             // 是否开启电流采集
    uint8_t is_temp_col;                // 是否开启温度震度
    uint8_t is_wave_col;                // 是否开启高频录波
    uint8_t reserve[2];                 // 4 byte 对齐保留
} ca_coll_proto_contact_t;

/* 采集模块属性结构体 */
typedef struct
{
    char buf_tx[CA_COLL_BUF_LEN];       // 发报文 buf
    char buf_rx[CA_COLL_BUF_LEN];       // 收报文 buf
    char cmd_buf[CA_COLL_BUF_LEN];      // 收到的完整报文 buf
    uint16_t pkt_id;                    // 报文索引
    uint8_t cmd_type;                   // 当前处理的命令类型
    uint8_t cmd;                        // 当前处理的命令
    ca_coll_dev_info_t dev_info;        // 环流信息
    ca_coll_dev_cfg_t dev_cfg;          // 环流设备配置
    ca_coll_dev_data_t dev_data;        // 当前环流数据
    ca_coll_cfg_t cfg;                  // 配置
    ca_coll_state_t state;              // 状态
    int32_t gpio_rs485_tx_en;           // RS485 tx enable gpio
    int32_t gpio_rs485_tx_en1;          // RS485 tx enable gpio
    int32_t gpio_4g_pw;                 // 4G power enable gpio
    int32_t gpio_4g_rst;                // 4G reset gpio
    pthread_mutex_t mutex;              // 收发线程同步使用.
} ca_coll_ctrl_t;

/* 4G 文件备份结构体 */
typedef struct
{
    char source[CA_COLL_4G_FILE_MAX];   // 备份文件
    char dest[CA_COLL_4G_FILE_MAX];     // 实际文件
    char mode[CA_COLL_4G_MODE_MAX];     // 文件权限
} ca_coll_4g_file_t;

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
ca_coll_ctrl_t ca_coll_ctrl;            // 环流采集全局控制结构
/* 串口配置命令行节点 */
cmd_node_t ca_coll_rs485_node =
{
    COLL_RS485_NODE,
    CONFIG_NODE,
    1,
    NULL,
};

/* 4G 文件备份列表 */
ca_coll_4g_file_t ca_coll_4g_file[CA_COLL_4G_FILE_CNT] =
{
    {"/home/Cable/bak/quectel-chat-connect", "/home/Cable/quectel-chat-connect", "600"},
    {"/home/Cable/quectel-chat-connect", "/etc/ppp/peers/quectel-chat-connect", "600"}
};

/* Private function prototypes -----------------------------------------------*/
/* description: 配置主机采样间隔
   param:
   return: CMD_XXX */
CMD(ca_coll_inr,
    ca_coll_inr_cmd,
    "collect interval <1-86400>",
    "Collect\n"
    "Collect interval\n"
    "Second\n")
{
    ca_coll_ctrl.cfg.coll_inr = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 4g 功能使能
   param:
   return: CMD_XXX */
CMD(ca_coll_4g,
    ca_coll_4g_cmd,
    "collect 4g (enable|disable)",
    "Collect\n"
    "4G\n"
    "Enable\n"
    "Disable\n")
{
    if (0 == strncmp(argv[0], "e", 1))
    {
        ca_coll_ctrl.cfg.is_4G = TRUE;
    }
    else
    {
        ca_coll_ctrl.cfg.is_4G = FALSE;
    }
    
    return CMD_SUCCESS;
}

/* description: 4g apn 配置
   param:
   return: CMD_XXX */
CMD(ca_coll_apn,
    ca_coll_apn_cmd,
    "collect apn WORD",
    "Collect\n"
    "VPN\n"
    "Name\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;

    snprintf(cfg->apn, CA_COLL_VPN_LEN, "%s", argv[0]);
    
    return CMD_SUCCESS;;
}

/* description: 朗德私有协议使能
   param:
   return: CMD_XXX */
CMD(ca_coll_land,
    ca_coll_land_cmd,
    "collect land (enable|disable)",
    "Collect\n"
    "LandPower protocol\n"
    "Enable\n"
    "Disable\n")

{
    if (0 == strncmp(argv[0], "e", 1))
    {
        ca_coll_ctrl.cfg.is_land = TRUE;
    }
    else
    {
        ca_coll_ctrl.cfg.is_land = FALSE;
    }
    
    return CMD_SUCCESS;
}

/* description: 朗德私有协议服务器地址配置
   param:
   return: CMD_XXX */
CMD(ca_coll_land_server_set,
    ca_coll_land_server_set_cmd,
    "collect land server A.B.C.D <1-65535>",
    "Collect\n"
    "LandPower protocol"
    "Server\n"
    "IPv4 address\n"
    "Server port\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;
    
    snprintf(cfg->land_ip, INET_ADDRSTRLEN, argv[0]);
    cfg->land_port = strtol((char*)argv[1], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 环流采样间隔
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_inr,
    ca_coll_dev_inr_cmd,
    "device interval <1-1440>",
    "Device\n"
    "Device interval\n"
    "Minute\n")
{
    ca_coll_ctrl.dev_cfg.interval = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 配置环流工频采样阈值
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_thr,
    ca_coll_dev_thr_cmd,
    "device threshold <1-5000>",
    "Device\n"
    "Device threshold\n"
    "A\n")
{
    ca_coll_ctrl.dev_cfg.threshold = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 配置高频采样间隔
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_wave_inr,
    ca_coll_dev_wave_inr_cmd,
    "device wave interval <1-255>",
    "Device\n"
    "Device wave\n"
    "Device wave threshold\n"
    "Hour\n")
{
    ca_coll_ctrl.dev_cfg.wave_interval = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 配置高频采样阈值
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_wave_thr,
    ca_coll_dev_wave_thr_cmd,
    "device wave threshold <1-1000>",
    "Device\n"
    "Device wave\n"
    "Device wave threshold\n"
    "mA\n")
{
    ca_coll_ctrl.dev_cfg.wave_threshold = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 配置环流主缆 id
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_main,
    ca_coll_dev_main_cmd,
    "device main cable <0-6>",
    "Device\n"
    "Device main\n"
    "Device main cable\n"
    "mA\n")
{
    ca_coll_ctrl.dev_cfg.main_cable = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 配置环流电压采集使能
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_voltage,
    ca_coll_dev_voltage_cmd,
    "device voltage (enable|disable)",
    "Device\n"
    "Device voltage\n"
    "Enable\n"
    "Disable\n")
{
    if (0 == strncmp(argv[0], "e", 1))
    {
        ca_coll_ctrl.dev_cfg.is_voltage_col = TRUE;
    }
    else
    {
        ca_coll_ctrl.dev_cfg.is_voltage_col = FALSE;
    }
    
    return CMD_SUCCESS;
}

/* description: 配置环流温度采集使能
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_temp,
    ca_coll_dev_temp_cmd,
    "device temperature (enable|disable)",
    "Device\n"
    "Device temperature\n"
    "Enable\n"
    "Disable\n")
{
    if (0 == strncmp(argv[0], "e", 1))
    {
        ca_coll_ctrl.dev_cfg.is_temp_col = TRUE;
    }
    else
    {
        ca_coll_ctrl.dev_cfg.is_temp_col = FALSE;
    }
    
    return CMD_SUCCESS;
}

/* description: 配置高频采集使能
   param:
   return: CMD_XXX */
CMD(ca_coll_dev_wave,
    ca_coll_dev_wave_cmd,
    "device wave (enable|disable)",
    "Device\n"
    "Device wave\n"
    "Enable\n"
    "Disable\n")
{
    if (0 == strncmp(argv[0], "e", 1))
    {
        ca_coll_ctrl.dev_cfg.is_wave_col = TRUE;
    }
    else
    {
        ca_coll_ctrl.dev_cfg.is_wave_col = FALSE;
    }
    
    return CMD_SUCCESS;
}

/* description: 朗德私有协议使能
   param:
   return: CMD_XXX */
CMD(ca_coll_mqtt_enable_set,
    ca_coll_mqtt_enable_set_cmd,
    "mqtt (enable|disable)",
    "MQTT\n"
    "Enable\n"
    "Disable\n")

{
    if (0 == strncmp(argv[0], "e", 1))
    {
        ca_coll_ctrl.cfg.is_MQTT = TRUE;
    }
    else
    {
        ca_coll_ctrl.cfg.is_MQTT = FALSE;
    }
    
    return CMD_SUCCESS;
}

/* description: 进入 RS485 端口模式
   param:
   return: CMD_XXX */
CMD(ca_coll_rs485_terminal,
    ca_coll_rs485_terminal_cmd,
    "interface rs485 collect",
    "Interface\n"
    "RS485\n"
    "Collect\n")
{
    uint8_t index = 0;

    ca_coll_rs485_node.param_num = index;
    snprintf(ca_coll_rs485_node.prompt, CA_COLL_USART_NAME_LEN, "%%s(interface rs485 coll)# ");
    
    vty->node = COLL_RS485_NODE;
    
    return CMD_SUCCESS;
}

/* description: 配置 RS485 设备
   param:
   return: CMD_XXX */
CMD(ca_coll_rs485_dev,
    ca_coll_rs485_dev_cmd,
    "device WORD",
    "RS485 device\n"
    "Device name\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;

    snprintf(cfg->dev, CA_COLL_USART_NAME_LEN, "%s", argv[0]);
    
    return CMD_SUCCESS;
}

/* description: 配置 RS485 波特率
   param:
   return: CMD_XXX */
CMD(ca_coll_rs485_baud,
    ca_coll_rs485_baud_cmd,
    "baud <2400-921600>",
    "RS485 baud\n"
    "baud rate\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;
    int32_t baud = 0;

    baud = strtol(argv[0], NULL, 10);
    switch(baud)
    {
    case 2400:
    case 4800:
    case 9600:
    case 19200:
    case 38400:
    case 57600:
    case 115200:
    case 460800:
    case 921600:
        break;
    default:
        return CMD_ERR_NO_MATCH;
    }

    cfg->baud = baud;
    
    return CMD_SUCCESS;
}

/* description: 配置 RS485 数据位
   param:
   return: CMD_XXX */
CMD(ca_coll_rs485_bits,
    ca_coll_rs485_bits_cmd,
    "bits <7-8>",
    "RS485 bits\n"
    "Bits number\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;

    cfg->bits = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}

/* description: 配置 RS485 奇偶校验位
   param:
   return: CMD_XXX */
CMD(ca_coll_rs485_parity,
    ca_coll_rs485_parity_cmd,
    "parity (n|o|e)",
    "RS485 parity\n"
    "None\n"
    "Odd\n"
    "Even\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;

    cfg->parity = argv[0][0];
    
    return CMD_SUCCESS;
}


/* description: 配置 RS485 停止位
   param:
   return: CMD_XXX */
CMD(ca_coll_rs485_stop,
    ca_coll_rs485_stop_cmd,
    "stop <1-2>",
    "RS485 stop\n"
    "Stop number\n")
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;

    cfg->stop = strtol(argv[0], NULL, 10);
    
    return CMD_SUCCESS;
}


/* description: 显示环流设备信息
   param:
   return: CMD_XXX */
CMD(ca_coll_show_device,
    ca_coll_show_device_cmd,
    "show device",
    "Show\n"
    "Device\n")
{
    ca_coll_show_dev();
    
    return CMD_SUCCESS;
}


/* description: 显示当前环流数据
   param:
   return: CMD_XXX */
CMD(ca_coll_show_device_data,
    ca_coll_show_device_data_cmd,
    "show device data",
    "Show\n"
    "Device\n"
    "Data\n")
{
    ca_coll_show_dev_data();
    
    return CMD_SUCCESS;
}


/* description: 显示当前环流状态
   param:
   return: CMD_XXX */
CMD(ca_coll_show_device_state,
    ca_coll_show_device_state_cmd,
    "show device state",
    "Show\n"
    "Device\n"
    "Data\n")
{
    ca_coll_show_dev_state();
    
    return CMD_SUCCESS;
}

/* Internal functions --------------------------------------------------------*/
/* description: 获取线程超时锁
   param: sec - 秒
          nsec - 纳秒
   return: E_XXX */
int32_t _ca_coll_lock_timeout(uint32_t sec, uint32_t nsec)
{
    struct timespec time_out;
    
    clock_gettime(CLOCK_REALTIME, &time_out);
    time_out.tv_sec += sec;
    time_out.tv_nsec += nsec;
    if (time_out.tv_nsec >= 1000000000)
    {
        time_out.tv_nsec -= 1000000000;
        time_out.tv_sec++;
    }
    
    return pthread_mutex_timedlock(&ca_coll_ctrl.mutex, &time_out);
}

/* description: 释放线程超时锁
   param:
   return: */
void _ca_coll_unlock(void)
{
    pthread_mutex_unlock(&ca_coll_ctrl.mutex);
}

/* description: 初始化报文头
   param: buf - 数据缓冲
          len - 报文长度, 不包含校验和
          cmd_type - 命令类型
          cmd - 命令字
   return: */
void _ca_coll_pkt_head_init(char *buf, uint16_t len, uint8_t cmd_type, uint8_t cmd)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)buf;
    
    head->len = len;
    head->dev_type_m = ca_coll_ctrl.dev_info.type_m;
    
    if (!ca_coll_ctrl.dev_info.type_s)
    {
        head->dev_type_s = 0xFF;
    }
    else
    {
        head->dev_type_s = ca_coll_ctrl.dev_info.type_s;
    }
    
    if (!ca_coll_ctrl.dev_info.id)
    {
        head->dev_id = 0xFFFFFFFF;
    }
    else
    {
        head->dev_id = ca_coll_ctrl.dev_info.id;
    }
    
    head->cmd_type = cmd_type;
    head->cmd = cmd;
    head->pkt_id = ca_coll_ctrl.pkt_id;
}

/* description: 报文发送
   param: 
   return: E_XXX */
int32_t _ca_coll_pkt_send(void)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)ca_coll_ctrl.buf_tx;
    ca_coll_proto_mul_head_t *m_head = (ca_coll_proto_mul_head_t*)(ca_coll_ctrl.buf_tx + sizeof(ca_coll_proto_head_t));
    uint16_t error_cnt = 0;
    uint16_t len = 0;
    uint32_t byte = 0;

    /* 超时重发 3 次 */
    while(error_cnt < CA_COLL_SEND_ERR_MAX)
    {
        /* 发送数据 */
        len = head->len + CA_COLL_CRC32_LEN;
        CA_COLL_RS485_TX_EN(1);
        byte = write(ca_coll_ctrl.cfg.fd, ca_coll_ctrl.buf_tx, len);
        usleep(byte * 90);
        CA_COLL_RS485_TX_EN(0);

        if (byte < 0)
        {
            DBG(DBG_M_CA_COLL_ERR, "COLL send ERR %d!\r\n", byte);
            error_cnt++;
            continue;
        }

        /* 调试打印 */
        DBG(DBG_M_CA_COLL, "COLL send(%d): %d %d %d %d\r\n", len, time(NULL), head->cmd_type, head->cmd, m_head->index);
        if (dbg_stat_get(DBG_M_CA_COLL))
        {
            buf_print(ca_coll_ctrl.buf_tx, len > 32 ? 32 : len);
        }

        /* 等待命令完成. */
        if (_ca_coll_lock_timeout(1, 0) != 0)
        {
            DBG(DBG_M_CA_COLL_ERR, "COLL send timeout ERR!\r\n");
            error_cnt++;
            continue;
        }

        /* 如果前 CA_COLL_ERR_CNT_MAX 发送失败, 这次就是恢复连接 */
        if (ca_coll_ctrl.state.send_err_cnt >= CA_COLL_ERR_CNT_MAX)
        {
            log_err(LOG_COLL, "Cable device reconnect!");
        }

        /* 发送完成退出 */
        ca_coll_ctrl.state.send_err_cnt = 0;
        return E_NONE;
    }

    /* 错误计数处理 */
    if (CA_COLL_ERR_CNT_MAX == ca_coll_ctrl.state.send_err_cnt)
    {
        ca_coll_ctrl.state.send_err_cnt++;
        log_err(LOG_COLL, "Cable device is disconnect!");
    }
    else if(ca_coll_ctrl.state.send_err_cnt <= (CA_COLL_ERR_CNT_MAX + 1))
    {
        ca_coll_ctrl.state.send_err_cnt++;
    }
    
    return E_TIMEOUT;
}

/* description: 开机联络报文发送
   param: 
   return: E_XXX */
int32_t _ca_coll_pkt_contact(void)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)ca_coll_ctrl.buf_tx;
    ca_coll_proto_contact_t *data = (ca_coll_proto_contact_t*)(ca_coll_ctrl.buf_tx + sizeof(ca_coll_proto_head_t));
    uint32_t *crc = NULL;

    /* 封装报文头 */
    _ca_coll_pkt_head_init(ca_coll_ctrl.buf_tx, sizeof(ca_coll_proto_head_t) + sizeof(ca_coll_proto_contact_t), CA_COLL_CT_REQUEST, CA_COLL_C_DEV_INFO);

    /* 封装数据 */
    data->utc = time(NULL) - CA_COLL_SHIQUCHA;
    data->interval = ca_coll_ctrl.dev_cfg.interval;
    data->wave_force = FALSE;
    data->wave_interval = ca_coll_ctrl.dev_cfg.wave_interval;
    data->threshold = ca_coll_ctrl.dev_cfg.threshold;
    data->wave_threshold = ca_coll_ctrl.dev_cfg.wave_threshold;
    data->is_updata = FALSE;
    data->main_cable = ca_coll_ctrl.dev_cfg.main_cable;
    data->normal_sleep = ca_coll_ctrl.dev_cfg.normal_sleep;
    data->is_voltage_col = ca_coll_ctrl.dev_cfg.is_voltage_col;
    data->is_temp_col = ca_coll_ctrl.dev_cfg.is_temp_col;
    data->is_wave_col = ca_coll_ctrl.dev_cfg.is_wave_col;

    /* 计算校验和 */
    crc = (uint32_t*)(ca_coll_ctrl.buf_tx + head->len);
    *crc = crc32(ca_coll_ctrl.buf_tx, head->len);

    /* 发送报文, 先置标志位, 再发送数据 */
    ca_coll_ctrl.cmd_type = CA_COLL_CT_REPLY;
    ca_coll_ctrl.cmd = CA_COLL_C_DEV_INFO;
    LD_E_RETURN(DBG_M_CA_COLL_ERR, _ca_coll_pkt_send());

    return E_NONE;
}

/* description: 请求实时数据报文发送
   param: 
   return: E_XXX */
int32_t _ca_coll_pkt_realdata(void)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)ca_coll_ctrl.buf_tx;
    ca_land_state_t* state = ca_land_state_get();
    ca_mqtt_state_t *ca_mqtt_state = ca_mqtt_state_get();
    uint32_t *crc = NULL;

    /* 封装报文头 */
    _ca_coll_pkt_head_init(ca_coll_ctrl.buf_tx, sizeof(ca_coll_proto_head_t), CA_COLL_CT_PRV_REQUEST, CA_COLL_PRV_REAL_DATA);

    /* 计算校验和 */
    crc = (uint32_t*)(ca_coll_ctrl.buf_tx + head->len);
    *crc = crc32(ca_coll_ctrl.buf_tx, head->len);

    /* 发送报文, 先置标志位, 再发送数据 */
    ca_coll_ctrl.cmd_type = CA_COLL_CT_PRV_REPLY;
    ca_coll_ctrl.cmd = CA_COLL_PRV_REAL_DATA;
    LD_E_RETURN(DBG_M_CA_COLL_ERR, _ca_coll_pkt_send());

    /* 通知向服务器发送数据 */
    if (ca_coll_ctrl.cfg.is_land)
    {
        state->is_send_data = TRUE;
    }

    if (ca_mqtt_state->is_connect)
    {
        ca_mqtt_data_report(&ca_coll_ctrl.dev_data);
    }

    return E_NONE;
}

/* description: APP 升级报文发送
   param: 
   return: E_XXX */
int32_t _ca_coll_pkt_update(void)
{
    char *buf = ca_coll_ctrl.buf_tx;
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)buf;
    ca_coll_proto_mul_head_t *head_m = (ca_coll_proto_mul_head_t*)(buf + sizeof(ca_coll_proto_head_t));
    char *data = buf + sizeof(ca_coll_proto_head_t) + sizeof(ca_coll_proto_mul_head_t);
    uint32_t *crc = NULL;
    int32_t len = 0;

    /* 打开升级文件描述符 */
    if (ca_coll_ctrl.state.fd_read <= 0)
    {
        ca_coll_ctrl.state.fd_read = open(CA_COLL_CABLE_FILE, O_RDONLY, 0777);
        if (ca_coll_ctrl.state.fd_read < 0)
        {
            DBG(DBG_M_CA_COLL_ERR, "Open " CA_COLL_CABLE_FILE " ERROR\r\n");
            return E_SYS_CALL;
        }
    }

    /* 置升级标志位 */
    ca_coll_ctrl.cmd_type = CA_COLL_CT_REPLY;
    ca_coll_ctrl.cmd = CA_COLL_C_UPDATE_APP;

    /* 升级开始 */
    while(1)
    {
        /* 封装报文头 */
        head_m->index = ca_coll_ctrl.state.update_index;
        len = read(ca_coll_ctrl.state.fd_read, data, 1024);
        if (len < 0)
        {
            close(ca_coll_ctrl.state.fd_read);
            ca_coll_ctrl.state.fd_read = 0;
            DBG(DBG_M_CA_COLL_ERR, "read " CA_COLL_CABLE_FILE " ERROR\r\n");
            return E_SYS_CALL;
        }
        head_m->len = len;
        _ca_coll_pkt_head_init(buf, sizeof(ca_coll_proto_head_t) + sizeof(ca_coll_proto_mul_head_t) + len, CA_COLL_CT_REQUEST, CA_COLL_C_UPDATE_APP);

        /* 计算校验和 */
        crc = (uint32_t*)(ca_coll_ctrl.buf_tx + head->len);
        *crc = crc32(ca_coll_ctrl.buf_tx, head->len);

        /* 发送数据 */
        if (_ca_coll_pkt_send() != E_NONE)
        {
            close(ca_coll_ctrl.state.fd_read);
            ca_coll_ctrl.state.fd_read = 0;
            DBG(DBG_M_CA_COLL_ERR, "send " CA_COLL_CABLE_FILE " ERROR\r\n");
            return E_ERROR;
        }

        /* 长度小于 1024 表示是最后一个报文 */
        if (len < 1024)
        {
            close(ca_coll_ctrl.state.fd_read);
            ca_coll_ctrl.state.fd_read = 0;
            break;
        }
    }

    return E_NONE;
}

/* description: 请求 APP 升级结果报文发送
   param: 
   return: E_XXX */
int32_t _ca_coll_pkt_update_rt(void)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)ca_coll_ctrl.buf_tx;
    uint32_t *crc = NULL;

    /* 封装报文头 */
    _ca_coll_pkt_head_init(ca_coll_ctrl.buf_tx, sizeof(ca_coll_proto_head_t), CA_COLL_CT_REQUEST, CA_COLL_C_UPDATE_APP_RT);

    /* 计算校验和 */
    crc = (uint32_t*)(ca_coll_ctrl.buf_tx + head->len);
    *crc = crc32(ca_coll_ctrl.buf_tx, head->len);

    /* 发送报文, 先置标志位, 再发送数据 */
    ca_coll_ctrl.cmd_type = CA_COLL_CT_REPLY;
    ca_coll_ctrl.cmd = CA_COLL_C_UPDATE_APP_RT;
    LD_E_RETURN(DBG_M_CA_COLL_ERR, _ca_coll_pkt_send());

    return E_NONE;
}

/* description: 环流数据发送处理线程
   param: 
   return: */
void *_ca_coll_handle_send(void *arg)
{
    int32_t utc = 0;
    int32_t now = 0;

    while(1)
    {
        /* 每 3s 轮询一次 */
        sleep(CA_COLL_LOOP_INR);

        /* 升级流程 */
        if (ca_coll_ctrl.state.is_update_cable)
        {
            ca_coll_ctrl.state.update_index = 0;
            if (E_NONE == _ca_coll_pkt_update())
            {
                _ca_coll_pkt_update_rt();
            }
            ca_coll_ctrl.state.is_update_cable = FALSE;
            /* 升级完成后等待 60s 再取数据 */
            utc += 60;
        }

        /* 判断采集间隔 */
        now = time(NULL);
        if (abs(now - utc) >= ca_coll_ctrl.cfg.coll_inr)
        {
            utc = now;

            /* 开机联络 */
            _ca_coll_pkt_contact();

            /* 读取数据 */
            _ca_coll_pkt_realdata();
        }
    }

    return NULL;
}

/* description: 环流设备信息报文处理
   param: cmd - 报文缓冲
   return: E_XXX */
int32_t _ca_coll_pkt_dev_info(char *cmd)
{
    ca_coll_dev_info_t *info = (ca_coll_dev_info_t*)(cmd + sizeof(ca_coll_proto_head_t));

    memcpy(&ca_coll_ctrl.dev_info, info, sizeof(ca_coll_dev_info_t));
    
    return E_NONE;
}

/* description: 环流设备升级报文处理
   param:
   return: E_XXX */
int32_t _ca_coll_pkt_update_reply(void)
{
    ca_coll_ctrl.state.update_index++;

    return E_NONE;
}

/* description: 环流设备升级结果报文处理
   param: cmd - 报文缓冲
   return: E_XXX */
int32_t _ca_coll_pkt_update_rt_reply(char *cmd)
{
    int32_t *rt = (int32_t*)(cmd + sizeof(ca_coll_proto_head_t));

    ca_dbg_update_rt_send(*rt);
    
    return E_NONE;
}

/* description: 环流设备数据报文处理
   param: cmd - 报文缓冲
   return: E_XXX */
int32_t _ca_coll_pkt_data(char *cmd)
{
    ca_coll_dev_data_t *data = (ca_coll_dev_data_t*)(cmd + sizeof(ca_coll_proto_head_t));

    memcpy(&ca_coll_ctrl.dev_data, data, sizeof(ca_coll_dev_data_t));
    ca_db_write(CA_DB_T_DATA, &ca_coll_ctrl.dev_data);
    
    return E_NONE;
}

/* description: 环流设备报文格式检查
   param: cmd - 报文缓冲
   return: E_XXX */
int32_t _ca_coll_pkt_check(char *cmd)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)cmd;

    /* 对主次设备号进行识别 */
    if ((head->dev_type_m != 1)
        || (ca_coll_ctrl.dev_info.type_s != 0 && head->dev_type_s != ca_coll_ctrl.dev_info.type_s))
    {
        DBG(DBG_M_CA_COLL_ERR, "Dev type %d:%d error\r\n", head->dev_type_m, head->dev_type_s);
        return E_NOT_FOUND;
    }

    /* 对设备 id 进行识别 */
    if (ca_coll_ctrl.dev_info.id != 0 && head->dev_id != ca_coll_ctrl.dev_info.id)
    {
        DBG(DBG_M_CA_COLL_ERR, "Dev id %x error\r\n", head->dev_id);
        return E_NOT_FOUND;
    }

    /* 验证 CRC32 */
    if (crc32(cmd, head->len) != (*(uint32_t*)(cmd + head->len)))
    {
        DBG(DBG_M_CA_COLL_ERR, "CRC %x:%x error\r\n", crc32(cmd, head->len), *(uint32_t*)(cmd + head->len));
        return E_ERROR;
    }

    /* pkt_id 验证 */
    if (head->pkt_id != ca_coll_ctrl.pkt_id)
    {
        DBG(DBG_M_CA_COLL_ERR, "Pkt id %d:%d error\r\n", ca_coll_ctrl.pkt_id, head->pkt_id);
        return E_ERROR;
    }

    /* cmd 验证 */
    if (head->cmd_type != ca_coll_ctrl.cmd_type
        || head->cmd != ca_coll_ctrl.cmd)
    {
        DBG(DBG_M_CA_COLL_ERR, "Cmd type %d:%d error\r\n", head->cmd_type, head->cmd);
        return E_ERROR;
    }

    return E_NONE;
}

/* description: 环流设备报文处理
   param: cmd - 报文缓冲
   return: E_XXX */
int32_t _ca_coll_pkt_process(char *cmd)
{
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)cmd;
    
    /* 报文头和 CRC 校验 */
    LD_E_RETURN(DBG_M_CA_COLL_ERR, _ca_coll_pkt_check(cmd));

    if (CA_COLL_CT_REPLY == head->cmd_type)
    {
        /* 共有命令处理 */
        switch (head->cmd)
        {
        case CA_COLL_C_DEV_INFO:
            _ca_coll_pkt_dev_info(cmd);
            break;
        case CA_COLL_C_UPDATE_APP:
            _ca_coll_pkt_update_reply();
            break;
        case CA_COLL_C_UPDATE_APP_RT:
            _ca_coll_pkt_update_rt_reply(cmd);
            break;
        default:
            DBG(DBG_M_CA_COLL_ERR, "Cmd %d not found\r\n", head->cmd);
            return E_NOT_FOUND;
        }
    }
    else if (CA_COLL_CT_PRV_REPLY == head->cmd_type)
    {
        /* 私有命令处理. */
        switch (head->cmd)
        {
        case CA_COLL_PRV_REAL_DATA:
            _ca_coll_pkt_data(cmd);
            break;
        default:
            DBG(DBG_M_CA_COLL_ERR, "Cmd %d not found\r\n", head->cmd);
            return E_NOT_FOUND;
        }
    }
    else
    {
        DBG(DBG_M_CA_COLL_ERR, "Cmd type %d not found\r\n", head->cmd_type);
        return E_NOT_FOUND;
    }

    /* 释放锁, 告知发送线程发送完成 */
    _ca_coll_unlock();
    return E_NONE;
}

/* description: 环流数据接收处理线程
   param:
   return: */
void *_ca_coll_handle_recv(void *arg)
{
    static uint8_t state = FALSE;
    static uint32_t utc = 0;
    static uint32_t cmd_buf_index = 0;
    uint32_t now = 0;
    ca_coll_proto_head_t *head = (ca_coll_proto_head_t*)ca_coll_ctrl.cmd_buf;
    ca_coll_proto_mul_head_t *m_head = (ca_coll_proto_mul_head_t*)(ca_coll_ctrl.cmd_buf + sizeof(ca_coll_proto_head_t));
    char *buf = ca_coll_ctrl.buf_rx;
    uint16_t len = 0;

    while(1)
    {
        /* 读取数据 */
        len = read(ca_coll_ctrl.cfg.fd, buf, CA_COLL_BUF_LEN);
        if (len < 0)
        {
            DBG(DBG_M_CA_COLL_ERR, "COLL recv ERR %d!\r\n", len);
            sleep(3);
            continue;
        }
    
        /* 超过 3s 没有收包说明收包超时, 重新开始收包 */
        now = time(NULL);
        if (now - utc > 3)
        {
            cmd_buf_index = 0;
            state = 0;
        }
        utc = now;

        /* 如果收包大于协议报文长度或者大于 buf 长度, 认为报文错误 */
        if (cmd_buf_index + len >= CA_COLL_BUF_LEN
            || (cmd_buf_index >= 2 && (cmd_buf_index + len) > (head->len + CA_COLL_CRC32_LEN)))
        {
            cmd_buf_index = 0;
            state = 0;
            continue;
        }

        /* 将 buf 中的数据解析成命令 */
        memcpy(&ca_coll_ctrl.cmd_buf[cmd_buf_index], buf, len);
        cmd_buf_index += len;

        if (!state)
        {
            if (cmd_buf_index < 2)
            {
                continue;
            }
        
            /* 报文长度不对, 重新收包 */
            if (head->len + CA_COLL_CRC32_LEN > CA_COLL_BUF_LEN)
            {
                cmd_buf_index = 0;
                continue;
            }

            /* 报文长度与收包长度不对称, 等待后续报文 */
            if (cmd_buf_index < head->len + CA_COLL_CRC32_LEN)
            {
                state = TRUE;
                continue;
            }
        }
        else
        {
            /* 报文长度与收包长度不对称, 等待后续报文 */
            if (cmd_buf_index < head->len + CA_COLL_CRC32_LEN)
            {
                continue;
            }

            state = FALSE;
        }

        /* 调试打印 */
        DBG(DBG_M_CA_COLL, "Recv(%d): %d %d %d %d\r\n", cmd_buf_index, utc, head->cmd_type, head->cmd, m_head->index);
        if (dbg_stat_get(DBG_M_CA_COLL))
        {
            buf_print(ca_coll_ctrl.cmd_buf, cmd_buf_index > 32 ? 32 : cmd_buf_index);
            //buf_print(ca_coll_ctrl.cmd_buf, cmd_buf_index);
        }

        /* 收报完成, 数据处理 */
        _ca_coll_pkt_process(ca_coll_ctrl.cmd_buf);
        cmd_buf_index = 0;
    }

    return NULL;
}

/* description: 4G gpio 设置
   param:
   return: E_XXX */
int32_t _ca_coll_4g_gpio_set(void)
{
    int32_t gpio = 0;

    gpio = gpio_export(CA_COLL_GPIO_4G_PW_EN);
    if (gpio < 0)
    {
        DBG(DBG_M_CA_COLL_ERR, "ERROR return %d!\r\n", gpio);
        return E_BAD_PARAM;
    }
    LD_E_RETURN(DBG_M_CA_COLL_ERR, gpio_dir_set(gpio, GPIO_DIR_OUT));
    ca_coll_ctrl.gpio_4g_pw = gpio;

    gpio = gpio_export(CA_COLL_GPIO_4G_RST);
    if (gpio < 0)
    {
        DBG(DBG_M_CA_COLL_ERR, "ERROR return %d!\r\n", gpio);
        return E_BAD_PARAM;
    }
    LD_E_RETURN(DBG_M_CA_COLL_ERR, gpio_dir_set(gpio, GPIO_DIR_OUT));
    ca_coll_ctrl.gpio_4g_rst = gpio;

    return E_NONE;
}

/* description: 4G APN 配置修改
   param: dest - 目标文件
          apn - APN
   return: E_XXX */
int32_t _ca_coll_4g_apn_change(char *dest, char *apn)
{
    int32_t i = 0;
    int32_t num = 0;
    char str[100][128] = {0};
    char linedata[128] = {0};
    FILE *fp = NULL;

    /* 打开文件 */
    fp = fopen(dest, "r");
    if (NULL == fp)
    {
        DBG(DBG_M_CA_COLL_ERR, "Open quectel-chat-connect ERROR\r\n");
        return E_SYS_CALL;
    }

    /* 修改 APN */
    // "OK \rAT+CGDCONT=1,"IP","3gnet",,0,0"
    while(fgets(linedata, sizeof(linedata) - 1, fp))
    {
        if (strstr(linedata, "AT+CGDCONT") != NULL)
        {
            sprintf(str[i],"OK \\rAT+CGDCONT=1,\"IP\",\"%s\",,0,0\n", apn);
        }
        else
        {
            strcpy(str[i], linedata);
        }
        i++;
    }
    fclose(fp);

    /* 回写文件 */
    num = i;
    fp = fopen(dest, "w");
    if (NULL == fp)
    {
        DBG(DBG_M_CA_COLL_ERR, "Open quectel-chat-connect ERROR\r\n");
        return E_SYS_CALL;
    }
    
    for(i = 0; i < num; i++)
    {
        fputs(str[i], fp);
    }
    fflush(fp);
    fclose(fp);
    
    return E_NONE;
}

/* description: 4G 文件备份检查
   param: index - 文件列表索引
   return: */
void _ca_coll_4g_file_backup(int32_t index)
{
    struct stat file_stat;
    char *source = ca_coll_4g_file[index].source;
    char *dest = ca_coll_4g_file[index].dest;
    char *mode = ca_coll_4g_file[index].mode;
    char cmd[128] = {0};
    int fd = -1;
    off_t size = 0;
    int32_t ret = -1;

    /* 查找文件 */
    if (!access(dest, F_OK))
    {
        fd = open(dest, O_RDONLY);
        if (fd != -1)
        {
            ret = fstat(fd, &file_stat);
            if (ret != -1)
            {
                size = file_stat.st_size;
            }
            close(fd);
        }
    }

    /* 文件是否正常 */
    if (size > 16)
    {
        return;
    }

    /* 备份文件 */
    log_err(LOG_COLL, "Backup file, %s!", dest);
    snprintf(cmd, 128, "cp %s %s", source, dest);
    system(cmd);
    snprintf(cmd, 128, "chmod %s %s", mode, dest);
    system(cmd);

    if (CA_COLL_4G_FILE_PPP_CONN == index)
    {
        _ca_coll_4g_apn_change(dest, ca_coll_ctrl.cfg.apn);
    }

    /* 重启设备 */
    reboot_system(LOG_COLL, BOOT_FILE_RECOVER);
}

/* description: 4G 连接监护线程
   param: 
   return: */
void *_ca_coll_handle_4g(void *arg)
{
    struct ifaddrs *ifa = NULL, *ifList = NULL;
    int8_t err_cnt = 0;
    int8_t file_index = 0;
    bool is_found = FALSE;

    /* 4G 初始化 */
    _ca_coll_4g_gpio_set();
    CA_COLL_4G_PW_EN(1);

    while(1)
    {
        /* 备份文件 */
        _ca_coll_4g_file_backup(file_index);
        file_index++;
        if (CA_COLL_4G_FILE_CNT == file_index)
        {
            file_index = 0;
        }

        /* 查询 4G 是否连接 */
        is_found = FALSE;
        if (getifaddrs(&ifList) < 0)
        {
            err_cnt++;
            sleep(20);
            continue;
        }

        for(ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
        {
            if (ifa->ifa_addr == NULL)
            {
                continue;
            }
                
            if  (ifa->ifa_addr->sa_family != AF_INET)
            {
                continue;
            }
            
            if (strncmp(ifa->ifa_name, "ppp0", 4))
            {
                continue;
            }

            is_found = TRUE;
            break;
        }

        freeifaddrs(ifList);

        /* 没有连接, 重新拨号 */
        if (is_found)
        {
            ca_coll_ctrl.state.is_4g_connect = TRUE;
            err_cnt = 0;
            sleep(20);
            continue;
        }
        else
        {
            ca_coll_ctrl.state.is_4g_connect = FALSE;
            DBG(DBG_M_CA_COLL, "4G connect start...\r\n");
            err_cnt++;
            if(err_cnt > 10)
            {
                reboot_system(LOG_COLL, BOOT_4G_ERR);
                sleep(20);
                continue;
            }

            system("killall pppd");
            CA_COLL_4G_RST(1);
            usleep(300*1000);
            CA_COLL_4G_RST(0);
            sleep(20);
            system("pppd call quectel-ppp &");
            sleep(20);
        }
    }

    return NULL;
}

/* description: RS485 GPIO 初始化
   param: 
   return: E_XXX */
int32_t _ca_coll_rs485_gpio_set(void)
{
    int32_t gpio = 0;

    gpio = gpio_export(CA_COLL_GPIO_RS485_TX_EN);
    if (gpio < 0)
    {
        DBG(DBG_M_CA_COLL_ERR, "ERROR return %d!\r\n", gpio);
        return E_BAD_PARAM;
    }
    LD_E_RETURN(DBG_M_CA_COLL_ERR, gpio_dir_set(gpio, GPIO_DIR_OUT));
    ca_coll_ctrl.gpio_rs485_tx_en = gpio;

    gpio = gpio_export(CA_COLL_GPIO_RS485_TX_EN1);
    if (gpio < 0)
    {
        DBG(DBG_M_CA_COLL_ERR, "ERROR return %d!\r\n", gpio);
        return E_BAD_PARAM;
    }
    LD_E_RETURN(DBG_M_CA_COLL_ERR, gpio_dir_set(gpio, GPIO_DIR_OUT));
    ca_coll_ctrl.gpio_rs485_tx_en1 = gpio;

    return E_NONE;
}

/* description: 串口配置通用设置
   param: 
   return: E_XXX */
int32_t _ca_coll_rs485_set(void)
{
    struct termios opt;
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;

    /* 清空串口接收缓冲区 */
    tcflush(cfg->fd, TCIOFLUSH);

    /* 获取串口配置参数 */
    tcgetattr(cfg->fd, &opt);

    opt.c_cflag &= ~(CBAUD);    // 清除数据位设置
    opt.c_cflag &= ~(PARENB);   // 清除校验位设置
    
    opt.c_iflag &= ~(INLCR| ICRNL);
    opt.c_iflag &= ~(IXON | IXOFF| IXANY);
    opt.c_oflag &= ~(OPOST);
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    /* 设置波特率 */
    switch(cfg->baud)
    {
    case 2400: 
        cfsetspeed(&opt, B2400);
        break;

    case 4800:
        cfsetspeed(&opt, B4800);
        break;

    case 9600: 
        cfsetspeed(&opt, B9600);
        break;
    
    case 19200:
        cfsetspeed(&opt, B9600);
        break;
    
    case 38400: 
        cfsetspeed(&opt, B38400);
        break;
    
    case 57600:
        cfsetspeed(&opt, B57600);
        break;
    
    case 115200:
        cfsetspeed(&opt, B115200);
        break;

    case 460800:
        cfsetspeed(&opt, B460800);
        break;

    case 921600:
        cfsetspeed(&opt, B921600);
        break;

    default:
        return E_BAD_PARAM;
    }

    /* 设置数据位 */
    switch(cfg->bits)
    {
        case 7: 
            opt.c_cflag |= CS7;
            break;

        case 8: 
            opt.c_cflag |= CS8;
            break;

        default:
            return E_BAD_PARAM;          
    }

    /* 设置校验位 */
    switch(cfg->parity)
    {
        /* 无奇偶校验 */
        case 'n':
        case 'N': 
            opt.c_cflag &= (~PARENB);   
            break;

        /* 奇校验 */
        case 'o':
        case 'O':  
            opt.c_cflag |= PARODD;
            break;
        
        /* 偶校验 */
        case 'e':
        case 'E':  
            opt.c_cflag |= PARENB;
            opt.c_cflag &= (~PARODD);
            break;

        default:
            return E_BAD_PARAM;
    }

    /* 设置停止位 */
    switch(cfg->stop)
    {
        case 1:
            opt.c_cflag &= ~CSTOPB;
            break;
        case 2:
            opt.c_cflag |= CSTOPB; 
            break;
        default:
            return E_BAD_PARAM;
    }
    
    /* 设置串口 */
    tcsetattr(cfg->fd, TCSANOW, &opt);

    return E_NONE;
}

/* description: 初始化 RS485 串口
   param: 
   return: E_XXX */
int32_t _ca_coll_rs485_open(void)
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;
    char dev[CA_COLL_USART_NAME_LEN] = {0};

    /* 打开驱动 */
    snprintf(dev, CA_COLL_USART_NAME_LEN, "/dev/%s", cfg->dev);
    cfg->fd = open(dev, O_RDWR | O_NOCTTY);
    if (cfg->fd < 0)
    {
        DBG(DBG_M_CA_COLL_ERR, "ERROR at open RS485 return %s!\r\n", safe_strerror(errno));
        cfg->fd = 0;
        return E_BAD_PARAM;
    }

    /* 设置串口参数 */
    if (_ca_coll_rs485_set() != E_NONE)
    {
        close(cfg->fd);
        cfg->fd = 0;
        DBG(DBG_M_CA_COLL_ERR, "ERROR at RS485 config!\r\n");
        return E_BAD_PARAM;
    }

    /* 设置 RS485 GPIO */
    if (_ca_coll_rs485_gpio_set() != E_NONE)
    {
        close(cfg->fd);
        cfg->fd = 0;
        DBG(DBG_M_CA_COLL_ERR, "ERROR at RS485 GPIO config!\r\n");
        return E_BAD_PARAM;
    }

    return E_NONE;
}

/* description: 全局配置保存函数
   param: 
   return: */
int _ca_coll_config_save(vty_t* vty)
{
    vty_out(vty, "collect interval %d%s", ca_coll_ctrl.cfg.coll_inr, VTY_NEWLINE);
    vty_out(vty, "collect 4g %s%s", ca_coll_ctrl.cfg.is_4G ? "enable" : "disable", VTY_NEWLINE);
    vty_out(vty, "collect apn %s%s", ca_coll_ctrl.cfg.apn, VTY_NEWLINE);
    vty_out(vty, "collect land %s%s", ca_coll_ctrl.cfg.is_land ? "enable" : "disable", VTY_NEWLINE);
    vty_out(vty, "collect land server %s %d%s", ca_coll_ctrl.cfg.land_ip, ca_coll_ctrl.cfg.land_port, VTY_NEWLINE);
    vty_out(vty, "!%s", VTY_NEWLINE);

    vty_out(vty, "device interval %d%s", ca_coll_ctrl.dev_cfg.interval, VTY_NEWLINE);
    vty_out(vty, "device threshold %d%s", ca_coll_ctrl.dev_cfg.threshold, VTY_NEWLINE);
    vty_out(vty, "device wave interval %d%s", ca_coll_ctrl.dev_cfg.wave_interval, VTY_NEWLINE);
    vty_out(vty, "device wave threshold %d%s", ca_coll_ctrl.dev_cfg.wave_threshold, VTY_NEWLINE);
    vty_out(vty, "device main cable %d%s", ca_coll_ctrl.dev_cfg.main_cable, VTY_NEWLINE);
    vty_out(vty, "device voltage %s%s", ca_coll_ctrl.dev_cfg.is_voltage_col ? "enable" : "disable", VTY_NEWLINE);
    vty_out(vty, "device temperature %s%s", ca_coll_ctrl.dev_cfg.is_temp_col ? "enable" : "disable", VTY_NEWLINE);
    vty_out(vty, "device wave %s%s", ca_coll_ctrl.dev_cfg.is_wave_col ? "enable" : "disable", VTY_NEWLINE);

    return 1;
}

/* description: 全局配置保存函数
   param: 
   return: */
int32_t _ca_coll_rs485_config_save(vty_t *vty)
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;
    array_t *configs = ca_coll_rs485_node.configs;
    ca_coll_rs485_cmd_save_config_f *func = NULL;
    uint8_t i = 0;

    vty_out(vty, "interface rs485 coll%s", VTY_NEWLINE);

    for(i = 0; i < array_active(configs); i++)
    {
        func = array_lookup(configs, i);
        if (!func)
        {
            continue;
        }
        
        func(vty, 1);
    }

    vty_out(vty, "  device %s%s", cfg->dev, VTY_NEWLINE);
    vty_out(vty, "  baud %d%s", cfg->baud, VTY_NEWLINE);
    vty_out(vty, "  bits %d%s", cfg->bits, VTY_NEWLINE);
    vty_out(vty, "  parity %c%s", cfg->parity, VTY_NEWLINE);
    vty_out(vty, "  stop %d%s", cfg->stop, VTY_NEWLINE);
    vty_out(vty, "!%s", VTY_NEWLINE);

    return E_NONE; 
}

/* Interface functions -------------------------------------------------------*/
/* description: 环流采集程序初始化入口函数
   param:
   return: E_XXX */
int32_t ca_coll_init(void)
{
    ca_coll_cfg_t *cfg = &ca_coll_ctrl.cfg;
    int32_t rv = E_NONE;

    /* 初始化变量 */
    ca_coll_ctrl.pkt_id = 0;
    ca_coll_ctrl.dev_info.type_m = 1;
    snprintf(cfg->dev, CA_COLL_USART_NAME_LEN, CA_COLL_USART_NAME);
    cfg->baud = 115200;
    cfg->bits = 8;
    cfg->parity = 'n';
    cfg->stop = 1;
    cfg->coll_inr = 60;
    cfg->is_4G = FALSE;
    snprintf(cfg->apn, CA_COLL_VPN_LEN, CA_COLL_APN_NAME);
    cfg->is_land = FALSE;
    snprintf(cfg->land_ip, INET_ADDRSTRLEN, CA_COLL_LAND_IP);
    cfg->land_port = CA_COLL_LAND_PORT;

    ca_coll_ctrl.dev_cfg.interval = 1;
    ca_coll_ctrl.dev_cfg.threshold = 1;
    ca_coll_ctrl.dev_cfg.wave_interval = 255;
    ca_coll_ctrl.dev_cfg.wave_threshold = 1000;
    ca_coll_ctrl.dev_cfg.main_cable = 0;
    ca_coll_ctrl.dev_cfg.is_voltage_col = TRUE;
    ca_coll_ctrl.dev_cfg.is_temp_col = FALSE;
    ca_coll_ctrl.dev_cfg.is_wave_col = FALSE;

    /* 注册 RS485 节点. */
    cmd_install_node(&ca_coll_rs485_node, _ca_coll_rs485_config_save);
    ca_coll_rs485_node.prompt = XMALLOC(MTYPE_CA_COLL, CA_COLL_USART_NAME_LEN);
    ca_coll_rs485_node.configs = array_init(CA_COLL_RS485_CMD_PRI_COUNT, MTYPE_CA_COLL);

    cmd_install_element(CONFIG_NODE, &ca_coll_rs485_terminal_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_inr_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_4g_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_apn_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_land_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_land_server_set_cmd);
    
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_inr_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_thr_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_wave_inr_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_wave_thr_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_main_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_voltage_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_temp_cmd);
    cmd_install_element(CONFIG_NODE, &ca_coll_dev_wave_cmd); 

    cmd_install_element(CONFIG_NODE, &ca_coll_mqtt_enable_set_cmd);

    cmd_install_element(COLL_RS485_NODE, &ca_coll_rs485_dev_cmd);
    cmd_install_element(COLL_RS485_NODE, &ca_coll_rs485_baud_cmd);
    cmd_install_element(COLL_RS485_NODE, &ca_coll_rs485_bits_cmd);
    cmd_install_element(COLL_RS485_NODE, &ca_coll_rs485_parity_cmd);
    cmd_install_element(COLL_RS485_NODE, &ca_coll_rs485_stop_cmd);

    cmd_install_element(COMMON_NODE, &ca_coll_show_device_cmd);
    cmd_install_element(COMMON_NODE, &ca_coll_show_device_data_cmd);
    cmd_install_element(COMMON_NODE, &ca_coll_show_device_state_cmd);

    /* 注册配置保存函数 */
    rv = cmd_config_node_config_register(CONFIG_PRI_CA_COLL, _ca_coll_config_save);
    if (rv != E_NONE)
    {
        log_err(LOG_COLL, "Command save register ERROR %d!", rv);
        return rv;
    }

    return E_NONE;
}

/* description: 环流采集程序初始化
   param:
   return: E_XXX */
int32_t ca_coll_init_after(void)
{
    struct sched_param param;
    pthread_attr_t attr;
    pthread_t pid;
    int32_t rv = E_NONE;

    /* 打开串口 */
    if ((rv = _ca_coll_rs485_open()) != E_NONE)
    {
        log_err(LOG_COLL, "Cable can't open rs485 %d!", rv);
        return rv;
    }

    /* 初始化线程锁, 用于发包线程与首保线程同步 */
    pthread_mutex_init(&ca_coll_ctrl.mutex, NULL);
    pthread_mutex_lock(&ca_coll_ctrl.mutex);

    /* 初始化采集发包线程 */
    /* 配置线程RR调度, 优先级 80 */
    pthread_attr_init(&attr);
    param.sched_priority = 80;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, &param);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _ca_coll_handle_send, NULL);
    if (rv != 0)
    {
        log_err(LOG_COLL, "Cable can't create collect pthread %d!", rv);
        return rv;
    }
    else
    {
        thread_m_add("CA_COLL_SEND", pid);
    }
    pthread_attr_destroy(&attr);

    /* 初始化采集收包线程 */
    /* 配置线程RR调度, 优先级 79 */
    pthread_attr_init(&attr);
    param.sched_priority = 79;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, &param);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _ca_coll_handle_recv, NULL);
    if (rv != 0)
    {
        log_err(LOG_COLL, "Cable can't create collect pthread %d!", rv);
        return rv;
    }
    else
    {
        thread_m_add("CA_COLL_RECV", pid);
    }
    pthread_attr_destroy(&attr);

    if (ca_coll_ctrl.cfg.is_4G)
    {
        /* 初始化 4G 线程 */
        /* 配置线程RR调度, 优先级 25 */
        pthread_attr_init(&attr);
        param.sched_priority = 25;
        pthread_attr_setschedpolicy(&attr, SCHED_RR);
        pthread_attr_setschedparam(&attr, &param);
        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
        rv = pthread_create(&pid, &attr, _ca_coll_handle_4g, NULL);
        if (rv != 0)
        {
            log_err(LOG_COLL, "Cable can't create collect pthread %d!", rv);
            return rv;
        }
        else
        {
            thread_m_add("CA_COLL_4G", pid);
        }
        pthread_attr_destroy(&attr);
    }

    return rv;
}

/* description: 设备配置结构体获取
   param:
   return: ca_coll_cfg_t - 设备配置结构体指针 */
ca_coll_cfg_t* ca_coll_cfg_get(void)
{
    return &ca_coll_ctrl.cfg;
}

/* description: 环流信息结构体获取
   param:
   return: ca_coll_dev_info_t - 环流信息结构体指针 */
ca_coll_dev_info_t* ca_coll_cable_info_get(void)
{
    return &ca_coll_ctrl.dev_info;
}

/* description: 环流配置结构体获取
   param:
   return: ca_coll_dev_cfg_t - 环流配置结构体指针 */
ca_coll_dev_cfg_t* ca_coll_cable_cfg_get(void)
{
    return &ca_coll_ctrl.dev_cfg;
}

/* description: 环流数据结构体获取
   param:
   return: ca_coll_dev_data_t - 环流数据结构体指针 */
ca_coll_dev_data_t* ca_coll_cable_data_get(void)
{
    return &ca_coll_ctrl.dev_data;
}

/* description: 设备状态结构体获取
   param:
   return: ca_coll_state_t - 设备状态结构体指针 */
ca_coll_state_t* ca_coll_cable_state_get(void)
{
    return &ca_coll_ctrl.state;
}

/* description: 环流升级使能配置
   param: enable - 使能标志
   return: */
void ca_coll_cable_update_set(bool enable)
{
    ca_coll_ctrl.state.is_update_cable = enable;
}

/* description: 环流信息显示
   param:
   return: */
void ca_coll_show_dev(void)
{
    ca_coll_dev_info_t *info = &ca_coll_ctrl.dev_info;
    
    printh("Boot version %s, compile time is %s\r\n", info->boot_version, info->boot_compile_time);
    printh("Img version %s, compile time is %s\r\n", info->img_version, info->img_compile_time);
    printh("Id: %08x\r\n", info->id);
    printh("Name: %s\r\n", info->name);
    printh("Device type %d:%d\r\n", info->type_m, info->type_s);
    printh("Factory time: %s\r\n\n", info->factory_time);
}

/* description: 环流数据显示
   param:
   return: */
void ca_coll_show_dev_data(void)
{
    ca_coll_dev_data_t *data = &ca_coll_ctrl.dev_data;
    uint8_t i = 0;
        
    printh("VCC %-0.3fv VBAT %-0.3fv VSC %-0.3fv ", data->vin / 1000.0, data->vbat / 1000.0, data->vsc / 1000.0);
    printh("Temperature %-0.1f℃ Run %ds Mode %d\r\n\n", data->temperature / 10.0, data->run_time, data->energy_mode);

    printh("Electricity(v):\r\n");
    for(i = 1; i <= CA_COLL_ADC_CH_SUM - 2; i++)
    {
        printh("CH%d      ", i);
    }
    printh("\r\n");
    for(i = 0; i < CA_COLL_ADC_CH_SUM - 2; i++)
    {
        printh("%-8.3f ", data->elec[i] / 1000.0);
    }
    printh("\r\n\n");

    printh("Sensor(℃) Short %s\r\n", data->sen_short ? "TRUE" : "FALSE");
    printh("CH VA T     X     Y     Z\r\n");
    for(i = 0; i < CA_COLL_SENSOR_SUM; i++)
    {
        printh("%-2d %-2d %-5.1f ", i + 1, data->sen_valid[i], data->sen_temp[i]);
        printh("%-5d %-5d %-5d\r\n", data->sen_x[i], data->sen_y[i], data->sen_z[i]);
    }
    printh("\r\n");
}

/* description: 设备状态显示
   param:
   return: */
void ca_coll_show_dev_state(void)
{
    ca_coll_state_t *state = &ca_coll_ctrl.state;
        
    printh("Connect: %s\r\n", state->send_err_cnt < CA_COLL_ERR_CNT_MAX ? "yes" : "no");
    printh("4G connect: %s\r\n", ca_coll_ctrl.state.is_4g_connect ? "yes" : "no");
    printh("Update state: %s\r\n", state->is_update_cable ? "yes" : "no");
    printh("Update index: %d\r\n\n", state->update_index);
}

#endif
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/

