/***************************************************************************** * file lib/process/ca_land.c * author YuLiang * version 1.0.0 * date 20-Dec-2023 * brief This file provides all the LandPower protocol 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 "cmd.h" #include "process.h" #include "ca_land.h" #include "ca_collect.h" /* Private define ------------------------------------------------------------*/ #define CA_LAND_BUF_SIZE 1512 #define CA_LAND_CMD_BUF_LEN 3024 #define CA_LAND_SHIQUCHA 28800 #define CA_LAND_CMD_REPLY_RD 0xC081 #define CA_LAND_CMD_REQ_ID 0xC401 #define CA_LAND_START_FLAG 0x5555AAAA #define CA_LAND_END_FLAG 0x5AA55AA5 #define CA_LAND_HEAD_LEN (sizeof(ca_land_head_t) + 8) //包头加包尾长度 /* Private macro -------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ /* BEGIN: 以下结构体为私有协议数据 */ #pragma pack(1) typedef struct{ uint32_t StartFlag1; //设备ID 0x40110304=ID=40110304 uint8_t FarmeType; //帧类型: 0x00 定时发送 uint32_t RunSecCnt; //系统运行时间 0x01020304=16909060秒 uint16_t Local_Vbat; //本机电池电压 0x1965= 6505/1000=6.501V int16_t RoomIn_Temp; //本机温度 0x0C02=(3074/10)-273.1=34.3摄氏度 float RMS_Ia; //CH1:A相接地电流 float RMS_Ib; //CH2:B相接地电流 float RMS_Ic; //CH3:C相接地电流 float RMS_In; //CH4: [主缆电流] float RMS_Ground; //CH5: [总接地电流] float RMS_rIa; //CH6:A相运行电流 float RMS_rIb; //CH7:B相运行电流 float RMS_rIc; //CH8:C相运行电流 int16_t RoomOut_Temp; //环境温度 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t I16A_Gx; //A相接头振动X轴 0x6655=(26197-16000=10197mg) uint16_t I16A_Gy; //A相接头振动Y轴 0x6655=(26197-16000=10197mg) uint16_t I16A_Gz; //A相接头振动Z轴 0x6655=(26197-16000=10197mg) uint16_t I16A_Temp1; //A相电缆接头温度 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t I16A_Temp2; //A相电缆表面温度 同上 uint16_t I16B_Gx; //B相接头振动X轴 0x6655=(26197-16000=10197mg) uint16_t I16B_Gy; //B相接头振动Y轴 0x6655=(26197-16000=10197mg) uint16_t I16B_Gz; //B相接头振动Z轴 0x6655=(26197-16000=10197mg) uint16_t I16B_Temp1; //B相接头温度 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t I16B_Temp2; //B相电缆表面温度 同上 uint16_t I16C_Gx; //C相接头振动X轴 0x6655=(26197-16000=10197mg) uint16_t I16C_Gy; //C相接头振动Y轴 0x6655=(26197-16000=10197mg) uint16_t I16C_Gz; //C相接头振动Z轴 0x6655=(26197-16000=10197mg) uint16_t I16C_Temp1; //C相接头温度 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t I16C_Temp2; //C相电缆表面温度 同上 uint8_t Status_AG; //A相姿态告警 0x01 告警 0x00正常 复归 故障 uint8_t Status_BG; //B相姿态告警 0x01 故障 0x00正常 uint8_t Status_CG; //C相姿态告警 0x01故障 0x00正常 uint8_t Status_A_OT; //A相温度超限告警 0x01故障 0x00正常 uint8_t Status_B_OT; //B相温度超限告警 0x01故障 0x00正常 uint8_t Status_C_OT; //C相温度超限告警 0x01故障 0x00正常 uint8_t Status_Bat; //电池电路硬件状态0x01故障 0x00正常 uint8_t Error_AT; //A相温度硬件状态 0x01故障 0x00正常 uint8_t Error_BT; //B相温度硬件状态 0x01故障 0x00正常 uint8_t Error_CT; //C相温度硬件状态 0x01故障 0x00正常 uint8_t Error_AG; //A相ADXL硬件状态 0x01故障 0x00正常 uint8_t Error_BG; //B相ADXL硬件状态 0x01故障 0x00正常 uint8_t Error_CG; //C相ADXL硬件状态 0x01故障 0x00正常 uint8_t Error_ROT; //环境温度硬件状态 0x01故障 0x00正常 uint16_t WaterLevel; //水位 0x20=32cm uint16_t RoomOut_Humidity; //环境湿度 0x0321 = (801/10) = 80.1% uint8_t Error_Humidity; //环境湿度硬件状态 1:告警,0:正常 uint32_t Gas_O2; //氧气浓度 0x000100013= (0x0013)=19 %VOL uint32_t Gas_CH4; //甲烷浓度 同上 uint32_t Gas_H2s; //硫化氢浓度 同上 uint32_t Gas_CO; //一氧化碳浓度 同上 float RMS_Va; //A相电压 float RMS_Vb; //B相电压 float RMS_Vc; //C相电压 uint16_t reserve; //保留 uint16_t endFlag; //值:0X0D 0X0A 结束符 “/r/n” }ca_land_rd_t;//实时数据报文 128 Byte typedef struct{ uint32_t StartFlag1; //设备ID 0x40110304=ID=40110304 uint8_t FarmeType; //帧类型: 0x01 告警发送 uint32_t RunSecCnt; //系统运行时间 0x01020304=16909060秒 uint8_t WaveNum; //当前的录波物理编号:最新的文件 值:[0-127] uint8_t RMS_Ig_Err; //总接地电流告 0x01 告警 0x00正常 uint16_t RMS_Ig_Before; //总接地电流变化前 0x0533=1331/10=133.1A uint16_t RMS_Ig_After; //总接地电流变化后 0x0533=1331/10=133.1A uint8_t RMS_Ia_Err; //A相电流告警 0x01 告警 0x00正常 uint16_t RMS_Ia_Before; //A相电流变化前 0x0533=1331/10=133.1A uint16_t RMS_Ia_After; //A相电流变化后 0x0533=1331/10=133.1A uint8_t Sanke_a_Err; //A相振动告警 0x01 告警 0x00正常 uint16_t Sanke_a_x; //A相X振动值 uint16_t Sanke_a_y; //A相Y振动值 uint16_t Sanke_a_z; //A相Z振动值 uint8_t Temper_a_Err; //A相温度告警 0x01 告警 0x00正常 uint8_t Temper_a_Change; //A相温度变化值 0x02 升高了2度 uint16_t Temper_a_Before; //A相温度前 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t Temper_a_After; //A相温度后 0x0C02=(3074/10)-273.1=34.3摄氏度 uint8_t RMS_Ib_Err; //B相电流告警 0x01 告警 0x00正常 uint16_t RMS_Ib_Before; //B相电流变化前 0x0533=1331/10=133.1A uint16_t RMS_Ib_After; //B相电流变化后 0x0533=1331/10=133.1A uint8_t Sanke_b_Err; //B相振动告警 0x01 告警 0x00正常 uint16_t Sanke_b_x; //B相X振动值 uint16_t Sanke_b_y; //B相Y振动值 uint16_t Sanke_b_z; //B相Z振动值 uint8_t Temper_b_Err; //B相温度告警 0x01 告警 0x00正常 uint8_t Temper_b_Change; //B相温度变化值 0x02 升高了2度 uint16_t Temper_b_Before; //B相温度前 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t Temper_b_After; //B相温度后 0x0C02=(3074/10)-273.1=34.3摄氏度 uint8_t RMS_Ic_Err; //C相电流告警 0x01 告警 0x00正常 uint16_t RMS_Ic_Before; //C相电流变化前 0x0533=1331/10=133.1A uint16_t RMS_Ic_After; //C相电流变化后 0x0533=1331/10=133.1A uint8_t Sanke_c_Err; //C相振动告警 0x01 告警 0x00正常 uint16_t Sanke_c_x; //C相X振动值 uint16_t Sanke_c_y; //C相Y振动值 uint16_t Sanke_c_z; //C相Z振动值 uint8_t Temper_c_Err; //C相温度告警 0x01 告警 0x00正常 uint8_t Temper_c_Change; //C相温度变化值 0x02 升高了2度 uint16_t Temper_c_Before; //C相温度前 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t Temper_c_After; //C相温度后 0x0C02=(3074/10)-273.1=34.3摄氏度 uint16_t endFlag; //值:0X0D 0X0A 结束符 “/r/n” }ca_land_war_t;//告警数据 71Byte typedef struct{ uint32_t StatusA; //24个通道的温度数据的正确性判断 Bit24: 0 开关量正常 1开关量报警 int16_t TempA[24]; //测温带的温度 (I16)/16【原始值】 uint32_t StatusB; int16_t TempB[24]; uint32_t StatusC; int16_t TempC[24]; } ca_land_tmp_t;//测温带数据 156 Byte typedef struct{ uint32_t proVer; //协议版本号 0x00000363 uint32_t utc; ca_land_rd_t rtd; uint32_t reslved1; //调试工具 用来存历史数据条数 ca_land_war_t war; uint8_t reslved2; ca_land_tmp_t mul; uint32_t reslved3; uint32_t powertatus; uint32_t vbat; uint32_t vout; uint32_t batteryStatus; float Gpslatitude; float Gpslongitude; uint8_t PowerFail[8]; //电源故障 485 uint32_t vsc; uint8_t reslved4[104]; } ca_land_date_t;//512Byte #pragma pack() typedef struct{ uint32_t Head; //StartFlag1 0X5555AAAA uint16_t Len; //报文长度:所有字节总长度 uint16_t Type; //报文类型 uint32_t ID; //从机ID: 【ex. 0x40111234 】 uint32_t LifeCnt; //主机每次加1,从机应答收到的值 uint16_t gNum; //报文分组数目:(1-65535); uint16_t gNumIndex; //报文分组编号;0 ~ (gNum-1) } ca_land_head_t;//朗德协议包头 typedef struct{ uint32_t CheckSum; uint32_t EndFlag1; } ca_land_tail_t;//朗德协议包尾 /* END: 以上结构体为私有协议数据 */ /* 私有协议全局控制结构 */ typedef struct{ int fd; // TCP server 监听使用的 socket char buf[CA_LAND_BUF_SIZE]; // 通讯使用收发包 buf ca_land_state_t state; // 协议状态 } ca_land_ctrl_t; ca_land_ctrl_t ca_land_ctrl; // 全局控制结构 /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ /* Internal functions --------------------------------------------------------*/ /* description: 显示当前状态 param: return: CMD_XXX */ CMD(ca_land_show_state, ca_land_show_state_cmd, "show land", "Show\n" "LandPower protocol\n") { ca_land_state_show(); return CMD_SUCCESS; } /* description: 私有协议 socket 属性配置 param: fd - socket 描述符 return: E_XXX */ int32_t _ca_land_socket_set(int fd) { //int keep_alive = 1; //int keep_idle = 60; //int keep_interval = 6; //int keep_count = 10; int keep_time = 30000; struct timeval timeout; #if 0 /* 开启保活, 保活参数表示 60 秒内无交互后, 每隔 6 秒检测一次. 10 次都没得到响应时会断开连接 */ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive))) { DBG(DBG_M_CA_LAND_ERR, "Error setsockopt(SO_KEEPALIVE) failed, return %s!\r\n", safe_strerror(errno)); return E_SYS_CALL; } if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle))) { DBG(DBG_M_CA_LAND_ERR, "Error setsockopt(TCP_KEEPIDLE) failed, return %s!\r\n", safe_strerror(errno)); return E_SYS_CALL; } if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval))) { DBG(DBG_M_CA_LAND_ERR, "Error setsockopt(TCP_KEEPINTVL) failed, return %s!\r\n", safe_strerror(errno)); return E_SYS_CALL; } if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (void *)&keep_count, sizeof(keep_count))) { DBG(DBG_M_CA_LAND_ERR, "Error setsockopt(TCP_KEEPCNT) failed, return %s!\r\n", safe_strerror(errno)); return E_SYS_CALL; } #endif /* 发送超时配置 */ if (setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, (void *)&keep_time, sizeof(keep_time))) { DBG(DBG_M_CA_LAND_ERR, "Error setsockopt(TCP_USER_TIMEOUT) failed, return %s!\r\n", safe_strerror(errno)); return E_SYS_CALL; } /* 发送超时 */ timeout.tv_sec = 30; timeout.tv_usec = 0; if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) { DBG(DBG_M_CA_LAND_ERR, "Error setsockopt(SO_SNDTIMEO) failed, return %s!\r\n", safe_strerror(errno)); return E_SYS_CALL; } return E_NONE; } /* description: 初始化报文头 param: cmd - 命令字 len - 数据长度 return: */ void _ca_land_head_init(uint16_t cmd, int32_t len) { char *pkt = ca_land_ctrl.buf; ca_land_head_t *head = (ca_land_head_t*)pkt; memset(pkt, 0, CA_LAND_BUF_SIZE); head->Head = CA_LAND_START_FLAG; head->Len = CA_LAND_HEAD_LEN + len; head->Type = cmd; head->ID = device_info.dev_id; head->LifeCnt = 1; head->gNum = 1; head->gNumIndex = 0; } /* description: 报文校验和计算 param: data_len - 报文总长度 return: */ void _ca_land_cheak_sum(int32_t data_len) { char *pkt = ca_land_ctrl.buf; uint32_t *temp = (uint32_t*)(pkt + 4); ca_land_tail_t *tail = (ca_land_tail_t*)(pkt + data_len - 8); int32_t i = 0; int32_t len = (data_len - 12) >> 2; // 起始位和报文尾不参与计算 uint32_t cheakSum = 0; for(i = 0; i < len; i++ ) { cheakSum += *temp; temp++; } tail->CheckSum = cheakSum; tail->EndFlag1 = CA_LAND_END_FLAG; } int32_t _ca_land_heart_send(void) { char *pkt = ca_land_ctrl.buf; ca_land_head_t *head = (ca_land_head_t*)pkt; int fd = ca_land_ctrl.fd; /* 初始化报文头 */ _ca_land_head_init(CA_LAND_CMD_REQ_ID, 36); _ca_land_cheak_sum(head->Len); /* 发送数据 */ if (send(fd, pkt, head->Len, 0) < 0) { DBG(DBG_M_CA_LAND_ERR, "LAND send ERR!\r\n"); return E_SYS_CALL; } /* 调试打印 */ DBG(DBG_M_CA_LAND, "LAND send(%d): %d %d\r\n", head->Len, time(NULL)); if (dbg_stat_get(DBG_M_CA_LAND)) { buf_print(pkt, 32); } return E_NONE; } /* description: 数据报文组装 param: data - 环流数据段 return: */ void _ca_land_date(ca_land_rd_t *data) { ca_coll_dev_data_t *dev_data = ca_coll_cable_data_get(); data->StartFlag1 = device_info.dev_id; data->endFlag = 0x0A0D; data->Local_Vbat = dev_data->vbat; data->RoomIn_Temp = dev_data->temperature +2731; data->RunSecCnt = start_time; data->RMS_Ia = dev_data->elec[0] / 1000.0; data->RMS_Ib = dev_data->elec[1] / 1000.0; data->RMS_Ic = dev_data->elec[2] / 1000.0; data->RMS_rIa = dev_data->elec[3] / 1000.0; data->RMS_Ground = dev_data->elec[4] / 1000.0; } /* description: 数据报文发送 param: data - 环流数据段 return: E_XXX */ int32_t _ca_land_real_data_send(void) { char *pkt = ca_land_ctrl.buf; ca_land_head_t *head = (ca_land_head_t*)pkt; ca_land_date_t *rd = (ca_land_date_t*)(pkt + sizeof(ca_land_head_t)); ca_coll_dev_data_t *dev_data = ca_coll_cable_data_get(); time_t now = time(NULL); int fd = ca_land_ctrl.fd; /* 初始化报文头 */ _ca_land_head_init(CA_LAND_CMD_REPLY_RD, sizeof(ca_land_date_t)); /* 填充数据 */ rd->proVer = 0x00000601; rd->utc = now - CA_LAND_SHIQUCHA; rd->powertatus = 0; rd->batteryStatus = 0; rd->vbat = dev_data->vbat; rd->vsc = dev_data->vsc; rd->vout = dev_data->vin; rd->Gpslatitude = 0; rd->Gpslongitude = 0; rd->reslved1 = 0; _ca_land_date(&rd->rtd); _ca_land_cheak_sum(head->Len); /* 发送数据 */ if (send(fd, pkt, head->Len, 0) < 0) { DBG(DBG_M_CA_LAND_ERR, "LAND send ERR!\r\n"); return E_SYS_CALL; } /* 调试打印 */ DBG(DBG_M_CA_LAND, "LAND send(%d): %d %d\r\n", head->Len, now); if (dbg_stat_get(DBG_M_CA_LAND)) { buf_print(pkt, 32); } return E_NONE; } /* description: 私有协议数据发送线程 param: return: */ void *_ca_land_handle_send(void *arg) { static uint8_t cnt = 0; ca_coll_cfg_t *cfg = ca_coll_cfg_get(); ca_coll_state_t *state = ca_coll_cable_state_get(); struct sockaddr_in server; int fd = ca_land_ctrl.fd; int8_t err_cnt = 0; while (!is_system_init) { sleep(1); } /* 绑定端口 */ bzero(&server, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(cfg->land_ip); server.sin_port = htons(cfg->land_port); printf("land_ip:%s land_port:%d\n", cfg->land_ip, cfg->land_port); while (1) { sleep(30); /* 创建 socket */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { log_err(LOG_CA_LAND, "ERROR at socket create return %s!", safe_strerror(errno)); continue; } if (connect(fd, (struct sockaddr*)&server, sizeof(server)) < 0) { DBG(DBG_M_CA_LAND_ERR, "ERROR at socket connect return %s!\r\n", safe_strerror(errno)); close(fd); /* 如果 4G 连接, 服务器未连接, 假 4G 连接需要重启设备 */ if (cfg->is_4G && state->is_4g_connect) { err_cnt++; if (err_cnt >= 10) { // 如果是私网卡会连不上 //reboot_system(LOG_CA_LAND, BOOT_CONNECT_ERR); } } else { err_cnt = 0; } continue; } _ca_land_socket_set(fd); ca_land_ctrl.fd = fd; /* 定时发送数据 */ cnt = 0; while(1) { if (ca_land_ctrl.state.is_send_data) { ca_land_ctrl.state.is_send_data = FALSE; if (_ca_land_real_data_send() != E_NONE) { ca_land_ctrl.state.is_connect = FALSE; DBG(DBG_M_CA_LAND_ERR, "ERROR at server disconnect!\r\n"); close(fd); break; } ca_land_ctrl.state.is_connect = TRUE; } else if(cnt >= 30) { cnt = 0; if (_ca_land_heart_send() != E_NONE) { ca_land_ctrl.state.is_connect = FALSE; DBG(DBG_M_CA_LAND_ERR, "ERROR at server disconnect!\r\n"); close(fd); break; } ca_land_ctrl.state.is_connect = TRUE; } sleep(1); cnt++; } } return NULL; } /* Interface functions -------------------------------------------------------*/ /* description: 私有协预初始化 param: return: E_XXX */ int32_t ca_land_init(void) { cmd_install_element(COMMON_NODE, &ca_land_show_state_cmd); return E_NONE; } /* description: 朗德协议初始化函数. param: 111.47.21.141 return: */ int32_t ca_land_init_after(void) { ca_coll_cfg_t *cfg = ca_coll_cfg_get(); struct sched_param param; pthread_attr_t attr; pthread_t pid; int32_t rv = 0; if (!cfg->is_land) { return E_NONE; } 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_land_handle_send, NULL); if (rv != 0) { log_err(LOG_CA_LAND, "Can't create debug pthread %d!", rv); return E_SYS_CALL; } else { thread_m_add("CA_LAND", pid); } pthread_attr_destroy(&attr); return E_NONE; } /* description: 私有协议状态结构体获取 param: return: ca_land_state_t - 私有协议状态结构体指针 */ ca_land_state_t* ca_land_state_get(void) { return &ca_land_ctrl.state; } /* description: 私有协议状态显示 param: return: */ void ca_land_state_show(void) { ca_land_state_t *state = &ca_land_ctrl.state; printh("Connect: %s\r\n\n", state->is_connect ? "yes" : "no"); } #endif /************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/