/***************************************************************************** * 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 * *

© COPYRIGHT(c) 2023 LandPower

* * 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 #include #include #include #include #include #include #include #include #include #include #include #include /* 私有头文件 */ #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, ¶m); 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, ¶m); 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, ¶m); 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 ****/