#include "board.h" #include "app_nb.h" #include "prj_com.h" #include "param.h" #include "rtc_run.h" #include "board.h" #include "tdc_tof.h" #include "bill.h" #include "alarm.h" #include "stat.h" #include "ymodem.h" Nb_Run_t NbRun; uint8_t NB_CHECK_TDC_COMPLETE_FLAG = 0; uint8_t NbIntStep = 0xFFu; rt_sem_t Nb_thread_run_sem = RT_NULL; // NB线程运行信号量 // 补报相关信息 NbAppFrame_t __attribute__((aligned(4))) NbAppFrame; uint8_t Nb_rx_data(void); void nb_run_init(void) { lpuart0_set_rx_itf(NbRxCh); // nb串口中断 // NbRun.rx_flag = FALSE; NbRun.rx_over_time = 0; NbRun.rx_idx = 0; memset(NbRun.rx_buf, 0, sizeof(NbRun.rx_buf)); memset(NbRun.tx_buf, 0, sizeof(NbRun.tx_buf)); NbRun.cur_at = at_end; NbRun.rsrp = 0; NbRun.rsrq = 0; NbRun.rssi = 0; NbRun.snr = 0; memset(NbRun.imei, 0, sizeof(NbRun.imei)); memset(NbRun.imsi, 0, sizeof(NbRun.imsi)); memset(NbRun.ccid, 0, sizeof(NbRun.ccid)); NbRun.task.count = 0; // NbRun.open_time_out = 0; memset(&NbAppFrame, 0, sizeof(NbAppFrame)); NbRun.status = 0; Nb_thread_run_sem = rt_sem_create("nb_thread_run", 0, RT_IPC_FLAG_FIFO); // 创建nb运行信号量 NbRun.get_imei_flag = 0; NbRun.get_imsi_flag = 0; NbRun.get_ccid_flag = 0; } static void NbBuildDataHeader(uint16_t data_len) { Param_t *param; GetParam(¶m); NbAppFrame.prefix[0] = 0xFE; NbAppFrame.prefix[1] = 0xFE; NbAppFrame.head = 0x68; NbAppFrame.type = 0x10; // memcpy(NbAppFrame.addr, param->dev_id, sizeof(param->dev_id)); NbAppFrame.addr[0] = param->dev_id[5]; NbAppFrame.addr[1] = param->dev_id[4]; NbAppFrame.addr[2] = param->dev_id[3]; NbAppFrame.addr[3] = param->dev_id[2]; NbAppFrame.addr[4] = param->dev_id[1]; NbAppFrame.addr[5] = param->dev_id[0]; // NbAppFrame.addr[6] = param->dev_id[0]; NbAppFrame.addr[6] = 0x00; NbAppFrame.ctrl = 0xA0; NbAppFrame.len[0] = (unsigned char)(data_len); NbAppFrame.len[1] = (unsigned char)(data_len >> 8); } static void NbBuildDataTail(uint16_t data_len) { uint8_t cs = 0; // cs cs = check_sum((uint8_t *)&NbAppFrame.head, 12); cs += check_sum(NbAppFrame.data, data_len); NbAppFrame.data[data_len] = cs; // 8位累加和 NbAppFrame.data[data_len + 1] = 0x16; // 结束位 } /* int NbRcvDataValid(void) { char *p = NULL; int16_t index1 = 0; uint16_t index2 = 0; char data[5] = {0}; uint32_t len; uint8_t cs; uint16_t data_len; if (Nb_rx_data() == 0) return 0; p = strstr((char *)NbRun.rx_buf, ","); if (p == NULL) { // LOG("no data recv flag\n"); NbClrRxBuf(); return 2; } index1 = 2; // strnchar((char *)NbRun.rx_buf, ':', 0); index2 = strnchar((char *)NbRun.rx_buf, ',', 0); // index1 = strnchar((char *)NbRun.rx_buf, ',', 0); // index2 = strnchar((char *)NbRun.rx_buf, ',', 1); if (index1 > index2 || index2 < 1 || (index2 - index1) > sizeof(data)) { NbClrRxBuf(); return 3; } memcpy(data, NbRun.rx_buf + index1, index2 - index1); str2int(data, &len); // 已发送数据长度 if (len < 14) { NbClrRxBuf(); return 4; } StrToHexBuf((char *)&NbAppFrame.head, (char *)(NbRun.rx_buf + index2 + 1), len); NbClrRxBuf(); if (NbAppFrame.head != 0x68) { // 帧头不正确 return 5; } data_len = (NbAppFrame.len[1] << 8) | NbAppFrame.len[0]; if (len != 14 + data_len) { // 已发送长度不正确 return 6; } cs = check_sum((uint8_t *)&NbAppFrame.head, len - 2); if (cs != NbAppFrame.data[data_len]) { // crc校验不正确 return 7; } if (NbAppFrame.data[data_len + 1] != 0x16) { // 结束符不正确 return 8; } return 1; }*/ char *get_frame_start(char *buf, uint16_t len) { if (buf == NULL || len < 2) { return NULL; } for (uint16_t i = 0; i <= len - 2; i++) { if ((uint8_t)buf[i] == 0x68 && (uint8_t)buf[i + 1] == 0x10) { return &buf[i]; } } return NULL; } int NbRcvDataValid(void) { char *p_comma = NULL; char *p_start = NULL; char len_str[8] = {0}; uint32_t total_frame_len = 0; uint8_t cs; uint8_t head_len = 0; uint16_t data_len; // 1. 检查是否有数据接收完成标志 if (Nb_rx_data() == 0) return 0; // 无数据,继续等待 // 2. 接收数据关键字 p_comma = strstr((char *)NbRun.rx_buf, "+QIRD: "); if (p_comma == NULL) { // 没有逗号,检查是否全是空白或OK,如果是则清除 if (strstr((char *)NbRun.rx_buf, "OK") != NULL || NbRun.rx_idx > 50) { NbClrRxBuf(); } return 0; // 数据可能不完整,继续等待 } p_start = p_comma + 7; //+QIRD:后的数据开始位置 while (*p_start >= '0' && *p_start <= '9') { len_str[head_len++] = *p_start++; } len_str[head_len] = '\0'; total_frame_len = atoi(len_str); // 数据总长度 if (total_frame_len < 14) { NbClrRxBuf(); return 4; } // 6. 检查 Hex 数据完整性 // char *p_hex_start = p_start + head_len + 2; // 包含关键字+ 长度域+ 回车换行符 char *p_hex_start = get_frame_start(p_start + 2, NbRun.rx_idx - (p_start + 2 - (char *)NbRun.rx_buf)); if (p_hex_start == NULL) { return 0; // 数据不完整,继续等待 } // 6.2 检查校验和 cs = 0; uint16_t available_chars = NbRun.rx_idx - (p_hex_start - (char *)NbRun.rx_buf); if (available_chars < total_frame_len) // 这里可能要改为接收二进制数据,(去掉*2) { return 0; // 数据不完整,继续等待 } // 7. Hex 转 Binary // StrToHexBuf((char *)&NbAppFrame.head, p_hex_start, total_frame_len);//如果是二进制数据,则不需要转换 memcpy((char *)&NbAppFrame.head, p_hex_start, total_frame_len); NbClrRxBuf(); // 8. 校验帧头 if (NbAppFrame.head != 0x68) return 5; // 9. 校验长度一致性 data_len = (NbAppFrame.len[1] << 8) | NbAppFrame.len[0]; if (total_frame_len != (14 + data_len)) return 6; // 10. 校验 Check Sum cs = check_sum((uint8_t *)&NbAppFrame.head, total_frame_len - 2); if (cs != NbAppFrame.data[data_len]) return 7; // 11. 校验帧尾 if (NbAppFrame.data[data_len + 1] != 0x16) return 8; return 1; // 验证成功 } void GetNbRun(Nb_Run_t **nb_run) { *nb_run = &NbRun; } void NbRxCh(uint8_t ch) { if (NbRun.rx_idx >= NB_RXLEN_MAX - 1) { return; } NbRun.rx_buf[NbRun.rx_idx++] = ch; NbRun.rx_over_time = 4; #ifdef DEBUG_NB // LOG("%c",ch); #endif } // void NbRxTimeout(void) //{ // if (NbRun.rx_over_time >= 2) { // NbRun.rx_over_time--; // } else if (NbRun.rx_over_time == 1) { // NbRun.rx_flag = TRUE; // NbRun.rx_over_time--; // } // } uint8_t Nb_rx_data(void) { if (NbRun.rx_over_time != 0) { NbRun.rx_over_time--; } if (NbRun.rx_over_time == 1) { #ifdef DEBUG_NB ///////////////////// unsigned short i; for (i = 0; i < NbRun.rx_idx; i++) LOG("%c", NbRun.rx_buf[i]); // LOG("%s\r\n",NbRun.rx_buf); /////////////////// #endif return 1; // 已收到数据 // } } return 0; } void NbClrRxBuf(void) { NbRun.rx_idx = 0; memset(NbRun.rx_buf, 0, sizeof(NbRun.rx_buf)); } int8_t get_nb_imsi_code(void) { char *p = NULL; char *q = NULL; uint8_t i, flag; p = strstr((char *)NbRun.rx_buf, "OK"); if (p == NULL) { return -1; } p = strstr((char *)NbRun.rx_buf, "\r\n"); // 找到第一个回车符 if (p != NULL && strlen(p) > 2) { q = strstr(p + 2, "\r\n"); // 第二个回车符,id在两个回车符之间 if (q != NULL && (q - p - 2) > 0 && (q - p - 2) < 16) { // 长度大于2小于15 *q = 0; strcpy(NbRun.imsi, p + 2); /// LOG("imsi = %s\n", NbRun.imsi); } } flag = 0; for (i = 0; NbRun.imsi[i] != 0; i++) { if (NbRun.imsi[i] < '0' || NbRun.imsi[i] > '9') { flag = 1; break; } } if (flag == 1 || i != 15) return -1; NbRun.get_imsi_flag = 1; // 已获取IMEI号标志 return 0; } int8_t get_nb_imei_code(void) { char *p = NULL; char *q = NULL; uint8_t i, flag; p = strstr((char *)NbRun.rx_buf, "OK"); if (p == NULL) { return -1; } p = strstr((char *)NbRun.rx_buf, "+CGSN:"); if (p != NULL) { p = strstr((char *)NbRun.rx_buf, "\""); // 找到第一个"所在位置 if (p != NULL && strlen(p + 1) > 1) { q = strstr(p + 1, "\"\r\n"); // 回车换行符所在位置 if (q != NULL && (q - p - 1) > 0 && (q - p - 1) < 16) { // 存在回车换行符,且:到换行符之间有信息,且长度小于16 *q = 0; // 把第二个"置0,作为字符串结束位置 strcpy(NbRun.imei, p + 1); } } } flag = 0; for (i = 0; NbRun.imei[i] != 0; i++) { if (NbRun.imei[i] < '0' || NbRun.imei[i] > '9') { flag = 1; break; } } if (flag == 1 || i != 15) return -1; NbRun.get_imei_flag = 1; // 已获取IMEI号标志 #ifdef APP_DEBUG_PRINT LOG("imei=%s\r\n", NbRun.imei); #endif return 0; } int8_t get_nb_ccid_code(void) { char *p = NULL; char *q = NULL; uint8_t i, flag; p = strstr((char *)NbRun.rx_buf, "OK"); if (p == NULL) { return -1; } p = strstr((char *)NbRun.rx_buf, ":"); p++; if (p != NULL && strlen(p) > 1) { q = strstr(p + 1, "\r\n"); if (q != NULL && (q - p - 1) > 0 && (q - p - 1) < 21) { *q = 0; strcpy(NbRun.ccid, p + 1); } } flag = 0; for (i = 0; NbRun.ccid[i] != 0; i++) { if (NbRun.ccid[i] < '0' || NbRun.ccid[i] > '9') { flag = 1; break; } } if (flag == 1 || i != 20) return -1; NbRun.get_ccid_flag = 1; // 已获取IMEI号标志 #ifdef APP_DEBUG_PRINT LOG("iccid=%s\r\n", NbRun.ccid); #endif return 0; } // NUESTATS:CELL,,,,,,, // NUESTATS:CELL,2401,476,1,-920,-41,-879,144 // 在 app_nb.c 中找到 NbSetUploadMode 函数并替换 void NbSetUploadMode(uint8_t mode) { // 【修改点】:始终更新 mode,使用 |= 确保不覆盖已有的触发原因 // 例如:如果之前是定时上报(0x02),现在发生了错误报警(0x10),mode 将变为 0x12 NbRun.upload.mode |= mode; // 只有在尚未标记上传任务时,才设置 flag 并唤醒线程 if (NbRun.upload.flag == FALSE) { NbRun.upload.flag = TRUE; NbRun.task.flag = 1; NbRun.cur_afn = 0x0010; rt_sem_release(Nb_thread_run_sem); // 触发nb线程 } } /////////////////等待接收指定的回复/////////////////////// // ack:回复的字符串 // timeout:等待超时时间 秒单位 //: 返回0:成功, -1:等待超时 ///////////////////////////////////// int8_t Nb_check_rec_valid(uint8_t state, char *ack) { // if(state == COAP_WAIT_REG){ // if(strstr((const char *)NbRun.rx_buf, ack) != NULL ) // } // else if (strstr((const char *)NbRun.rx_buf, ack) != NULL) { // 收到要求的回复 return 1; // 收到正确回复,进行下一步 } if (strstr((char *)&NbRun.rx_buf[0], "\r\nOK\r\n") != NULL || strstr((char *)&NbRun.rx_buf[0], "\r\nERROR\r\n") != NULL) { return -1; // 错误回复,重新发送 } return 0; // 其他需要继续等待 } void nb_send_data_route(unsigned char *data, uint16_t len) // 发送数据 TCP { char tmp[16] = {0}; memset(NbRun.tx_buf, 0, sizeof(NbRun.tx_buf)); strcpy((char *)NbRun.tx_buf, "AT+QISENDEX=0,"); // at指令 发送数据 // int2str(len, tmp); // 转换成字符串 strcat((char *)NbRun.tx_buf, "\""); HexBufToStr((char *)NbRun.tx_buf + strlen((char *)NbRun.tx_buf), data, len); // 不加前面的指令部分,只发送数据部分 strcat((char *)NbRun.tx_buf, "\""); strcat((char *)NbRun.tx_buf, "\r\n\0"); // 末尾回车换行符 // NbRoutineEventPro((char *)NbRun.tx_buf, (char *)NB_NMGS_ACK, 3, 60); } UPLOAD_DEF upload_dat; const uint8_t monthday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /////////////////////////////////////////// /////////////////////////////////main.c中运行,判断是否应该触发nb线程////////////////////////////////////////////// // 参数 无 // 返回 无 //////////////////////////////////////////////////////////////////////////////////////////////////////////////// uint8_t settlement_day_upload_check(void) { // 结算日上报 if (Paramx.settlement_day == 0) return 0; rtc_run_t *rr; get_rtc_run(&rr); uint8_t days, setday; days = monthday[rr->month - 1]; if (rr->year % 4 == 0 && rr->month == 2) { days++; } // 计算当月天数 //////////////////////确定当天上报是否是结算日上报///////////////////////////////////// int32_t now, start; now = (int32_t)(3600 * rr->hour + 60 * rr->minute + rr->second); start = (int32_t)(Paramx.report_base_hour * 3600 + Paramx.report_base_min * 60 + (Paramx.report_base_sec & 0xfe)); if (Paramx.settlement_day == 0) { return 0; } if (Paramx.settlement_day > days) { setday = days; } else { setday = Paramx.settlement_day; } if (now < start) return 0; if (setday == rr->day && now - start <= 5) { return NB_UPLOAD_SETTLE_DAY; } return 0; } uint8_t regular_upload_check(void) { // 定时上报 uint32_t *alarmbit; all_alarm_status(&alarmbit); if ((*alarmbit & ERR_LOW_POWER) != 0) return 0; // 低电压不上报 uint8_t i; rtc_run_t *rr; get_rtc_run(&rr); int32_t now, start; now = (int32_t)(3600 * rr->hour + 60 * rr->minute + rr->second); start = (int32_t)(Paramx.report_base_hour * 3600 + Paramx.report_base_min * 60 + (Paramx.report_base_sec & 0xfe)); if (now < start) return 0; // 未到上报时间 if (((now - start) % (Paramx.report_interval * 60)) <= 5) return NB_UPLOAD_TIMER; return 0; } uint8_t dma_upload_check(void) { uint32_t *alarmbit; all_alarm_status(&alarmbit); if ((*alarmbit & ERR_LOW_POWER) != 0) return 0; // 低电压DMA不上报 if (Paramx.dma_interval == 0) return 0; // 无dma上报 int32_t start_sec, end_sec, now_sec; rtc_run_t *rr; get_rtc_run(&rr); start_sec = (int32_t)(3600 * Paramx.dma_report_start[0] + 60 * Paramx.dma_report_start[1] + Paramx.dma_report_start[2]); end_sec = (int32_t)(3600 * Paramx.dma_report_end[0] + 60 * Paramx.dma_report_end[1] + Paramx.dma_report_end[2]); now_sec = (int32_t)(3600 * rr->hour + 60 * rr->minute + rr->second); if (now_sec < start_sec || now_sec > end_sec + 3) return 0; // 不在dma时间范围内 if (((now_sec - start_sec) % (Paramx.dma_interval * 60)) <= 5) { return NB_UPLOAD_DMA; } return 0; } ////////////////故障上报监测/////////////////////// // 参数:无 // 返回:有故障NB_UPLOAD_ERR 无故障:0 /////////////////////////////////////////////// uint8_t err_upload_check(void) { uint32_t *alarmbit; uint32_t temp; if (StartUp < 15) return 0; all_alarm_status(&alarmbit); temp = (upload_dat.err_mem_dat & (*alarmbit)) ^ (*alarmbit); // 检查是否有异常bit被置位 if (temp != 0) { upload_dat.err_mem_dat = *alarmbit; return NB_UPLOAD_ERR; } return 0; } uint8_t rept_upload_check(void) { // 重复上报监测 uint8_t i; rtc_run_t *rr; get_rtc_run(&rr); tm_t now_time; now_time.year = rr->year; now_time.month = rr->month; now_time.day = rr->day; now_time.hour = rr->hour; now_time.min = rr->minute; now_time.sec = rr->second; uint32_t now_sec = (uint32_t)(now_time.hour * 3600 + now_time.min * 60 + now_time.sec); uint32_t start_sec = (uint32_t)(upload_dat.rept_time.hour * 3600 + upload_dat.rept_time.min * 60 + (upload_dat.rept_time.sec & 0xfc)); uint8_t ret = 0; if (upload_dat.rept_runflag != 0) { // if (datecmp(now_time, upload_dat.rept_time) >= 0) if (now_sec >= start_sec && now_sec <= start_sec + 3) { upload_dat.rept_runflag = 0; return NB_UPLOAD_REPT; } } return 0; } // 手动上报 uint8_t manual_upload_check(void) { if (upload_dat.manual_runflag == 1) { upload_dat.manual_runflag = 0; return NB_UPLOAD_MANUAL; } return 0; } short up_signal_cnt = -100; // 用于上报过程中动态显示信号强度 /////////////////////////////////main.c中运行,判断是否应该触发nb线程,1秒1一次////////////////////////////////////////////// // 参数 无 // 返回 无 //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 在 app_nb.c 中找到 nb_task_run_check 函数并替换 void nb_task_run_check(void) { uint8_t trik_flag = 0; extern const int16_t rsrp_level[5]; // 信号强度动态显示逻辑保持不变 if (NbRun.upload.flag != 0) { up_signal_cnt = (up_signal_cnt + 1) % 5; NbRun.rsrp = rsrp_level[up_signal_cnt]; } else NbRun.rsrp = NbRun.rsrp_bak; #ifdef debug_dog if (StartUp == 5) { rt_enter_critical(); McuFlash_EraseSegment(DIFF_PATCH_PACKET_START, DIFF_PATCH_PACKET_SIZE); rt_exit_critical(); rt_enter_critical(); uint8_t i; for (i = 0; i < 16; i++) xx[i] = i + 1; write_flash_memory(xx, DIFF_PATCH_PACKET_START, 16); rt_exit_critical(); while (1) ; } #endif if (NbRun.reset_mcu_cnt != 0) { NbRun.reset_mcu_cnt--; if (NbRun.reset_mcu_cnt == 2) LOG("ready to reset mcu\r\n"); if (NbRun.reset_mcu_cnt == 1) { // watchdog_init(WdtResetEn, WdtT3s28); // feed_dog(); NVIC_SystemReset(); // while (1); // { // } } // NVIC_SystemReset(); } // 【修改点】:不再因为 NbRun.upload.flag 为真而直接 return // 即使正在上传,我们也要检查是否有新的触发原因(如错误报警),并将其加入 mode trik_flag = settlement_day_upload_check(); trik_flag |= regular_upload_check(); trik_flag |= dma_upload_check(); trik_flag |= err_upload_check(); // 错误报警通常优先级最高,但也作为位之一 trik_flag |= rept_upload_check(); trik_flag |= manual_upload_check(); if (trik_flag != 0) { // 调用 NbSetUploadMode,它内部会处理 |= 逻辑和线程唤醒逻辑 NbSetUploadMode(trik_flag); } // 注意:如果上传已经完成或失败,NbRun.upload.flag 会在 NbTread 末尾清零 // 此时下一次 nb_task_run_check 运行时会正常触发新的上传任务 } // 上报数据准备,在tx_buf中 void nb_upload_data_ready(void) { NbBuildDataReport(); // 组织上报数据,在NbAppFrame中 uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; // 数据总长度 nb_send_data_route((uint8_t *)&NbAppFrame, len); } void nb_set_ip_para_ack(void) { // NbBuildSetIpAndPortRsp(); nb_set_para_rept(5, 0x20, 1); uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); } void nb_set_regular_report_para_ack(int8_t ret) { // NbBuildSetReportPointRsp(ret); nb_set_para_rept(5, 0x21, ret); uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); } void nb_set_dma_report_para_ack(int8_t ret) { /// NbBuildSetDmaReportPointRsp(ret); nb_set_para_rept(5, 0x22, ret); uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); } void nb_set_rtc_data_ack(int8_t ret) { // NbBuildSetDateTimeRsp(ret); nb_set_para_rept(5, 0x23, ret); } void nb_set_flow_alarm_para_ack(int8_t ret) { // NbBuildSetFlowAlarmThresRsp(ret); nb_set_para_rept(5, 0x24, ret); uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); } void nb_set_pressure_alarm_para_ack(int8_t ret) { // NbBuildSetPressureAlarmThresRsp(ret); nb_set_para_rept(5, 0x25, ret); } void nb_set_temp_alarm_para_ack(int8_t ret) { // NbBuildSetTempAlarmThresRsp(ret); nb_set_para_rept(5, 0x26, ret); } void nb_set_settlment_day_para_ack(int8_t ret) { // NbBuildSetSettleRsp(ret); nb_set_para_rept(5, 0x27, ret); } void nb_set_meter_bottom_data_ack(int8_t ret) { // NbBuildSetBaseReadingRsp(ret); nb_set_para_rept(5, 0x28, ret); } void nb_read_month_record_ack(void) { NbBuildReportMonthRecord(); } void nb_read_day_record_ack(void) { NbBuildReportDayRecord(); } int8_t nb_read_hour_record_ack(void) { uint32_t start, end, ndate; uint8_t loop_count = 0; // 用于限制循环次数 start = (NbAppFrame.data[5] << 16) | (NbAppFrame.data[6] << 8) | NbAppFrame.data[7]; end = (NbAppFrame.data[9] << 16) | (NbAppFrame.data[10] << 8) | NbAppFrame.data[11]; rtc_run_t *rr; get_rtc_run(&rr); // 获取当前时间 ndate = (rr->year << 16) | (rr->month << 8) | rr->day; // 计算30天前的日期边界 uint32_t b30date = ndate; for (uint8_t i = 0; i < 30; i++) b30date = prev_day(b30date); // --- 修改部分开始 --- // 原逻辑:如果时间不合法,直接返回 -1 // 新逻辑:如果时间不合法,构建一个长度为248且数据全为0的帧发送一次 // 检查合法性 uint8_t is_invalid = 0; if (start > ndate || end < b30date || start > end) { is_invalid = 1; } // 校正 start if (start < b30date) start = b30date; // 校正 end if (end > ndate) end = ndate; // 再次检查校正后是否合法 if (start > end) { is_invalid = 1; } if (is_invalid) { uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; // 定义正常的数据长度 (参考 NbBuildReportHourRecord) uint16_t data_len = 248; // 构建帧头 NbBuildDataHeader(data_len); // 设置 AFN 为 0x34 (Read Hour Record Response) NbAppFrame.data[0] = 0x34; NbAppFrame.data[1] = 0x00; // 设置 MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); // 将数据区域全部清零 (NbBuildDataHeader 不会自动清零 data 区域,需手动处理) // 注意:NbAppFrame.data 的大小通常足够容纳 248 字节 memset(&NbAppFrame.data[4], 0, data_len); // 构建帧尾 (包含校验和) NbBuildDataTail(data_len); uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); // 发送 AT 命令 if (at_cmd_send(COAP_QLWDATASEND, (char *)NbRun.tx_buf, NB_AT_ACK, 3, 60) == -1) return -1; return 0; // 返回成功,表示已处理本次请求 } // --- 修改部分结束 --- ndate = start; uint16_t len; // 无限循环处理所有有效日期,但限制 loop_count < 12 以防死循环或数据过多 while (ndate <= end && loop_count < 12) { NbBuildReportHourRecord((uint8_t)(ndate >> 16), (uint8_t)(ndate >> 8), (uint8_t)(ndate)); len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); if (at_cmd_send(COAP_QLWDATASEND, (char *)NbRun.tx_buf, NB_AT_ACK, 3, 60) == -1) return -1; ndate = next_day(ndate); loop_count++; // 增加计数器 } return 0; } int8_t nb_read_5min_record_ack(void) { // 将大型局部变量改为静态变量,以减少栈使用 static tm_t start, end, cur; static tm_t now_tm, limit_start_tm; uint16_t len; uint8_t loop_count = 0; // ? // 1. е???? start.year = NbAppFrame.data[5]; start.month = NbAppFrame.data[6]; start.day = NbAppFrame.data[7]; start.hour = NbAppFrame.data[8]; start.min = NbAppFrame.data[9]; start.sec = 0; end.year = NbAppFrame.data[11]; end.month = NbAppFrame.data[12]; end.day = NbAppFrame.data[13]; end.hour = NbAppFrame.data[14]; end.min = NbAppFrame.data[15]; end.sec = 0; // 2. ????15?? rtc_run_t *rr; get_rtc_run(&rr); // ?? tm_t (??) now_tm.year = rr->year; now_tm.month = rr->month; now_tm.day = rr->day; now_tm.hour = rr->hour; now_tm.min = rr->minute; now_tm.sec = rr->second; // 15??? uint32_t date32 = (rr->year << 16) | (rr->month << 8) | rr->day; for (uint8_t i = 0; i < 15; i++) { date32 = prev_day(date32); } limit_start_tm.year = (date32 >> 16) & 0xFF; limit_start_tm.month = (date32 >> 8) & 0xFF; limit_start_tm.day = date32 & 0xFF; limit_start_tm.hour = 0; limit_start_tm.min = 0; limit_start_tm.sec = 0; // 3. ??: start ? end uint8_t is_invalid = 0; if (datecmp(start, end) > 0) { is_invalid = 1; } // 4. ?Ч??Χ // ЧΧ: [limit_start_tm, now_tm] if (datecmp(end, limit_start_tm) < 0) // ?15? { is_invalid = 1; } if (datecmp(start, now_tm) > 0) // ????? { is_invalid = 1; } // 5. У start end ЧΧ if (datecmp(start, limit_start_tm) < 0) { start = limit_start_tm; } if (datecmp(end, now_tm) > 0) { end = now_tm; } // ?μУ? if (datecmp(start, end) > 0) { is_invalid = 1; } // --- ???? --- // ????388??0?? if (is_invalid) { uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; // uint16_t data_len = 388; // ?? NbBuildDataHeader(data_len); // AFN ? 0x37 (Read 5Min Record Response) NbAppFrame.data[0] = 0x37; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); // ? (NbBuildDataHeader ? data ?) // ??NbAppFrame.data ?С? 388 ? memset(&NbAppFrame.data[4], 0, data_len); // ?β (У) NbBuildDataTail(data_len); len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); // AT if (at_cmd_send(COAP_QLWDATASEND, (char *)NbRun.tx_buf, NB_AT_ACK, 3, 60) == -1) return -1; return 0; // ???? } // --- ??? --- // 6. ??12 cur = start; // 初始化 cur while (datecmp(cur, end) < 0) // ? while ???м { // ? if (loop_count >= 12) { break; } NbBuildReport5MinRecord(&cur); len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); if (at_cmd_send(COAP_QLWDATASEND, (char *)NbRun.tx_buf, NB_AT_ACK, 3, 60) == -1) return -1; // 2С? time_add_secs(&cur, &cur, 2 * 60 * 60); loop_count++; // ? } return 0; } void nb_read_log_record_ack(void) { NbBuildReportLogRecord(); } /////// 设置开始升级信息///////////////////// /// 返回 1:成功 2:版本号不对 3:长度超限 int8_t nb_set_patch_info(void) { PATCH_INFO_DEF dat; uint8_t *src = &NbAppFrame.data[4]; memcpy(dat.cur_ver, src, 5); memcpy(dat.new_ver, src + 5, 5); dat.patch_len = src[10] + ((uint16_t)src[11] << 8) + ((uint32_t)src[12] << 16) + ((uint32_t)src[13] << 24); dat.per_packet_len = src[14] + 256 * src[15]; dat.packet_crc = src[16] + 256 * src[17]; uint8_t ret = set_patch_info(&dat); // 返回1,2,3 //1成功 // if(ret != 0)return -1; // else return 0; return ret; } //////////////////////////////////// // 1.升级成功结束 // 2:升级失败,crc校验失败 // 3.升级失败,多次尝试未获得升级包 // 4.失败,未能成功运行 // 5.失败版本不匹配 // 0x10:重新读取本包数据 // 0x20:获取下一包 /////////////////////////////////// void nb_set_patch_info_ack(int8_t stat) { // if(stat>=1 && stat <= 6 ){ if (stat <= 3) NbBuildReportUpdateStatus(stat); //} else // if(stat == 0x10){ NbBuildGetPackage(); //} // else if(stat == 0x20){ // NbBuildGetPackage(); // } } /////////////在采样中断结束,刚好允许进入低功耗时发送指令,避免接收失败////////////////////// // dat:要发送的数据 //////////////////////////////////// // uint8_t NB_CHECK_TDC_COMPLETE_FLAG // check G_TDC_CAN_DEEP_SLEEP 0->1 void send_nb_data_after_sample(char *dat) { extern uint8_t rtc_1s_flag; if (NB_CHECK_TDC_COMPLETE_FLAG == 1 && rtc_1s_flag == 1) { nb_tx_str(dat); rtc_1s_flag = 0; } NB_CHECK_TDC_COMPLETE_FLAG = 0; } int8_t rec_data_check(void) { // 1. 主动请求数据 send_nb_data_after_sample("AT+QIRD=0,1500\r\n"); // 读取数据,最长1500 tcp限制 // 2. 尝试解析数据 uint8_t result = NbRcvDataValid(); // --- 情况 A: 没有收到完整数据或正在等待 --- if (result == 0) { // 如果是 OTA 过程,允许较长的等待时间 if (NbRun.cur_afn == 0x50 || NbRun.cur_afn == 0x51) { NbRun.get_patch_time_out++; if (NbRun.get_patch_time_out < 120) // 约 5-10秒 return 1; // 继续等待 else return -1; // OTA 超时 } return 1; // 普通命令继续等待 } // --- 情况 B: 数据校验失败 (格式错误、CS错误等) --- if (result > 1) { #ifdef DEBUG_NB LOG("Rcv Err: %d\r\n", result); #endif // 收到垃圾数据,清除状态,让上层循环重试 AT+NMGR // 返回 1 表示“本次尝试失败,请重试”,而不是“继续等待当前缓冲” return 1; } // --- 情况 C: 数据校验成功 (result == 1) --- NbRun.get_patch_time_out = 0; // 重置 OTA 超时计数 uint16_t afn = (NbAppFrame.data[1] << 8) | NbAppFrame.data[0]; uint16_t data_len = (NbAppFrame.len[1] << 8) | NbAppFrame.len[0]; // 更新当前 AFN 状态,用于后续多包交互(如 OTA) if (afn != 0x51) { // 数据包通常不改变 cur_afn,除非是第一个包 NbRun.cur_afn = afn; } int8_t ret = 0; uint8_t need_reply = 0; // 标记是否需要发送应用层回复 uint8_t update_status = 0; #ifdef DEBUG_NB LOG("Rec OK, AFN=0x%04X, Len=%d\r\n", afn, data_len); #endif // --- 业务逻辑分发 --- switch (afn) { case 0x20: // Set IP/Port if (data_len != 16) return 1; ret = set_server_ip_port_para((SERVER_IP_PORT_DEF *)&NbAppFrame.data[4]); nb_set_ip_para_ack(); need_reply = 1; break; case 0x21: // Set Regular Report if (data_len != 9) return 1; { REGULAR_REPORT_DEF dat; dat.report_base_hour = NbAppFrame.data[4]; dat.report_base_min = NbAppFrame.data[5]; dat.report_base_sec = NbAppFrame.data[6]; dat.report_interval = NbAppFrame.data[7] + 256 * NbAppFrame.data[8]; ret = set_regular_report_para(&dat); } nb_set_regular_report_para_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x22: // Set DMA Report if (data_len != 11) return 1; { DMA_REGULAR_REPORT_DEF dat; dat.dma_report_start_hour = NbAppFrame.data[4]; dat.dma_report_start_min = NbAppFrame.data[5]; dat.dma_report_start_sec = NbAppFrame.data[6]; dat.dma_report_end_hour = NbAppFrame.data[7]; dat.dma_report_end_min = NbAppFrame.data[8]; dat.dma_report_end_sec = NbAppFrame.data[9]; dat.dma_interval = NbAppFrame.data[10]; ret = set_dma_regular_report_para(&dat); } nb_set_dma_report_para_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x23: // Set RTC if (data_len != 11) return 1; { tm_t tm; // tm.year = ((NbAppFrame.data[4] << 8) | NbAppFrame.data[5]) % 2000; tm.year = NbAppFrame.data[5]; tm.month = NbAppFrame.data[6]; tm.day = NbAppFrame.data[7]; tm.hour = NbAppFrame.data[8]; tm.min = NbAppFrame.data[9]; tm.sec = NbAppFrame.data[10]; ret = SetRtcTime(&tm); } save_cur_flow_flag = 1; nb_set_rtc_data_ack(ret); need_reply = 1; break; case 0x24: // Set Flow Alarm if (data_len != 18) return 1; { FLOW_ALARM_PARA_DEF flow_alarm_para; flow_alarm_para.large_flow_alarm_thres = (NbAppFrame.data[7] << 24) | (NbAppFrame.data[6] << 16) | (NbAppFrame.data[5] << 8) | NbAppFrame.data[4]; flow_alarm_para.large_flow_cnti_moni_time = (NbAppFrame.data[9] << 8) | NbAppFrame.data[8]; flow_alarm_para.cnti_flow_cnti_moni_time = (NbAppFrame.data[11] << 8) | NbAppFrame.data[10]; flow_alarm_para.leak_flow_alarm_thres = (NbAppFrame.data[15] << 24) | (NbAppFrame.data[14] << 16) | (NbAppFrame.data[13] << 8) | NbAppFrame.data[12]; flow_alarm_para.leak_flow_cnti_moni_time = (NbAppFrame.data[17] << 8) | NbAppFrame.data[16]; ret = set_flow_alarm_para(&flow_alarm_para); } nb_set_flow_alarm_para_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x25: // Set Press Alarm if (data_len != 6) return 1; ret = set_press_alarm_para((PRESS_ALARM_PARA_DEF *)&NbAppFrame.data[4]); nb_set_pressure_alarm_para_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x26: // Set Temp Alarm if (data_len != 8) return 1; ret = set_temp_alarm_para((TEMP_ALARM_PARA_DEF *)&NbAppFrame.data[4]); nb_set_temp_alarm_para_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x27: // Set Settlement Day if (data_len != 5) return 1; ret = set_settlement_day_para((SETTLEMENT_DAY_DEF *)&NbAppFrame.data[4]); nb_set_settlment_day_para_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x28: // Set Bottom Data if (data_len != 12) return 1; ret = set_meter_bottom_data((METER_BOTTOM_DEF *)&NbAppFrame.data[4]); nb_set_meter_bottom_data_ack(ret == 0 ? 1 : ret); need_reply = 1; break; case 0x30: // Read Month Record nb_read_month_record_ack(); need_reply = 1; break; case 0x32: // Read Day Record nb_read_day_record_ack(); need_reply = 1; break; case 0x34: // Read Hour Record // 注意:nb_read_hour_record_ack 内部已经完成了所有分包发送和 AT 交互 ret = nb_read_hour_record_ack(); if (ret == -1) { LOG("Read Hour Err\r\n"); return -1; // 严重错误,退出上传会话 } // 内部已处理完毕,不需要再执行底部的通用回复逻辑 return 0; case 0x36: // Read 5Min Record ret = nb_read_5min_record_ack(); if (ret == -1) { LOG("Read 5Min Err\r\n"); return -1; } // 内部已处理完毕 return 0; case 0x38: // Read Log nb_read_log_record_ack(); need_reply = 1; break; case 0x40: // Close Link LOG("Close Link\r\n"); return 2; // 特殊返回码,指示关闭链接 case 0x50: // OTA Start if (data_len != 22) return 1; ret = nb_set_patch_info(); if (ret != 1) { nb_set_patch_info_ack(ret); need_reply = 1; } else { NbRun.mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; nb_set_patch_info_ack(0x10); // Request first package need_reply = 1; } NbRun.get_patch_retry_cnt = 0; break; case 0x51: // OTA Data Package // 检查数据长度是否匹配预期 if (data_len != (8 + PatchInfo.per_packet_len)) { update_status = 0x20; // Resend request } else { DIFF_PACKET_DEF *dat = (DIFF_PACKET_DEF *)&NbAppFrame.data[4]; ret = write_diff_patch_to_flash(dat); if (ret == 1) { // Success NbRun.get_patch_retry_cnt = 0; if (PatchInfo.update_status == 1) { update_status = 0x10; // Request next } else { update_status = 1; // Finish } } else { update_status = ret; // Error code } } // 重试计数逻辑 if (update_status == 0x10 || update_status == 1) { NbRun.get_patch_retry_cnt = 0; } else { NbRun.get_patch_retry_cnt++; } if (NbRun.get_patch_retry_cnt > 20) { update_status = 3; // Fail } if (update_status <= 3) { NbRun.cur_afn = 0; // Reset AFN after handling packet } nb_set_patch_info_ack(update_status); need_reply = 1; if (update_status >= 1 && update_status <= 6) { // OTA finished or failed, will return 2 later } break; default: LOG("Unknown AFN: 0x%04X\r\n", afn); return 1; // Ignore unknown, wait for next } // --- 发送应用层回复 --- if (need_reply == 1) { uint16_t len = ((NbAppFrame.len[1] << 8) | NbAppFrame.len[0]) + 16; nb_send_data_route((uint8_t *)&NbAppFrame, len); // 发送 AT 指令并将数据发往网络 ret = at_cmd_send(55, (char *)NbRun.tx_buf, NB_AT_ACK, 6, 5); if (ret == -1) { return -1; // Send failed } // 如果是 OTA 结束状态,返回 2 以退出循环 if (afn == 0x51 && (update_status >= 1 && update_status <= 6)) { return 2; } return 0; // Success } return 1; // Should not reach here if logic is correct } void rtc_add_secs(TIME_DEF *dest, rtc_run_t *src, unsigned short secs) { unsigned short dat; // dat=secs; dest->second = (src->second + dat) % 60; dat = (src->second + dat) / 60; dest->minute = (src->minute + dat) % 60; dat = (src->minute + dat) / 60; dest->hour = (dat + src->hour) % 24; } int8_t connected_flag; void NbTread(void) { uint32_t retry_time; uint8_t vflag = 0; uint16_t len; uint8_t ncnt; char ncdp_str[64]; uint8_t npos; //////////////读取ID等初始化流程///////////////////////////////////////// G_NB_CAN_DEEP_SLEEP = 0; // 不允许休眠 nb_power_on(0); rt_thread_mdelay(2000); do { npos = 0; if (at_cmd_send(npos++, NB_AT, NB_AT_ACK, 10, 2) == -1) break; // if (at_cmd_send(npos++, NB_ATE0, NB_ATE0_ACK, 3, 2) == -1) // 关闭回显 break; /////////////设置频段////////////////////////// if(Paramx.nb_band_flag!=0) {if (at_cmd_send(npos++, "AT+QCFG=\"band\",0,0x10,0,1\r\n", NB_AT_ACK, 3, 5) == -1) break; set_nb_band(); if (at_cmd_send(1, NB_CFUN0, NB_AT_ACK, 3, 3) == -1) break; rt_thread_mdelay(1000); if (at_cmd_send(1, NB_CFUN1, NB_AT_ACK, 3, 3) == -1) break; rt_thread_mdelay(1200); } ////////////////////////////////////////// // if (at_cmd_send(1, NB_CFUN0, NB_AT_ACK, 3, 2) == -1) // break; // rt_thread_mdelay(1000); // if (at_cmd_send(1, NB_CFUN1, NB_AT_ACK, 3, 3) == -1) // break; // rt_thread_mdelay(1200); if (at_cmd_send(npos++, NB_CGSN, NB_CGSN_ACK, 3, 2) == -1) break; get_nb_imei_code(); if (at_cmd_send(npos++, NB_CIMI, NB_AT_ACK, 3, 2) == -1) break; get_nb_imsi_code(); if (at_cmd_send(npos++, NB_NCCID, NB_NCCID_ACK, 3, 2) == -1) // different from BC28 break; get_nb_ccid_code(); } while (0); nb_power_off(); ////////////////////////////////////////////////////////////////// // G_NB_CAN_DEEP_SLEEP = 1;//允许休眠 while (1) { G_NB_CAN_DEEP_SLEEP = 1; // 允许休眠 rt_sem_take(Nb_thread_run_sem, RT_WAITING_FOREVER); // 等信号量 npos = 0; // #ifdef DEBUG_NB #ifdef APP_DEBUG_PRINT LOG("upload_begin type: %s\r\n", get_upload_type_str(NbRun.upload.mode)); if (NbRun.upload.mode & 0x10) LOG("error_code = %X\r\n", upload_dat.err_mem_dat); #endif // #endif // 既不是定时上报,也不是重复上报,并且没有子服务,也没有主服务,则跳过 if ((NbRun.upload.mode & (NB_UPLOAD_TIMER | NB_UPLOAD_REPT)) == 0 && check_sub_service_flag() == 0 && check_main_service_flag() == 0) { NbRun.upload.flag = 0; NbRun.upload.mode = 0; // 本次上传结束后清零 LOG("no service upload!\r\n"); continue; } G_NB_CAN_DEEP_SLEEP = 0; // 不允许休眠 nb_power_on(0); rt_thread_mdelay(1000); do { if (at_cmd_send(npos++, NB_AT, NB_AT_ACK, 10, 2) == -1) // at测试 break; if (at_cmd_send(npos++, NB_ATE0, NB_ATE0_ACK, 3, 2) == -1) // 关闭回显 break; if (at_cmd_send(npos++, "AT+IPR=9600\r\n", NB_AT_ACK, 3, 2) == -1) break; nb_power_on(3); rt_thread_mdelay(500); if (NbRun.get_imei_flag == 0) // 读取imei号 { // 读取IMEI号 if (at_cmd_send(npos++, NB_CGSN, NB_CGSN_ACK, 3, 2) == -1) break; get_nb_imei_code(); } npos = 5; if (at_cmd_send(npos++, NB_CMEE1, NB_CMEE_ACK, 3, 2) == -1) // 错误上报方式 BC800需要 break; retry_time = 0; vflag = 1; while (at_cmd_send(npos, "AT+CPIN?\r\n", "+CPIN: READY", 3, 3) == -1) { retry_time++; if (retry_time > 3) { vflag = 0; break; } if (at_cmd_send(npos + 1, NB_CFUN0, NB_AT_ACK, 3, 2) == -1) { vflag = 0; break; } // FUN0格式 rt_thread_mdelay(1000); if (at_cmd_send(npos + 2, NB_CFUN1, NB_AT_ACK, 3, 3) == -1) // 设置最大模式,开始搜网 { vflag = 0; break; } rt_thread_mdelay(1200); } npos += 3; if (vflag == 0) break; if (NbRun.get_ccid_flag == 0) // 读取ccid号 { // 读取ccid号 ncnt = 0; if (at_cmd_send(npos, NB_NCCID, NB_NCCID_ACK, 3, 2) == -1) { break; } get_nb_ccid_code(); } npos++; if (NbRun.get_imsi_flag == 0) // 读取imsi号 { // 读取imsi号 if (at_cmd_send(npos, NB_CIMI, NB_AT_ACK, 3, 2) == -1) break; get_nb_imsi_code(); } npos++; if (at_cmd_send(npos++, "AT+COPS=0\r\n", NB_AT_ACK, 3, 2) == -1) // 自动选择网络 break; rt_thread_mdelay(500); retry_time = 0; while (1) { // 内循环判断 if (retry_time >= 3) // 已经3个循环结束 { vflag = 0; break; } else if (retry_time != 0) // 首次进入不复位 { if (at_cmd_send(npos, NB_CFUN0, NB_AT_ACK, 3, 2) == -1) break; // FUN0格式 rt_thread_mdelay(1000); if (at_cmd_send(npos + 1, NB_CFUN1, NB_AT_ACK, 3, 3) == -1) // 设置最大模式,开始搜网 break; rt_thread_mdelay(1200); } if (at_cmd_send2(npos + 2, "AT+CEREG?\r\n", "+CEREG: 0,1", "+CREG: 0,5", 30, 2) == -1) // 查询网络注册状态TT_ACK, 3, 2) == -1) // PS域附着 { retry_time++; // 重试次数 continue; } // 状态值1/5都属于正常 if (at_cmd_send(npos + 3, NB_CGATT, NB_CGATT_ACK, 3, 2) == -1) // PS域附着 break; // retry_time = 0; if (at_cmd_send(npos + 4, NB_CGATT_QUERY, NB_CGATT_QUERY_ACK, 20, 2) == -1) { // 查询附着状态 retry_time++; // 重试次数 continue; // 重新注册 } vflag = 1; break; } npos += 5; if (vflag == 0) break; // 失败后退出的循环 vflag = 0; // if (at_cmd_send(14, NB_CSQ, NB_CSQ_ACK, 3, 2) == -1) // 查询无线信号强度 BC800有 // break; // get_nb_csq(); ncnt = 5; vflag = 1; while (ncnt--) // 查询无线信号强度 BC800有 { if (at_cmd_send(npos++, "AT+QENG=\"servingcell\"\r\n", "+QENG: \"servingcell\"", 3, 2) == -1) { vflag = 0; break; } if (get_nb_signal(NbRun.rx_buf, NbRun.rx_idx) == 1) break; rt_thread_mdelay(1000); } if (vflag == 0) break; vflag = 0; if (at_cmd_send(npos++, "AT+QICSGP=1,1,\"CTNET\",\"\",\"\",1\r\n", NB_AT_ACK, 3, 2) == -1) // 配置场景 break; if (at_cmd_send(npos++, "AT+QIACT=1\r\n", NB_AT_ACK, 3, 3) == -1) // 激活场景 break; if (at_cmd_send(npos++, "AT+QIACT?\r\n", "+QIACT: 1,1", 3, 3) == -1) // 查询场景激活状态 break; if ((NbRun.upload.mode & NB_UPLOAD_TIMER) != 0) { if (at_cmd_send(npos, "AT+QNTP=1,\"202.112.10.36\",123\r\n", "+QNTP: 0,", 1, 15) == 0) { save_datetime_from_at(NbRun.rx_buf, NbRun.rx_idx); LOG("get net time!\t\n"); } } npos++; int16_t link_code; vflag = 0x80; //////////////////////////////////tcp主连接与发送数据//////////////////////////////////////// if (((upload_dat.rept_addr & BIT0) != 0 || NbRun.upload.mode != NB_UPLOAD_REPT) && check_main_service_flag() == 1) { vflag &= 0x0f; do { #ifdef APP_DEBUG_PRINT LOG("main service upload\r\n"); #endif vflag = 0; sprintf(ncdp_str, "AT+QIOPEN=1,0,\"TCP\",\"%d.%d.%d.%d\",%d,0,0\r\n", // 倒数第二个0:本地ip自动 最后一个0: 缓存模式 Paramx.main_server_ip[0], Paramx.main_server_ip[1], Paramx.main_server_ip[2], Paramx.main_server_ip[3], Paramx.main_server_port); if (at_cmd_send(npos, ncdp_str, "+QIOPEN: 0,", 2, 10) == -1) // 建立TCP连接,等待连接成功 break; if (get_code(NbRun.rx_buf, "+QIOPEN: 0,", NbRun.rx_idx, &link_code) == -1) break; if (link_code != 0) break; // 发送数据,等待接收指令,收到指令后执行,超时退出 nb_upload_data_ready(); rt_thread_mdelay(100); // 准备上报数据 if (at_cmd_send(npos + 1, (char *)NbRun.tx_buf, "SEND OK", 3, 3) == -1) // 发送上报数据 break; vflag = 1; // 上报成功标志 int8_t ret; retry_time = 50; // 12秒 NbRun.cur_afn = 0; ////////////////////////////////////// while (retry_time--) { ret = rec_data_check(); rt_thread_mdelay(100); if (ret == 0) retry_time = 150; // 判断接收数据是否合法,以及读取协议层afn和长度,并回复 else if (ret == -1 || ret == 2) break; } } while (0); at_cmd_send(npos + 2, "AT+QICLOSE=0\r\n", NB_AT_ACK, 3, 3); // 关闭tcpLINK } npos += 3; ////////////////////////////备用连接///////////////////////////////////////////////////// if (((upload_dat.rept_addr & BIT1) != 0 || NbRun.upload.mode != NB_UPLOAD_REPT) && check_sub_service_flag() == 1) { vflag &= 0x0f; do { #ifdef APP_DEBUG_PRINT LOG("sub service upload\r\n"); #endif sprintf(ncdp_str, "AT+QIOPEN=1,0,\"TCP\",\"%d.%d.%d.%d\",%d,0,0\r\n", // 倒数第二个0:本地ip自动 最后一个0: 缓存模式 Paramx.sub_server_ip[0], Paramx.sub_server_ip[1], Paramx.sub_server_ip[2], Paramx.sub_server_ip[3], Paramx.sub_server_port); if (at_cmd_send(npos, ncdp_str, "+QIOPEN: 0,", 2, 10) == -1) // 建立TCP连接,等待连接成功 break; if (get_code(NbRun.rx_buf, "+QIOPEN: 0,", NbRun.rx_idx, &link_code) == -1) break; if (link_code != 0) break; // 发送数据,等待接收指令,收到指令后执行,超时退出 nb_upload_data_ready(); rt_thread_mdelay(100); // 准备上报数据 if (at_cmd_send(npos + 1, (char *)NbRun.tx_buf, "SEND OK", 3, 3) == -1) // 发送上报数据 break; vflag |= 2; // 上报成功标志 int8_t ret; retry_time = 50; // 12秒 NbRun.cur_afn = 0; ////////////////////////////////////// while (retry_time--) { ret = rec_data_check(); rt_thread_mdelay(100); if (ret == 0) retry_time = 50; // 判断接收数据是否合法,以及读取协议层afn和长度,并回复 else if (ret == -1 || ret == 2) break; } } while (0); at_cmd_send(npos + 2, "AT+QICLOSE=0\r\n", NB_AT_ACK, 3, 2); // 关闭tcpLINK } } while (0); // 主循环 // NB_RUN_UPDATE_FLAG = 0; nb_power_off(); if (vflag & 0x80) LOG("no service upload!\r\n"); else if ((vflag & 3) != 0) { if ((vflag & BIT0) != 0) LOG("MAIN upload ok!\r\n"); if ((vflag & BIT1) != 0) LOG("SUB upload ok!\r\n"); } else if (vflag == 0) LOG("upload fail\r\n"); if ((vflag & 3) != 3 && vflag != 0x80) // 有失败的情况 { // 上报失败,补报判别 if (NbRun.upload.mode & ~(NB_UPLOAD_REPT | NB_UPLOAD_DMA | NB_UPLOAD_MANUAL)) { // 除补报,DMA,手动上报之外,其他需要补报 upload_dat.rept_runflag = ((~vflag) & 0x3); // 补报 upload_dat.rept_addr = upload_dat.rept_runflag; rtc_run_t *rr; get_rtc_run(&rr); tm_t ntm; ntm.year = rr->year; ntm.month = rr->month; ntm.day = rr->day; ntm.hour = rr->hour; ntm.min = rr->minute; ntm.sec = rr->second; time_add_secs(&upload_dat.rept_time, &ntm, 3600); } } // NbRun.void_load_timout = NB_VOID_UPLOAD_TIME;//这段时间不能上报60s NbRun.upload.flag = 0; NbRun.upload.mode = 0; // 本次上传结束后清零 } // while(1) } /////////////添加上报过程中动态显示信号///////////////////// ////////////////////发送at指令并等待处理接收数据////////////////////////////////////////////////////// // stat:当前所在流程位置,和每个指令一一对应 // cmd_str:at指令字符串 // ack:要求本次回复的字符串 // retry_time: 重试次数 // time_out: 超时时间 秒单位 // 成功:返回0 失败返回-1 /////////////////////////////////////////////////////////////////////////////////////////////////////// int8_t at_cmd_send(uint8_t stat, char *cmd_str, char *ack, uint16_t retry_time, uint8_t time_out) { uint16_t retry = retry_time; uint16_t time_cnt; int8_t ret = 0; NbRun.cur_state = stat; NbRun.ack = ack; if (time_out > 250) time_out = 250; // 最大不超过25秒 time_cnt = time_out * 10; //////////// // retry=250; ////////////// // LOG("nbstat=%d\r\n",stat); while (retry--) { NbClrRxBuf(); time_cnt = time_out * 10; nb_tx_str(cmd_str); while (--time_cnt) { if (Nb_rx_data() == 1) { // if (stat > COAP_CGATT_QUERY && stat < COAP_WAIT_REG)//EC800 不再需要了 // { // if (memstr_exist(NbRun.rx_buf, NbRun.rx_idx, "+QLWEVTIND:3", 12) == 1) // { // connected_flag = 1; // } // } ////////////////////////////////////////////// ret = Nb_check_rec_valid(stat, ack); if (ret == 1) { LOG("."); return 0; } // 收到正确的回复} else if (ret == -1) break; // else if(ret == 0)continue; } rt_thread_mdelay(NB_REC_DELAY_TICK); // 等100ms } if (time_cnt == 0) { // 超时未收到数据 continue; } if (Nb_check_rec_valid(stat, ack) == TRUE) { // 检查是否收到正确回复 return 0; } } return -1; } int8_t at_cmd_send2(uint8_t stat, char *cmd_str, char *ack1, char *ack2, uint16_t retry_time, uint8_t time_out) { uint16_t retry = retry_time; uint16_t time_cnt; int8_t ret1, ret2; NbRun.cur_state = stat; NbRun.ack = ack1; if (time_out > 250) time_out = 250; // 最大不超过25秒 time_cnt = time_out * 10; //////////// // retry=250; ////////////// // LOG("nbstat=%d\r\n",stat); while (retry--) { NbClrRxBuf(); time_cnt = time_out * 10; nb_tx_str(cmd_str); while (--time_cnt) { if (Nb_rx_data() == 1) { ret1 = Nb_check_rec_valid(stat, ack1); if (ret1 == 1) { LOG("."); return 0; } // 收到正确的回复} ret2 = Nb_check_rec_valid(stat, ack2); if (ret2 == 1) { LOG("."); return 0; } // 收到正确的回复 if (ret1 == -1 || ret2 == -1) break; // else if(ret == 0)continue; } rt_thread_mdelay(NB_REC_DELAY_TICK); // 等100ms } if (time_cnt == 0) { // 超时未收到数据 continue; } if (Nb_check_rec_valid(stat, ack1) == TRUE) { // 检查是否收到正确回复 return 0; } if (Nb_check_rec_valid(stat, ack2) == TRUE) { // 检查是否收到正确回复 return 0; } } return -1; } static void BuildDataReportMeter(rtc_run_t *rr, uint8_t *data) { rtc_run_t *max_fr_rr; // total positive water uint32_t temp = (uint32_t)(FwCumFlow * 0.0001 + 0.5); // unit: 0.01m^3 data[0] = (uint8_t)temp; data[1] = (uint8_t)(temp >> 8); data[2] = (uint8_t)(temp >> 16); data[3] = (uint8_t)(temp >> 24); // total reverse water temp = (uint32_t)(BwCumFlow * 0.0001 + 0.5); // unit: 0.01m^3 data[4] = (uint8_t)temp; data[5] = (uint8_t)(temp >> 8); data[6] = (uint8_t)(temp >> 16); data[7] = (uint8_t)(temp >> 24); // Daily maximum flow rate temp = (uint32_t)(get_max_flow_rate() * 1000); // 3位小数点 #ifdef DEBUG_NB // temp = 12345; #endif data[8] = (uint8_t)(temp); data[9] = (uint8_t)(temp >> 8); data[10] = (uint8_t)(temp >> 16); data[11] = (uint8_t)(temp >> 24); // Daily maximum flow rate time get_max_flow_rate_time(&max_fr_rr); if (max_fr_rr == NULL || max_fr_rr->year == 0 || max_fr_rr->month == 0 || max_fr_rr->day == 0) memset(&data[12], 0, 7); else { data[12] = 0x14; data[13] = max_fr_rr->year; data[14] = max_fr_rr->month; data[15] = max_fr_rr->day; data[16] = max_fr_rr->hour; data[17] = max_fr_rr->minute; data[18] = max_fr_rr->second; } #ifdef DEBUG_NB // data[13] = 26; // data[14] = 3; // data[15] = 31; // data[16] = 9; // data[17] = 30; // data[18] = 45; #endif // water temperature if (WaterTemp > 100.0f || WaterTemp < 0.0f) temp = -1; else temp = (uint32_t)(WaterTemp * 10 + 0.5); #ifdef DEBUG_NB // temp = -300; #endif data[19] = (uint8_t)(temp); data[20] = (uint8_t)(temp >> 8); // water pressure if (Paramx.pressure_sensor == 1) { data[21] = (uint8_t)(WaterPressure * 100); } else data[21] = 0x00; // 无压力传感器,修改为0 #ifdef DEBUG_NB // data[21]=213; #endif // battery voltage unit: 0.1V /* if (McuVbat > McuVTxbat) { // 判断哪个电池低就上传哪个数据 temp = McuVTxbat / 100; } else temp = McuVbat / 100; */ temp = McuVbat / 100; // wxl2026-6-2 data[22] = (unsigned char)temp; data[23] = 0x14; data[24] = rr->year; data[25] = rr->month; data[26] = rr->day; data[27] = rr->hour; data[28] = rr->minute; data[29] = rr->second; } static void BuildDataReportParam(uint8_t *data) { Param_t *param; Nb_Run_t *nb_run; GetParam(¶m); GetNbRun(&nb_run); // version data[0] = 'V'; data[1] = VER_TYPE; data[2] = VER_OPERATOR; data[3] = VER_MODULE; data[4] = VER_FIRMWARE; // diameter data[5] = (unsigned char)(param->diameter); data[6] = (unsigned char)(param->diameter >> 8); // channel number data[7] = param->chan_num; // main server ip data[8] = param->main_server_ip[0]; data[9] = param->main_server_ip[1]; data[10] = param->main_server_ip[2]; data[11] = param->main_server_ip[3]; // main server port data[12] = (unsigned char)(param->main_server_port); data[13] = (unsigned char)(param->main_server_port >> 8); // sub server ip data[14] = param->sub_server_ip[0]; data[15] = param->sub_server_ip[1]; data[16] = param->sub_server_ip[2]; data[17] = param->sub_server_ip[3]; // sub server port data[18] = (unsigned char)(param->sub_server_port); data[19] = (unsigned char)(param->sub_server_port >> 8); // report base time data[20] = param->report_base_hour; data[21] = param->report_base_min; data[22] = param->report_base_sec; // report interval minutes data[23] = (unsigned char)(param->report_interval); data[24] = (unsigned char)(param->report_interval >> 8); // DMA report start time data[25] = param->dma_report_start[0]; data[26] = param->dma_report_start[1]; data[27] = param->dma_report_start[2]; // DMA report end time data[28] = param->dma_report_end[0]; data[29] = param->dma_report_end[1]; data[30] = param->dma_report_end[2]; // DMA report interval time data[31] = param->dma_interval; // settlement day data[32] = param->settlement_day; // high temperature alarm threshold data[33] = (unsigned char)(param->high_temp_alarm_thres); data[34] = (unsigned char)(param->high_temp_alarm_thres >> 8); // low temperature alarm threshold data[35] = (unsigned char)(param->low_temp_alarm_thres); data[36] = (unsigned char)(param->low_temp_alarm_thres >> 8); // large flow alarm threshold data[37] = (unsigned char)(param->large_flow_alarm_thres); data[38] = (unsigned char)(param->large_flow_alarm_thres >> 8); data[39] = (unsigned char)(param->large_flow_alarm_thres >> 16); data[40] = (unsigned char)(param->large_flow_alarm_thres >> 24); // large flow continue monitor time data[41] = (unsigned char)(param->large_flow_cnti_moni_time); data[42] = (unsigned char)(param->large_flow_cnti_moni_time >> 8); // continue flow continue monitor time data[43] = (unsigned char)(param->cnti_flow_cnti_moni_time); data[44] = (unsigned char)(param->cnti_flow_cnti_moni_time >> 8); // leak flow alarm threshold data[45] = (unsigned char)(param->leak_flow_alarm_thres); data[46] = (unsigned char)(param->leak_flow_alarm_thres >> 8); data[47] = (unsigned char)(param->leak_flow_alarm_thres >> 16); data[48] = (unsigned char)(param->leak_flow_alarm_thres >> 24); // leak flow continue monitor time data[49] = (unsigned char)(param->leak_flow_cnti_moni_time); data[50] = (unsigned char)(param->leak_flow_cnti_moni_time >> 8); // high press alarm threshold data[51] = (unsigned char)(param->high_press_alarm_thres); // low press alarm threshold data[52] = (unsigned char)(param->low_press_alarm_thres); // pressure sensor data[53] = param->pressure_sensor; } static void BuildDataReportNb(uint8_t *data) { Nb_Run_t *nb_run; char tmp[3]; uint16_t i; GetNbRun(&nb_run); // IMEI len = 8 if (strlen(nb_run->imei) == 15) { tmp[0] = '0'; tmp[1] = nb_run->imei[0]; tmp[2] = 0; StrToHexBuf((char *)&data[0], tmp, 2); StrToHexBuf((char *)&data[1], &(nb_run->imei[1]), 14); } else { StrToHexBuf((char *)&data[0], tmp, 16); } // cell id in AT+NUESTATS response data[8] = (unsigned char)(nb_run->bts_id); data[9] = (unsigned char)(nb_run->bts_id >> 8); data[10] = 0x00; data[11] = 0x00; // physical cell id data[12] = (unsigned char)(nb_run->physical_cell_id); data[13] = (unsigned char)(nb_run->physical_cell_id >> 8); // rsrp data[14] = (unsigned char)(nb_run->rsrp); data[15] = (unsigned char)(nb_run->rsrp >> 8); // snr data[16] = (unsigned char)(nb_run->snr); data[17] = (unsigned char)(nb_run->snr >> 8); // csq data[18] = nb_run->rssi; // iccid len = 10 for (i = 0; i < 10; i++) { // 这里是不是错了 data[19 + i] = (unsigned char)(((nb_run->ccid[2 * i] - '0') << 4) | (nb_run->ccid[2 * i + 1] - '0')); } } static void BuildMonthRecord(rtc_run_t *rr, uint8_t *data) { uint32_t date = ((rr->year << 16) | (rr->month << 8)); uint16_t i; bill_t bill; uint8_t vflag; for (i = 0; i < 2; i++) { date = prev_month(date); vflag = read_month_bill((uint8_t)(date >> 16), (uint8_t)(date >> 8), &bill); if (vflag != 0) continue; // no data if (bill.date[0] != (uint8_t)(date >> 16) || bill.date[1] != (uint8_t)(date >> 8)) continue; // 不是当月数据 data[i * 11] = 0x14; data[i * 11 + 1] = bill.date[0]; data[i * 11 + 2] = bill.date[1]; data[i * 11] = 0x14; data[i * 11 + 1] = bill.date[0]; data[i * 11 + 2] = bill.date[1]; data[i * 11 + 3] = (unsigned char)(bill.fw_used); data[i * 11 + 4] = (unsigned char)(bill.fw_used >> 8); data[i * 11 + 5] = (unsigned char)(bill.fw_used >> 16); data[i * 11 + 6] = (unsigned char)(bill.fw_used >> 24); data[i * 11 + 7] = (unsigned char)(bill.bw_used); data[i * 11 + 8] = (unsigned char)(bill.bw_used >> 8); data[i * 11 + 9] = (unsigned char)(bill.bw_used >> 16); data[i * 11 + 10] = (unsigned char)(bill.bw_used >> 24); } } static void BuildDayRecord(rtc_run_t *rr, uint8_t *data) { uint16_t i; bill_t bill; uint32_t date = ((rr->year << 16) | (rr->month << 8) | rr->day); uint8_t vflag; for (i = 0; i < 5; i++) { date = prev_day(date); vflag = read_day_bill((uint8_t)(date >> 8), (uint8_t)date, &bill); if (vflag != 0) continue; // no data if (bill.date[0] != (uint8_t)(date >> 16) || bill.date[1] != (uint8_t)(date >> 8) || bill.date[2] != (uint8_t)date) continue; // 不是当天的数据 data[i * 12] = 0x14; data[i * 12 + 1] = bill.date[0]; data[i * 12 + 2] = bill.date[1]; data[i * 12 + 3] = bill.date[2]; data[i * 12 + 4] = (unsigned char)(bill.fw_used); data[i * 12 + 5] = (unsigned char)(bill.fw_used >> 8); data[i * 12 + 6] = (unsigned char)(bill.fw_used >> 16); data[i * 12 + 7] = (unsigned char)(bill.fw_used >> 24); data[i * 12 + 8] = (unsigned char)(bill.bw_used); data[i * 12 + 9] = (unsigned char)(bill.bw_used >> 8); data[i * 12 + 10] = (unsigned char)(bill.bw_used >> 16); data[i * 12 + 11] = (unsigned char)(bill.bw_used >> 24); } } static void BuildHourRecord(rtc_run_t *rr, uint8_t *data) { uint8_t i; hour_bill_t bill; uint8_t vflag; uint32_t date = ((rr->year << 16) | (rr->month << 8) | rr->day); date = prev_day(date); data[0] = 0x14; data[1] = (unsigned char)(date >> 16); data[2] = (unsigned char)(date >> 8); data[3] = (unsigned char)(date); for (i = 0; i < 24; i++) { vflag = read_hour_bill((uint8_t)date, i, &bill); if (vflag != 0) // 数据不合法,放弃 continue; if ((bill.date[0] != data[1]) || (bill.date[1] != data[2]) || (bill.date[2] != data[3])) { continue; } // 不是当前日期数据,丢弃 memcpy(&data[4 + i * 3], bill.fw_used, 3); memcpy(&data[4 + 72 + i * 3], bill.bw_used, 3); data[4 + 144 + i] = bill.pressure; memcpy(&data[4 + 168 + i * 3], bill.flow_rate, 3); } } void NbBuildDataReport(void) { rtc_run_t *rr; Nb_Run_t *nb_run; uint16_t data_len = 448; uint32_t *err_no; // current date time get_rtc_run(&rr); GetNbRun(&nb_run); NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x10; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = 0x00; NbAppFrame.data[3] = 0x00; // DC // report trigger mode NbAppFrame.data[4] = nb_run->upload.mode; BuildDataReportMeter(rr, &NbAppFrame.data[5]); // meter part len 30 BuildDataReportParam(&NbAppFrame.data[35]); // param part len 54 BuildDataReportNb(&NbAppFrame.data[89]); // nb part len 29 BuildMonthRecord(rr, &NbAppFrame.data[118]); // month record part len 22 BuildDayRecord(rr, &NbAppFrame.data[140]); // day record part len 60 BuildHourRecord(rr, &NbAppFrame.data[200]); // day record part len 244 // error no all_alarm_status(&err_no); //(*err_no) &= 0xffff0fff; NbAppFrame.data[444] = (uint8_t)(*err_no); NbAppFrame.data[445] = (uint8_t)(*err_no >> 8); NbAppFrame.data[446] = (uint8_t)(*err_no >> 16); NbAppFrame.data[447] = (uint8_t)(*err_no >> 24); NbBuildDataTail(data_len); } void nb_set_para_rept(uint16_t dat_len, uint16_t afn, int8_t ret) { // uint16_t data_len = 5; // uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; NbBuildDataHeader(dat_len); // AFN NbAppFrame.data[0] = (uint8_t)afn; NbAppFrame.data[1] = (uint8_t)(afn >> 8); // MID // NbAppFrame.data[2] = (uint8_t)(mid); // NbAppFrame.data[3] = (uint8_t)(mid >> 8); NbAppFrame.data[4] = ret; // set success NbBuildDataTail(dat_len); } void NbBuildReportMonthRecord(void) { uint16_t data_len = 202; uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; uint8_t ret; // rtc_run_t *rr; // get_rtc_run(&rr); NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x31; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); uint8_t *data = &NbAppFrame.data[4]; get_18month_record_data(data); NbBuildDataTail(data_len); } void NbBuildReportDayRecord(void) { uint16_t data_len = 364; uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; uint8_t ret; NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x33; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); uint8_t *data = &NbAppFrame.data[4]; get_30day_record_data(data); NbBuildDataTail(data_len); } void NbBuildReportHourRecord(uint8_t year, uint8_t month, uint8_t day) { uint16_t data_len = 248; uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; // rtc_run_t *rr; // get_rtc_run(&rr); NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x35; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); uint8_t *data = &NbAppFrame.data[4]; get_hour_record_data(year, month, day, data); NbBuildDataTail(data_len); } void NbBuildReport5MinRecord(tm_t *date) { // 年月日时分 uint16_t data_len = 388; uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; uint32_t i = 0; uint8_t ret; hour_bill_t bill; tm_t src; tm_t dest; NbBuildDataHeader(data_len); memset(NbAppFrame.data, 0, sizeof(NbAppFrame.data)); // AFN NbAppFrame.data[0] = 0x37; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); uint8_t *data = &NbAppFrame.data[4]; GET_5MIN_REOCORD_DEF mdate; mdate.start_time[0] = date->year; mdate.start_time[1] = date->month; mdate.start_time[2] = date->day; mdate.start_time[3] = date->hour; mdate.start_time[4] = date->min; mdate.start_time[5] = date->sec; time_add_secs(&dest, date, 2 * 3600); mdate.end_time[0] = dest.year; mdate.end_time[1] = dest.month; mdate.end_time[2] = dest.day; mdate.end_time[3] = dest.hour; mdate.end_time[4] = dest.min; mdate.end_time[5] = dest.sec; get_5min_record_data(&mdate, 0, data); NbBuildDataTail(data_len); } void NbBuildReportLogRecord(void) { uint16_t data_len = 394; uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; uint8_t i; int8_t j; uint8_t num = 0; alarm_t alarm; uint8_t rec_len = 13; NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x39; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); uint8_t *data = &NbAppFrame.data[4]; num = get_alarm_num(); if (num > 30) { num = 30; } get_log_record(0, num, data); NbBuildDataTail(data_len); } void NbBuildGetPackage(void) { uint16_t data_len = 21; uint16_t mid = (NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; Param_t *param; NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x51; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); GetParam(¶m); uint8_t *data = &NbAppFrame.data[4]; data[0] = 'V'; data[1] = VER_TYPE; data[2] = VER_OPERATOR; data[3] = VER_MODULE; data[4] = VER_FIRMWARE; data[5] = 'V'; data[6] = VER_TYPE; data[7] = VER_OPERATOR; data[8] = VER_MODULE; data[9] = VER_FIRMWARE + 1; data[10] = (uint8_t)(PatchInfo.per_packet_len); data[11] = (uint8_t)(PatchInfo.per_packet_len >> 8); data[12] = (uint8_t)(PatchInfo.cur_num); data[13] = (uint8_t)(PatchInfo.cur_num >> 8); data[14] = (uint8_t)(PatchInfo.packet_crc); data[15] = (uint8_t)(PatchInfo.packet_crc >> 8); data[16] = 1; NbBuildDataTail(data_len); } void NbBuildReportUpdateStatus(int8_t stat) { uint16_t data_len = 5; uint16_t mid = NbRun.mid; //(NbAppFrame.data[3] << 8) | NbAppFrame.data[2]; Param_t *param; NbBuildDataHeader(data_len); // AFN NbAppFrame.data[0] = 0x52; NbAppFrame.data[1] = 0x00; // MID NbAppFrame.data[2] = (uint8_t)(mid); NbAppFrame.data[3] = (uint8_t)(mid >> 8); NbAppFrame.data[4] = stat; NbBuildDataTail(data_len); } #ifdef DEBUG_LOG_WARM void Debug_log(void) { // #ifdef DEBUG_NB uint8_t i; for (i = 0; i < 32; i++) { write_alarm(i / 2, i % 2, i * 10 + 10); } // #endif } #endif #ifdef APP_DEBUG_PRINT static char str[50]; char *get_upload_type_str(uint8_t type) { char cnt = 0; memset(str, 0, 50); if (type & 0x1) strcat(str, "manual,"); if (type & 0x2) strcat(str, "regular,"); if (type & 0x4) strcat(str, "rept,"); if (type & 0x8) strcat(str, "settlement,"); if (type & 0x10) strcat(str, "error,"); if (type & 0x20) strcat(str, "dma"); return str; } #endif uint8_t memstr_exist(uint8_t *hay, size_t hay_len, const char *sub, size_t sub_len) { if (sub_len == 0) return 1; if (sub_len > hay_len) return 0; for (size_t i = 0; i <= hay_len - sub_len; i++) { if (memcmp(hay + i, sub, sub_len) == 0) { return 1; } } return 0; } int8_t get_code(uint8_t *buf, const char *keyword, uint16_t dat_len, int16_t *ret) { char *pos; buf[dat_len] = 0; pos = strstr((char *)buf, keyword); if (pos == NULL) return -1; uint16_t dat = 0; uint16_t i; char vflag; pos = pos + strlen(keyword); vflag = 0; uint8_t flag = 0; if (*pos == '+' || *pos == '-') { if (*pos == '-') flag = 1; pos++; } while (*pos >= '0' && *pos <= '9') { dat *= 10; dat += *pos - '0'; pos++; vflag = 1; } if (vflag == 0) return -1; if (flag == 1) dat = -dat; *ret = dat; return 1; } //+QNTP: 0,"2019/09/09,01:32:42+32" void save_datetime_from_at(uint8_t *buf, uint16_t dat_len) { tm_t tm; int16_t zone = 0; char *p; buf[dat_len] = 0; // 确保字符串终止 p = strstr((char *)buf, "\""); // 定位到双引号开始处 if (p == NULL) return; // 利用 sscanf 直接格式化提取年月日时分秒和时区 // 格式: 2019/09/09,01:32:42+32 if (sscanf(p + 1, "%hu/%hu/%hu,%u:%u:%u%hd", &tm.year, &tm.month, &tm.day, &tm.hour, &tm.min, &tm.sec, &zone) != 7) { return; // 解析失败或格式不匹配 } // 合法性检查 if (tm.year < 26 || tm.month == 0 || tm.month > 12 || tm.day == 0 || tm.day > 31 || tm.hour > 23 || tm.min > 59 || tm.sec > 59) return; // 时区转换 (保留原逻辑:正负时区分别调用不同的时间计算函数) if (zone > 0) { time_add_secs(&tm, &tm, zone * 15 * 60); } else if (zone < 0) // 增加非零判断,避免对0进行无意义的减法 { time_sub_secs(&tm, &tm, -zone * 15 * 60); // 传入正数 } tm.year %= 100; SetRtcTime(&tm); } //+QENG: "servingcell","NOCONN","LTE","FDD",460,11,EA6CE03,77,100,1,5,5,EA15,-94,-8,-66,24,26 // rsrp rwrq rssi sinr uint8_t get_nb_signal(uint8_t *buf, uint16_t dat_len) { char temp[8]; char *p; int8_t index1 = 0, index2 = 0; int16_t *out_val = NULL; uint8_t ret = 0; buf[dat_len] = 0; // 确保终止位 p = strstr((char *)buf, "servingcell"); if (p == NULL) return 0; index1 = strnchar(p, ',', 12); // 依次提取 rsrp_bak, rsrq, rssi, snr for (uint8_t i = 0; i < 4; i++) { if (index1 == -1) break; // 上一次查找失败,直接退出 index2 = strnchar(p, ',', 13 + i); if (index2 != -1 && index1 < index2) { memset(temp, 0, sizeof(temp)); memcpy(temp, p + index1 + 1, index2 - index1 - 1); int16_t val = atoi(temp); switch (i) { case 0: NbRun.rsrp_bak = val; break; case 1: NbRun.rsrq = val; break; case 2: NbRun.rssi = val; ret = 1; break; } } index1 = index2; // 将当前的右边界作为下一次的左边界 } return ret; } uint8_t check_sub_service_flag(void) { uint8_t vflag; vflag = 1; if (Paramx.main_server_ip[0] == Paramx.sub_server_ip[0] && Paramx.main_server_ip[1] == Paramx.sub_server_ip[1] && Paramx.main_server_ip[2] == Paramx.sub_server_ip[2] && Paramx.main_server_ip[3] == Paramx.sub_server_ip[3] && Paramx.main_server_port == Paramx.sub_server_port) { vflag = 0; } // 主从ip相同,端口号相同 if (Paramx.sub_server_ip[0] == 0 || Paramx.sub_server_port == 0) // 从ip为空,端口号为空 { vflag = 0; } return vflag; } uint8_t check_main_service_flag(void) { uint8_t vflag; vflag = 1; if (Paramx.main_server_ip[0] == 0 || Paramx.main_server_port == 0) // 从ip为空,端口号为空 { vflag = 0; } return vflag; }