/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-07-06 murmur the first version */ #include #include #include #include "func.h" #define LOG_TAG "func" #define LOG_LVL LOG_LVL_DBG #include //3.2.1双模通信功能 //1为TT通信,0为BD短报文通信 void setCommMode(int isTT) { if (isTT) { //change mode } else { } //write to cfg file set_cfg("commMode", isTT); LOG_D("set commMode to %s",isTT?"TT":"BD"); } int getCommMode() { //load from cfg file int flag = get_cfg("commMode"); if (flag < 0) { LOG_W("get mode fault."); } return flag; } //3.2.2状态自检 /** * 系统自检,自动发送自检结果 */ extern int df(const char *path); /** * 获取磁盘剩余空间大小,单位MB * @param path指定磁盘挂载的路径,为空时表示挂载在根目录的磁盘 * @return 返回结果,单位MB */ static uint16_t getFreeSpace(const char *path) { long long cap; struct statfs buffer; int result = dfs_statfs(path ? path : "/", &buffer); if (result != 0) { LOG_E("dfs_statfs failed."); return 0; } cap = (uint16_t)((long long)buffer.f_bsize) * ((long long)buffer.f_bfree) / 1024LL / 1024LL ;//转换为MB return cap; } void ddf() { LOG_D("free space of flash is %d MB.",getFreeSpace(NULL)); uint16_t rst = getFreeSpace("/sd"); LOG_D("free space of sd is %d MB.%02X,%02X",rst,rst>>8,rst&0xff); } MSH_CMD_EXPORT(ddf,getFreeSpace); /** * 獲取電池電量。 * @return 返回電量百分比 */ RT_WEAK int getPowerLevel(void) { return 0; } /** * 上傳天通數據 * @param din 待發送數據 * @param len 待發送數據的長度 * @return */ RT_WEAK int uploadData(uint8_t *din, size_t len) { return 0; } void sysSelfTest() { rt_uint8_t rst[100]={0x5A, 0xA5, 0x32, 0x3E, 0x0A, 0x41}; int p = 6; rt_uint8_t sysSta=1,xh=0,jh=0,commSpeed=0; rst[p++]=0x00; rst[p++]=0x09; rst[p++] = sysSta; rst[p++] = xh; rst[p++] = jh; rst[p++] = commSpeed; rst[p++] = getPowerLevel(); //flash 剩餘空間 uint16_t cap = getFreeSpace("/"); rst[p++] = (uint8_t)(cap >> 8); rst[p++] = (uint8_t)(cap & 0xff); //SD卡剩餘空間 cap = getFreeSpace("/sd"); rst[p++] = (uint8_t)(cap >> 8); rst[p++] = (uint8_t)(cap & 0xff); rst[p++] = bccCRC(rst+2, p-1); ////校验位为1个字节,采用异或运算,从指令的第3个字节开始,到奇偶校验位的前一个字节结束 rst[p++] = 0xED; //结束位 LOG_HEX("selfTestRes",16,rst,p); //发送结果 uploadData(rst,p); } MSH_CMD_EXPORT(sysSelfTest,sysSelfTest); //3.2.3日志记录 //日志功能由各函数通过LOG_D()实现 //3.2.4自毁功能 /** * 设置自毁功能开关 * @param setON 1-自毁功能开启,0-关闭 */ void setSelfDestructSWT(int setON) { //write to cfg file set_cfg("SelfDesSW", setON); LOG_D("set SelfDesSW to %s",setON?"ON":"OFF"); } /** * 获取自毁开关状态 * @return 1-自毁功能开启,0-关闭 */ int getSelfDestructSWT() { //load from cfg file int flag = get_cfg("SelfDesSW"); if (flag < 0) { LOG_W("get mode fault."); } return flag; } /** * 启动自毁 */ void selfDestruct() { if (getSelfDestructSWT()) { //硬件自毁 LOG_W("SELF DESTRUCT START."); } } //3.2.5开、关窗功能 extern void updateAlarm(int *t); /** * 更新开窗时间,目前支持两组开窗时段。更新会清除之前的开窗设置 */ void setCommWindow(int *t) { updateAlarm(t); LOG_D("更新开窗时间完成。"); } /** * 手动控制开窗 * @param t 开窗时长,单位分钟,时间到则自动关窗。t=0时需要手动关窗。 */ void openWindow(int t) { //开启TT pwTT_thread_entry("1");//开机 if (!t) { LOG_D("手动开窗完成,需手动关窗。"); return; } //设置定时器,定时器到则关窗 /* 创建定时器,单次定时器 */ rt_timer_t timer1; timer1 = rt_timer_create("window", pwTT_thread_entry, 0, rt_tick_from_millisecond(t*60*1000), RT_TIMER_FLAG_ONE_SHOT); /* 启动定时器 */ if (timer1 != RT_NULL) { rt_timer_start(timer1); LOG_D("手动开窗完成,%d分钟后自动关窗。",t); } } /** * 手动关窗 */ void closeWindow() { pwTT_thread_entry("0");//关 机 LOG_D("手动关窗完成。"); } //3.2.6工作参数配置、状态查询 //包含浮体自身、标体透传、两者结合 //这里主要实现浮体自身 //浮体自身的参数配置各功能函数有实现,此处需规定参数下发协议和解码实现 //3.2.8定时自报位置信息 //每小时传数据时同步传位置信息 //3.2.9深度异常告警 int packDeepMsg(uint8_t *din, int len, uint8_t *dout) { //获取并更新位置信息 uint8_t loc[]={}; rt_memcpy(din+6, loc, 8);//位置数据从【】字节开始,共8个字节 //加密。因加密后数据长度会变化,故不能只加密位置数据。 uint8_t cd[200]; size_t nlen = cryp_data(din, len, cd); //打包数据 static MSG cfg; rt_memset(&cfg, 0, sizeof(MSG)); // 分配空间 char *fin; time2Str(fin); packInit(&cfg, fin, 0);//写入配置 size_t rst = packMsg(&cfg, din, nlen, dout); LOG_D("位置数据打包完成"); return rst; } //3.2.10位置异常告警 //map.c中实现 /** * 判断是否在电子围栏内部 * @param x 当前位置经度 * @param y 当前位置纬度 * @return 在内部则返回true,反之false */ static int isInFence(float x, float y) { float polyX[]={},polyY[]={}; int polyCorners = mapParse("/map.geojson",polyX,polyY); return pointInPolygon(polyCorners,polyX,polyY,x,y); } /** * 设置位置告警功能开关 * @param setON 1-告警功能开启,0-关闭 */ void setLocationAlertSWT(int setON) { } /** * 检查是否在电子围栏内部,并发出告警 */ void checkLocAndAlert() { //get Lon and Lat float x,y; if (!x || !y) { LOG_W("location info is not ready."); return; } int isIN = isInFence(x, y); if (isIN) { LOG_I("设备在预设范围内,位置正常。"); } else { LOG_W("警告!设备不在预设范围内!"); //告警 } } //3.2.12数据存储区清空 // /** * 采用格式化命令对存储区进行清空,谨慎使用! */ void clearAllData() { mkfs("elm", "W25Q128");//format flash mkfs("elm","sd0");//format SD } /** * @brief TT根据下发的指令执行对应的功能 * * @param din 待执行的指令数据 * @param len 数据长度,单位字节 */ void ttRunCMD(uint8_t *din, size_t len) { int cmd=(din[4]<<8) + din[5]; switch (cmd) { case _CFG_COMM_MODE: /* code */ break; case _CMD_SELF_TEST: break; case _CFG_SELF_DESTRUCT: break; case _CMD_SELF_DESTRUCT: break; case _CFG_COMM_WINDOW: break; case _CMD_OPEN_WINDOW: break; case _CMD_CLOSE_WINDOW: break; case _CFG_LOCATION_ALERT: break; case _CMD_CLEAR_DATA: break; default: LOG_W("未支持的指令。"); break; } } //原计划将指令粗解析放在上位机,考虑到上位机到位时间晚,现放到MCU端 /** * @brief 校验、解析3S数据,以0x5AA5开头0xED结尾。 * * @param din 待解析数据 * @param count 待解析数据长度 */ void parse3SData(uint8_t *din, size_t count) { uint8_t head[]={0x5a,0xA5}; //header[4] addr[2]  func[2]  len[2]  data[N]  fcrc[1] tail[1] uint8_t rst = memcmp(din,head,sizeof(head[0])); if (rst) { LOG_W("0x%02X%02X != 0x%5AA5,帧头不匹配",din[0],din[1]); return; } if (din[count-2] != bccCRC(din+2,count-2-2))//校验位为1个字节,采用异或运算,从指令的第3个字节开始,到奇偶校验位的前一个字节结束 { LOG_W("0x%02X != 0x%02X,校验值不匹配",din[count-2] , bccCRC(din+2,count-2-2)); return; } if (din[count-1] != 0xED) { LOG_W("0x%02X != 0xED,帧尾不匹配",din[count-1]); return; } uint8_t dout[200]; size_t len=0; if (din[3] == ADDR_TT)//仅给TT的消息 { ttRunCMD(din,len); } if (din[3] == ADDR_3S)//给3S的指令,需要再加工,返回数据可能也需要再加工 { // LOG_D("直接调用小彭的函数进行处理。"); // extern void xpParse(uint8_t *din, size_t len); // xpParse(din,len); //以下处理逻辑作废 //绝大部分数据均需要转换。详见《主要指令一览》 // int cmd = (din[4]<<8) + din[5] // switch (cmd) // { // case _CMD_RTC_CHECK: // case 0: // /* 不转换 */ // break; // default: // //转换 // char rst[]=""; // bytes2str(din,count,16,"",rst); // break; // } // //发送 } } /** * @brief 解析TT数据,TT收到的指令必是单指令,解析容易。 * * @param din * @param len */ void parseTTData(uint8_t *din, size_t len) { uint8_t head[]={0x88,0xAA,0xBB,0x88, 0x00,0x01, 0x00,0x22, 0x70,0x21, 0x00,0xaa, 0x00, 0x27,0x22,0x22,0x22 }; //fstart[4] fnum[2]  bak[2]  ftype[2]  fdlen[2]  fcrc[1] ftccid[4] uint8_t rst = memcmp(din,head,sizeof(head)); if (rst) { LOG_W("帧头不匹配"); return; } uint8_t id[30]=""; LOG_D("get new data: id=\"%s\", cur/all=[%d/%d]",bytes2str(din+17,7,10,"_",id),din[25],din[26]); if (din[24] >> 7) // fcfg=数据类型。解析TT收到的数据时仅需解析“命令”,“数据”传输是单向的。 { LOG_W("浮标端仅接受指令,暂不支持数据。"); return; } uint8_t rawData[200]; uint8_t rawDataLen=len-27; memcpy(rawData, din + 27, rawDataLen); parse3SData(rawData,rawDataLen); // switch (din[24] & 0x7F) // 识别指令的目标地址 // { // case _ONLY_FOR_TT: // ttRunCMD(rawData, len - 27); // break; // case _ONLY_FOR_3S: // /* code */ // // 发送数据至3S // break; // case _FOR_BOTH: // // 发送数据至3S // // 等待返回数据 // // 打包 // // 上传 // break; // default: // LOG_W("未识别到指令的目标地址"); // break; // } }