/* * 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 "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状态自检 /** * 获取磁盘剩余空间大小,单位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 d_getFreeSpace() { 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); } /** * 獲取電池電量。 * @return 返回電量百分比 */ RT_WEAK int getPowerLevel(void) { return 0; } /** * 上傳天通數據 * @param din 待發送數據 * @param len 待發送數據的長度 * @return */ RT_WEAK int upSend(uint8_t *din, size_t len) { //此函数有打包操作,需线程操作 LOG_D("upsend."); return 0; uint8_t dout[200]; //打包数据 static MSG cfg; rt_memset(&cfg, 0, sizeof(MSG)); // 分配空间 char *fin; time2Str(fin); packInit(&cfg, fin, 0); //写入配置 size_t rst = packMsg(&cfg, din, len, dout); LOG_HEX("upSend", 16, dout, rst); return 0; } /** * 系统自检,自动发送自检结果 */ void selfTest() { LOG_D("FUNC = selftest"); 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); //发送结果 upSend(rst,p); } //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工作参数配置、状态查询 //包含浮体自身、标体透传、两者结合 //这里主要实现浮体自身 //浮体自身的参数配置各功能函数有实现,此处需规定参数下发协议和解码实现 //获取当前位置 /** * 获取当前经纬度信息 * @param dout 存储结果的数组 * @param cnt 获取几组位置数据 * @return 数组长度。0表示位置数据未准备好。 */ RT_WEAK int getLoc(uint8_t * dout, size_t cnt) { LOG_D("待实现获取位置函数,此处以0xCD代替"); uint8_t tmp[200]; memset(tmp,0xCD,200); memcpy(dout,tmp,cnt*10); return cnt*10;//4+4+2 } /** * 确保获取正常的位置数据 * @param dout 位置数据结果 * @param pairCnt 获取几组位置数据 * @return 数组长度 */ static int getAndCheckLoc(uint8_t *dout, size_t pairCnt) { uint8_t loc[200]; size_t cnt = getLoc(loc,pairCnt); while (!cnt) { static i=0; LOG_W("位置信息还未准备好。"); rt_thread_mdelay(4000);//状态数据默认3s更新一次 cnt = getLoc(loc,pairCnt); if (i++ > 20) { LOG_E("位置信息获取异常"); break; } } memcpy(dout,loc,cnt); return cnt; } /** * c回应深度数据,含位置信息 * @param din * @param len * @return */ int reportDepth(uint8_t *din,size_t len) { uint8_t rst[len]; memcpy(rst,din,len); // 000000000000000000005AA53E320608000A170407101B33FFFFFFFF24ED getAndCheckLoc(rst+0,1);//有10个位置的空余字节,尚不清楚定义 upSend(rst, len); } //天通指令类型:0x70 0x01 //3.2.8定时自报位置信息 //每小时传数据时同步传位置信息 //与告警信息共用编码方式 //首字节00为定时发送,未检测围栏;其它为在围栏外 //单次最多可上报15条位置信息 /** * 按15条经纬度打包位置信息 * @param dout 存储位置信息的数组 * @return 数组大小 */ static int packLocMsg(uint8_t *dout) { uint8_t alertMsg[200] = { 0x5A, 0xA5, ADDR_ANJI, ADDR_TT, _CFG_LOCATION_ALERT >> 8, _CFG_LOCATION_ALERT & 0xFF, 0, 0 }; alertMsg[8] = 0; //首字节00为定时发送,未检测围栏;其它为在围栏外 int len = time2Byte(alertMsg+9);//添加时间戳 len += (getAndCheckLoc(alertMsg + 9 + len, 15)+1); //p指向第2个数据 alertMsg[7] = len; //update len of raw data alertMsg[8 + len] = bccCRC(alertMsg + 2, 8 + len - 2); //update CRC alertMsg[8 + len + 1] = 0xED; len = 8 + len + 2; LOG_HEX("packLocMsg", 16, alertMsg, len); //bccCRC+Tail memcpy(dout, alertMsg, len); return len; } static void d_packLocMsg(void) { uint8_t tmp[200]; packLocMsg(tmp); } /** * 加密位置信息。对位置信息的更改需在加密前操作 * @param din 存储待加密位置信息的数组 * @param len 带加密长度 * @param dout 存储加密结果的数组 * @return 加密后的长度 */ static int cryptLocMsg(uint8_t *din, size_t len, uint8_t *dout) { //加密。因加密后数据长度会变化,故不能只加密位置数据。 uint8_t cd[200]={0xAB,0xAB}; size_t nlen = 0; //#define FULL_DATA_CRYPT #ifdef FULL_DATA_CRYPT nlen = cryp_data(din, len, cd); #else //单独加密时在加密后数据头部添加0xABAB便于识别、解析 nlen = cryp_data(din, len, cd + 2) + 2; #endif memcpy(dout, cd, nlen); return nlen; } int packAndSendLoc() { uint8_t rst[200]; int len = packLocMsg(rst); LOG_D("len=%d",len); len = cryptLocMsg(rst, len, rst); LOG_D("len=%d",len); LOG_HEX("crypt",16,rst,len); } //3.2.9深度异常告警 int packDeepMsg(uint8_t *din, int len, uint8_t *dout) { //获取并更新位置信息 uint8_t loc[]={}; rt_memcpy(din+6, loc, 8);//位置数据从【】字节开始,共8个字节 //加密。因加密后数据长度会变化,故不能只加密位置数据。 size_t nlen = cryptByte(din, len, dout); LOG_D("位置数据加密完成"); upSend(dout, nlen); } //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 uint8_t loc[8]; getAndCheckLoc(loc,1); float x,y =0; memcpy(&x,loc,4); memcpy(&y,loc+4,4); int isIN = isInFence(x, y); if (isIN) { LOG_I("设备在预设范围内,位置正常。"); } else { LOG_W("警告!设备不在预设范围内!"); uint8_t msg[200]; int len = packLocMsg(msg); len = cryptLocMsg(msg,len,msg); //告警信息与3.2.8定时自报位置信息的共用编码方式 // uint8_t alertMsg[]={0x5A,0xA5,ADDR_ANJI,ADDR_TT}; // alertMsg[4] = _CFG_LOCATION_ALERT >> 8; // alertMsg[5] = _CFG_LOCATION_ALERT & 0xFF; // // //加密。 // uint8_t cd[200]; // size_t nlen = cryptByte(din, len, cd); // upSend(alertMsg, sizeof(alertMsg)); } } //3.2.12数据存储区清空 // /** * 采用格式化命令对存储区进行清空,谨慎使用! */ void clearAllData() { mkfs("elm","sd0");//format SD mkfs("elm", "W25Q128");//format flash } /** * @brief TT根据下发的指令执行对应的功能 * * @param din 待执行的指令数据 * @param len 数据长度,单位字节 */ void ttRunCMD(uint8_t *din, size_t len) { /** +--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 8+N | 9+N | 10+N | +--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+ | Header | Header | targetAddr | sourceAddr | mainCMD | subCMD | lenH | lenL | data1 | ... | dataN | BbccCRC | tail | +--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+ */ int cmd = (din[4] << 8) + din[5]; // int para[] switch (cmd) { case _CFG_COMM_MODE: setCommMode(din[8]); break; case _CMD_SELF_TEST: selfTest(); break; case _CFG_SELF_DESTRUCT: setSelfDestructSWT(din[8]); break; case _CMD_SELF_DESTRUCT: selfDestruct(); break; case _CFG_COMM_WINDOW: setCommWindow(din+8);//只支持两组开窗 break; case _CMD_OPEN_WINDOW: openWindow(din[7]?((din[8] << 8) + din[9]):0);//两字节开窗时间 break; case _CMD_CLOSE_WINDOW: closeWindow(); break; case _CFG_LOCATION_ALERT: setLocationAlertSWT(din[8]); break; case _CMD_CLEAR_DATA: clearAllData(); break; default: LOG_W("未支持的指令。"); break; } } RT_WEAK int xpParse(uint8_t * din, size_t len) { LOG_D("直接调用小彭的函数进行处理。"); return 0; } //原计划将指令粗解析放在上位机,考虑到上位机到位时间晚,现放到MCU端 /** * @brief 校验、解析3S数据,以0x5AA5开头0xED结尾。 * * @param din 待解析数据 * @param count 待解析数据长度 */ void parse3SData(uint8_t *din, size_t count) { /** +--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 8+N | 9+N | 10+N | +--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+ | Header | Header | targetAddr | sourceAddr | mainCMD | subCMD | lenH | lenL | data1 | ... | dataN | BbccCRC | tail | +--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+ */ 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[2] == ADDR_TT)//仅给TT的消息 { ttRunCMD(din,count); } if (din[3] == ADDR_3S)//给3S的指令,需要再加工,返回数据可能也需要再加工 { // xpParse(din,count); //以下处理逻辑作废 //绝大部分数据均需要转换。详见《主要指令一览》 // 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) { /** * +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+ * | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | * +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+ * | fstart[4] | fnum[2] | bak[2] | ftype[2] | fdlen[2] | fcrc[1] | ftccid[4] | rawData[N] | * | 0x88,0xAA,0xBB,0x88 | 0x00,0x01 | 0x00,0x22 | 0x70,0x21 | 0x00,0xAA | 0x00 | 0x27,0x22,0x22,0x22 | | * +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+ */ 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[10]; // size_t n=isInByte(din, len, head, 10, rst); // uint8_t ndin[200]; // for (size_t i = 0; i < n; i++) // { // int n=rst[i+1] // memcpy(ndin,din,rst[i+1]-rst[i]); // } uint8_t rst = memcmp(din,head,10);//只比较到ftype 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; // } } /** * 缓存任务数据 * @param din 单次收到的任务数据 * @param len 任务数据长度 * @return 0--正常,-1--异常 */ int cacheData(uint8_t *din, size_t len) { static int cnt = 0; LOG_D("cached size=%d", cnt); char rootDir[22] = "/sd/rxdata/"; // if (!opendir(rootDir)) { mkdir(rootDir, 0); } strcat(rootDir, "2023_07_19/"); //name of cachefile static char f[60] = ""; if (cnt == 0 || cnt > 1024) { //广播待发送文件 cnt = 0; //更新时戳 char ts[30] = ""; time2Str(ts); //更新文件夹 strncpy(rootDir + strlen(rootDir) - 9, ts, 8); // if (!opendir(rootDir)) { mkdir(rootDir, 0); } //更新文件名 f[0] = '\0'; strcat(f, rootDir); // strcpy(f,rootDir); strcat(f, "23_07_19_16_38_36_36.bin"); LOG_D("need to creat new file"); strncpy(f + strlen(rootDir), ts, strlen(ts)); } LOG_D("f=%s", f); int fd = open(f, O_WRONLY | O_CREAT | O_APPEND); if (fd < 0) { LOG_E("open file %s failed!", f); return -RT_ERROR; } else { int rst = write(fd, din, len); if (rst != len) { LOG_E("write to file %s failed!", f); close(fd); return -RT_ERROR; } cnt += len; close(fd); } return RT_EOK; } void d_cacheData() { uint8_t demo[200]; size_t len = sizeof(demo); memset(demo,0xAB,len); cacheData(demo, len); } #define FUNC_DEMO #ifdef FUNC_DEMO //测试时导出命令到控制台 MSH_CMD_EXPORT(d_getFreeSpace,getFreeSpace); MSH_CMD_EXPORT(selfTest,sysSelfTest); MSH_CMD_EXPORT(d_packLocMsg,dpackLocMsg); MSH_CMD_EXPORT(packAndSendLoc,packAndSendLoc); MSH_CMD_EXPORT(d_cacheData,d_cacheData); #endif