2023-07-10 02:42:26 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
#include "func.h"
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
#define LOG_TAG "func"
|
|
|
|
|
#define LOG_LVL LOG_LVL_DBG
|
|
|
|
|
#include <ulog.h>
|
|
|
|
|
|
2023-08-01 08:27:21 +00:00
|
|
|
|
extern SYS_CFG scfg;
|
2023-08-19 06:41:13 +00:00
|
|
|
|
extern int cryptSingleMsg(uint8_t *din, size_t len, uint8_t *dout);
|
2023-08-22 10:23:41 +00:00
|
|
|
|
extern int pointInPolygon(int polyCorners,float polyX[], float polyY[],float x,float y);
|
2023-08-29 11:29:58 +00:00
|
|
|
|
extern rt_sem_t okToreport;
|
2023-08-31 08:12:44 +00:00
|
|
|
|
extern void resetTM(void);
|
2023-09-02 08:39:08 +00:00
|
|
|
|
unsigned long getFileSize(char *file);
|
2023-09-07 07:24:38 +00:00
|
|
|
|
void updateSysRTC(uint8_t *din, size_t len);
|
2023-08-29 11:29:58 +00:00
|
|
|
|
#define CFG_ACK
|
|
|
|
|
/**
|
|
|
|
|
* 配置文件后的ack信号
|
|
|
|
|
*/
|
|
|
|
|
void ack(void)
|
|
|
|
|
{
|
|
|
|
|
#ifdef CFG_ACK
|
|
|
|
|
rt_sem_release(okToreport);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2023-07-26 08:26:53 +00:00
|
|
|
|
/**
|
|
|
|
|
* 将收发数据以ASCII字符形式存入log
|
|
|
|
|
* @param din 待存储数据
|
|
|
|
|
* @param len 数据长度
|
2023-08-24 03:06:44 +00:00
|
|
|
|
* @return 1-发送的数据,0-接收的数据,其它为发送失败的数据
|
2023-07-26 08:26:53 +00:00
|
|
|
|
*/
|
|
|
|
|
int trDataTolog(uint8_t *din, size_t len, uint8_t isTx)
|
2023-08-26 11:36:36 +00:00
|
|
|
|
{
|
|
|
|
|
// return RT_EOK;
|
2023-08-08 08:38:50 +00:00
|
|
|
|
char fn[60] = "\n";
|
|
|
|
|
initDataLogPath(fn);
|
2023-07-26 08:26:53 +00:00
|
|
|
|
|
|
|
|
|
int fd = open(fn, O_WRONLY | O_CREAT | O_APPEND);
|
2023-08-24 03:06:44 +00:00
|
|
|
|
|
|
|
|
|
//没有加锁,多试几次再报错
|
|
|
|
|
int trycnt=0;
|
|
|
|
|
while (fd < 0)
|
2023-07-26 08:26:53 +00:00
|
|
|
|
{
|
2023-08-25 08:36:00 +00:00
|
|
|
|
rt_thread_mdelay(200);
|
2023-08-24 03:06:44 +00:00
|
|
|
|
fd = open(fn, O_WRONLY | O_CREAT | O_APPEND);
|
|
|
|
|
trycnt +=1;
|
|
|
|
|
if (trycnt>5) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 08:36:00 +00:00
|
|
|
|
if (fd<0) {
|
2023-07-26 08:26:53 +00:00
|
|
|
|
LOG_E("open file %s failed!", fn);
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-08-08 08:38:50 +00:00
|
|
|
|
char log[30]="\n";
|
|
|
|
|
getTimestmp(log+1);
|
2023-07-26 08:26:53 +00:00
|
|
|
|
strcat(log,isTx?" [T]: ":" [R]: ");
|
2023-08-08 08:38:50 +00:00
|
|
|
|
write(fd, log, strlen(log));
|
2023-07-26 08:26:53 +00:00
|
|
|
|
|
2023-08-08 08:38:50 +00:00
|
|
|
|
int rst = write(fd, din, len);
|
|
|
|
|
if (rst != len)
|
2023-07-26 08:26:53 +00:00
|
|
|
|
{
|
|
|
|
|
LOG_E("write to file %s failed!", fn);
|
|
|
|
|
close(fd);
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
close(fd);
|
|
|
|
|
return RT_EOK;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-27 04:31:54 +00:00
|
|
|
|
extern rt_sem_t cfgUpdate;
|
2023-07-10 02:42:26 +00:00
|
|
|
|
//3.2.1双模通信功能
|
|
|
|
|
//1为TT通信,0为BD短报文通信
|
|
|
|
|
void setCommMode(int isTT)
|
|
|
|
|
{
|
2023-07-26 00:44:18 +00:00
|
|
|
|
LOG_I("FUNC = setCommMode");
|
2023-07-10 02:42:26 +00:00
|
|
|
|
if (isTT) {
|
|
|
|
|
//change mode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
//write to cfg file
|
2023-08-27 04:31:54 +00:00
|
|
|
|
// set_cfg("commMode", isTT);
|
|
|
|
|
scfg.commMode=isTT;
|
|
|
|
|
rt_sem_release(cfgUpdate);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
LOG_D("set commMode to %s",isTT?"TT":"BD");
|
2023-08-29 11:29:58 +00:00
|
|
|
|
ack();
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int getCommMode()
|
|
|
|
|
{
|
|
|
|
|
//load from cfg file
|
2023-08-27 04:31:54 +00:00
|
|
|
|
// int flag = get_cfg("commMode");
|
|
|
|
|
// if (flag < 0) {
|
|
|
|
|
// LOG_W("get mode fault.");
|
|
|
|
|
// }
|
|
|
|
|
// return flag;
|
|
|
|
|
return scfg.commMode;
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//3.2.2状态自检
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* 获取磁盘剩余空间大小,单位MB
|
|
|
|
|
* @param path指定磁盘挂载的路径,为空时表示挂载在根目录的磁盘
|
|
|
|
|
* @return 返回结果,单位MB
|
|
|
|
|
*/
|
|
|
|
|
static uint16_t getFreeSpace(const char *path)
|
2023-07-10 02:42:26 +00:00
|
|
|
|
{
|
2023-07-18 11:23:48 +00:00
|
|
|
|
long long cap;
|
|
|
|
|
struct statfs buffer;
|
|
|
|
|
int result = dfs_statfs(path ? path : "/", &buffer);
|
|
|
|
|
if (result != 0)
|
|
|
|
|
{
|
|
|
|
|
LOG_E("dfs_statfs failed.");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
cap = (uint16_t)((long long)buffer.f_bsize) * ((long long)buffer.f_bfree) / 1024LL / 1024LL ;//转换为MB
|
|
|
|
|
return cap;
|
|
|
|
|
}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
|
|
|
|
void d_getFreeSpace()
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* 獲取電池電量。
|
|
|
|
|
* @return 返回電量百分比
|
|
|
|
|
*/
|
|
|
|
|
RT_WEAK int getPowerLevel(void)
|
|
|
|
|
{
|
2023-09-02 07:23:32 +00:00
|
|
|
|
return 0x63;
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-07-25 06:55:03 +00:00
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
rt_uint8_t len;
|
2023-09-05 10:53:01 +00:00
|
|
|
|
rt_uint8_t data[200];
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-07-25 06:55:03 +00:00
|
|
|
|
} SMSG;//single messgae
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-08-25 00:08:30 +00:00
|
|
|
|
rt_sem_t one_msg_send_done=RT_NULL;
|
2023-07-25 06:55:03 +00:00
|
|
|
|
void upSend_thread_entry(void* parameter)
|
|
|
|
|
{
|
|
|
|
|
SMSG* msg = RT_NULL;
|
|
|
|
|
msg = (SMSG*) parameter;
|
2023-08-25 00:08:30 +00:00
|
|
|
|
// LOG_D("1--%p",msg);
|
2023-08-21 03:17:40 +00:00
|
|
|
|
// LOG_HEX("--",16,msg->data,msg->len);
|
2023-07-29 08:08:15 +00:00
|
|
|
|
|
2023-08-21 03:17:40 +00:00
|
|
|
|
//check status
|
2023-08-25 08:36:00 +00:00
|
|
|
|
uint8_t trycnt = 0;
|
2023-08-30 02:45:32 +00:00
|
|
|
|
while (!isTTjh()) //判断TT状态
|
2023-07-29 08:08:15 +00:00
|
|
|
|
{
|
|
|
|
|
rt_thread_mdelay(4000);
|
|
|
|
|
trycnt += 1;
|
|
|
|
|
if (trycnt > 3)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-25 08:36:00 +00:00
|
|
|
|
if (!isTTjh()) {
|
2023-07-29 08:08:15 +00:00
|
|
|
|
//cache to file
|
|
|
|
|
LOG_W("TT is not ready, try to cache %d bytes data to file.",msg->len);
|
|
|
|
|
cacheDataToFile(msg->data, msg->len);
|
2023-07-29 14:09:35 +00:00
|
|
|
|
return ;
|
2023-07-29 08:08:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-07-25 06:55:03 +00:00
|
|
|
|
//打包数据
|
2023-09-05 10:53:01 +00:00
|
|
|
|
uint8_t dout[300];
|
2023-07-25 06:55:03 +00:00
|
|
|
|
static MSG cfg;
|
|
|
|
|
rt_memset(&cfg, 0, sizeof(MSG)); // 分配空间
|
|
|
|
|
char fin[30];
|
|
|
|
|
time2Str(fin);
|
|
|
|
|
strcat(fin,".bin");
|
|
|
|
|
packInit(&cfg, fin, 0); //写入配置
|
|
|
|
|
cfg.fcurpiece[0] = 1;
|
|
|
|
|
cfg.fallpiece[0] = 1;
|
2023-08-22 10:23:41 +00:00
|
|
|
|
#ifdef CRYPT_BEFRE_PACK
|
2023-08-19 06:41:13 +00:00
|
|
|
|
//crypt before pack
|
|
|
|
|
uint8_t tmp[200];
|
|
|
|
|
uint8_t len = cryptSingleMsg(msg->data, msg->len, tmp);
|
2023-09-05 10:53:01 +00:00
|
|
|
|
if (len>170) {
|
|
|
|
|
LOG_W("length of msg is too long.[%d->%d]",msg->len,len);
|
|
|
|
|
}
|
2023-08-19 06:41:13 +00:00
|
|
|
|
size_t rst = packMsg(&cfg, tmp, len, dout);//packMsgs
|
2023-08-22 10:23:41 +00:00
|
|
|
|
#else
|
|
|
|
|
size_t rst = packMsg(&cfg, tmpmsg->data, msg->len, dout);//packMsgs
|
|
|
|
|
#endif
|
2023-07-25 06:55:03 +00:00
|
|
|
|
LOG_HEX("upSend", 27, dout, rst);
|
|
|
|
|
if (rst)
|
2023-08-22 10:23:41 +00:00
|
|
|
|
{
|
2023-07-25 06:55:03 +00:00
|
|
|
|
if (sendMsg(dout, rst) == RT_EOK)
|
|
|
|
|
{
|
2023-09-01 08:59:49 +00:00
|
|
|
|
LOG_I("send %d bytes to TT Done.",rst);
|
2023-07-28 08:29:57 +00:00
|
|
|
|
}
|
|
|
|
|
else//发送失败,实例不存在此种情况
|
2023-08-22 10:23:41 +00:00
|
|
|
|
{
|
2023-07-28 08:29:57 +00:00
|
|
|
|
LOG_E("send error, try to cache %d bytes data to file.",msg->len);
|
|
|
|
|
cacheDataToFile(msg->data, msg->len);
|
|
|
|
|
}
|
2023-07-25 06:55:03 +00:00
|
|
|
|
}
|
2023-08-25 08:36:00 +00:00
|
|
|
|
// rt_sem_release(one_msg_send_done);
|
|
|
|
|
// list_thread();
|
2023-07-29 14:09:35 +00:00
|
|
|
|
return ;
|
2023-07-25 06:55:03 +00:00
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* 上傳天通數據
|
|
|
|
|
* @param din 待發送數據
|
|
|
|
|
* @param len 待發送數據的長度
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2023-08-29 11:29:58 +00:00
|
|
|
|
int upSend(uint8_t *din, size_t len)
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
2023-07-26 08:26:53 +00:00
|
|
|
|
LOG_D("try to upsend to TT.");
|
2023-08-26 08:49:19 +00:00
|
|
|
|
//return;
|
2023-08-25 08:36:00 +00:00
|
|
|
|
// if (one_msg_send_done == RT_NULL) {
|
|
|
|
|
// one_msg_send_done = rt_sem_create("senddone", 1, RT_IPC_FLAG_PRIO);
|
|
|
|
|
// }
|
2023-08-25 00:08:30 +00:00
|
|
|
|
|
2023-07-25 06:55:03 +00:00
|
|
|
|
static SMSG msg;
|
2023-08-31 08:12:44 +00:00
|
|
|
|
rt_memset(&msg, 0, sizeof(SMSG));
|
|
|
|
|
rt_memcpy(msg.data,din,len);
|
2023-07-25 06:55:03 +00:00
|
|
|
|
msg.len=len;
|
2023-08-25 00:08:30 +00:00
|
|
|
|
// LOG_D("0--%p",&msg);
|
|
|
|
|
//仅创建一个线程用于发送
|
2023-08-25 08:36:00 +00:00
|
|
|
|
// if (rt_sem_take(one_msg_send_done, rt_tick_from_millisecond(10000)) != RT_EOK) {
|
|
|
|
|
// //10s等待,上一发送仍未完成
|
|
|
|
|
// LOG_E("wait error, try to cache %d bytes data to file.",msg.len);
|
|
|
|
|
// cacheDataToFile(msg.data, msg.len);
|
|
|
|
|
// return -RT_ERROR;
|
|
|
|
|
// }
|
2023-07-25 06:55:03 +00:00
|
|
|
|
/* 创建 serial 线程 */
|
2023-09-01 03:38:33 +00:00
|
|
|
|
rt_thread_t thread = rt_thread_create("upSend", upSend_thread_entry, (void *) &msg, 1024 * 5, 26, 10);
|
2023-07-25 06:55:03 +00:00
|
|
|
|
/* 创建成功则启动线程 */
|
|
|
|
|
if (thread != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_thread_startup(thread);
|
2023-07-26 08:26:53 +00:00
|
|
|
|
return RT_EOK;
|
2023-07-25 06:55:03 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG_E("thread 'upSend' create failure.");
|
2023-07-26 08:26:53 +00:00
|
|
|
|
return -RT_ERROR;
|
2023-07-25 06:55:03 +00:00
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* 系统自检,自动发送自检结果
|
|
|
|
|
*/
|
2023-08-26 08:49:19 +00:00
|
|
|
|
void selfTest(void)
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
2023-08-27 04:31:54 +00:00
|
|
|
|
// return;
|
2023-07-20 08:18:42 +00:00
|
|
|
|
LOG_I("FUNC = selftest");
|
2023-08-25 08:36:00 +00:00
|
|
|
|
rt_uint8_t rst[200]={0x5A, 0xA5, ADDR_ANJI, ADDR_TT, _CMD_SELF_TEST>>8, _CMD_SELF_TEST & 0xff};
|
2023-07-18 11:23:48 +00:00
|
|
|
|
int p = 6;
|
2023-08-15 09:10:18 +00:00
|
|
|
|
rt_uint8_t sysSta=1,commSpeed=0;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
//长度
|
2023-07-18 11:23:48 +00:00
|
|
|
|
rst[p++]=0x00;
|
|
|
|
|
rst[p++]=0x09;
|
|
|
|
|
|
2023-08-26 08:49:19 +00:00
|
|
|
|
rst[p++] = sysSta; //系统状态
|
|
|
|
|
rst[p++] = getXh();//xh,jh //信号值
|
|
|
|
|
rst[p++] = isTTjh(); //激活
|
|
|
|
|
rst[p++] = commSpeed; //速率
|
|
|
|
|
// rst[p++] = scfg.commMode;
|
|
|
|
|
rst[p++] = getPowerLevel(); //电量
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-08-26 08:49:19 +00:00
|
|
|
|
//flash 剩餘空間 //FLASH剩余空间
|
2023-07-18 11:23:48 +00:00
|
|
|
|
uint16_t cap = getFreeSpace("/");
|
|
|
|
|
rst[p++] = (uint8_t)(cap >> 8);
|
|
|
|
|
rst[p++] = (uint8_t)(cap & 0xff);
|
|
|
|
|
|
|
|
|
|
//SD卡剩餘空間
|
2023-08-26 08:49:19 +00:00
|
|
|
|
cap = getFreeSpace("/sd"); //SD卡剩余空间
|
2023-07-18 11:23:48 +00:00
|
|
|
|
rst[p++] = (uint8_t)(cap >> 8);
|
|
|
|
|
rst[p++] = (uint8_t)(cap & 0xff);
|
|
|
|
|
|
2023-08-17 01:22:02 +00:00
|
|
|
|
//添加配置文件信息
|
2023-08-26 08:49:19 +00:00
|
|
|
|
uint8_t size = sizeof(scfg); //配置文件信息
|
2023-08-17 01:22:02 +00:00
|
|
|
|
memcpy(rst + p,&scfg, size);
|
|
|
|
|
p+=size;
|
|
|
|
|
|
|
|
|
|
rst[7] = p-8;//更新数据长度
|
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
rst[p++] = bccCRC(rst+2, p-1); ////校验位为1个字节,采用异或运算,从指令的第3个字节开始,到奇偶校验位的前一个字节结束
|
|
|
|
|
rst[p++] = 0xED; //结束位
|
|
|
|
|
|
2023-09-08 08:38:30 +00:00
|
|
|
|
// LOG_HEX("selfTestRes",16,rst,p);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
//发送结果
|
2023-07-19 10:57:54 +00:00
|
|
|
|
upSend(rst,p);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
//3.2.3日志记录
|
|
|
|
|
//日志功能由各函数通过LOG_D()实现
|
|
|
|
|
|
|
|
|
|
//3.2.4自毁功能
|
|
|
|
|
/**
|
|
|
|
|
* 设置自毁功能开关
|
|
|
|
|
* @param setON 1-自毁功能开启,0-关闭
|
|
|
|
|
*/
|
|
|
|
|
void setSelfDestructSWT(int setON)
|
|
|
|
|
{
|
2023-07-26 00:44:18 +00:00
|
|
|
|
LOG_I("FUNC = set selfdestruct");
|
2023-07-10 02:42:26 +00:00
|
|
|
|
//write to cfg file
|
2023-08-27 04:31:54 +00:00
|
|
|
|
// set_cfg("selfDesSW", setON);
|
|
|
|
|
scfg.selfDesSW=setON;
|
|
|
|
|
rt_sem_release(cfgUpdate);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
LOG_D("set SelfDesSW to %s",setON?"ON":"OFF");
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 获取自毁开关状态
|
|
|
|
|
* @return 1-自毁功能开启,0-关闭
|
|
|
|
|
*/
|
|
|
|
|
int getSelfDestructSWT()
|
|
|
|
|
{
|
2023-07-26 00:44:18 +00:00
|
|
|
|
LOG_I("FUNC = get selfdestruct");
|
2023-07-10 02:42:26 +00:00
|
|
|
|
//load from cfg file
|
2023-08-27 04:31:54 +00:00
|
|
|
|
// int flag = get_cfg("selfDesSW");
|
|
|
|
|
// if (flag < 0) {
|
|
|
|
|
// LOG_W("get mode fault.");
|
|
|
|
|
// }
|
|
|
|
|
// return flag;
|
|
|
|
|
return scfg.selfDesSW;
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 启动自毁
|
|
|
|
|
*/
|
|
|
|
|
void selfDestruct()
|
|
|
|
|
{
|
|
|
|
|
if (getSelfDestructSWT()) {
|
|
|
|
|
//硬件自毁
|
|
|
|
|
LOG_W("SELF DESTRUCT START.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//3.2.5开、关窗功能
|
2023-07-26 00:44:18 +00:00
|
|
|
|
extern void updateAlarm(uint8_t *t, size_t len);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
/**
|
|
|
|
|
* 更新开窗时间,目前支持两组开窗时段。更新会清除之前的开窗设置
|
|
|
|
|
*/
|
2023-07-26 00:44:18 +00:00
|
|
|
|
void setCommWindow(uint8_t *t, size_t len)
|
2023-07-10 02:42:26 +00:00
|
|
|
|
{
|
2023-07-26 00:44:18 +00:00
|
|
|
|
LOG_I("FUNC = setCommWindow");
|
2023-09-05 10:53:01 +00:00
|
|
|
|
size_t cnt = sizeof(scfg.openWindowTime);
|
|
|
|
|
if (len>cnt) {
|
|
|
|
|
LOG_W("only support %d windows",cnt/4);
|
|
|
|
|
len = cnt;
|
|
|
|
|
}
|
|
|
|
|
len= len/4*4;//窗口需要成对
|
2023-08-31 08:12:44 +00:00
|
|
|
|
|
2023-09-04 09:45:03 +00:00
|
|
|
|
updateAlarm(t,len);
|
2023-08-27 04:31:54 +00:00
|
|
|
|
memcpy(scfg.openWindowTime,t,len);
|
|
|
|
|
rt_sem_release(cfgUpdate);
|
2023-08-08 06:31:16 +00:00
|
|
|
|
// LOG_D("updated new CommWindow zone");
|
2023-08-29 11:29:58 +00:00
|
|
|
|
ack();
|
2023-08-08 06:31:16 +00:00
|
|
|
|
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
2023-08-23 03:02:26 +00:00
|
|
|
|
|
|
|
|
|
void d_sw(void)
|
|
|
|
|
{
|
|
|
|
|
uint8_t cfg[]={0x03, 0x1F, 0x03, 0x20, 0x07, 0x1F, 0x09, 0x1E};
|
|
|
|
|
setCommWindow(cfg, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-09-01 03:38:33 +00:00
|
|
|
|
/**
|
|
|
|
|
* 存储手动开窗状态,便于意外重启后恢复状态
|
|
|
|
|
* @param isManualWindow 负数代表无,0代表手动开窗不自动关,整数代表自动关窗超时时间
|
|
|
|
|
*/
|
2023-09-03 08:54:23 +00:00
|
|
|
|
void setManualWindow(int isManualWindow)
|
2023-09-01 03:38:33 +00:00
|
|
|
|
{
|
2023-09-05 10:53:01 +00:00
|
|
|
|
scfg.isMaWin = isManualWindow;
|
2023-09-01 03:38:33 +00:00
|
|
|
|
set_cfg("isMaWin", isManualWindow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void closeWindow(void);
|
|
|
|
|
/**
|
|
|
|
|
* 是否手动开窗状态
|
|
|
|
|
* @return 1-手动开窗模式,0-无
|
|
|
|
|
*/
|
|
|
|
|
int isManualWindow(void)
|
2023-09-05 10:53:01 +00:00
|
|
|
|
{
|
|
|
|
|
int rst = scfg.isMaWin;
|
|
|
|
|
if (rst >= 0) {
|
|
|
|
|
rst = 1;
|
|
|
|
|
LOG_I("is in manual open mode.");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
rst = 0;
|
|
|
|
|
}
|
|
|
|
|
return rst;
|
|
|
|
|
}
|
|
|
|
|
void checkManualWindow(void)
|
2023-09-01 03:38:33 +00:00
|
|
|
|
{
|
2023-09-06 01:51:08 +00:00
|
|
|
|
int rst = scfg.isMaWin;
|
2023-09-01 03:38:33 +00:00
|
|
|
|
if (rst < 0) {//none
|
2023-09-05 10:53:01 +00:00
|
|
|
|
return;
|
2023-09-01 03:38:33 +00:00
|
|
|
|
}
|
2023-09-05 10:53:01 +00:00
|
|
|
|
|
2023-09-01 03:38:33 +00:00
|
|
|
|
if (rst == 0) {//manual open without auto close
|
2023-09-05 10:53:01 +00:00
|
|
|
|
return;
|
2023-09-01 03:38:33 +00:00
|
|
|
|
}
|
|
|
|
|
//设置定时器,定时器到则关窗
|
|
|
|
|
/* 创建定时器,单次定时器 */
|
|
|
|
|
rt_timer_t timer1;
|
2023-09-06 01:51:08 +00:00
|
|
|
|
timer1 = rt_timer_create("manualModeWindow", closeWindow,
|
2023-09-01 03:38:33 +00:00
|
|
|
|
RT_NULL, rt_tick_from_millisecond(rst*60*1000),
|
|
|
|
|
RT_TIMER_FLAG_ONE_SHOT);
|
|
|
|
|
/* 启动定时器 */
|
|
|
|
|
if (timer1 != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_timer_start(timer1);
|
|
|
|
|
LOG_D("手动开窗完成,%d分钟后自动关窗。",rst);
|
|
|
|
|
}
|
2023-09-05 10:53:01 +00:00
|
|
|
|
return;
|
2023-09-01 03:38:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-10 02:42:26 +00:00
|
|
|
|
/**
|
|
|
|
|
* 手动控制开窗
|
|
|
|
|
* @param t 开窗时长,单位分钟,时间到则自动关窗。t=0时需要手动关窗。
|
|
|
|
|
*/
|
|
|
|
|
void openWindow(int t)
|
|
|
|
|
{
|
2023-09-01 03:38:33 +00:00
|
|
|
|
//保存记录
|
|
|
|
|
setManualWindow(t);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
//开启TT
|
2023-08-21 09:18:07 +00:00
|
|
|
|
initTT();
|
|
|
|
|
setWindowMode();
|
2023-08-23 06:41:29 +00:00
|
|
|
|
//手动开窗优先级最高,自动开窗其次,高优先级会屏蔽低优先级
|
|
|
|
|
stopAlarm();
|
2023-07-10 02:42:26 +00:00
|
|
|
|
if (!t) {
|
|
|
|
|
LOG_D("手动开窗完成,需手动关窗。");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//设置定时器,定时器到则关窗
|
|
|
|
|
/* 创建定时器,单次定时器 */
|
|
|
|
|
rt_timer_t timer1;
|
2023-09-01 03:38:33 +00:00
|
|
|
|
timer1 = rt_timer_create("window", closeWindow,
|
2023-08-21 09:18:07 +00:00
|
|
|
|
RT_NULL, rt_tick_from_millisecond(t*60*1000),
|
2023-07-10 02:42:26 +00:00
|
|
|
|
RT_TIMER_FLAG_ONE_SHOT);
|
|
|
|
|
/* 启动定时器 */
|
|
|
|
|
if (timer1 != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_timer_start(timer1);
|
|
|
|
|
LOG_D("手动开窗完成,%d分钟后自动关窗。",t);
|
|
|
|
|
}
|
2023-08-29 11:29:58 +00:00
|
|
|
|
ack();
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 手动关窗
|
|
|
|
|
*/
|
2023-09-01 03:38:33 +00:00
|
|
|
|
void closeWindow(void)
|
2023-07-10 02:42:26 +00:00
|
|
|
|
{
|
2023-09-01 03:38:33 +00:00
|
|
|
|
setManualWindow(-1);
|
|
|
|
|
// 恢复RTC
|
|
|
|
|
startAlarm();
|
2023-08-21 09:18:07 +00:00
|
|
|
|
deInitTT();
|
2023-07-10 02:42:26 +00:00
|
|
|
|
LOG_D("手动关窗完成。");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//3.2.6工作参数配置、状态查询
|
|
|
|
|
//包含浮体自身、标体透传、两者结合
|
|
|
|
|
//这里主要实现浮体自身
|
|
|
|
|
//浮体自身的参数配置各功能函数有实现,此处需规定参数下发协议和解码实现
|
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
//获取当前位置
|
|
|
|
|
/**
|
|
|
|
|
* 获取当前经纬度信息
|
|
|
|
|
* @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);
|
2023-09-06 09:25:38 +00:00
|
|
|
|
|
|
|
|
|
//定位故障,临时填充数据
|
|
|
|
|
if (!cnt) {
|
2023-09-07 11:46:03 +00:00
|
|
|
|
// LOG_W("NONE Loc data, using 0x37 replaced");
|
2023-09-06 09:25:38 +00:00
|
|
|
|
cnt = pairCnt*10;
|
|
|
|
|
memset(loc,0x37,cnt);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-18 07:36:41 +00:00
|
|
|
|
if (!cnt) {
|
2023-08-22 10:23:41 +00:00
|
|
|
|
return 0;
|
|
|
|
|
// LOG_W("位置信息还未准备好。");
|
2023-08-18 07:36:41 +00:00
|
|
|
|
}
|
2023-08-22 10:23:41 +00:00
|
|
|
|
// else {
|
|
|
|
|
// memcpy(dout,loc,cnt);
|
|
|
|
|
// }
|
|
|
|
|
// return cnt;
|
2023-08-18 07:36:41 +00:00
|
|
|
|
|
2023-08-22 10:23:41 +00:00
|
|
|
|
// while (!cnt)
|
|
|
|
|
// {
|
|
|
|
|
// return 0;
|
|
|
|
|
// static uint8_t i=0;
|
|
|
|
|
// LOG_W("位置信息还未准备好。");
|
|
|
|
|
// rt_thread_mdelay(4000);//状态数据默认3s更新一次
|
|
|
|
|
// cnt = getLoc(loc,pairCnt);
|
|
|
|
|
// if (i++ > 20) {
|
|
|
|
|
// LOG_E("位置信息获取异常");
|
|
|
|
|
//// break;
|
|
|
|
|
// memset(dout,0x37,pairCnt*10);
|
|
|
|
|
// return pairCnt*10;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2023-07-19 10:57:54 +00:00
|
|
|
|
memcpy(dout,loc,cnt);
|
|
|
|
|
return cnt;
|
|
|
|
|
}
|
2023-08-19 06:41:13 +00:00
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* c回应深度数据,含位置信息
|
|
|
|
|
* @param din
|
|
|
|
|
* @param len
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
int reportDepth(uint8_t *din,size_t len)
|
|
|
|
|
{
|
|
|
|
|
// 000000000000000000005AA53E320608000A170407101B33FFFFFFFF24ED
|
2023-08-04 06:09:39 +00:00
|
|
|
|
LOG_I("FUNC = reportDepth");
|
2023-07-25 01:02:31 +00:00
|
|
|
|
//获取并更新位置信息
|
|
|
|
|
uint8_t dout[200];
|
2023-08-31 08:12:44 +00:00
|
|
|
|
rt_memset(dout,0,200);
|
2023-07-25 01:02:31 +00:00
|
|
|
|
size_t nlen = getAndCheckLoc(dout, 1);
|
|
|
|
|
|
|
|
|
|
rt_memcpy(dout+10, din, len);//5aa5从第11字节开始,共len个字节
|
|
|
|
|
LOG_HEX("depth",16,dout,len+10);
|
2023-09-05 10:53:01 +00:00
|
|
|
|
upSend(dout, len+10);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
|
|
|
|
//天通指令类型:0x70 0x01
|
2023-08-01 08:27:21 +00:00
|
|
|
|
/**
|
2023-08-08 06:31:16 +00:00
|
|
|
|
* 从数组更新系统各项工作参数
|
2023-08-01 08:27:21 +00:00
|
|
|
|
* @param cfg 参数数组
|
2023-08-30 02:45:32 +00:00
|
|
|
|
* @param len 数组长度,为零时使用当前系统的scfg
|
2023-08-01 08:27:21 +00:00
|
|
|
|
*/
|
2023-08-08 06:31:16 +00:00
|
|
|
|
void updateAllSysCfg(uint8_t *cfg, size_t len)
|
2023-08-01 08:27:21 +00:00
|
|
|
|
{
|
|
|
|
|
LOG_I("FUNC = update SYSCFG");
|
2023-08-30 02:45:32 +00:00
|
|
|
|
if (len) {
|
|
|
|
|
memcpy(&scfg,cfg,len);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-19 06:41:13 +00:00
|
|
|
|
//详见SYS_CFG的定义
|
2023-08-03 03:04:50 +00:00
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
uint8_t sendInterval;
|
|
|
|
|
uint8_t maxTTWaitTime;
|
|
|
|
|
uint8_t maxTTRetryCnt;
|
|
|
|
|
uint8_t minTTPeriCnt;
|
|
|
|
|
uint8_t minTTsinal;
|
|
|
|
|
uint8_t timeout;
|
|
|
|
|
uint8_t openWindowTime[8];
|
|
|
|
|
uint16_t maxSizePerFile;
|
|
|
|
|
uint8_t commMode;
|
|
|
|
|
uint8_t selfDesSW;
|
|
|
|
|
uint8_t locAlert;
|
2023-08-19 06:41:13 +00:00
|
|
|
|
uint8_t locRepInterval;
|
2023-08-03 03:04:50 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2023-08-30 02:45:32 +00:00
|
|
|
|
static int rst = 1;
|
2023-08-29 11:29:58 +00:00
|
|
|
|
rst &= set_cfg("sendInterval",scfg.sendInterval);
|
|
|
|
|
rst &= set_cfg("maxTTWaitTime",scfg.maxTTWaitTime);
|
|
|
|
|
rst &= set_cfg("maxTTRetryCnt",scfg.maxTTRetryCnt);
|
|
|
|
|
rst &= set_cfg("minTTPeriCnt",scfg.minTTPeriCnt);
|
|
|
|
|
rst &= set_cfg("minTTsinal",scfg.minTTsinal);
|
|
|
|
|
rst &= set_cfg("timeout",scfg.timeout);
|
2023-08-31 08:12:44 +00:00
|
|
|
|
|
2023-09-06 01:51:08 +00:00
|
|
|
|
char tmpstr[sizeof(scfg.openWindowTime)*4];
|
2023-08-03 03:04:50 +00:00
|
|
|
|
bytes2str(scfg.openWindowTime, 8, 10, ",", tmpstr);
|
2023-08-30 02:45:32 +00:00
|
|
|
|
rst &= set_cfgs("openWindowTime",tmpstr);
|
2023-08-31 08:12:44 +00:00
|
|
|
|
|
2023-08-03 03:04:50 +00:00
|
|
|
|
|
2023-08-29 11:29:58 +00:00
|
|
|
|
rst &= set_cfg("maxSizePerFile", scfg.maxSizePerFile);
|
|
|
|
|
rst &= set_cfg("commMode",scfg.commMode);
|
|
|
|
|
rst &= set_cfg("selfDesSW",scfg.selfDesSW);
|
|
|
|
|
rst &= set_cfg("locAlert",scfg.locAlert);
|
|
|
|
|
rst &= set_cfg("locRepInterval",scfg.locRepInterval);
|
2023-09-04 09:45:03 +00:00
|
|
|
|
rst &= set_cfg("isMaWin",scfg.isMaWin);
|
2023-08-30 02:45:32 +00:00
|
|
|
|
|
2023-08-29 11:29:58 +00:00
|
|
|
|
if (rst) {
|
|
|
|
|
LOG_I("set value success.");
|
|
|
|
|
}
|
2023-09-02 11:33:16 +00:00
|
|
|
|
// if ((uint16_t)getFileSize("cfg.ini") != (uint16_t)getFileSize("sd/cfg.ini")) {
|
|
|
|
|
// LOG_D("backup cfg file to sd");
|
|
|
|
|
// copy("cfg.ini", "sd/cfg.ini");
|
|
|
|
|
// }
|
2023-08-01 08:27:21 +00:00
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* 上报系统各项工作参数
|
|
|
|
|
*/
|
|
|
|
|
void reportSysCfg()
|
|
|
|
|
{
|
|
|
|
|
LOG_I("FUNC = report SYSCFG");
|
2023-08-03 03:04:50 +00:00
|
|
|
|
// scfg.commMode=1;
|
|
|
|
|
// scfg.timeout=5;
|
|
|
|
|
// scfg.maxSizePerFile=1024;
|
|
|
|
|
// uint8_t w[]={0x01,2,3,4,5,6,7,8};
|
|
|
|
|
// memcpy(scfg.openWindowTime,w,8);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
|
|
|
|
|
uint8_t size = sizeof(scfg);
|
|
|
|
|
uint8_t rst[200] = { 0x5A, 0xA5, ADDR_ANJI, ADDR_TT, _CFG_UPDATE_CFG >> 8, _CFG_UPDATE_CFG & 0xff };
|
|
|
|
|
int p = 6;
|
|
|
|
|
rst[p++] = 0;
|
|
|
|
|
rst[p++] = size;
|
|
|
|
|
|
|
|
|
|
memcpy(rst + p,&scfg, size);
|
|
|
|
|
p+=size;
|
|
|
|
|
rst[p] = bccCRC(rst + 2, p-2);
|
|
|
|
|
rst[++p] = 0xED;
|
|
|
|
|
|
|
|
|
|
size = p+1;
|
2023-08-17 01:22:02 +00:00
|
|
|
|
LOG_HEX("cfg",16,rst,size);
|
2023-08-04 06:09:39 +00:00
|
|
|
|
upSend(rst, size);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
|
|
|
|
|
// LOG_HEX("scfg",16,&scfg,size);
|
2023-08-04 06:09:39 +00:00
|
|
|
|
// LOG_HEX("cfg",16,rst,p+1);
|
2023-08-17 01:22:02 +00:00
|
|
|
|
// updateAllSysCfg(rst+8, rst[7]);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
}
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
//3.2.8定时自报位置信息
|
|
|
|
|
//每小时传数据时同步传位置信息
|
2023-07-19 10:57:54 +00:00
|
|
|
|
//与告警信息共用编码方式
|
2023-08-01 08:27:21 +00:00
|
|
|
|
//首字节为是否越界,默认为00,此处为预留。
|
2023-07-19 10:57:54 +00:00
|
|
|
|
//单次最多可上报15条位置信息
|
|
|
|
|
|
|
|
|
|
/**
|
2023-09-03 08:54:23 +00:00
|
|
|
|
* 按15条经纬度打包位置信息。由[是否越界]+[时戳]+[15组数据]组成。
|
2023-08-01 08:27:21 +00:00
|
|
|
|
* 是否越界为预留,不在此判断。
|
|
|
|
|
* 15组数据采集顺序为MOB(most oldest bit),越新的数据离时戳越近
|
2023-08-19 06:41:13 +00:00
|
|
|
|
* 由于加密后数据最大会增加16字节,后位置数据条数改为10
|
2023-07-19 10:57:54 +00:00
|
|
|
|
* @param dout 存储位置信息的数组
|
|
|
|
|
* @return 数组大小
|
|
|
|
|
*/
|
2023-08-22 10:23:41 +00:00
|
|
|
|
#define _LOC_CNT 10
|
2023-07-19 10:57:54 +00:00
|
|
|
|
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 };
|
|
|
|
|
|
|
|
|
|
|
2023-09-03 08:54:23 +00:00
|
|
|
|
alertMsg[8] = 0; //首字节00为定时发送,未检测围栏;其它为在围栏外
|
|
|
|
|
int len = time2Byte(alertMsg+9) + 1;//添加时间戳,len为数据长度
|
|
|
|
|
size_t rst = getAndCheckLoc(alertMsg + 8 + len, _LOC_CNT);
|
|
|
|
|
if (!rst) {//无有效位置数据
|
2023-08-22 10:23:41 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2023-09-03 08:54:23 +00:00
|
|
|
|
len += rst;
|
|
|
|
|
|
|
|
|
|
//add battery info
|
|
|
|
|
uint8_t temp[60];
|
|
|
|
|
rst = getBattInfo(temp);
|
|
|
|
|
memcpy(alertMsg+8+len,temp,rst);
|
|
|
|
|
len += rst;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2023-08-25 00:08:30 +00:00
|
|
|
|
// LOG_HEX("packLocMsg", 16, alertMsg, len); //bccCRC+Tail
|
2023-07-19 10:57:54 +00:00
|
|
|
|
memcpy(dout, alertMsg, len);
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
static void d_packLocMsg(void)
|
|
|
|
|
{
|
2023-09-05 10:53:01 +00:00
|
|
|
|
// uint8_t tmp[200];
|
|
|
|
|
// size_t len = packLocMsg(tmp);
|
2023-08-19 06:41:13 +00:00
|
|
|
|
// len = cryptSingleMsg(tmp, len, tmp);
|
2023-09-05 10:53:01 +00:00
|
|
|
|
uint8_t din[]={0x5A,0xA5,0x3E,0x32,0x06,0x08,0x00,0x0A,0x17,0x09,0x05,0x0F,0x24,0x0B,0xFF,0xFF,0xFF,0xFF,0x33,0xED};
|
|
|
|
|
reportDepth(din, sizeof(din));
|
|
|
|
|
// LOG_HEX("c",16,tmp,len);
|
2023-09-03 08:54:23 +00:00
|
|
|
|
// upSend(tmp, len);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
2023-08-31 08:12:44 +00:00
|
|
|
|
/**
|
|
|
|
|
* 获取信号信息,以时戳开头
|
|
|
|
|
* @param dout
|
|
|
|
|
*/
|
|
|
|
|
RT_WEAK getSignals(uint8_t *dout, uint8_t len);
|
|
|
|
|
static int packSignal(uint8_t *dout)
|
|
|
|
|
{
|
|
|
|
|
uint8_t signalMsg[200] =
|
|
|
|
|
{ 0x5A, 0xA5, ADDR_ANJI, ADDR_TT, _INFO_SIGNAL >> 8, _INFO_SIGNAL & 0xFF, 0, 0 };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int len = time2Byte(signalMsg+8);//添加时间戳
|
|
|
|
|
len += getSignals(signalMsg + 8 + len, _LOC_CNT) + 1; //p指向第2个数据
|
|
|
|
|
if (len == 0x08) {//无有效位置数据
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signalMsg[7] = len; //update len of raw data
|
|
|
|
|
signalMsg[8 + len] = bccCRC(signalMsg + 2, 8 + len - 2); //update CRC
|
|
|
|
|
signalMsg[8 + len + 1] = 0xED;
|
|
|
|
|
|
|
|
|
|
len = 8 + len + 2;
|
|
|
|
|
// LOG_HEX("packLocMsg", 16, alertMsg, len); //bccCRC+Tail
|
|
|
|
|
memcpy(dout, signalMsg, len);
|
|
|
|
|
return len;
|
|
|
|
|
}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-09-08 09:03:44 +00:00
|
|
|
|
/**
|
|
|
|
|
* 获取电池信息,带时戳
|
|
|
|
|
*/
|
|
|
|
|
void reportBattInfo(void)
|
|
|
|
|
{
|
|
|
|
|
uint8_t battInfo[200] = { 0x5A, 0xA5, ADDR_ANJI, ADDR_TT, _INFO_BATT >> 8, _INFO_BATT & 0xFF, 0, 0 };
|
|
|
|
|
|
|
|
|
|
uint8_t len = time2Byte(battInfo + 8); //添加时间戳
|
2023-09-10 01:39:21 +00:00
|
|
|
|
uint8_t blen = getBattRAWInfo(battInfo + 8 + len);
|
2023-09-08 09:03:44 +00:00
|
|
|
|
len += blen;
|
|
|
|
|
|
|
|
|
|
battInfo[7] = len; //update len of raw data
|
|
|
|
|
battInfo[8 + len] = bccCRC(battInfo + 2, 8 + len - 2); //update CRC
|
|
|
|
|
battInfo[8 + len + 1] = 0xED;
|
|
|
|
|
|
|
|
|
|
len = 8 + len + 2;
|
|
|
|
|
LOG_HEX("battInfo", 16, battInfo, len); //bccCRC+Tail
|
|
|
|
|
|
|
|
|
|
upSend(battInfo, len);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-19 06:41:13 +00:00
|
|
|
|
///**
|
|
|
|
|
// * 加密位置信息。对位置信息的更改需在加密前操作
|
|
|
|
|
// * @param din 存储待加密位置信息的数组
|
|
|
|
|
// * @param len 带加密长度
|
|
|
|
|
// * @param dout 存储加密结果的数组
|
|
|
|
|
// * @return 加密后的长度
|
|
|
|
|
// */
|
|
|
|
|
//int cryptSingleMsg(uint8_t *din, size_t len, uint8_t *dout)
|
|
|
|
|
//{ //加密。因加密后数据长度会变化,故不能只加密位置数据。
|
|
|
|
|
// uint8_t cd[200]={0xAB,0xBA};
|
|
|
|
|
// size_t nlen = 0;
|
|
|
|
|
//
|
|
|
|
|
////#define FULL_DATA_CRYPT
|
|
|
|
|
//#ifdef FULL_DATA_CRYPT
|
|
|
|
|
// nlen = cryp_data(din, len, cd);
|
|
|
|
|
//#else
|
|
|
|
|
// //单独加密时在加密后数据头部添加0xABBA便于识别、解析
|
|
|
|
|
// nlen = cryp_data(din, len, cd + 2) + 2;
|
|
|
|
|
//#endif
|
|
|
|
|
// memcpy(dout, cd, nlen);
|
|
|
|
|
// return nlen;
|
|
|
|
|
//}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-08-01 08:27:21 +00:00
|
|
|
|
//static uint8_t locMsg[200];
|
|
|
|
|
//static rt_timer_t repLoc;
|
|
|
|
|
//static rt_sem_t isReadyToSendLoc;
|
|
|
|
|
|
|
|
|
|
int isInFence(uint8_t *loc);
|
|
|
|
|
/**
|
|
|
|
|
* 打包并检测位置数据 默认10s更新一次
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2023-08-30 10:00:42 +00:00
|
|
|
|
static int itime=0;//间隔时间
|
2023-08-29 11:29:58 +00:00
|
|
|
|
void reportLoc_thread_entry(void *parameter)
|
2023-08-01 08:27:21 +00:00
|
|
|
|
{
|
2023-08-30 10:00:42 +00:00
|
|
|
|
#define CHECK_INTERVAL 6
|
|
|
|
|
//static int i=0;
|
2023-08-30 02:45:32 +00:00
|
|
|
|
while (isTCPok())
|
2023-08-01 08:27:21 +00:00
|
|
|
|
{
|
2023-08-31 08:12:44 +00:00
|
|
|
|
|
|
|
|
|
rt_thread_mdelay(CHECK_INTERVAL*1000); //默认6s刷新一次
|
2023-08-30 10:00:42 +00:00
|
|
|
|
// static uint32_t i = 0;
|
2023-08-01 08:27:21 +00:00
|
|
|
|
int isReadyToSendLoc=0;
|
|
|
|
|
uint8_t rst[200];
|
|
|
|
|
|
2023-08-31 08:12:44 +00:00
|
|
|
|
size_t len = packLocMsg(rst);
|
2023-08-22 10:23:41 +00:00
|
|
|
|
if (!len) {//无有效位置数据
|
2023-08-24 03:06:44 +00:00
|
|
|
|
// return;
|
2023-08-29 11:29:58 +00:00
|
|
|
|
rt_thread_mdelay(500);//添加延时避免死循环卡死线程
|
2023-08-22 10:23:41 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-08-01 08:27:21 +00:00
|
|
|
|
|
|
|
|
|
//检测是否在围栏内
|
2023-09-07 00:57:45 +00:00
|
|
|
|
uint8_t isLocOk[]={0x37,0x37,0x37};
|
|
|
|
|
if (memcmp(rst+len-11-4,isLocOk,sizeof(isLocOk)) != 0 && !isInFence(rst+len-11-4))//不在围栏内。定位正常才判断围栏
|
2023-08-01 08:27:21 +00:00
|
|
|
|
{
|
2023-09-02 07:23:32 +00:00
|
|
|
|
rst[8] |= 1<<7;
|
2023-08-01 08:27:21 +00:00
|
|
|
|
isReadyToSendLoc = 1;
|
|
|
|
|
}
|
2023-08-30 10:00:42 +00:00
|
|
|
|
itime += 1 ;
|
2023-08-31 08:12:44 +00:00
|
|
|
|
// LOG_D("%d/%d s",itime*CHECK_INTERVAL,scfg.locRepInterval * 60);
|
|
|
|
|
if (itime*CHECK_INTERVAL >= scfg.locRepInterval * 60)//定时发送,默认5分钟
|
2023-08-01 08:27:21 +00:00
|
|
|
|
{
|
2023-08-30 10:00:42 +00:00
|
|
|
|
itime = 0;
|
2023-08-01 08:27:21 +00:00
|
|
|
|
isReadyToSendLoc = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isReadyToSendLoc) {
|
2023-08-31 08:12:44 +00:00
|
|
|
|
// LOG_HEX("loc",16,rst,len);
|
|
|
|
|
if (isTTjh()) {//loc数据不用缓存,故需要激活才发送
|
|
|
|
|
upSend(rst, len);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
itime = 0;
|
|
|
|
|
}
|
2023-08-01 08:27:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-24 03:06:44 +00:00
|
|
|
|
void reportLoc()
|
2023-07-19 10:57:54 +00:00
|
|
|
|
{
|
2023-08-31 08:12:44 +00:00
|
|
|
|
rt_thread_t thread = rt_thread_create("RPLoc", reportLoc_thread_entry, RT_NULL, 1024 * 10, 27, 10);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
/* 创建成功则启动线程 */
|
|
|
|
|
if (thread != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_thread_startup(thread);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG_E("thread 'PCLoc' create failure.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
|
|
|
|
}
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
//3.2.9深度异常告警
|
2023-07-21 08:02:32 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将位置信息合入深度信息
|
|
|
|
|
* @param din
|
|
|
|
|
* @param len
|
|
|
|
|
* @param dout
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2023-07-25 01:02:31 +00:00
|
|
|
|
int depthAlert(uint8_t *din, int len)
|
2023-07-10 02:42:26 +00:00
|
|
|
|
{
|
|
|
|
|
//获取并更新位置信息
|
2023-07-25 01:02:31 +00:00
|
|
|
|
// uint8_t loc[10];
|
|
|
|
|
// uint8_t dout[200];
|
|
|
|
|
// size_t nlen = getAndCheckLoc(dout, 1);
|
|
|
|
|
//
|
|
|
|
|
// rt_memcpy(dout+10, din, len);//5aa5从第11字节开始,共len个字节
|
|
|
|
|
// LOG_HEX("depth",16,dout,len+10);
|
|
|
|
|
// //加密。因加密后数据长度会变化,故不能只加密位置数据。
|
|
|
|
|
// upSend(dout, nlen);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//3.2.10位置异常告警
|
|
|
|
|
//map.c中实现
|
|
|
|
|
/**
|
|
|
|
|
* 判断是否在电子围栏内部
|
|
|
|
|
* @param x 当前位置经度
|
|
|
|
|
* @param y 当前位置纬度
|
|
|
|
|
* @return 在内部则返回true,反之false
|
|
|
|
|
*/
|
2023-08-01 08:27:21 +00:00
|
|
|
|
static int isInPolgon(float x, float y)
|
2023-07-10 02:42:26 +00:00
|
|
|
|
{
|
2023-08-22 10:23:41 +00:00
|
|
|
|
float polyX[10]={},polyY[10]={};
|
|
|
|
|
|
2023-07-10 02:42:26 +00:00
|
|
|
|
int polyCorners = mapParse("/map.geojson",polyX,polyY);
|
2023-08-22 10:23:41 +00:00
|
|
|
|
|
2023-07-10 02:42:26 +00:00
|
|
|
|
return pointInPolygon(polyCorners,polyX,polyY,x,y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置位置告警功能开关
|
|
|
|
|
* @param setON 1-告警功能开启,0-关闭
|
|
|
|
|
*/
|
|
|
|
|
void setLocationAlertSWT(int setON)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-01 08:27:21 +00:00
|
|
|
|
* 判断给定位置是否在电子围栏内部
|
|
|
|
|
* @param loc 经纬度数组,8个字节
|
|
|
|
|
* @return 1-
|
2023-07-10 02:42:26 +00:00
|
|
|
|
*/
|
2023-08-01 08:27:21 +00:00
|
|
|
|
int isInFence(uint8_t *loc)
|
2023-07-10 02:42:26 +00:00
|
|
|
|
{
|
2023-08-22 10:23:41 +00:00
|
|
|
|
// LOG_HEX("x=",16,loc,8);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
float x,y =0;
|
2023-08-22 10:23:41 +00:00
|
|
|
|
x=*((float *)loc);
|
|
|
|
|
y=*((float *)(loc+4));
|
|
|
|
|
// LOG_D("%lf-%lf", x, y);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
int isIN = isInPolgon(x, y);
|
2023-07-10 02:42:26 +00:00
|
|
|
|
if (isIN) {
|
2023-08-30 02:45:32 +00:00
|
|
|
|
LOG_D("设备在围栏内,位置正常。");
|
2023-08-01 08:27:21 +00:00
|
|
|
|
return 1;
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2023-08-22 10:23:41 +00:00
|
|
|
|
LOG_W("警告!设备不在围栏内!");
|
2023-08-01 08:27:21 +00:00
|
|
|
|
return 0;
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-22 10:23:41 +00:00
|
|
|
|
void d_isInFence(void)
|
|
|
|
|
{
|
|
|
|
|
uint8_t rst[200];
|
|
|
|
|
|
|
|
|
|
int len = packLocMsg(rst);
|
|
|
|
|
if (!len)
|
|
|
|
|
{ //无有效位置数据
|
|
|
|
|
LOG_W("无有效位置数据");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//检测是否在围栏内
|
|
|
|
|
isInFence(rst + len - 11);
|
|
|
|
|
//不在围栏内,1+4+4+1+bcc+0x4D
|
|
|
|
|
}
|
2023-08-01 08:27:21 +00:00
|
|
|
|
//创建定时任务,默认时间10秒钟,异常则发送
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
|
|
|
|
//3.2.12数据存储区清空
|
|
|
|
|
//
|
|
|
|
|
/**
|
|
|
|
|
* 采用格式化命令对存储区进行清空,谨慎使用!
|
|
|
|
|
*/
|
|
|
|
|
void clearAllData()
|
|
|
|
|
{
|
|
|
|
|
mkfs("elm","sd0");//format SD
|
2023-07-19 10:57:54 +00:00
|
|
|
|
mkfs("elm", "W25Q128");//format flash
|
2023-07-10 02:42:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-24 03:06:44 +00:00
|
|
|
|
/**
|
|
|
|
|
* 重发数据
|
|
|
|
|
* @param din
|
|
|
|
|
* @param len
|
|
|
|
|
*/
|
|
|
|
|
void reSend(uint8_t *din, uint8_t len)
|
|
|
|
|
{
|
|
|
|
|
//file example: sd/rxdata/2023_08_23/23_08_23_08_31_44_14.bin
|
|
|
|
|
LOG_I("FUNC = resend file");
|
|
|
|
|
char f[60]="";
|
|
|
|
|
strcat(f,ROOT_PATH_DATA);
|
|
|
|
|
|
|
|
|
|
char str[30]="";
|
|
|
|
|
bytes2str(din, len-1, 10, "_", str);
|
2023-08-25 00:08:30 +00:00
|
|
|
|
strcat(f,"20");
|
2023-08-24 03:06:44 +00:00
|
|
|
|
strncat(f,str,8);
|
|
|
|
|
strcat(f,"/");
|
|
|
|
|
strcat(f,str);
|
|
|
|
|
strcat(f,".bin");
|
|
|
|
|
LOG_D("resend '%s'",f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t var = 7; var < len; var++) {
|
|
|
|
|
postFileInfo(f,din[var]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-10 02:42:26 +00:00
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief TT根据下发的指令执行对应的功能
|
|
|
|
|
*
|
|
|
|
|
* @param din 待执行的指令数据
|
|
|
|
|
* @param len 数据长度,单位字节
|
|
|
|
|
*/
|
2023-08-30 10:00:42 +00:00
|
|
|
|
void ttRunCMD_thread_entry(uint8_t *din, size_t len)
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
2023-08-30 10:00:42 +00:00
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
/**
|
|
|
|
|
+--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+
|
|
|
|
|
| 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[]
|
2023-07-18 11:23:48 +00:00
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
|
|
|
|
case _CFG_COMM_MODE:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
setCommMode(din[8]);
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CMD_SELF_TEST:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
selfTest();
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CFG_SELF_DESTRUCT:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
setSelfDestructSWT(din[8]);
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CMD_SELF_DESTRUCT:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
selfDestruct();
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CFG_COMM_WINDOW:
|
2023-08-01 08:27:21 +00:00
|
|
|
|
setCommWindow(din+8,din[7]);//只支持两组开窗,开窗时间为小时、分钟,UTC+0
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CMD_OPEN_WINDOW:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
openWindow(din[7]?((din[8] << 8) + din[9]):0);//两字节开窗时间
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CMD_CLOSE_WINDOW:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
closeWindow();
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CFG_LOCATION_ALERT:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
setLocationAlertSWT(din[8]);
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
case _CMD_CLEAR_DATA:
|
2023-07-19 10:57:54 +00:00
|
|
|
|
clearAllData();
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
2023-08-01 08:27:21 +00:00
|
|
|
|
case _CFG_UPDATE_CFG:
|
|
|
|
|
if (din[7]) {//数据长度不为0则为覆写参数
|
2023-08-08 06:31:16 +00:00
|
|
|
|
updateAllSysCfg(din+8, din[7]);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
}
|
|
|
|
|
else {//数据长度为0则为请求参数
|
|
|
|
|
reportSysCfg();
|
|
|
|
|
}
|
2023-08-22 10:23:41 +00:00
|
|
|
|
break;
|
2023-08-24 03:06:44 +00:00
|
|
|
|
case _CMD_RETRY_DATA:
|
|
|
|
|
reSend(din+8, din[7]);
|
|
|
|
|
break;
|
2023-08-31 08:12:44 +00:00
|
|
|
|
|
|
|
|
|
case _INFO_BATT:
|
2023-09-08 09:03:44 +00:00
|
|
|
|
reportBattInfo();
|
2023-08-31 08:12:44 +00:00
|
|
|
|
break;
|
2023-09-07 07:24:38 +00:00
|
|
|
|
case _CMD_SET_RTC:
|
|
|
|
|
updateSysRTC(din+8, din[7]);
|
|
|
|
|
break;
|
2023-07-18 11:23:48 +00:00
|
|
|
|
default:
|
2023-07-25 01:02:31 +00:00
|
|
|
|
LOG_W("0x%04X=未支持的指令。",cmd);
|
2023-07-18 11:23:48 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-30 10:00:42 +00:00
|
|
|
|
void ttRunCMD(uint8_t *din, size_t len)
|
|
|
|
|
{
|
|
|
|
|
static SMSG msg;
|
2023-08-31 08:12:44 +00:00
|
|
|
|
rt_memset(&msg, 0, sizeof(SMSG));
|
|
|
|
|
rt_memcpy(msg.data,din,len);
|
2023-08-30 10:00:42 +00:00
|
|
|
|
msg.len=len;
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-08-30 10:00:42 +00:00
|
|
|
|
ttRunCMD_thread_entry(din, len);
|
|
|
|
|
|
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-07-26 08:26:53 +00:00
|
|
|
|
RT_WEAK int formatAndSendTo3S(uint8_t * din, size_t len)
|
2023-07-19 10:57:54 +00:00
|
|
|
|
{
|
2023-07-21 08:02:32 +00:00
|
|
|
|
// LOG_D("直接调用小彭的函数进行处理。");
|
2023-08-04 06:09:39 +00:00
|
|
|
|
LOG_I("FUNC = forward to 3S");
|
2023-07-21 08:02:32 +00:00
|
|
|
|
char str[200] = "RCV:";
|
|
|
|
|
|
|
|
|
|
int cmd = (din[4] << 8) + din[5];
|
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
|
|
|
|
case _CMD_RTC_REQUEST: //不加前后缀的指令,仅有少数
|
|
|
|
|
bytes2str(din, len, 16, "", str);
|
|
|
|
|
break;
|
|
|
|
|
default: //默认加前后缀
|
|
|
|
|
bytes2str(din, len, 16, "", str + 4);
|
|
|
|
|
strcat(str, "\r\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
LOG_D("send '%s' to 3S.", str);
|
|
|
|
|
sendTo3S(str, strlen(str));//作为字符串发送
|
2023-07-20 07:29:37 +00:00
|
|
|
|
return 0;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
2023-09-05 10:53:01 +00:00
|
|
|
|
//#define CHECK_3S_DATA
|
2023-07-21 08:02:32 +00:00
|
|
|
|
int chk3SDataValid(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]
|
|
|
|
|
if (memcmp(din,head,sizeof(head)))
|
|
|
|
|
{
|
|
|
|
|
LOG_W("0x5AA5[√] != 0x%02X%02X[×],帧头不匹配",din[0],din[1]);
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (din[count-2] != bccCRC(din+2,count-2-2))//校验位为1个字节,采用异或运算,从指令的第3个字节开始,到奇偶校验位的前一个字节结束
|
|
|
|
|
{
|
|
|
|
|
LOG_W("0x%02X[√] != 0x%02X[×],校验值不匹配", bccCRC(din+2,count-2-2),din[count-2] );
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (din[count-1] != 0xED)
|
|
|
|
|
{
|
|
|
|
|
LOG_W("0xED[√] != 0x%02X[×],帧尾不匹配",din[count-1]);
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
// LOG_D("valid data.");
|
|
|
|
|
return RT_EOK;
|
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
//原计划将指令粗解析放在上位机,考虑到上位机到位时间晚,现放到MCU端
|
|
|
|
|
/**
|
|
|
|
|
* @brief 校验、解析3S数据,以0x5AA5开头0xED结尾。
|
|
|
|
|
*
|
|
|
|
|
* @param din 待解析数据
|
|
|
|
|
* @param count 待解析数据长度
|
|
|
|
|
*/
|
|
|
|
|
void parse3SData(uint8_t *din, size_t count)
|
|
|
|
|
{
|
2023-07-19 10:57:54 +00:00
|
|
|
|
/**
|
|
|
|
|
+--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+
|
|
|
|
|
| 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 |
|
|
|
|
|
+--------+--------+------------+------------+---------+--------+------+------+-------+-----+-------+---------+------+
|
|
|
|
|
*/
|
2023-07-21 08:02:32 +00:00
|
|
|
|
if (chk3SDataValid(din, count) != RT_EOK) {
|
2023-09-05 10:53:01 +00:00
|
|
|
|
#ifdef CHECK_3S_DATA
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
2023-08-24 03:06:44 +00:00
|
|
|
|
//有效的数据才能复位超时
|
2023-08-31 08:12:44 +00:00
|
|
|
|
|
|
|
|
|
if (isTTon()) {
|
|
|
|
|
resetTM();
|
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
// uint8_t dout[200];
|
2023-07-21 08:02:32 +00:00
|
|
|
|
// 未采用switch case
|
2023-07-20 07:29:37 +00:00
|
|
|
|
if (din[2] == ADDR_TT)//仅给TT的消息
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
2023-07-19 10:57:54 +00:00
|
|
|
|
ttRunCMD(din,count);
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
2023-08-08 06:31:16 +00:00
|
|
|
|
else if (din[2] == ADDR_3S)//给3S的指令,需要再加工,返回数据可能也需要再加工
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
|
|
|
|
//
|
2023-07-19 10:57:54 +00:00
|
|
|
|
|
2023-07-26 08:26:53 +00:00
|
|
|
|
formatAndSendTo3S(din,count);
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
2023-08-08 06:31:16 +00:00
|
|
|
|
else if (din[2] == ADDR_ANJI)
|
2023-07-21 08:02:32 +00:00
|
|
|
|
{
|
|
|
|
|
//可能需要对回传信息再加工,如查询深度需要加入位置坐标
|
|
|
|
|
//或是缓存任务数据
|
|
|
|
|
//故需要对数据进行简单判断
|
|
|
|
|
int cmd = (din[4] << 8) + din[5];
|
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
2023-09-05 10:53:01 +00:00
|
|
|
|
// case _CMD_DEPTH_REQUEST:
|
|
|
|
|
// reportDepth(din, count);
|
|
|
|
|
// break;
|
|
|
|
|
case _INFO_DEPTH_ANSWER:
|
2023-07-25 01:02:31 +00:00
|
|
|
|
reportDepth(din, count);
|
2023-07-21 08:02:32 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2023-08-08 06:31:16 +00:00
|
|
|
|
LOG_I("pass-through raw data.");
|
2023-08-25 00:08:30 +00:00
|
|
|
|
if (isTTjh())
|
2023-07-26 08:26:53 +00:00
|
|
|
|
{//如果TT为开机状态(具备通信状态?待商榷)则不缓存直接发
|
|
|
|
|
upSend(din, count);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cacheDataToFile(din, count);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-21 08:02:32 +00:00
|
|
|
|
}
|
|
|
|
|
// upSend(din, count);
|
2023-08-04 06:09:39 +00:00
|
|
|
|
}
|
2023-08-08 06:31:16 +00:00
|
|
|
|
else {
|
|
|
|
|
LOG_W("无法解析的目标地址");
|
2023-09-02 11:33:16 +00:00
|
|
|
|
// cacheDataToFile(din, count);
|
|
|
|
|
if (isTTjh())
|
|
|
|
|
{//如果TT为开机状态(具备通信状态?待商榷)则不缓存直接发
|
|
|
|
|
upSend(din, count);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cacheDataToFile(din, count);
|
|
|
|
|
}
|
2023-08-08 06:31:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 08:18:42 +00:00
|
|
|
|
void chkACK(uint8_t *msg, size_t size)
|
|
|
|
|
{
|
2023-08-15 09:10:18 +00:00
|
|
|
|
rt_uint8_t ackgood[] = { 0x88, 0xAA, 0xBB, 0x88, 0x41, 0x43, 0x4B }; //前四字节=帧头、后三字节=ACK
|
2023-07-20 08:18:42 +00:00
|
|
|
|
if (rt_memcmp(msg, ackgood, 4) == 0 && rt_memcmp(msg + size - 3, ackgood + 4, 3) == 0)
|
|
|
|
|
{
|
|
|
|
|
LOG_I("data is ACK.");
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-07-21 08:02:32 +00:00
|
|
|
|
LOG_W("INVALID DATA.");
|
2023-07-20 08:18:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-30 10:00:42 +00:00
|
|
|
|
//#define TR_USE_THREAD
|
2023-08-26 11:36:36 +00:00
|
|
|
|
#ifdef TR_USE_THREAD
|
2023-07-18 11:23:48 +00:00
|
|
|
|
/**
|
|
|
|
|
* @brief 解析TT数据,TT收到的指令必是单指令,解析容易。
|
|
|
|
|
*
|
|
|
|
|
* @param din
|
|
|
|
|
* @param len
|
|
|
|
|
*/
|
2023-08-26 08:49:19 +00:00
|
|
|
|
void parseTTData_thread_entry(void *parameter)
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
2023-08-26 08:49:19 +00:00
|
|
|
|
|
|
|
|
|
SMSG* msg = RT_NULL;
|
|
|
|
|
msg = (SMSG*) parameter;
|
|
|
|
|
uint8_t din[500];
|
|
|
|
|
memcpy(din,msg->data,msg->len);
|
|
|
|
|
size_t len = (size_t)msg->len;
|
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+
|
|
|
|
|
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|
|
|
|
|
* +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+
|
|
|
|
|
* | fstart[4] | fnum[2] | bak[2] | ftype[2] | fdlen[2] | fcrc[1] | ftccid[4] | rawData[N] |
|
2023-07-20 08:18:42 +00:00
|
|
|
|
* | 0x88,0xAA,0xBB,0x88 | 0x00,0x01 | 0x00,0x00 | 0x70,0x21 | 0x00,0xAA | 0x00 | 0x27,0x22,0x22,0x22 | |
|
2023-07-19 10:57:54 +00:00
|
|
|
|
* +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+
|
|
|
|
|
*/
|
2023-08-15 09:10:18 +00:00
|
|
|
|
uint8_t head[]={0x88,0xAA,0xBB,0x88, 0xFF,0xFF, 0x00,0x00, 0x70,0x21, 0x00,0xaa, 0x00, 0x27,0x22,0x22,0x22 };
|
2023-07-18 11:23:48 +00:00
|
|
|
|
//fstart[4] fnum[2] bak[2] ftype[2] fdlen[2] fcrc[1] ftccid[4]
|
2023-07-21 08:02:32 +00:00
|
|
|
|
uint8_t index[10];
|
2023-08-15 09:10:18 +00:00
|
|
|
|
size_t n=isInByte(din, len, head, 4, index);//仅判断帧头,因ACK不一样
|
2023-07-20 08:18:42 +00:00
|
|
|
|
uint8_t ndin[200];
|
|
|
|
|
if (!n) {
|
2023-07-29 08:08:15 +00:00
|
|
|
|
LOG_W("TTData中无匹配帧");
|
2023-07-18 11:23:48 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-07-20 08:18:42 +00:00
|
|
|
|
for (size_t i = 0; i < n; i++)
|
2023-07-18 11:23:48 +00:00
|
|
|
|
{
|
2023-07-20 08:18:42 +00:00
|
|
|
|
//按帧头分割
|
2023-07-21 08:02:32 +00:00
|
|
|
|
int cnt=(i+1<n)?index[i+1]-index[i]:len-index[i];
|
|
|
|
|
memcpy(ndin,din+index[i],cnt);
|
2023-07-27 07:46:45 +00:00
|
|
|
|
if (n>1) {
|
|
|
|
|
LOG_HEX("frame",16,ndin,cnt);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 08:18:42 +00:00
|
|
|
|
|
|
|
|
|
//判断是否为ACK
|
2023-07-21 08:02:32 +00:00
|
|
|
|
if ((ndin[10]<<8) | ndin[11] == 0x03) {//数据长度只有3
|
2023-07-20 08:18:42 +00:00
|
|
|
|
chkACK(ndin, cnt);
|
|
|
|
|
}
|
|
|
|
|
//数据
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-08-21 03:17:40 +00:00
|
|
|
|
resetTM();//非ACK信号才能触发延时逻辑
|
2023-07-20 08:18:42 +00:00
|
|
|
|
uint8_t rst = memcmp(ndin,head,10);//只比较到ftype
|
|
|
|
|
if (rst)
|
|
|
|
|
{
|
|
|
|
|
LOG_W("帧头不匹配");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t id[30]="";
|
|
|
|
|
LOG_I("data info: id=\"%s\", cur/all=[%d/%d]",bytes2str(ndin+17,7,10,"_",id),ndin[25],ndin[26]);
|
|
|
|
|
if (ndin[24] >> 7) // fcfg=数据类型。解析TT收到的数据时仅需解析“命令”,“数据”传输是单向的。
|
|
|
|
|
{
|
|
|
|
|
LOG_W("浮标端仅接受指令,暂不支持数据。");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint8_t rawData[200];
|
|
|
|
|
uint8_t rawDataLen=cnt-27;
|
|
|
|
|
memcpy(rawData, ndin + 27, rawDataLen);
|
|
|
|
|
parse3SData(rawData,rawDataLen);
|
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2023-08-26 08:49:19 +00:00
|
|
|
|
void parseTTData(uint8_t *din, size_t len)
|
|
|
|
|
{
|
|
|
|
|
static SMSG msg;
|
|
|
|
|
memset(&msg, 0, sizeof(SMSG));
|
|
|
|
|
memcpy(msg.data,din,len);
|
|
|
|
|
msg.len=len;
|
|
|
|
|
|
|
|
|
|
/* 创建 serial 线程 */
|
|
|
|
|
rt_thread_t thread = rt_thread_create("parseTT", parseTTData_thread_entry, (void *) &msg, 1024 * 5, 27-1, 10);
|
|
|
|
|
/* 创建成功则启动线程 */
|
|
|
|
|
if (thread != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_thread_startup(thread);
|
|
|
|
|
return RT_EOK;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG_E("thread 'parseTT' create failure.");
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-08-26 08:49:19 +00:00
|
|
|
|
}
|
2023-08-26 11:36:36 +00:00
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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,0x00 | 0x70,0x21 | 0x00,0xAA | 0x00 | 0x27,0x22,0x22,0x22 | |
|
|
|
|
|
* +---------------------+-----------+-----------+-----------+-----------+---------+---------------------+------------+
|
|
|
|
|
*/
|
|
|
|
|
uint8_t head[]={0x88,0xAA,0xBB,0x88, 0xFF,0xFF, 0x00,0x00, 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 index[10];
|
|
|
|
|
size_t n=isInByte(din, len, head, 4, index);//仅判断帧头,因ACK不一样
|
|
|
|
|
uint8_t ndin[200];
|
|
|
|
|
if (!n) {
|
|
|
|
|
LOG_W("TTData中无匹配帧");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
//按帧头分割
|
|
|
|
|
int cnt=(i+1<n)?index[i+1]-index[i]:len-index[i];
|
|
|
|
|
memcpy(ndin,din+index[i],cnt);
|
|
|
|
|
if (n>1) {
|
|
|
|
|
LOG_HEX("frame",16,ndin,cnt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//判断是否为ACK
|
|
|
|
|
if ((ndin[10]<<8) | ndin[11] == 0x03) {//数据长度只有3
|
|
|
|
|
chkACK(ndin, cnt);
|
|
|
|
|
}
|
|
|
|
|
//数据
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
resetTM();//非ACK信号才能触发延时逻辑
|
|
|
|
|
uint8_t rst = memcmp(ndin,head,10);//只比较到ftype
|
|
|
|
|
if (rst)
|
|
|
|
|
{
|
|
|
|
|
LOG_W("帧头不匹配");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t id[30]="";
|
|
|
|
|
LOG_I("data info: id=\"%s\", cur/all=[%d/%d]",bytes2str(ndin+17,7,10,"_",id),ndin[25],ndin[26]);
|
|
|
|
|
if (ndin[24] >> 7) // fcfg=数据类型。解析TT收到的数据时仅需解析“命令”,“数据”传输是单向的。
|
|
|
|
|
{
|
|
|
|
|
LOG_W("浮标端仅接受指令,暂不支持数据。");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint8_t rawData[200];
|
|
|
|
|
uint8_t rawDataLen=cnt-27;
|
|
|
|
|
memcpy(rawData, ndin + 27, rawDataLen);
|
|
|
|
|
parse3SData(rawData,rawDataLen);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2023-07-26 08:26:53 +00:00
|
|
|
|
/**
|
2023-07-29 08:08:15 +00:00
|
|
|
|
* get file size in byte
|
2023-07-26 08:26:53 +00:00
|
|
|
|
* @param file file name whith fullpath
|
|
|
|
|
*/
|
|
|
|
|
unsigned long getFileSize(char *file)
|
|
|
|
|
{
|
|
|
|
|
struct stat stat;
|
|
|
|
|
char *fullpath, *path;
|
2023-07-29 14:09:35 +00:00
|
|
|
|
unsigned long rst=0;
|
2023-07-26 08:26:53 +00:00
|
|
|
|
|
|
|
|
|
#ifdef DFS_USING_WORKDIR
|
|
|
|
|
/* open current working directory */
|
|
|
|
|
path = rt_strdup("/");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* build full path for each file */
|
|
|
|
|
fullpath = dfs_normalize_path(path, file);
|
|
|
|
|
|
|
|
|
|
rt_memset(&stat, 0, sizeof(struct stat));
|
|
|
|
|
if (dfs_file_stat(fullpath, &stat) == 0)
|
|
|
|
|
{
|
2023-07-29 14:09:35 +00:00
|
|
|
|
rst = stat.st_size;
|
2023-07-26 08:26:53 +00:00
|
|
|
|
}
|
2023-07-29 14:09:35 +00:00
|
|
|
|
rt_free(fullpath);
|
|
|
|
|
rt_free(path);
|
|
|
|
|
return rst;
|
2023-07-26 08:26:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void d_getFileSize(int argc, char ** argv)
|
|
|
|
|
{
|
|
|
|
|
// char *f;
|
|
|
|
|
uint16_t size = (uint16_t)getFileSize(argv[1]);
|
|
|
|
|
LOG_D("size = %d Byte.",size);
|
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-09-05 10:53:01 +00:00
|
|
|
|
/**
|
|
|
|
|
* 判断文件是否存在,参数为文件完整路径
|
|
|
|
|
* @param f
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
int isFileExit(char *f)
|
|
|
|
|
{
|
|
|
|
|
int rst = 0;
|
|
|
|
|
int fd = open(f, O_RDONLY);
|
|
|
|
|
if (fd > 0)
|
|
|
|
|
{
|
|
|
|
|
rst = 1;
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
return rst;
|
|
|
|
|
}
|
2023-07-29 08:08:15 +00:00
|
|
|
|
extern SYS_CFG scfg;
|
|
|
|
|
static uint8_t iscdlock=0;
|
2023-07-20 07:29:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* 缓存任务数据
|
|
|
|
|
* @param din 单次收到的任务数据
|
|
|
|
|
* @param len 任务数据长度
|
|
|
|
|
* @return 0--正常,-1--异常
|
|
|
|
|
*/
|
2023-07-26 08:26:53 +00:00
|
|
|
|
int cacheDataToFile(uint8_t *din, size_t len)
|
2023-07-19 10:57:54 +00:00
|
|
|
|
{
|
2023-07-29 08:08:15 +00:00
|
|
|
|
LOG_I("FUNC = cache to upsend");
|
2023-08-25 08:36:00 +00:00
|
|
|
|
int rst = -RT_ERROR;
|
2023-08-26 11:36:36 +00:00
|
|
|
|
//return RT_EOK;
|
2023-09-02 07:23:32 +00:00
|
|
|
|
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-07-29 08:08:15 +00:00
|
|
|
|
//lock file
|
2023-08-25 08:36:00 +00:00
|
|
|
|
while (iscdlock) {
|
2023-07-29 08:08:15 +00:00
|
|
|
|
rt_thread_mdelay(1000);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
2023-09-02 07:23:32 +00:00
|
|
|
|
static char f[60]="";//必须是static,否则log2file报错
|
|
|
|
|
getLstCacheFileName(f);
|
2023-07-29 08:08:15 +00:00
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
int fd = open(f, O_WRONLY | O_CREAT | O_APPEND);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
{
|
|
|
|
|
LOG_E("open file %s failed!", f);
|
2023-08-25 08:36:00 +00:00
|
|
|
|
goto _exit;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-08-25 08:36:00 +00:00
|
|
|
|
iscdlock=1;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
int rst = write(fd, din, len);
|
|
|
|
|
if (rst != len)
|
|
|
|
|
{
|
|
|
|
|
LOG_E("write to file %s failed!", f);
|
2023-08-25 08:36:00 +00:00
|
|
|
|
goto _exit;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
2023-07-29 08:08:15 +00:00
|
|
|
|
// cnt += len;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
close(fd);
|
2023-07-29 14:09:35 +00:00
|
|
|
|
unsigned long size = getFileSize(f);
|
|
|
|
|
LOG_I("cached %d bytes data to '%s', new size is %ld bytes.",len,f,size);
|
2023-07-29 08:08:15 +00:00
|
|
|
|
|
2023-07-29 09:39:28 +00:00
|
|
|
|
if (size > scfg.maxSizePerFile) {
|
2023-08-31 08:12:44 +00:00
|
|
|
|
updateCacheFileName();//更新文件名,置于线程前避免冲突
|
2023-07-29 08:08:15 +00:00
|
|
|
|
postFileInfo(f,0);//加入待发列表
|
|
|
|
|
}
|
2023-08-25 08:36:00 +00:00
|
|
|
|
rst=RT_EOK;
|
|
|
|
|
}
|
|
|
|
|
_exit:
|
|
|
|
|
if (fd) {
|
|
|
|
|
close(fd);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
2023-07-29 08:08:15 +00:00
|
|
|
|
iscdlock=0;
|
|
|
|
|
|
2023-08-25 08:36:00 +00:00
|
|
|
|
return rst;
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
|
|
|
|
void d_cacheData()
|
|
|
|
|
{
|
|
|
|
|
uint8_t demo[200];
|
|
|
|
|
size_t len = sizeof(demo);
|
|
|
|
|
memset(demo,0xAB,len);
|
2023-07-26 08:26:53 +00:00
|
|
|
|
cacheDataToFile(demo, len);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
}
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-08-26 11:36:36 +00:00
|
|
|
|
//#define TR_USE_THREAD
|
2023-08-26 08:49:19 +00:00
|
|
|
|
#ifdef TR_USE_THREAD
|
|
|
|
|
void parseRS232_thread_entry(void *parameter)
|
2023-07-21 08:02:32 +00:00
|
|
|
|
{
|
2023-08-26 08:49:19 +00:00
|
|
|
|
|
|
|
|
|
SMSG* msg = RT_NULL;
|
|
|
|
|
msg = (SMSG*) parameter;
|
|
|
|
|
uint8_t din[500];
|
|
|
|
|
memcpy(din,msg->data,msg->len);
|
|
|
|
|
size_t len = (size_t)msg->len;
|
|
|
|
|
|
2023-07-21 08:02:32 +00:00
|
|
|
|
//有HEX有ASCII,统一按HEX解析
|
2023-07-25 01:02:31 +00:00
|
|
|
|
//部分数据以10字节(20字符)的0数据开始,如深度查询,心跳包等
|
|
|
|
|
//处理思路是先不管前导0,对应指令手动加前导数据
|
2023-07-29 14:09:35 +00:00
|
|
|
|
// uint8_t asciiHead[]={0x41, 0x54, 0x2B, 0x53, 0x4E, 0x44,};//"AT+SND"
|
2023-07-25 01:02:31 +00:00
|
|
|
|
uint8_t hexHead[]={0x5a, 0xa5};//"5AA5"
|
|
|
|
|
//由于帧头有多种,且ascii和hex混发,无法处理粘包
|
|
|
|
|
|
2023-07-29 08:08:15 +00:00
|
|
|
|
// LOG_D("data is %s",isDataASCII(din, len)?"ASCII":"BIN");
|
2023-07-25 01:02:31 +00:00
|
|
|
|
//check
|
2023-07-29 08:08:15 +00:00
|
|
|
|
// if (memcmp(din,asciiHead,sizeof(asciiHead)) == 0) {
|
|
|
|
|
if (isDataASCII(din, len)) {
|
2023-07-25 01:02:31 +00:00
|
|
|
|
//ascii
|
|
|
|
|
LOG_I("type = ASCII");
|
2023-07-29 08:08:15 +00:00
|
|
|
|
trDataTolog(din, len, 0);
|
|
|
|
|
|
2023-07-26 08:26:53 +00:00
|
|
|
|
|
2023-07-25 01:02:31 +00:00
|
|
|
|
uint8_t index[10];
|
|
|
|
|
uint8_t tmpHead[]={0x35, 0x41, 0x41, 0x35};
|
|
|
|
|
size_t n=isInByte(din, len, tmpHead, sizeof(tmpHead), index);
|
|
|
|
|
// LOG_D("n=%d",n);
|
|
|
|
|
if (!n) {
|
2023-07-29 08:08:15 +00:00
|
|
|
|
LOG_W("RS232中无匹配帧");
|
2023-07-25 01:02:31 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
//按帧头分割
|
|
|
|
|
uint8_t ndin[400];
|
|
|
|
|
int cnt=(i+1<n)?index[i+1]-index[i]:len-index[i];
|
|
|
|
|
memcpy(ndin,din+index[i],cnt);
|
|
|
|
|
ndin[cnt]='\0';
|
|
|
|
|
uint8_t tmp[200];
|
|
|
|
|
size_t ncnt = str2Byte(ndin, 2, 16, tmp);
|
|
|
|
|
LOG_HEX("frame",16,tmp,ncnt);
|
|
|
|
|
if (chk3SDataValid(tmp, ncnt) != RT_EOK) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
parse3SData(tmp,ncnt);
|
|
|
|
|
}
|
2023-07-21 08:02:32 +00:00
|
|
|
|
}
|
2023-07-25 01:02:31 +00:00
|
|
|
|
else//如果不是ASCII则统一按HEX计// if (memcmp(din,hexHead,sizeof(hexHead)) == 0)
|
|
|
|
|
{
|
2023-07-29 14:09:35 +00:00
|
|
|
|
//bin
|
2023-07-29 08:08:15 +00:00
|
|
|
|
LOG_I("type = BIN");
|
2023-07-26 08:26:53 +00:00
|
|
|
|
char tmp[200]="";
|
|
|
|
|
trDataTolog(bytes2str(din, len, 16, " ", tmp), strlen(tmp), 0);
|
2023-07-29 08:08:15 +00:00
|
|
|
|
|
2023-07-25 01:02:31 +00:00
|
|
|
|
|
|
|
|
|
uint8_t index[10];
|
|
|
|
|
size_t n=isInByte(din, len, hexHead, sizeof(hexHead), index);
|
|
|
|
|
if (!n) {
|
2023-07-29 08:08:15 +00:00
|
|
|
|
LOG_W("RS232中无匹配帧");
|
2023-07-25 01:02:31 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
//按帧头分割
|
|
|
|
|
uint8_t ndin[200];
|
|
|
|
|
int cnt=(i+1<n)?index[i+1]-index[i]:len-index[i];
|
|
|
|
|
memcpy(ndin,din+index[i],cnt);
|
|
|
|
|
LOG_HEX("frame",16,ndin,cnt);
|
|
|
|
|
if (chk3SDataValid(ndin, cnt) != RT_EOK) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
parse3SData(ndin,cnt);
|
|
|
|
|
}
|
2023-07-21 08:02:32 +00:00
|
|
|
|
}
|
2023-07-25 01:02:31 +00:00
|
|
|
|
|
|
|
|
|
|
2023-07-21 08:02:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-26 08:49:19 +00:00
|
|
|
|
void parseRS232(uint8_t *din, size_t len)
|
|
|
|
|
{
|
|
|
|
|
static SMSG msg;
|
|
|
|
|
memset(&msg, 0, sizeof(SMSG));
|
|
|
|
|
memcpy(msg.data, din, len);
|
|
|
|
|
msg.len = len;
|
2023-07-21 08:02:32 +00:00
|
|
|
|
|
2023-08-26 08:49:19 +00:00
|
|
|
|
/* 创建 serial 线程 */
|
|
|
|
|
rt_thread_t thread = rt_thread_create("parse232", parseRS232_thread_entry, (void *) &msg, 1024 * 5, 27 - 1, 10);
|
|
|
|
|
/* 创建成功则启动线程 */
|
|
|
|
|
if (thread != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_thread_startup(thread);
|
|
|
|
|
return RT_EOK;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG_E("thread 'parse232' create failure.");
|
|
|
|
|
return -RT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
2023-07-21 08:02:32 +00:00
|
|
|
|
|
2023-08-26 08:49:19 +00:00
|
|
|
|
void parseRS232(uint8_t *din, size_t len)
|
|
|
|
|
{
|
|
|
|
|
//有HEX有ASCII,统一按HEX解析
|
|
|
|
|
//部分数据以10字节(20字符)的0数据开始,如深度查询,心跳包等
|
|
|
|
|
//处理思路是先不管前导0,对应指令手动加前导数据
|
2023-09-05 10:53:01 +00:00
|
|
|
|
uint8_t asciiHead[]={0x41, 0x54, 0x2B, 0x53, 0x4E, 0x44,};//"AT+SND"
|
2023-08-26 08:49:19 +00:00
|
|
|
|
uint8_t hexHead[]={0x5a, 0xa5};//"5AA5"
|
|
|
|
|
//由于帧头有多种,且ascii和hex混发,无法处理粘包
|
|
|
|
|
|
|
|
|
|
// LOG_D("data is %s",isDataASCII(din, len)?"ASCII":"BIN");
|
|
|
|
|
//check
|
2023-09-05 10:53:01 +00:00
|
|
|
|
if (memcmp(din,asciiHead,sizeof(asciiHead)) == 0) {
|
|
|
|
|
// if (isDataASCII(din, len) || memcmp(din,asciiHead,sizeof(asciiHead)) == 0) {
|
2023-08-26 08:49:19 +00:00
|
|
|
|
//ascii
|
|
|
|
|
LOG_I("type = ASCII");
|
|
|
|
|
trDataTolog(din, len, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t index[10];
|
|
|
|
|
uint8_t tmpHead[]={0x35, 0x41, 0x41, 0x35};
|
|
|
|
|
size_t n=isInByte(din, len, tmpHead, sizeof(tmpHead), index);
|
|
|
|
|
// LOG_D("n=%d",n);
|
|
|
|
|
if (!n) {
|
2023-09-05 10:53:01 +00:00
|
|
|
|
LOG_W("RS232中无匹配帧[ASCII]");
|
2023-08-26 08:49:19 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
//按帧头分割
|
|
|
|
|
uint8_t ndin[400];
|
|
|
|
|
int cnt=(i+1<n)?index[i+1]-index[i]:len-index[i];
|
|
|
|
|
memcpy(ndin,din+index[i],cnt);
|
|
|
|
|
ndin[cnt]='\0';
|
|
|
|
|
uint8_t tmp[200];
|
2023-09-05 10:53:01 +00:00
|
|
|
|
size_t ncnt = str2Byte(ndin, 2, 16, tmp)-1;//结尾有\r\n
|
2023-09-08 08:38:30 +00:00
|
|
|
|
if (n>1) LOG_HEX("frame",16,tmp,ncnt);
|
|
|
|
|
// if (chk3SDataValid(tmp, ncnt) != RT_EOK) {
|
|
|
|
|
//#ifdef CHECK_3S_DATA
|
|
|
|
|
// return;
|
|
|
|
|
//#endif
|
|
|
|
|
// }
|
2023-08-26 08:49:19 +00:00
|
|
|
|
parse3SData(tmp,ncnt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else//如果不是ASCII则统一按HEX计// if (memcmp(din,hexHead,sizeof(hexHead)) == 0)
|
|
|
|
|
{
|
|
|
|
|
//bin
|
|
|
|
|
LOG_I("type = BIN");
|
|
|
|
|
char tmp[200]="";
|
|
|
|
|
trDataTolog(bytes2str(din, len, 16, " ", tmp), strlen(tmp), 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t index[10];
|
|
|
|
|
size_t n=isInByte(din, len, hexHead, sizeof(hexHead), index);
|
|
|
|
|
if (!n) {
|
2023-09-05 10:53:01 +00:00
|
|
|
|
LOG_W("RS232中无匹配帧[BIN]");
|
2023-08-26 08:49:19 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
|
{
|
|
|
|
|
//按帧头分割
|
|
|
|
|
uint8_t ndin[200];
|
|
|
|
|
int cnt=(i+1<n)?index[i+1]-index[i]:len-index[i];
|
|
|
|
|
memcpy(ndin,din+index[i],cnt);
|
2023-09-08 08:38:30 +00:00
|
|
|
|
if (n>1) LOG_HEX("frame",16,ndin,cnt);
|
|
|
|
|
// if (chk3SDataValid(ndin, cnt) != RT_EOK) {
|
|
|
|
|
//#ifdef CHECK_3S_DATA
|
|
|
|
|
// return;
|
|
|
|
|
//#endif
|
|
|
|
|
// }
|
2023-08-26 08:49:19 +00:00
|
|
|
|
parse3SData(ndin,cnt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
2023-07-27 07:46:45 +00:00
|
|
|
|
#include <netdev_ipaddr.h>
|
|
|
|
|
#include <netdev.h>
|
|
|
|
|
/**
|
|
|
|
|
* check eth
|
|
|
|
|
* @return 1-UP,0-DOWN
|
|
|
|
|
*/
|
|
|
|
|
int isEthUP()
|
|
|
|
|
{
|
2023-08-21 09:18:07 +00:00
|
|
|
|
//netdev.c
|
2023-07-27 07:46:45 +00:00
|
|
|
|
extern struct netdev *netdev_default;
|
2023-08-21 09:18:07 +00:00
|
|
|
|
int rst = (netdev_default->flags & 0x01U) ? 1:0; //0x01 = NETDEV_FLAG_UP
|
2023-07-27 07:46:45 +00:00
|
|
|
|
// LOG_D("eth is %s.",rst?"up":"down");
|
2023-08-21 09:18:07 +00:00
|
|
|
|
// rst &= (netdev_default->flags & 0x04U) ? 1:0;//0x04 NETDEV_FLAG_LINK_UP
|
2023-07-27 07:46:45 +00:00
|
|
|
|
return rst;
|
|
|
|
|
}
|
2023-07-21 08:02:32 +00:00
|
|
|
|
|
|
|
|
|
|
2023-08-29 11:29:58 +00:00
|
|
|
|
void reportInfo_thread(void)
|
|
|
|
|
{
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
if (rt_sem_take(okToreport, RT_WAITING_FOREVER) == RT_EOK)
|
|
|
|
|
{
|
2023-09-07 01:14:27 +00:00
|
|
|
|
if (isTTjh()) {
|
|
|
|
|
LOG_D("send cfg ACK");
|
|
|
|
|
selfTest();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LOG_W("TT is not ready, ACK is invalid.");
|
|
|
|
|
}
|
2023-08-29 11:29:58 +00:00
|
|
|
|
}
|
2023-07-21 08:02:32 +00:00
|
|
|
|
|
2023-08-29 11:29:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void reportINFO(void)
|
|
|
|
|
{
|
|
|
|
|
/* 创建 serial 线程 */
|
2023-08-31 08:12:44 +00:00
|
|
|
|
rt_thread_t thread = rt_thread_create("reportINFO", reportInfo_thread, RT_NULL, 1024 * 5, 27, 10);
|
2023-08-29 11:29:58 +00:00
|
|
|
|
/* 创建成功则启动线程 */
|
|
|
|
|
if (thread != RT_NULL)
|
|
|
|
|
{
|
|
|
|
|
rt_thread_startup(thread);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG_E("thread 'updatecfg' create failure.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//INIT_APP_EXPORT(reportINFO);
|
2023-07-21 08:02:32 +00:00
|
|
|
|
|
2023-09-07 07:24:38 +00:00
|
|
|
|
/**
|
|
|
|
|
* 设置3S的RTC时间
|
|
|
|
|
*/
|
|
|
|
|
void set3SRTC(void)
|
|
|
|
|
{
|
|
|
|
|
uint8_t cmd[16]={0x5A, 0xA5, ADDR_3S, ADDR_ANJI, _CMD_SET_RTC >> 8, _CMD_SET_RTC & 0xFF, 0x00, 0x06 };
|
2023-09-08 09:26:13 +00:00
|
|
|
|
|
|
|
|
|
time2Byte(cmd+8);
|
|
|
|
|
cmd[14]=bccCRC(cmd+2, 16-4);
|
2023-09-07 07:24:38 +00:00
|
|
|
|
cmd[15]=0xED;
|
2023-09-08 09:26:13 +00:00
|
|
|
|
LOG_HEX("cmdRTC",16,cmd,sizeof(cmd));
|
2023-09-07 07:24:38 +00:00
|
|
|
|
formatAndSendTo3S(cmd, sizeof(cmd));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 更新系统RTC时间
|
|
|
|
|
* @param din
|
|
|
|
|
* @param len
|
|
|
|
|
*/
|
|
|
|
|
void updateSysRTC(uint8_t *din, size_t len)
|
|
|
|
|
{
|
|
|
|
|
LOG_I("FUNC = sest RTC");
|
|
|
|
|
|
|
|
|
|
struct tm tm_new = { 0 };
|
|
|
|
|
time_t old = (time_t)0;
|
|
|
|
|
time_t now = (time_t)0;
|
|
|
|
|
|
|
|
|
|
int err = get_timestamp(&old);
|
|
|
|
|
if (err != RT_EOK)
|
|
|
|
|
{
|
|
|
|
|
LOG_E("Get current timestamp failed. %d", err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
din[0] += 2000 - 1900;
|
|
|
|
|
din[1] -= 1;
|
|
|
|
|
// memcpy(tm_new, din, sizeof(tm_new));
|
|
|
|
|
tm_new.tm_year = din[0];
|
|
|
|
|
tm_new.tm_mon = din[1];
|
|
|
|
|
tm_new.tm_mday = din[2];
|
|
|
|
|
tm_new.tm_hour = din[3];
|
|
|
|
|
tm_new.tm_min = din[4];
|
|
|
|
|
tm_new.tm_sec = din[5];
|
|
|
|
|
|
|
|
|
|
/* converts the local time into the calendar time. */
|
|
|
|
|
now = mktime(&tm_new);
|
|
|
|
|
err = set_timestamp(now);
|
|
|
|
|
if (err != RT_EOK)
|
|
|
|
|
{
|
|
|
|
|
LOG_E("set date failed. %d", err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_timestamp(&now); /* get new timestamp */
|
|
|
|
|
rt_kprintf("old: %.*s", 25, ctime(&old));
|
|
|
|
|
rt_kprintf("now: %.*s", 25, ctime(&now));
|
|
|
|
|
|
|
|
|
|
//ACK
|
|
|
|
|
uint8_t rst[16]={0x5A, 0xA5, ADDR_ANJI, ADDR_TT, _INFO_RTC_ANSWER >> 8, _INFO_RTC_ANSWER & 0xFF, 0x00, 0x06 };
|
|
|
|
|
uint8_t tm[10];
|
|
|
|
|
time2Byte(tm);
|
|
|
|
|
memcpy(rst+8,tm,6);
|
|
|
|
|
rst[14]=bccCRC(rst+2, 14);
|
|
|
|
|
rst[15]=0xED;
|
|
|
|
|
LOG_HEX("sRTC",16,rst,sizeof(rst));
|
|
|
|
|
if (isTTjh()) {
|
|
|
|
|
upSend(rst, sizeof(rst));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2023-07-19 10:57:54 +00:00
|
|
|
|
#define FUNC_DEMO
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
2023-07-19 10:57:54 +00:00
|
|
|
|
#ifdef FUNC_DEMO //测试时导出命令到控制台
|
|
|
|
|
MSH_CMD_EXPORT(d_getFreeSpace,getFreeSpace);
|
|
|
|
|
MSH_CMD_EXPORT(selfTest,sysSelfTest);
|
|
|
|
|
MSH_CMD_EXPORT(d_packLocMsg,dpackLocMsg);
|
2023-08-24 03:06:44 +00:00
|
|
|
|
MSH_CMD_EXPORT(reportLoc,采集并发送位置信息);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
MSH_CMD_EXPORT(d_cacheData,d_cacheData);
|
2023-07-26 08:26:53 +00:00
|
|
|
|
MSH_CMD_EXPORT(d_getFileSize,d_getFileSize);
|
2023-07-27 07:46:45 +00:00
|
|
|
|
MSH_CMD_EXPORT(isEthUP,isEthUP);
|
2023-08-01 08:27:21 +00:00
|
|
|
|
MSH_CMD_EXPORT(reportSysCfg,reportSysCfg);
|
2023-08-22 10:23:41 +00:00
|
|
|
|
MSH_CMD_EXPORT(d_isInFence,d_isInFence);
|
2023-08-23 03:02:26 +00:00
|
|
|
|
MSH_CMD_EXPORT(d_sw,msw);
|
2023-09-08 09:26:13 +00:00
|
|
|
|
MSH_CMD_EXPORT(set3SRTC,发送指令设置RTC);
|
2023-07-19 10:57:54 +00:00
|
|
|
|
#endif
|
2023-07-18 11:23:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|