/****************************************************************** ** Copyright (c) 2009 Wuhan Mozit Technology Co., Ltd . ** FileName: core.c ** Author: BigHead ** Mail: jsrenyw@icloud.com ** Editor: bighead ** Date: 2020-11-10 13:34 ** Version: 2020-11-10 ** File Description: 业务逻辑核心代码实现部分 ******************************************************************/ #include "core.h" #include "cmd.h" //指令发送表device_command: #define CTR_RRS 1 //远程重启 #define CTR_SFR 2 //设备恢复出厂设置 #define CTR_RS 3 //服务器跳转 #define CTR_OTA 4 //ota升级 #define CTR_RDC 5 //远程手动调光 #define CTR_RSC 6 //远程开关控制 需回复 #define CTR_RCN 7 //删除子节点指令 需回复 #define CFG_BKS 8 //备份服务器配置 需回复 #define CFG_ULI 9 //设备数据上传间隔配置 需回复 #define CFG_STS 10 //开关时间段设置 需回复 #define CFG_SRV 11 //额定电压设置 需回复 #define CFG_SRC 12 //额定电流设置 需回复 #define CFG_NAT 13 //下发节点信息表配置 #define CFG_GNV 14 //获取子节点版本号 #define CFG_SCM 15 //开关控制模式(拉合闸)设置 需回复 #define CFG_DGS 16 //调光节点分组设置 #define CFG_DGM 17 //调光节点分组数据修改 #define CFG_MGM 18 //电表节点分组设置 需回复 #define CFG_SAT 19 //传感器报警阀值设置 需回复 #define CTR_RGC 20 //远程开关编组控制 需回复 //继电器状态: #define SW_ON 0 //0x0000:继电器合闸 #define SW_MANUAL_CONTROL 32 //0x0020:继电器远程拉闸 #define SW_TIMER_CONTROL 16 //0x0010: 继电器自动时间段拉闸 #define SW_TEMPERATURE_HUMIDITY_ALARM 513 //0x0201:继电器温湿度报警拉闸 需报警 #define SW_FIRE_ALARM 514 //0x0202:继电器烟感报警拉闸 需报警 #define SW_TILT_ALARM 515 //0x0203:继电器倾斜报警拉闸 需报警 #define SW_LOCK_ALARM 516 //0x0204:继电器锁具报警拉闸 需报警 #define SW_LINE_TEMPERATURE_ALARM 517 //0x0205:继电器线温报警拉闸 需报警 #define SW_OVERVOLTAGE 1 //0x0001:过压拉闸 需报警 #define SW_UNDERVOLTAGE 2 //0x0002:欠压拉闸 需报警 #define SW_OVERLOAD_ALARM 4 //0x0004: 过载拉闸 需报警 #define SW_ELECTRIC_EXCESS 8 //0x0008:用电超额拉闸 #define SW_OVER_SWITCH_OUT_COUNT 64 //0x0040:超自动合闸次数 #define SW_FAST_CURRENT 128 //0x0080:快速电流拉闸 int sw[] = {513,514,515,516,517,1,2,4}; typedef enum {false = 0,true =1} bool; static pthread_t threads[32]; #define CRC32_POLYNOMIAL 0xEDB88320 list_node* file_list = NULL; //文件链表 list_node* user_list = NULL; //设备链表 int have_table = 0; static uint32_t crc_table32[256]; //CRC查询表 unsigned short ota_file_packet_len = 1024;//ota文件每包的数据长度 //按照大端模式解析数据 //将1个整型数据填充到缓存中 #define INT_TO_BUFF(buff, pos, val) \ buff[(pos)] = (val)&0x000000FF; \ buff[(pos) + 1] = ((val)&0x0000FF00) >> 8; \ buff[(pos) + 2] = ((val)&0x00FF0000) >> 16; \ buff[(pos) + 3] = ((val)&0xFF000000) >> 24 //从缓存中提取1个整型数据 #define INT_FROM_BUFF(buff, pos) ((buff[(pos) + 3] << 24) + (buff[(pos) + 2] << 16) + (buff[(pos) + 1] << 8) + buff[(pos)]) //从缓存中提取1个整型数据 #define SHORT_FROM_BUFF(buff, pos) ((buff[(pos) + 1] << 8) + buff[(pos)]) /****************************************************************** * Function Name: db_init * Arguments: * Return Value: void * Date: 2020-11-10 * Editor: bighead * Description: 程序启动时刻,调用该初始化函数多次,初始化不同mysql 连接 ******************************************************************/ int db_init(MYSQL *db) { int ret = 1; if (db) { MYSQL *_db = db; ret = mysqlConnetInit(_db) ? 1 : 0; } else { elog("MYSQL.var is NULL \n "); exit(0); } return ret; } /****************************************************************** * Function Name: core_init * Arguments: * Return Value: void * Date: 2020-11-10 * Editor: bighead * Description: 程序启动时刻,调用该初始化函数仅 一次,该函数不允许重复调用 ******************************************************************/ int core_init(void) { int ret = 0; // 加载Topic服务配置 initTopicConf(); //创建队列 //m_pque = CreateQueue(1000); // init mqtt.client init_mqtt_client(); sleep(1); // 注册Topic regTopicFromTable(); //创建处理任务队列线程 //if(NUM_THREADS_UPLOAD_g>0) // CreateDbHandThread(); return ret; } /****************************************************************** * Function Name: core_heart * Arguments: * Return Value: void * Date: 2020-11-10 * Editor: bighead * Description: 每次主线程心跳调用, 约200ms一次, 可以调整频率在decode.c ******************************************************************/ int core_heart(unsigned int nowTime) { int ret=0; ret=nowTime; return ret; } /****************************************************************** * Function Name: core_heart_1s * Arguments: * Return Value: void * Date: 2020-11-10 * Editor: bighead * Description: 每次主线程心跳调用, 约1s一次,不超过2s, 在core_heart心跳函数之后调用 * 可以调整频率在decode.c ******************************************************************/ int core_heart_1s(unsigned int nowTime) { int ret = 0; ret = nowTime; //时间同步广播 ret = TimeSyncBroadcast(); //从数据库取指令 SendCmdFromDb(); return ret; } //初始化时候进行部分测试,可以随意添加 int core_do_test(void) { //decode_msg_handle("TEST", NULL, NULL); print_stats(); return 0; } //定期调用会话统计信息, 分钟级别 int core_stats(void) { print_stats(); return 0; } /****************************************************************** * Function Name: core_alarm_gateway * Arguments: * Return Value: void * Date: 2023-12-5 * Editor: cc * Description: 扫描网关设备数据 ******************************************************************/ int CoreAlarmGateway(void) { static MYSQL *conn = NULL; MYSQL_RES* res = NULL; MYSQL_ROW row; char query[1024] = {0}; int row_count; if (!conn) { // 先申请内存再初始化 conn = malloc(sizeof(MYSQL)); db_init(conn); } sprintf(query,"SELECT t1.device_mac,t1.gateway_mac,t2.device_name,t2.control_mode,t1.update_time," "t1.current_on_off,t1.current_electric,t1.original_relay,t2.swith_time1,t2.swith_time2,t2.swith_time3,t2.swith_time4," "t2.electric_wave_set,t2.electric_wave_min,t2.electric_wave_max,t1.command_update_time " "FROM dev_status AS t1 " "INNER JOIN dev_info_gateway AS t2 " "ON t1.device_mac = t2.device_mac " "WHERE TIMESTAMPDIFF(MINUTE, t1.update_time, NOW()) < 10 " "AND t1.current_online=1 " "AND t1.original_relay IS NOT NULL " "AND t1.current_electric IS NOT NULL " "AND t1.electric_update_flag=1 " "AND t1.alarm_enable=1 "); excuteSql(conn,query); res = mysql_store_result(conn); if (NULL == res) { debug("NULL == res \n"); return 1; } //获取行数 row_count = mysql_num_rows(res); if(row_count<1) { mysql_free_result(res); debug("row_count<1 查询记录为0\n"); return 1; } debug("CoreAlarmGateway row_count:%d \r\n",row_count); //循环取出 while((row = mysql_fetch_row(res))) { char device_mac[30] = {0}; //0节点 char gateway_mac[30] = {0}; //1网关 char device_name[256] = {0}; //2设备名称 int control_mode = 0; //3控制模式 char update_time[20]; //4更新时间 int current_on_off = 0; //5开关状态 int current_electric = 0; //6电流 int original_relay = 0; //7原始开关值 char switch_time1[10] = {0}; //8开关时间1 char switch_time2[10] = {0}; //9开关时间2 char switch_time3[10] = {0}; //10开关时间3 char switch_time4[10] = {0}; //11开关时间4 int electric_wave_set = 0; //12电流波峰值 double electric_wave_min = 0.0; //13电流波峰值 double electric_wave_max = 0.0; //14电流波峰值 char command_update_time[20]; //15指令更新时间 struct tm tm_time; struct tm tm_time2; char timeString[20]; int result,result1,result2,result3,result4; char update_sql[100]={0}; char strTemp[256] ={0}; //取出数据 strcpy(device_mac,row[0]); strcpy(gateway_mac,row[1]); strcpy(device_name,row[2]); control_mode = atoi(row[3]); strptime(row[4], "%Y-%m-%d %H:%M:%S", &tm_time); strftime(update_time, sizeof(update_time), "%Y-%m-%d %H:%M:%S", &tm_time); current_on_off = atoi(row[5]); current_electric = atoi(row[6]); original_relay = atoi(row[7]); if(row[8]) strcpy(switch_time1,row[8]); if(row[9]) strcpy(switch_time2,row[9]); if(row[10]) strcpy(switch_time3,row[10]); if(row[11]) strcpy(switch_time4,row[11]); if(row[12]) electric_wave_set = atoi(row[12]); if(row[13]) electric_wave_min = atof(row[13]); if(row[14]) electric_wave_max = atof(row[14]); if(row[15]) strptime(row[15], "%Y-%m-%d %H:%M:%S", &tm_time2); strftime(command_update_time, sizeof(command_update_time), "%Y-%m-%d %H:%M:%S", &tm_time2); // log("CoreAlarmGateway : device_mac:'%s',gateway_mac:'%s',device_name:%s,control_mode:%d,update_time:%s,current_on_off:%d," // "current_electric:%d,original_relay:%d,switch_time1:%s,switch_time2:%s,switch_time3:%s,switch_time4:%s," // "electric_wave_set:%d,electric_wave_min:%f,electric_wave_max:%f \n", // device_mac,gateway_mac,device_name,control_mode,update_time,current_on_off,current_electric,original_relay, // switch_time1,switch_time2,switch_time3,switch_time4,electric_wave_set,electric_wave_min,electric_wave_max); //更新数据取出标记 sprintf(update_sql,"update dev_status set electric_update_flag=2 where device_mac='%s' and gateway_mac='%s' ",device_mac,gateway_mac); excuteSql(conn,update_sql); //判断继电器数据,有报警跳过后续判断 result = RelayAlarm(gateway_mac,"0F0000000001",original_relay,conn); if(result==0) continue; //判断更新的数据是否在指令下发的2minute内 if(row[15]) { tm_time2.tm_min +=2; //将指令更新时间加2分钟 mktime(&tm_time2);//重新计算时间 int result = compareModifiedTimes(tm_time2,tm_time); if(result>=0)//指令更新时间在2分钟内跳过后续判断 { debug("网关:%s,compareModifiedTimes tm_time2>=tm_time ------------>skip\n",device_name); continue; } } strftime(timeString, sizeof(timeString), "%H:%M", &tm_time); //判断是否在时间段点的2min以内 if(compareWithUpdateTime(timeString,switch_time1,switch_time2,switch_time3,switch_time4)) continue; if(!row[8] && !row[9]) continue; debug("timeString:%s \n",timeString); //判断自动模式 if(control_mode==0) { int isInTime1 = 0; int isInTime2 = 0; if(row[8]&&row[9]&&(strlen(switch_time1)>4)&&(strlen(switch_time2)>4))//第1个时间段不为空 { debug("网关:%s,strlen(switch_time1)=%zu,strlen(switch_time2)=%zu\n",device_name,strlen(switch_time1),strlen(switch_time2)); result1 = compareTimes(switch_time1,timeString); result2 = compareTimes(timeString,switch_time2); //时间正常顺序 if(compareTimes(switch_time1,switch_time2)<=0) { debug("网关:%s,时间正常顺序,compareTimes switch_time1<=switch_time2\n",device_name); //合闸时间段 if (result1<=0 && result2<=0) isInTime1=1; } //时间非正常顺序 else { debug("网关:%s,时间非正常顺序,compareTimes switch_time1>=switch_time2\n",device_name); //合闸时间段 if (result1<=0 || result2<=0) isInTime1=1; } } else //第1个时间段为空 { isInTime1=2; } if(row[10]&&row[11]&&(strlen(switch_time3)>4)&&(strlen(switch_time4)>4))//第2个时间段不为空 { debug("网关:%s,strlen(switch_time3)=%zu,strlen(switch_time4)=%zu\n",device_name,strlen(switch_time3),strlen(switch_time4)); result3 = compareTimes(switch_time3,timeString); result4 = compareTimes(timeString,switch_time4); //时间正常顺序 if(compareTimes(switch_time3,switch_time4)<=0) { debug("网关:%s,时间正常顺序,compareTimes switch_time3<=switch_time4\n",device_name); //合闸时间段 if (result3<=0 && result4<=0) isInTime2=1; } //时间非正常顺序 else { debug("网关:%s,时间非正常顺序,compareTimes switch_time3>=switch_time4\n",device_name); //合闸时间段 if (result3<=0 || result4<=0) isInTime2=1; } } else //第1个时间段为空 { isInTime2=2; } if((isInTime1==1||isInTime2==1) && current_on_off==0) { debug("网关:%s,合闸时间段继电器拉闸 \n",device_name); int ret=ElectricAlarm(gateway_mac,"0F0000000001",device_name,device_name,1101,"合闸时间段继电器拉闸",conn); if(ret==0) continue; } else if((isInTime1==0||isInTime2==0) && current_on_off==1) { debug("网关:%s,拉闸时间段继电器合闸 \n",device_name); int ret=ElectricAlarm(gateway_mac,"0F0000000001",device_name,device_name,1102,"拉闸时间段继电器合闸",conn); if(ret==0) continue; } } //判断继电器拉闸状态有电流 if(current_on_off==0 && current_electric>100) { debug("网关:%s,继电器拉闸状态有电流 \n",device_name); int ret=ElectricAlarm(gateway_mac,"0F0000000001",device_name,device_name,1103,"继电器拉闸状态有电流",conn); if(ret==0) continue; } //判断继电器合闸状态无电流 if(current_on_off==1 && current_electric<100) { debug("网关:%s,继电器合闸状态无电流 \n",device_name); int ret=ElectricAlarm(gateway_mac,"0F0000000001",device_name,device_name,1104,"继电器合闸状态无电流",conn); if(ret==0) continue; } //判断继电器合闸状态电流偏离正常阈值 if(current_on_off==1 && electric_wave_set==3) { double fcurrent_electric = current_electric/10000.0; if(fcurrent_electric<(electric_wave_min*0.9)|| fcurrent_electric>(electric_wave_max*1.1)) { debug("网关:%s,继电器合闸状态电流偏离正常值 \n",device_name); sprintf(strTemp,"继电器合闸状态电流偏离正常值,当前值:%.3f,阈值:%.3f-%.3f",fcurrent_electric,electric_wave_min,electric_wave_max); int ret=ElectricAlarm(gateway_mac,"0F0000000001",device_name,device_name,1105,strTemp,conn); if(ret==0) continue; } } } mysql_free_result(res); return 0; } /****************************************************************** * Function Name: CoreAlarmNode * Arguments: * Return Value: void * Date: 2023-12-8 * Editor: cc * Description: 扫描节点设备数据 ******************************************************************/ int CoreAlarmNode(void) { static MYSQL *conn2 = NULL; MYSQL_RES* res = NULL; MYSQL_ROW row; char query[1024] = {0}; int row_count; if (!conn2) { // 先申请内存再初始化 conn2 = malloc(sizeof(MYSQL)); db_init(conn2); } sprintf(query,"SELECT t1.device_mac,t1.gateway_mac,t3.device_name,t2.device_name,t2.control_mode,t1.update_time,t1.current_on_off,t1.current_electric," "t1.original_relay,t2.swith_time1,t2.swith_time2,t2.swith_time3,t2.swith_time4,t2.electric_wave_set,t2.electric_wave_min,t2.electric_wave_max,t1.command_update_time " "FROM dev_status AS t1 " "INNER JOIN dev_info_node AS t2 " "ON t1.device_mac = t2.device_mac " "AND t1.gateway_mac=t2.gateway_mac " "INNER JOIN dev_info_gateway AS t3 " "ON t2.gateway_mac = t3.device_mac " "WHERE TIMESTAMPDIFF(MINUTE, t1.update_time, NOW()) < 10 " "AND t1.current_online=1 " "AND t1.original_relay IS NOT NULL " "AND t1.current_electric IS NOT NULL " "AND t1.electric_update_flag=1 " "AND t1.alarm_enable=1 "); excuteSql(conn2,query); res = mysql_store_result(conn2); if (NULL == res) { debug("NULL == res \n"); return 1; } //获取行数 row_count = mysql_num_rows(res); if(row_count<1) { mysql_free_result(res); debug("row_count<1 查询记录为0 \n"); return 1; } debug("CoreAlarmNode row_count:%d \r\n",row_count); //循环取出 while((row = mysql_fetch_row(res))) { char device_mac[30] = {0}; //0节点 char gateway_mac[30] = {0}; //1网关 char gateway_name[256] = {0}; //2设备名称 char node_name[256] = {0}; //3节点名称 int control_mode = 0; //4控制模式 char update_time[20]; //5更新时间 int current_on_off = 0; //6开关状态 int current_electric = 0; //7电流 int original_relay = 0; //8原始开关值 char switch_time1[10] = {0}; //9开关时间1 char switch_time2[10] = {0}; //10开关时间2 char switch_time3[10] = {0}; //11开关时间3 char switch_time4[10] = {0}; //12开关时间4 int electric_wave_set = 0; //13电流波峰值 double electric_wave_min = 0.0; //14电流波峰值 double electric_wave_max = 0.0; //15电流波峰值 struct tm tm_time; struct tm tm_time2; char timeString[20]; int result,result1,result2,result3,result4; char update_sql[100]={0}; char strTemp[256] ={0}; //取出数据 strcpy(device_mac,row[0]); strcpy(gateway_mac,row[1]); strcpy(gateway_name,row[2]); strcpy(node_name,row[3]); control_mode = atoi(row[4]); strptime(row[5], "%Y-%m-%d %H:%M:%S", &tm_time); strftime(update_time, sizeof(update_time), "%Y-%m-%d %H:%M:%S", &tm_time); current_on_off = atoi(row[6]); current_electric = atoi(row[7]); original_relay = atoi(row[8]); if(row[9]) strcpy(switch_time1,row[9]); if(row[10]) strcpy(switch_time2,row[10]); if(row[11]) strcpy(switch_time3,row[11]); if(row[12]) strcpy(switch_time4,row[12]); if(row[13]) electric_wave_set = atoi(row[13]); if(row[14]) electric_wave_min = atof(row[14]); if(row[15]) electric_wave_max = atof(row[15]); if(row[16]) strptime(row[15], "%Y-%m-%d %H:%M:%S", &tm_time2); // log("CoreAlarmNode : device_mac:'%s',gateway_mac:'%s',gateway_name:%s,node_name:%s,control_mode:%d,update_time:%s,current_on_off:%d," // "current_electric:%d,original_relay:%d,switch_time1:%s,switch_time2:%s,switch_time3:%s,switch_time4:%s," // "electric_wave_set:%d,electric_wave_min:%f,electric_wave_max:%f \n", // device_mac,gateway_mac,gateway_name,node_name,control_mode,update_time,current_on_off,current_electric,original_relay, // switch_time1,switch_time2,switch_time3,switch_time4,electric_wave_set,electric_wave_min,electric_wave_max); //更新数据取出标记 sprintf(update_sql,"update dev_status set electric_update_flag=2 where device_mac='%s' and gateway_mac='%s' ",device_mac,gateway_mac); excuteSql(conn2,update_sql); //判断继电器数据 result = RelayAlarm(gateway_mac,device_mac,original_relay,conn2); if(result==0) continue; //判断更新的数据是否在指令下发的2minute内 if(row[16]) { tm_time2.tm_min +=2; //将指令更新时间加2分钟 mktime(&tm_time2);//重新计算时间 int result = compareModifiedTimes(tm_time2,tm_time); if(result>=0)//指令更新时间在2分钟内跳过后续判断 { debug("网关:%s,节点:%s,compareModifiedTimes tm_time2>=tm_time------------>skip\n",gateway_name,node_name); continue; } } strftime(timeString, sizeof(timeString), "%H:%M", &tm_time); //判断是否在时间段点的2min以内 if(compareWithUpdateTime(timeString,switch_time1,switch_time2,switch_time3,switch_time4)) continue; if(!row[9] || !row[10] || !row[11] || !row[12]) continue; debug("timeString:%s \n",timeString); //判断自动模式 if(control_mode==0) { int isInTime1 = 0; int isInTime2 = 0; if(row[9]&&row[10]&&(strlen(switch_time1)>4)&&(strlen(switch_time2)>4))//第1个时间段不为空 { debug("网关:%s,节点:%s,strlen(switch_time1)=%zu,strlen(switch_time2)=%zu\n",gateway_name,node_name,strlen(switch_time1),strlen(switch_time2)); result1 = compareTimes(switch_time1,timeString); result2 = compareTimes(timeString,switch_time2); //时间正常顺序 if(compareTimes(switch_time1,switch_time2)<=0) { debug("网关:%s,节点:%s,compareTimes switch_time1<=switch_time2\n",gateway_name,node_name); //合闸时间段 if (result1<=0 && result2<=0) isInTime1=1; } //时间非正常顺序 else { debug("网关:%s,节点:%s,compareTimes switch_time1>=switch_time2\n",gateway_name,node_name); //合闸时间段 if (result1<=0 || result2<=0) isInTime1=1; } } else //第1个时间段为空 { isInTime1=2; } if(row[11]&&row[12]&&(strlen(switch_time3)>4)&&(strlen(switch_time4)>4))//第2个时间段不为空 { debug("网关:%s,节点:%s,strlen(switch_time3)=%zu,strlen(switch_time4)=%zu\n",gateway_name,node_name,strlen(switch_time3),strlen(switch_time4)); result3 = compareTimes(switch_time3,timeString); result4 = compareTimes(timeString,switch_time4); //时间正常顺序 if(compareTimes(switch_time3,switch_time4)<=0) { debug("网关:%s,节点:%s,compareTimes switch_time3<=switch_time4\n",gateway_name,node_name); //合闸时间段 if (result3<=0 && result4<=0) isInTime2=1; } //时间非正常顺序 else { debug("网关:%s,节点:%s,compareTimes switch_time3>=switch_time4\n",gateway_name,node_name); //合闸时间段 if (result3<=0 || result4<=0) isInTime2=1; } } else //第1个时间段为空 { isInTime2=2; } if((isInTime1==1||isInTime2==1) && current_on_off==0) { debug("网关:%s,节点:%s,合闸时间段继电器拉闸 \n",gateway_name,node_name); int ret=ElectricAlarm(gateway_mac,device_mac,gateway_name,node_name,1101,"合闸时间段继电器拉闸",conn2); if(ret==0) continue; } else if((isInTime1==0||isInTime2==0) && current_on_off==1) { debug("网关:%s,节点:%s,拉闸时间段继电器合闸 \n",gateway_name,node_name); int ret=ElectricAlarm(gateway_mac,device_mac,gateway_name,node_name,1102,"拉闸时间段继电器合闸",conn2); if(ret==0) continue; } } //判断继电器拉闸状态有电流 if(current_on_off==0 && current_electric>100) { debug("网关:%s,节点:%s,继电器拉闸状态有电流 \n",gateway_name,node_name); int ret=ElectricAlarm(gateway_mac,device_mac,gateway_name,node_name,1103,"继电器拉闸状态有电流",conn2); if(ret==0) continue; } //判断继电器合闸状态无电流 if(current_on_off==1 && current_electric<100) { debug("网关:%s,节点:%s,继电器合闸状态无电流 \n",gateway_name,node_name); int ret=ElectricAlarm(gateway_mac,device_mac,gateway_name,node_name,1104,"继电器合闸状态无电流",conn2); if(ret==0) continue; } //判断继电器合闸状态电流偏离正常阈值 if(current_on_off==1 && electric_wave_set==3) { double fcurrent_electric = current_electric/10000.0; if(fcurrent_electric<(electric_wave_min*0.9)|| fcurrent_electric>(electric_wave_max*1.1)) { debug("网关:%s,节点:%s,继电器合闸状态电流偏离正常值 \n",gateway_name,node_name); sprintf(strTemp,"继电器合闸状态电流偏离正常值,当前值:%.3f,阈值:%.3f-%.3f",fcurrent_electric,electric_wave_min,electric_wave_max); int ret=ElectricAlarm(gateway_mac,device_mac,gateway_name,node_name,1105,strTemp,conn2); if(ret==0) continue; } } } mysql_free_result(res); return 0; } /****************************************************************** * Function Name: ElectricAlarm * Arguments: * Return Value: void * Date: 2023-12-7 * Editor: cc * Description: 电流数据告警判断 ******************************************************************/ int ElectricAlarm(char* mac,char* subMac,char* gatewayName,char* nodeName,int alarmCode,char* alarmStr,MYSQL* _db) { //判断是否需要报警 MYSQL_RES* res = NULL; MYSQL_ROW row; char querySql[256] = {0}; char strName[256] = {0}; char strAlarm[512] = {0}; char updateSql[1024] = {0}; char insertSql[512] = {0}; int queryRow = 0; int fault_state=0; int fault_count=0; struct tm tm_time; //网关或节点名称 if (strcmp(subMac,"0F0000000001") == 0)//网关 { sprintf(strName,"设备名称:%s",gatewayName); } else { sprintf(strName,"设备名称:%s,节点:%s",gatewayName,nodeName); } //格式化报警内容 sprintf(strAlarm,"%s,%s,时间:%s \r\n",strName,alarmStr,GetCurrentTime()); //查询当前报警类型是否正在报警 fault_state 1: 未处理 2: 故障正在处理中 3: 处理完成 sprintf(querySql,"select fault_state,fault_count,fault_time from dev_fault where device_mac = '%s' and device_mac_node = '%s' and relay_code = %d ",mac,subMac,alarmCode); excuteSql(_db,querySql); res = mysql_store_result(_db); queryRow = mysql_num_rows(res); if (res && queryRow>0)//有记录 { debug("res && queryRow>0 querySql:%s\n",querySql); row = mysql_fetch_row(res); fault_state = atoi(row[0]); fault_count = atoi(row[1]); if(row[2]) strptime(row[2], "%Y-%m-%d %H:%M:%S", &tm_time); debug("fault_state:%d fault_count:%d\n",fault_state,fault_count); if(fault_state == 1 || fault_state == 2)//正在报警 { log("查询到当前报警类型正在报警,不再重复报警,alarmStr:%s\n",strAlarm); mysql_free_result(res); return 1; } if(alarmCode>1100)//电流报警进行3次判断 { //在更新次数前检查是否是连续的告警次数,否则重新计数,通过检查上一次告警时间是否在5min以内 if(row[2]) { tm_time.tm_min +=5; //将指令更新时间加5分钟 mktime(&tm_time);//重新计算时间 int result=compareWithCurrentTime(tm_time); if(result>=0)//如果在5min以内 { if(fault_count >= 2)//加上本次报警次数达到3次 { //更新到报警故障表 sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 1,fault_time = now(),fault_count=0 where " "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,mac,subMac,alarmCode); log("触发告警(电流): %s\n",strAlarm); } else { fault_count = fault_count + 1; //更新报警次数,并不报警 sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 3,fault_time = now(),fault_count=%d where " "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,fault_count,mac,subMac,alarmCode); debug("网关:%s,节点:%s,电流告警更新次数(不报警)\n",gatewayName,nodeName); } } else//不在5min以内 计数重新开始 { //更新到报警故障表 sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 3,fault_time = now(),fault_count=1 where " "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,mac,subMac,alarmCode); debug("不在5min以内 计数重新开始(电流): %s\n",strAlarm); } } else //没有时间字段 计数重新开始 { //更新到报警故障表 sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 3,fault_time = now(),fault_count=1 where " "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,mac,subMac,alarmCode); debug("没有时间字段 计数重新开始(电流): %s\n",strAlarm); } } else { //更新到报警故障表 sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 1,fault_time = now() where " "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,mac,subMac,alarmCode); log("触发告警: %s\n",strAlarm); } debug("updateSql: %s\n",updateSql); excuteSql(_db,updateSql); } else//没有查询到记录 { debug("没有记录 querySql:%s",querySql); sprintf(insertSql,"insert into dev_fault(device_mac,device_mac_node,fault_details,relay_code,fault_state,fault_time)values('%s','%s','%s',%d,1,now())", mac,subMac,strAlarm,alarmCode); log("触发告警(插入): %s\n",strAlarm); excuteSql(_db,insertSql); } mysql_free_result(res); // //更新到报警故障表 // sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 1,fault_time = now() where " // "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,mac,subMac,alarmCode); // excuteSql(_db,updateSql); // affectedRows = mysql_affected_rows(_db); // if (affectedRows < 1) // { // sprintf(insertSql,"insert into dev_fault(device_mac,device_mac_node,fault_details,relay_code,fault_state,fault_time)values('%s','%s','%s',%d,1,now())", // mac,subMac,strAlarm,alarmCode); // //log("strAlarm:%s\n",strAlarm); // excuteSql(_db,insertSql); // } return 0; } //扫描数据库发送指令 int SendCmdFromDb(void) { static MYSQL *_db = NULL; MYSQL_RES* res = NULL; MYSQL_ROW row; char query[1024] = {0}; char deleteSql[256] = {0}; char updateSql[256] = {0}; int row_count; if (!_db) { // 先申请内存再初始化 _db = malloc(sizeof(MYSQL)); db_init(_db); } sprintf(query,"select id,device_mac,device_mac_node,device_command,device_group,device_on_off," "device_luminance,dimming_mode,node_type,control_mode from dev_cmd_send where device_mac is not null and current_online = 1 and device_type < 10"); excuteSql(_db,query); res = mysql_store_result(_db); if (NULL == res) { return 1; } //获取行数 row_count = mysql_num_rows(res); if(row_count<1) { //debug("%s 没有查询到指令 \n",mac); mysql_free_result(res); return 1; } //循环取出 while((row = mysql_fetch_row(res))) { int id = 0; char device_mac[30] = {0}; //网关mac 1 char device_mac_node[30] = {0}; //节点mac 2 int device_command = 0; //指令类型 3 int device_group = 0; //组号 4 int device_on_off = 0; //开关 5 int device_luminance = 0; //亮度 6 int dimming_mode = 0; //调光类型 7 int node_type = 0; //节点类型 8 int control_mode = 0; //开关控制模式 9 int ret = 0; if(row[0]) id = atoi(row[0]); if(!row[1] || strlen((char*)row[1])!=24) { //删除指令 sprintf(deleteSql,"delete from dev_cmd_send where id = %d",id); excuteSql(_db,deleteSql); debug("网关mac不正确!"); continue; } strcpy(device_mac,row[1]); if(row[2]) strcpy(device_mac_node,row[2]); if(row[3]) device_command = atoi(row[3]); if(row[4]) device_group = atoi(row[4]); if(row[5]) device_on_off = atoi(row[5]); if(row[6]) device_luminance = atoi(row[6]); if(row[7]) dimming_mode = atoi(row[7]); if(row[8]) node_type = atoi(row[8]); if(row[9]) control_mode = atoi(row[9]); debug("SendCmdFromDb 行数:%d ,device_mac:'%s',device_mac_node:'%s',device_command:%d,device_group:%d,device_on_off:%d," "device_luminance:%d,dimming_mode:%d,node_type:'%d',control_mode:%d \n", row_count,device_mac,device_mac_node,device_command,device_group,device_on_off,device_luminance,dimming_mode,node_type,control_mode); //删除指令 sprintf(deleteSql,"delete from dev_cmd_send where id = %d",id); excuteSql(_db,deleteSql); switch (device_command) { case CTR_RRS: //远程重启 { ret = SendRestart(device_mac); } break; case CTR_SFR: //设备恢复出厂设置 { ret = SendFactoryReset(device_mac); } break; case CTR_RS: //服务器跳转 { ret = SendServerJump(device_mac,_db); } break; case CTR_OTA: //ota升级 { ret = SendRemoteUpgrade(device_mac,_db); } break; case CTR_RDC: //远程手动调光 { if(row[4] && row[6]) //row[7] 调光类型判断暂去掉 { ret = SendManualDimming(device_mac,device_group,device_luminance,dimming_mode); } } break; case CTR_RSC: //远程开关控制 需回复 { if (row[2] && (strlen(device_mac_node) == 12) && row[5] && row[8]) //节点 { ret = SendSwitchControl(device_mac,device_mac_node,node_type,device_on_off); sprintf(updateSql,"update dev_status set command_update_time = now() where gateway_mac = '%s' and device_mac='%s'",device_mac,device_mac_node); } else if(!row[2] && row[5] && row[8]) //网关 { ret = SendSwitchControl(device_mac,"",node_type,device_on_off); sprintf(updateSql,"update dev_status set command_update_time = now() where gateway_mac = '%s' and device_mac='%s'",device_mac,device_mac); } if(ret == 0) { excuteSql(_db,updateSql); } } break; case CTR_RCN: //删除子节点指令 需回复 { if(row[2] && (strlen(device_mac_node) == 12) && row[8]) { ret = SendDeleteNode(device_mac,device_mac_node,node_type); } } break; case CFG_BKS: //备份服务器配置 需回复 { ret = SendBakIP(device_mac,_db); } break; case CFG_ULI: //设备数据上传间隔配置 需回复 { ret = SendDataInterval(device_mac,_db); } break; case CFG_STS: //开关时间段设置 需回复 { if (row[2] && (strlen(device_mac_node) == 12)) //节点 { ret = SendOnOffTimeMuilt(device_mac,device_mac_node,_db); sprintf(updateSql,"update dev_status set command_update_time = now() where gateway_mac = '%s' and device_mac='%s'",device_mac,device_mac_node); } else if(!row[2]) //网关 { ret = SendOnOffTimeMuilt(device_mac,"",_db); sprintf(updateSql,"update dev_status set command_update_time = now() where gateway_mac = '%s' and device_mac='%s'",device_mac,device_mac); } if(ret == 0) { excuteSql(_db,updateSql); } } break; case CFG_SRV: //额定电压设置 需回复 { if (row[2] && (strlen(device_mac_node) == 12)) //节点 { ret = SendRatedVoltage(device_mac,device_mac_node,_db); } else if(!row[2]) //网关 { ret = SendRatedVoltage(device_mac,"",_db); } } break; case CFG_SRC: //额定电流设置 需回复 { if (row[2] && (strlen(device_mac_node) == 12)) //节点 { ret = SendRatedCurrent(device_mac,device_mac_node,_db); } else if(!row[2]) //网关 { ret = SendRatedCurrent(device_mac,"",_db); } } break; case CFG_NAT: //下发节点信息表配置 { ret = SendSubInfo(device_mac,_db); } break; case CFG_GNV: //获取子节点版本号 { if (row[2] && (strlen(device_mac_node) == 12)) //节点 { ret = SendGetSubVersion(device_mac,device_mac_node); } } break; case CFG_SCM: //开关控制模式(拉合闸)设置 需回复 { if (row[2] && (strlen(device_mac_node) == 12)) //节点 { ret = SendSwitchModeFromDB(device_mac,device_mac_node,_db); } else if(!row[2]) //网关 { ret = SendSwitchModeFromDB(device_mac,"",_db); } } break; case CFG_DGS: //调光节点分组设置 { if (row[2] && (strlen(device_mac_node) == 12)) //节点 { ret = SendDimmingNodeGroup(device_mac,device_mac_node,_db); } } break; case CFG_DGM: //调光节点分组数据修改 { if(row[4]) { ret = SendDimmingNodeGroupModify(device_mac,device_group,_db); } } break; case CFG_MGM: //电表节点分组设置 需回复 { } break; case CFG_SAT: //传感器报警阀值设置 需回复 { ret = SendSensorThreshold(device_mac,_db); } break; case CTR_RGC: //远程开关编组控制 需回复 { if(row[4] && row[5]) { ret = SendSwitchGroupControl(device_mac,device_group,2,device_on_off); } } break; default: debug("指令不能识别:%d",device_command); break; } } mysql_free_result(res); return 0; } //时间同步广播 int TimeSyncBroadcast(void) { //TIME_SYNC_INTERVAL_g 配置文件读取 if(timeGloble_g%TIME_SYNC_INTERVAL_g==0) { int ret = 0; ret = SendNTP(""); debug("时间同步广播结果:%d,当前时间同步间隔 %d \n",ret,TIME_SYNC_INTERVAL_g); } return 0; } //1.字节流转换为十六进制字符串 void ByteToHexStr(const unsigned char* source, char* dest, int sourceLen) { short i; unsigned char highByte, lowByte; for (i = 0; i < sourceLen; i++) { highByte = source[i] >> 4; lowByte = source[i] & 0x0f ; highByte += 0x30; if (highByte > 0x39) dest[i * 2] = highByte + 0x07; else dest[i * 2] = highByte; lowByte += 0x30; if (lowByte > 0x39) dest[i * 2 + 1] = lowByte + 0x07; else dest[i * 2 + 1] = lowByte; } return ; } //2.字节流转换为十六进制字符串 void Hex2Str( const char *sSrc, char *sDest, int nSrcLen ) { int i; char szTmp[3]; for( i = 0; i < nSrcLen; i++ ) { sprintf( szTmp, "%02X", (unsigned char) sSrc[i] ); memcpy( &sDest[i * 2], szTmp, 2 ); } return ; } //十六进制字符串转换为字节流 void HexStrToByte(const char* source, unsigned char* dest, int sourceLen) { short i; unsigned char highByte, lowByte; for (i = 0; i < sourceLen; i += 2) { highByte = toupper(source[i]); lowByte = toupper(source[i + 1]); if (highByte > 0x39) highByte -= 0x37; else highByte -= 0x30; if (lowByte > 0x39) lowByte -= 0x37; else lowByte -= 0x30; dest[i / 2] = (highByte << 4) | lowByte; } return ; } //改变字节顺序 void EndianSwap(unsigned char *pData, int startIndex, int length) { int i,cnt,end,start; cnt = length / 2; start = startIndex; end = startIndex + length - 1; unsigned char tmp; for (i = 0; i < cnt; i++) { tmp = pData[start+i]; pData[start+i] = pData[end-i]; pData[end-i] = tmp; } return ; } //获取当前月份历史表 char* GetCurrentNYTable(void) { time_t t = time(0); static char str[64] = {0}; strftime(str,sizeof(str),"dev_status_history_%Y%m",localtime(&t)); return str; } //获取系统时间年月日 时分秒 char* GetCurrentTime(void) { time_t t = time(0); static char str[64] = {0}; strftime(str,sizeof(str),"%Y%m%d %H:%M:%S",localtime(&t)); return str; } //字符串中查找指定字符的个数 int find_char(char str[], char substr[]) { int count = 0,i,j,check; int len = strlen(str); int sublen = strlen(substr); for(i = 0; i < len; i++) { check = 1; for(j = 0; j + i < len && j < sublen; j++) { if(str[i+j] != substr[j]) { check = 0; break; } } if(check == 1) { count++; i = i + sublen; } } return count; } //从主题中查找指定'/'分割的段 char* find_string(char* topicName, int section) { static char tmp[50][50]; memset(tmp,0x0,2500); char* p1 = (char*)malloc(1024); int i = 0; while ((p1 = strchr(topicName, '/')) != NULL) { strncpy(tmp[i], topicName,strlen(topicName)-strlen(p1)); topicName = p1 + 1; i++; } strncpy(tmp[i], topicName, strlen(topicName)); free(p1); return tmp[section]; } //判断是否属于报警code int IsRelayAlarmCode(int relayCode) { int i = 0; for(i = 0;i<(int)(sizeof(sw)/sizeof(int));i++) { if(relayCode == sw[i]) return 1; } return -1; } //继电器报警 int RelayAlarm(char* mac,char* subMac,int relayCode,MYSQL* _db) { MYSQL_RES* res = NULL; MYSQL_ROW row; char querySql[256] = {0}; char updateSql[256] = {0}; char insertSql[256] = {0}; char strAlarm[256] = {0}; bool isExist = false; char strAlarmType[256] = {0}; char strName[256] = {0}; char macName[256] = {0}; char subMacName[256] = {0}; int affectedRows = 0; int queryRow = 0; //判断是否需要报警 int i = 0; for(i = 0;i<(int)(sizeof(sw)/sizeof(int));i++) { if(relayCode == sw[i]) isExist = true; } if(!isExist) return 1; //查询当前报警类型是否正在报警 fault_state 1: 未处理 2: 故障正在处理中 3: 处理完成 sprintf(querySql,"select * from dev_fault where device_mac = '%s' and device_mac_node = '%s' and relay_code = %d and (fault_state = 1 or fault_state = 2)",mac,subMac,relayCode); excuteSql(_db,querySql); res = mysql_store_result(_db); queryRow = mysql_num_rows(res); if (res && queryRow>0) { //debug("res && queryRow>0 querySql:%s",querySql); mysql_free_result(res); return 1; } mysql_free_result(res); //查询网关节点名称 if (strcmp(subMac,"0F0000000001") == 0)//网关 { sprintf(querySql,"select device_name from dev_info_gateway where device_mac = '%s' ",mac); excuteSql(_db,querySql); res = mysql_store_result(_db); if (NULL == res) { return 1; } row = mysql_fetch_row(res); if (NULL == row) { mysql_free_result(res); return 1; } if(!row[0]) { debug("device_name 为空"); mysql_free_result(res); return 1; } strcpy(macName,row[0]); sprintf(strName,"设备名称:%s",macName); mysql_free_result(res); } else { sprintf(querySql,"select a.device_name as 'gateway_name',b.device_name as 'node_name' from dev_info_gateway a,dev_info_node b " "where a.device_mac = b.gateway_mac and b.gateway_mac = '%s' and b.device_mac = '%s'",mac,subMac); excuteSql(_db,querySql); res = mysql_store_result(_db); if (NULL == res) { return 1; } row = mysql_fetch_row(res); if (NULL == row) { mysql_free_result(res); return 1; } if(!row[0] || !row[1]) { debug("gateway_name 或者 node_name 为空 "); mysql_free_result(res); return 1; } strcpy(macName,row[0]); strcpy(subMacName,row[1]); mysql_free_result(res); sprintf(strName,"设备:%s,节点:%s",macName,subMacName); } //判断报警类型 if(relayCode == SW_TEMPERATURE_HUMIDITY_ALARM) { strcpy(strAlarmType,"温湿度报警拉闸"); } else if(relayCode == SW_FIRE_ALARM) { strcpy(strAlarmType,"烟感报警拉闸"); } else if(relayCode == SW_TILT_ALARM) { strcpy(strAlarmType,"倾斜报警拉闸"); } else if(relayCode == SW_LOCK_ALARM) { strcpy(strAlarmType,"锁具报警拉闸"); } else if(relayCode == SW_LINE_TEMPERATURE_ALARM) { strcpy(strAlarmType,"线温报警拉闸"); } else if(relayCode == SW_OVERVOLTAGE) { strcpy(strAlarmType,"过压报警拉闸"); } else if(relayCode == SW_UNDERVOLTAGE) { strcpy(strAlarmType,"欠压报警拉闸"); } else if(relayCode == SW_OVERLOAD_ALARM) { strcpy(strAlarmType,"过载报警拉闸"); } else if(relayCode == SW_ELECTRIC_EXCESS) { strcpy(strAlarmType,"用电超额报警拉闸"); } else if(relayCode == SW_OVER_SWITCH_OUT_COUNT) { strcpy(strAlarmType,"超自动合闸次数报警拉闸"); } else if(relayCode == SW_FAST_CURRENT) { strcpy(strAlarmType,"快速电流报警拉闸"); } else { debug("无法识别报警!"); return 1; } //格式化报警内容 sprintf(strAlarm,"%s,%s,时间:%s \n",strName,strAlarmType,GetCurrentTime()); log(strAlarm); //更新到报警故障表 sprintf(updateSql,"update dev_fault set fault_details = '%s',fault_state = 1,fault_time = now() where " "device_mac = '%s' and device_mac_node = '%s' and relay_code = %d",strAlarm,mac,subMac,relayCode); excuteSql(_db,updateSql); affectedRows = mysql_affected_rows(_db); if (affectedRows < 1) { sprintf(insertSql,"insert into dev_fault(device_mac,device_mac_node,fault_details,relay_code,fault_state,fault_time)values('%s','%s','%s',%d,1,now())", mac,subMac,strAlarm,relayCode); excuteSql(_db,insertSql); } return 0; } //比较两个时间字符串的大小 int compareTimes(const char *time1, const char *time2) { struct tm tm_base, tm_time1, tm_time2; // 设置基准日期为当前日期 time_t current_time = time(NULL); localtime_r(¤t_time, &tm_base); // 解析时间字符串1 if (strptime(time1, "%H:%M", &tm_time1) == 0) { fprintf(stderr, "Failed to parse time1\n"); return 0; // or handle the error in an appropriate way } // 解析时间字符串2 if (strptime(time2, "%H:%M", &tm_time2) == 0) { fprintf(stderr, "Failed to parse time2\n"); return 0; // or handle the error in an appropriate way } // 设置日期部分为基准日期的日期部分 tm_time1.tm_year = tm_base.tm_year; tm_time1.tm_mon = tm_base.tm_mon; tm_time1.tm_mday = tm_base.tm_mday; tm_time2.tm_year = tm_base.tm_year; tm_time2.tm_mon = tm_base.tm_mon; tm_time2.tm_mday = tm_base.tm_mday; // 将 struct tm 结构转换为 time_t 类型 time_t t1 = mktime(&tm_time1); time_t t2 = mktime(&tm_time2); // 检查转换是否成功 if (t1 == -1 || t2 == -1) { fprintf(stderr, "Failed to convert time to time_t\n"); return 0; // or handle the error in an appropriate way } // 比较时间并返回相应的结果 if (t1 < t2) { return -1; } else if (t1 > t2) { return 1; } else { return 0; } } //比较两个时间结构体的的大小 int compareModifiedTimes(struct tm t1, struct tm t2) { // 将时间结构体转换为秒数 time_t time1 = mktime(&t1); time_t time2 = mktime(&t2); // 比较秒数 if (time1 < time2) { return -1; } else if (time1 > time2) { return 1; } else { return 0; } } //比较给定时间和系统当前时间的函数 int compareWithCurrentTime(struct tm t1) { // 获取系统当前时间 time_t currentTime; time(¤tTime); // 将给定时间结构体转换为秒数 time_t givenTime = mktime(&t1); // 比较秒数 if (givenTime < currentTime) { return -1; } else if (givenTime > currentTime) { return 1; } else { return 0; } } // 将时间字符串转换为秒数 int timeStringToSeconds(const char *timeString) { int hours, minutes; if (sscanf(timeString, "%02d:%02d", &hours, &minutes) == 2) { return hours * 3600 + minutes * 60; } return INT_MIN; // 解析失败 } // 比较两个时间字符串的时间差(秒) int compareTimeDifference(const char *timeString1, const char *timeString2) { int seconds1 = timeStringToSeconds(timeString1); int seconds2 = timeStringToSeconds(timeString2); debug("seconds1:%d,seconds2:%d\n",seconds1,seconds2); if (seconds1 != INT_MIN && seconds2 != INT_MIN) { return seconds1 - seconds2; } else { return INT_MIN; // 解析失败 } } //更新时间和拉合闸的时间点比较 int compareWithUpdateTime(const char *timeString,const char *time1,const char *time2,const char *time3,const char *time4) { int diff1 = compareTimeDifference(timeString, time1); // 解析时间字符串1 if (strlen(time1)==5 && diff1!= INT_MIN) { debug("time1:%s,和更新时间:%s,差值为%d\n",time1 ,timeString,diff1); if(fabs(diff1)<120)//更新时间在2分钟内 { debug("time1更新时间在2分钟内\n"); return 1; } } int diff2 = compareTimeDifference(timeString, time2); // 解析时间字符串2 if (strlen(time2)==5 && diff2!= INT_MIN) { debug("time2:%s,和更新时间:%s,差值为%d\n",time2 ,timeString,diff2); if(fabs(diff2)<120)//更新时间在2分钟内 { debug("time2更新时间在2分钟内\n"); return 1; } } int diff3 = compareTimeDifference(timeString, time3); // 解析时间字符串3 if (strlen(time1)==5 && diff3!= INT_MIN) { debug("time3:%s,和更新时间:%s,差值为%d\n",time3 ,timeString,diff3); if(fabs(diff3)<120)//更新时间在2分钟内 { debug("time1更新时间在2分钟内\n"); return 1; } } int diff4 = compareTimeDifference(timeString, time4); // 解析时间字符串4 if (strlen(time4)==5 && diff4!= INT_MIN) { debug("time4:%s,和更新时间:%s,差值为%d\n",time4 ,timeString,diff4); if(fabs(diff4)<120)//更新时间在2分钟内 { debug("time4更新时间在2分钟内\n"); return 1; } } return 0; } //将需要处理sql放入队列 void* sendtoqueue(ArrQueue *pQueue,char* stringSql) { ElemType *elememt = (ElemType *)malloc(sizeof(ElemType)); if(elememt==NULL) { printf("0 malloc(sizeof(ElemType)) \n"); exit(0); } memset(elememt,0,sizeof(ElemType)); if(strlen(stringSql)>1024) { printf("stringSql length is too long \n"); exit(0); } elememt->len = strlen(stringSql); strcpy(elememt->buf,stringSql); EnQueue(pQueue,elememt); return NULL; } //循环从队列中取出数据处理 void* getqueue(void *data) { MYSQL *_db = NULL; ElemType *elememt; if (!_db) { // 先申请内存再初始化 _db = malloc(sizeof(MYSQL)); db_init(_db); } struct my_thread_info *info = data; while(1) { usleep(1000*1); DeQueue(m_pque,&elememt); if(elememt == NULL) { printf("elememt == NULL,number:%d\n",info->which); continue; } excuteSql(_db,elememt->buf); free(elememt); //log("getqueue sql number:%d \n",info->which); } free(info); return NULL; } //创建任务线程处理数据库执行 void CreateDbHandThread(void) { int rc; long t; struct my_thread_info *info; sleep(1); for (t = 0; t < NUM_THREADS_UPLOAD_g; t++) { info = malloc(sizeof(struct my_thread_info)); info->which = t; rc = pthread_create(&threads[t], NULL, getqueue, info); if (rc) { printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } printf("pthread_create() threadid:%lu is started \n",threads[t]); sleep(1); } // for (t = 0; t < NUM_THREADS; t++) // { // pthread_join(threads[t], NULL); // } } //打印队列状态,队列处理线程状态,10min打印一次 int PrintQueueThreadState(void) { if(timeGloble_g%600 == 0 && NUM_THREADS_UPLOAD_g>0) { int i = 0; char strState[256] = {0}; int pthread_kill_err; memset(strState,0x0,256); sprintf(strState,"pid:%d,队列大小:%d",getpid(),GetLength(m_pque)); for(i=0;i> 1) ^ CRC32_POLYNOMIAL; } else { crc = crc >> 1; } } crc_table32[i] = crc; } } //获得CRC uint32_t get_crc32(uint32_t crcinit, uint8_t *bs, uint32_t bssize) { uint32_t crc = crcinit ^ 0XFFFFFFFF; while(bssize--) { crc = (crc>>8)^crc_table32[(crc&0XFF)^*bs++]; } return crc ^ 0XFFFFFFFF; } //根据mac查询对应升级文件名 char* GetFileNameByMac(list_node* u_list,char* mac) { list_node* list = u_list; if(list == NULL) return NULL; while(list) { upgrade_user_info* info = (upgrade_user_info*)list->data; if(strcmp(info->mac,mac) == 0) { return info->ota_file_name; } list = list->next; } return NULL; } //根据文件名和包序号查询分包数据 ota_info* GetOtaInfoByFileName(list_node* f_list,char* filename) { list_node* list = f_list; if(list == NULL) return NULL; while(list) { ota_info* info = (ota_info*)list->data; if(strcmp(info->new_file_name,filename) == 0) { return info; } list = list->next; } return NULL; } /****************************************************************** * Function Name: UpgradeFileAddFromDB * Arguments: * Return Value: void * Date: 2022-04-02 * Editor: chw * Description: 新增升级文件映射节点 ******************************************************************/ void UpgradeFileAddFromDB(list_node* f_list,char* filename,MYSQL *_db,int ota_mode) { char logstr[512] = {0}; unsigned short ota_file_total_packets = 0; int file_length_bytes = 0; unsigned int ota_length_bytes = 0; list_node* list = f_list; ota_info* info = (ota_info*)malloc(sizeof(ota_info)); memset(info,0x0,sizeof(ota_info)); ota_info* s = GetOtaInfoByFileName(file_list,filename); if(s != NULL) { log("有文件节点,不新增! \n"); free(info); info = NULL; return; } //读取数据库升级文件 unsigned char *fp = readfile_from_db(filename,_db,&file_length_bytes); if (file_length_bytes <= 0) { log("%d file_length_bytes <= 0 ! \n",file_length_bytes); free(info); info = NULL; return; } //计算数据库升级文件crc32校验 init_crc32_tab(); uint32_t crc_value = 0; if(calc_crc32_from_buff((const char*)fp,file_length_bytes,&crc_value) == 0) { log("crc_value = %x\n",crc_value); } //全量 if(ota_mode == 1) { ota_length_bytes = file_length_bytes; memcpy(info->buf,fp,file_length_bytes); crc_value = get_crc32(0,info->buf,file_length_bytes);//ota文件crc校验 info->ota_mode = 1;//模式 strcpy(info->new_file_name,filename);//新文件名 info->new_file_len = (uint32_t)file_length_bytes;//新文件长度 info->ota_file_len = (uint32_t)file_length_bytes;//升级文件长度 info->new_file_crc = crc_value;//新文件crc32校验 info->ota_file_crc = info->new_file_crc;//升级文件crc32校验 ota_file_total_packets = (uint32_t)file_length_bytes/ota_file_packet_len + 1; info->ota_file_total_packets = ota_file_total_packets;//升级文件总包数 info->ota_file_packet_len = ota_file_packet_len;//升级文件每包数据长度 sprintf(logstr,"新文件名:%s,新文件长度:%d,升级文件长度:%d,升级文件总包数:%d,升级文件每包数据长度:%d,新文件crc32校验:%X,升级文件crc32校验:%X \n", info->new_file_name,info->new_file_len,info->ota_file_len,info->ota_file_total_packets,info->ota_file_packet_len,info->new_file_crc,info->ota_file_crc); log(logstr); } //差分 else { ota_length_bytes = (unsigned int)file_length_bytes; memcpy(info->buf,fp,ota_length_bytes); crc_value = get_crc32(0,info->buf,ota_length_bytes);//ota文件crc校验 info->ota_mode = 0;//模式 memcpy(info->old_file_name,fp,48);//旧文件名 info->old_file_len = INT_FROM_BUFF(fp,48);//旧文件长度 info->old_file_crc = INT_FROM_BUFF(fp,52);//旧文件crc32 校验值 memcpy(info->new_file_name,fp+56,48);//新文件名 info->new_file_len = INT_FROM_BUFF(fp,104);//新文件的长度 info->new_file_crc = INT_FROM_BUFF(fp,108);//新文件crc32 校验值 info->ota_state = (char)0;//升级状态 info->ota_file_len = ota_length_bytes;//ota 文件的长度 info->ota_file_crc = crc_value;//ota 文件的crc32校验值 info->ota_file_total_packets = ota_length_bytes/ota_file_packet_len + 1;//ota文件的总的包数 info->ota_file_packet_len = ota_file_packet_len;//ota文件每包的数据长度 log("info->buf 模式:%d,旧文件名:%s,旧文件长度:%d,旧文件crc32:%X,新文件名:%s,新文件的长度:%d,新文件crc32:%X,ota状态:%d,ota文件的长度:%d," "ota文件的crc32:%X,ota文件的总的包数:%d,ota文件每包的数据长度:%d \n", info->ota_mode,info->old_file_name,info->old_file_len, info->old_file_crc,info->new_file_name,info->new_file_len,info->new_file_crc,info->ota_state,info->ota_file_len,info->ota_file_crc, info->ota_file_total_packets,info->ota_file_packet_len); } if(fp != NULL) { free(fp); fp = NULL; } //第一个文件 if(list == NULL) { file_list = list_create((void*)info); log("当前为文件映射第一个节点 \n"); } //没有文件节点 else { list_insert_end(file_list,(void*)info); log("没有文件节点,加入! \n"); } return; } //数据库查询升级文件名 void GetUpgradeFileFromDB(char *mac,MYSQL *_db,char* filename) { MYSQL_RES* res = NULL; MYSQL_ROW row; char query[1024] = {0}; char ota_file[256] = {0}; int ota_mode = 0; sprintf(query,"select file_name,ota_mode from dev_info_gateway where device_mac ='%s'",mac); excuteSql(_db,query); res = mysql_store_result(_db); if (NULL == res) { return ; } row = mysql_fetch_row(res); if (NULL == row) { mysql_free_result(res); return ; } if(!row[0] || !row[1]) { log("%s file_name、ota_mode为空 \n",mac); mysql_free_result(res); return ; } strcpy(ota_file,row[0]); ota_mode = atoi(row[1]); strcpy(filename,ota_file); mysql_free_result(res); //log("mac:%s,file_name:%s \n",mac,ota_file); return ; } /****************************************************************** * Function Name: readfile_from_db * Arguments: * Return Value: void * Date: 2022-04-29 * Editor: chw * Description: 从数据库读取升级文件 ******************************************************************/ unsigned char *readfile_from_db(const char *filename, MYSQL *_db,int *file_length_bytes) { //MYSQL_RES* res = NULL; //MYSQL_ROW row; char sql[512] = {0}; int ret = 0; //unsigned long *lengths; unsigned char* data; MYSQL_BIND result; MYSQL_STMT* stmt = mysql_stmt_init(_db); assert(NULL!=stmt); sprintf(sql,"select file_src from dev_upgrade_file where file_name ='%s'",filename); ret = mysql_stmt_prepare(stmt, sql, strlen(sql)); assert(0==ret); ret = mysql_stmt_execute(stmt); assert(0==ret); memset(&result,0x0,sizeof(result)); unsigned long total_length = 0; result.buffer_type = MYSQL_TYPE_LONG_BLOB; result.length = &total_length; ret = mysql_stmt_bind_result(stmt, &result); assert(0==ret); ret = mysql_stmt_store_result(stmt); assert(0==ret); ret = mysql_stmt_fetch(stmt); if(total_length <= 0) { log("total_length <= 0!\n"); mysql_stmt_close(stmt); return NULL; } unsigned long start = 0; //char buf[1024] = {0}; //lengths = mysql_fetch_lengths(stmt); data = (unsigned char*)malloc(total_length); *file_length_bytes = total_length; if(data == NULL) { log("malloc data failed!"); return NULL; } log("readfile_from_db函数,total_length=%lu\n", total_length); while (start0) { if(remain > (int)size) { crc = get_crc32(crc, (uint8_t*)(buff + index), size); index += size; remain = remain-(int)size; } else { crc = get_crc32(crc, (uint8_t*)(buff + index), remain); index += remain; remain = 0; } } *uiCrcValue = crc; return 0; } #ifdef UPGRADE_BY_FILE //获取文件长度 int GetFileLength(char* file_path) { FILE* pFile = fopen(file_path,"r"); if(pFile == NULL) { return -1; } fseek(pFile,0,SEEK_END); unsigned int file_length_bytes = ftell(pFile); fclose(pFile); pFile = NULL; return file_length_bytes; } //获得文件CRC int32_t calc_file_crc32(const char *pFileName, uint32_t *uiCrcValue) { if(!pFileName || !uiCrcValue) { log("bad parameter\n"); return -1; } if(access(pFileName, F_OK|R_OK)!=0) { log("file not exist or reading file has not permission\n"); return -1; } const uint32_t size = 2*1024; uint8_t crcbuf[size]; uint32_t rdlen; uint32_t crc = 0; // CRC初始值为0 FILE *fd = NULL; if((fd = fopen(pFileName, "r"))==NULL) { log("to do crc 32 check, open file error\n"); return -1; } while((rdlen=fread(crcbuf, sizeof(uint8_t), size, fd))>0) { crc = get_crc32(crc, crcbuf, rdlen); } log("calc_file_crc32:%X \n",crc); *uiCrcValue = crc; fclose(fd); return 0; } //读取文件 // unsigned char *read_myfile(const char *path, int *file_size) // { // if(!path||!file_size) // { // log("bad parameter\n"); // return NULL; // } // //access // if(access(path, F_OK|R_OK)) // { // log("access test file error\n"); // return NULL; // } // //read file // struct stat calib_stat; // int ret = stat(path, &calib_stat); // if(ret!=0) // { // log("File error\n"); // return NULL; // } // FILE *fd = fopen(path, "r"); // if(NULL == fd) // { // log("Open test file error\n"); // return NULL; // } // unsigned char *buffer = malloc(calib_stat.st_size); // if(!buffer) // { // log("malloc error\n"); // fclose(fd); // return NULL; // } // memset(buffer, 0, calib_stat.st_size); // int totalSize = 0, readed = 0; // while((readed = fread(buffer + totalSize,1, 1024,fd)) > 0) // { // totalSize += readed; // } // *file_size = totalSize; // fclose(fd); // return buffer; // } // //增加用户 // void UpgradeUserAdd(list_node* u_list,char* mac,char* file_name) // { // list_node* list = u_list; // upgrade_user_info* info = (upgrade_user_info*)malloc(sizeof(upgrade_user_info)); // strcpy(info->mac,mac); // strcpy(info->ota_file_name,file_name); // //第一个用户 // if(list == NULL) // { // user_list = list_create((void*)info); // } // else // { // //没查询到用户 // if(GetFileNameByMac(u_list,mac) == NULL) // { // list_insert_end(u_list,(void*)info); // } // //查询到用户,修改 // else // { // while(list) // { // upgrade_user_info* old_info = (upgrade_user_info*)list->data; // if(strcmp(old_info->mac,mac) == 0) // { // list->data = (void*)info; // free(old_info); // old_info = NULL; // break; // } // list = list->next; // } // } // } // return; // } // //新增升级文件映射节点 // void UpgradeFileAdd(list_node* f_list,char* filename,int ota_mode) // { // char file_path[256]; // char logstr[512]; // unsigned short ota_file_total_packets = 0; // unsigned int file_length_bytes = 0; // unsigned int ota_length_bytes = 0; // list_node* list = f_list; // ota_info* info = (ota_info*)malloc(sizeof(ota_info)); // memset(info,0x0,sizeof(ota_info)); // sprintf(file_path,"/home/cao/ota/%s",filename); // //计算文件crc32校验 // init_crc32_tab(); // uint32_t crc_value = 0; // if(calc_file_crc32(file_path, &crc_value) == 0) // { // log("file_path:%s,crc_value = %x\n", file_path,crc_value); // } // //读取文件 // unsigned char *fp = read_myfile(file_path,(int*)&file_length_bytes); // if (file_length_bytes <= 0) // { // log("%s file_length_bytes <= 0 ! \n",file_path); // free(info); // info = NULL; // return; // } // //全量 // if(ota_mode == 1) // { // ota_length_bytes = file_length_bytes; // memcpy(info->buf,fp,file_length_bytes); // crc_value = get_crc32(0,info->buf,file_length_bytes);//ota文件crc校验 // info->ota_mode = 1;//模式 // strcpy(info->new_file_name,filename);//新文件名 // info->new_file_len = file_length_bytes;//新文件长度 // info->ota_file_len = file_length_bytes;//升级文件长度 // info->new_file_crc = crc_value;//新文件crc32校验 // info->ota_file_crc = info->new_file_crc;//升级文件crc32校验 // ota_file_total_packets = file_length_bytes/ota_file_packet_len + 1; // info->ota_file_total_packets = ota_file_total_packets;//升级文件总包数 // info->ota_file_packet_len = ota_file_packet_len;//升级文件每包数据长度 // sprintf(logstr,"新文件名:%s,新文件长度:%d,升级文件长度:%d,升级文件总包数:%d,升级文件每包数据长度:%d,新文件crc32校验:%X,升级文件crc32校验:%X \n", // info->new_file_name,info->new_file_len,info->ota_file_len,info->ota_file_total_packets,info->ota_file_packet_len,info->new_file_crc,info->ota_file_crc); // log(logstr); // } // //差分 // else // { // ota_length_bytes = file_length_bytes-130; // memcpy(info->buf,fp+130,ota_length_bytes); // //crc_value = get_crc32(0,info->buf,file_length_bytes-130);//ota文件crc校验 // info->ota_mode = 0;//模式 // memcpy(info->old_file_name,fp+1,48);//旧文件名 // info->old_file_len = INT_FROM_BUFF(fp,49);//旧文件长度 // info->old_file_crc = INT_FROM_BUFF(fp,53);//旧文件crc32 校验值 // memcpy(info->new_file_name,fp+58,48);//新文件名 // info->new_file_len = INT_FROM_BUFF(fp,105);//新文件的长度 // info->new_file_crc = INT_FROM_BUFF(fp,109);//新文件crc32 校验值 // info->ota_state = (char)*(fp+113);//ota 状态 // info->ota_file_len = INT_FROM_BUFF(fp,114);//ota 文件的长度 // info->ota_file_crc = INT_FROM_BUFF(fp,118);//ota 文件的crc32校验值 // info->ota_file_total_packets = SHORT_FROM_BUFF(fp,122);//ota文件的总的包数 // info->ota_file_packet_len = SHORT_FROM_BUFF(fp,124);//ota文件每包的数据长度 // info->boot_addr = INT_FROM_BUFF(fp,126);//app 程序的启动地址 // log("info->buf 模式:%d,旧文件名:%s,旧文件长度:%d,旧文件crc32:%X,新文件名:%s,新文件的长度:%d,新文件crc32:%X,ota状态:%d,ota文件的长度:%d," // "ota文件的crc32:%X,ota文件的总的包数:%d,ota文件每包的数据长度:%d,app程序的启动地址:%X \n", info->ota_mode,info->old_file_name,info->old_file_len, // info->old_file_crc,info->new_file_name,info->new_file_len,info->new_file_crc,info->ota_state,info->ota_file_len,info->ota_file_crc, // info->ota_file_total_packets,info->ota_file_packet_len,info->boot_addr); // } // //log("info->buf crc = %x ,file_length_bytes = %d,ota_length_bytes = %d \n", crc_value,file_length_bytes,ota_length_bytes); // if(fp != NULL) // { // free(fp); // } // //第一个文件 // if(list == NULL) // { // file_list = list_create((void*)info); // log("当前为文件映射第一个节点 \n"); // } // else // { // ota_info* s = GetOtaInfoByFileName(file_list,filename); // if(s == NULL) // { // list_insert_end(file_list,(void*)info); // log("没有文件节点,加入! \n"); // } // else // { // log("有文件节点,不新增! \n"); // } // } // return; // } #endif