Files
BLE-USB_Dongle/apps/usr_le_code/usr_dengle_sched.c
T

663 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file usr_dengle_sched.c
* @brief Dengle 参数定时下发实现(含 Flash 断电保存)
* @author cyWu <1917507415@qq.com>
* @date 2026.07.30
* @version V1.2.0
* @history
* - V1.0.0, 2026.07.30, cyWu, 首次发布
* - V1.1.0, 2026.07.30, cyWu, 增加 Flash 持久化
* - V1.1.1, 2026.07.30, cyWu, 修复设置后立即发送导致栈溢出复位
* - V1.2.0, 2026.07.30, cyWu, USB 连接时暂停定时发送
* - V1.2.1, 2026.07.30, cyWu, 改为 COM(DTR) 开闭控制暂停/恢复
*
* USB 设置帧 payloadCMD=0x70)格式:
* [0] slot 槽位 0~9
* [1] enable 0=关闭该槽 1=使能
* [2..5] period_ms 发送周期(大端 ms,建议 >=100
* [6..7] cmd_id CBCT/CBFD→ycmd.cmd_id;其它→ble_proto.cmd(大端)
* [8] data_n int 模式= int32 个数;bytes 模式= 字节数
* [9..] data int 模式= data_n 个大端 int32
* bytes 模式= data_n 字节原始数据
*
* 发送策略(按设备当前 prodcode):
* - CBCT / CBFD → REPORTCMD + ycmd.arg_int32[](参考 usr_test_bleproto_runcmd
* - 其它 → REPORTCMD_V2 + bytes_data(参考 usr_test_protocol
******************************************************************************/
#include "system/includes.h"
#include "app_config.h"
#include "usr_dengle_sched.h"
#include "usr_usb_jb.h"
#include "bleproto_api.h"
#include "syscfg_id.h"
#if TCFG_USB_SLAVE_CDC_ENABLE
#include "usb/device/cdc.h"
#endif
#define LOG_TAG_CONST APP
#define LOG_TAG "[DENGLE]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
/*============================================================================*/
/* 私有宏 */
/*============================================================================*/
#define DENGLE_TX_BUF_SZ (384)
#define DENGLE_PROD_CBCT "CBCT"
#define DENGLE_PROD_CBFD "CBFD"
#define DENGLE_FLASH_MAGIC (0xD6)
#define DENGLE_FLASH_VER (0x01)
#define DENGLE_FLASH_HALF (5) /* 前半/后半各 5 个槽 */
extern void usr_ble_send_data(u8 *data, u16 len);
extern u16 usr_get_conn_service(void);
/*============================================================================*/
/* 私有类型 */
/*============================================================================*/
/** 发送模式:由设备 prodcode 决定 */
typedef enum {
DENGLE_MODE_INT32 = 0, /**< CBCT/CBFDint32 数组 → REPORTCMD */
DENGLE_MODE_BYTES /**< 其它:bytes → REPORTCMD_V2 */
} DengleMode_t;
/** 运行时单条定时配置(含 elapsed,不落盘) */
typedef struct {
uint8_t enable;
uint8_t mode;
uint16_t cmd_id;
uint32_t period_ms;
uint32_t elapsed_ms;
uint8_t data_n;
union {
int32_t i32[DENGLE_INT_MAX];
uint8_t bytes[DENGLE_BYTES_MAX];
} data;
} DengleSlot_t;
/** Flash 单槽结构(不含 elapsed */
typedef struct {
uint8_t enable;
uint8_t mode;
uint16_t cmd_id;
uint32_t period_ms;
uint8_t data_n;
uint8_t data[DENGLE_BYTES_MAX];
} DengleSlotFlash_t;
/** 半区 Flash 块:5 个槽 */
typedef struct {
uint8_t magic;
uint8_t ver;
uint8_t reserved[2];
DengleSlotFlash_t slots[DENGLE_FLASH_HALF];
} DengleFlashHalf_t;
/*============================================================================*/
/* 静态变量 */
/*============================================================================*/
static DengleSlot_t s_slots[DENGLE_SLOT_MAX];
static uint8_t s_inited;
static int s_tick_timer;
static int s_save_timer;
static int s_imm_send_timer;
static uint8_t s_imm_send_slot;
static uint8_t s_txbuf[DENGLE_TX_BUF_SZ];
/* 大结构体放静态区,避免 USB/定时器回调栈溢出 */
static DengleFlashHalf_t s_flash_half;
static bleproto_appdata_desc_t s_encode_desc;
static uint8_t s_usb_pause_logged;
/*============================================================================*/
/* 工具函数 */
/*============================================================================*/
static uint16_t dengle_read_u16_be(const uint8_t *p)
{
return ((uint16_t)p[0] << 8) | p[1];
}
static uint32_t dengle_read_u32_be(const uint8_t *p)
{
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
((uint32_t)p[2] << 8) | p[3];
}
/**
* @brief 根据当前设备 prodcode 选择发送模式
*/
static DengleMode_t dengle_get_mode_by_prodcode(void)
{
uint8_t prod[USR_PRODCODE_LEN];
usr_prodcode_get(prod, NULL);
if (memcmp(prod, DENGLE_PROD_CBCT, USR_PRODCODE_LEN) == 0 ||
memcmp(prod, DENGLE_PROD_CBFD, USR_PRODCODE_LEN) == 0) {
return DENGLE_MODE_INT32;
}
return DENGLE_MODE_BYTES;
}
/**
* @brief 上位机已打开 COM 时暂停 Dengle 定时发送
* @note 设备可一直插着 USB;仅 COM 打开=配置态,COM 关闭后恢复自动发送
* @return 1=应暂停发送 0=允许发送
*/
static uint8_t dengle_is_usb_pause(void)
{
#if TCFG_USB_SLAVE_CDC_ENABLE
return cdc_is_com_open() ? 1 : 0;
#else
return 0;
#endif
}
/*============================================================================*/
/* Flash 读写 */
/*============================================================================*/
/**
* @brief 运行时槽 → Flash 槽
*/
static void dengle_slot_to_flash(const DengleSlot_t *src, DengleSlotFlash_t *dst)
{
memset(dst, 0, sizeof(*dst));
dst->enable = src->enable;
dst->mode = src->mode;
dst->cmd_id = src->cmd_id;
dst->period_ms = src->period_ms;
dst->data_n = src->data_n;
memcpy(dst->data, src->data.bytes, DENGLE_BYTES_MAX);
}
/**
* @brief Flash 槽 → 运行时槽(elapsed 清零)
*/
static void dengle_slot_from_flash(const DengleSlotFlash_t *src, DengleSlot_t *dst)
{
memset(dst, 0, sizeof(*dst));
dst->enable = src->enable;
dst->mode = src->mode;
dst->cmd_id = src->cmd_id;
dst->period_ms = src->period_ms;
dst->data_n = src->data_n;
dst->elapsed_ms = 0;
memcpy(dst->data.bytes, src->data, DENGLE_BYTES_MAX);
}
/**
* @brief 保存全部 Dengle 配置到 Flash(分两半写入,控制单条 VM 长度)
* @note 须在任务/定时器上下文调用,禁止在中断里调用
*/
static void dengle_flash_save(void)
{
int ret;
uint8_t i;
/* 前半:slot 0~4 */
memset(&s_flash_half, 0, sizeof(s_flash_half));
s_flash_half.magic = DENGLE_FLASH_MAGIC;
s_flash_half.ver = DENGLE_FLASH_VER;
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
dengle_slot_to_flash(&s_slots[i], &s_flash_half.slots[i]);
}
ret = syscfg_write(CFG_USER_DENGLE_L, (u8 *)&s_flash_half, sizeof(s_flash_half));
if (ret <= 0) {
printf("dengle flash L write fail ret=%d\n", ret);
}
/* 后半:slot 5~9 */
memset(&s_flash_half, 0, sizeof(s_flash_half));
s_flash_half.magic = DENGLE_FLASH_MAGIC;
s_flash_half.ver = DENGLE_FLASH_VER;
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
dengle_slot_to_flash(&s_slots[DENGLE_FLASH_HALF + i], &s_flash_half.slots[i]);
}
ret = syscfg_write(CFG_USER_DENGLE_H, (u8 *)&s_flash_half, sizeof(s_flash_half));
if (ret <= 0) {
printf("dengle flash H write fail ret=%d\n", ret);
} else {
printf("dengle flash saved, active=%u\n", dengle_sched_get_active_count());
}
}
/**
* @brief 延时落盘:先回 ACK,避免与组包发送叠在同一深调用栈
*/
static void dengle_flash_save_timeout(void *priv)
{
(void)priv;
s_save_timer = 0;
dengle_flash_save();
}
static void dengle_flash_save_defer(void)
{
if (s_save_timer) {
sys_timeout_del(s_save_timer);
s_save_timer = 0;
}
s_save_timer = sys_timeout_add(NULL, dengle_flash_save_timeout, 50);
}
/**
* @brief 从上电 Flash 恢复 Dengle 配置
*/
static void dengle_flash_load(void)
{
int ret;
uint8_t i;
/* 前半 */
memset(&s_flash_half, 0, sizeof(s_flash_half));
ret = syscfg_read(CFG_USER_DENGLE_L, (u8 *)&s_flash_half, sizeof(s_flash_half));
if (ret > 0 && s_flash_half.magic == DENGLE_FLASH_MAGIC &&
s_flash_half.ver == DENGLE_FLASH_VER) {
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
dengle_slot_from_flash(&s_flash_half.slots[i], &s_slots[i]);
}
} else {
printf("dengle flash L empty/invalid ret=%d\n", ret);
}
/* 后半 */
memset(&s_flash_half, 0, sizeof(s_flash_half));
ret = syscfg_read(CFG_USER_DENGLE_H, (u8 *)&s_flash_half, sizeof(s_flash_half));
if (ret > 0 && s_flash_half.magic == DENGLE_FLASH_MAGIC &&
s_flash_half.ver == DENGLE_FLASH_VER) {
for (i = 0; i < DENGLE_FLASH_HALF; i++) {
dengle_slot_from_flash(&s_flash_half.slots[i],
&s_slots[DENGLE_FLASH_HALF + i]);
}
} else {
printf("dengle flash H empty/invalid ret=%d\n", ret);
}
printf("dengle flash loaded, active=%u\n", dengle_sched_get_active_count());
}
/*============================================================================*/
/* BLE 发送(两条路径) */
/*============================================================================*/
/**
* @brief CBCT/CBFDREPORTCMD + ycmd.arg_int32[]
* @note 对齐 usr_test_bleproto_runcmd 组包方式
*/
static void dengle_send_int32(const DengleSlot_t *slot)
{
bleproto_reportcmd_req_t *req;
int elen;
uint8_t i;
if (!slot || usr_get_conn_service() == 0) {
return;
}
memset(&s_encode_desc, 0, sizeof(s_encode_desc));
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
s_encode_desc.header.msgid = usr_ble_rx_msgid_get();
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD;
req = &s_encode_desc.datast.reportcmd_req;
req->ycmd.cmd_id = slot->cmd_id;
req->ycmd.arg_int32_count = slot->data_n;
req->ycmd.arg_string_count = 0;
req->ycmd.arg_bytes_count = 0;
for (i = 0; i < slot->data_n && i < DENGLE_INT_MAX; i++) {
req->ycmd.arg_int32[i] = slot->data.i32[i];
}
elen = bleproto_appdata_encode(s_txbuf, sizeof(s_txbuf), sizeof(s_txbuf), &s_encode_desc);
printf("dengle INT32 cmd_id=%u n=%u elen=%d\n", slot->cmd_id, slot->data_n, elen);
if (elen > 0) {
usr_ble_send_data(s_txbuf, (u16)elen);
}
}
/**
* @brief 其它产品:REPORTCMD_V2 + bytes_data
* @note 对齐 usr_test_protocol 组包方式
*/
static void dengle_send_bytes(const DengleSlot_t *slot)
{
ble_proto_packet_t *pkt;
int elen;
if (!slot || usr_get_conn_service() == 0) {
return;
}
memset(&s_encode_desc, 0, sizeof(s_encode_desc));
s_encode_desc.header.version = 0;
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
s_encode_desc.header.msgid = usr_ble_rx_msgid_get();
s_encode_desc.header.encrypt = 0;
s_encode_desc.header.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
pkt = &s_encode_desc.datast.reportcmd_v2_req;
pkt->cmd = slot->cmd_id;
pkt->reserved = 0;
pkt->gateway_int_cnt = 0;
pkt->device_int_len = 0;
pkt->device_str_len = 0;
pkt->device_bytes_len = slot->data_n;
pkt->bytes_data = slot->data.bytes;
elen = bleproto_appdata_encode(s_txbuf, sizeof(s_txbuf), sizeof(s_txbuf), &s_encode_desc);
printf("dengle BYTES cmd=%u n=%u elen=%d\n", slot->cmd_id, slot->data_n, elen);
if (elen > 0) {
usr_ble_send_data(s_txbuf, (u16)elen);
}
}
static void dengle_send_slot(const DengleSlot_t *slot)
{
if (!slot || !slot->enable || slot->data_n == 0) {
return;
}
/* USB 连着电脑时不发,避免配置阶段干扰 */
if (dengle_is_usb_pause()) {
return;
}
if (slot->mode == DENGLE_MODE_INT32) {
dengle_send_int32(slot);
} else {
dengle_send_bytes(slot);
}
}
/**
* @brief 延时立即发送:与 USB 解析/ACK 错开,降低栈峰值
*/
static void dengle_imm_send_timeout(void *priv)
{
uint8_t slot_id = (uint8_t)((u32)priv);
s_imm_send_timer = 0;
if (slot_id < DENGLE_SLOT_MAX) {
dengle_send_slot(&s_slots[slot_id]);
}
}
static void dengle_imm_send_defer(uint8_t slot_id)
{
s_imm_send_slot = slot_id;
if (s_imm_send_timer) {
sys_timeout_del(s_imm_send_timer);
s_imm_send_timer = 0;
}
s_imm_send_timer = sys_timeout_add((void *)((u32)slot_id),
dengle_imm_send_timeout, 30);
}
/*============================================================================*/
/* 定时节拍 */
/*============================================================================*/
static void dengle_tick(void *priv)
{
uint8_t i;
DengleSlot_t *slot;
(void)priv;
/* USB 在线:冻结计时,断开后从完整周期重新开始,避免拔线瞬间连发 */
if (dengle_is_usb_pause()) {
if (!s_usb_pause_logged) {
s_usb_pause_logged = 1;
printf("dengle pause: COM open\n");
}
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
s_slots[i].elapsed_ms = 0;
}
return;
}
if (s_usb_pause_logged) {
s_usb_pause_logged = 0;
printf("dengle resume: COM closed\n");
}
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
slot = &s_slots[i];
if (!slot->enable || slot->period_ms == 0) {
continue;
}
slot->elapsed_ms += DENGLE_TICK_MS;
if (slot->elapsed_ms >= slot->period_ms) {
slot->elapsed_ms = 0;
dengle_send_slot(slot);
}
}
}
/*============================================================================*/
/* 对外接口 */
/*============================================================================*/
Dengle_Ret_t dengle_sched_init(void)
{
memset(s_slots, 0, sizeof(s_slots));
dengle_flash_load();
s_inited = 1;
if (s_tick_timer == 0) {
s_tick_timer = sys_timer_add(NULL, dengle_tick, DENGLE_TICK_MS);
}
printf("dengle_sched_init ok\n");
return DENGLE_OK;
}
Dengle_Ret_t dengle_sched_clear_all(void)
{
if (!s_inited) {
return DENGLE_ERR_NOT_INIT;
}
memset(s_slots, 0, sizeof(s_slots));
dengle_flash_save_defer();
printf("dengle clear all\n");
return DENGLE_OK;
}
uint8_t dengle_sched_get_active_count(void)
{
uint8_t i;
uint8_t cnt = 0;
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
if (s_slots[i].enable) {
cnt++;
}
}
return cnt;
}
Dengle_Ret_t dengle_sched_usb_set(const uint8_t *payload, uint16_t payload_len)
{
DengleSlot_t *slot;
DengleMode_t mode;
uint8_t slot_id;
uint8_t enable;
uint8_t data_n;
uint8_t i;
uint16_t need;
if (!s_inited) {
return DENGLE_ERR_NOT_INIT;
}
if (!payload || payload_len < 1) {
return DENGLE_ERR_PARAM;
}
slot_id = payload[0];
enable = payload[1];
if (slot_id >= DENGLE_SLOT_MAX) {
return DENGLE_ERR_SLOT;
}
slot = &s_slots[slot_id];
memset(slot, 0, sizeof(*slot));
/* 仅关闭该槽 */
if (enable == 0) {
dengle_flash_save_defer();
printf("dengle slot%u disabled\n", slot_id);
return DENGLE_OK;
}
mode = dengle_get_mode_by_prodcode();
data_n = payload[8];
if (mode == DENGLE_MODE_INT32) {
if (data_n == 0 || data_n > DENGLE_INT_MAX) {
return DENGLE_ERR_PARAM;
}
need = 9 + (uint16_t)data_n * 4;
if (payload_len < need) {
return DENGLE_ERR_PARAM;
}
for (i = 0; i < data_n; i++) {
slot->data.i32[i] = (int32_t)dengle_read_u32_be(&payload[9 + i * 4]);
}
} else {
if (data_n == 0 || data_n > DENGLE_BYTES_MAX) {
return DENGLE_ERR_PARAM;
}
need = 9 + data_n;
if (payload_len < need) {
return DENGLE_ERR_PARAM;
}
memcpy(slot->data.bytes, &payload[9], data_n);
}
slot->enable = 1;
slot->mode = (uint8_t)mode;
slot->period_ms = dengle_read_u32_be(&payload[2]);
slot->cmd_id = dengle_read_u16_be(&payload[6]);
slot->data_n = data_n;
slot->elapsed_ms = 0;
if (slot->period_ms < DENGLE_TICK_MS) {
slot->period_ms = DENGLE_TICK_MS;
}
printf("dengle set slot=%u mode=%u period=%ums cmd=%u n=%u\n",
slot_id, slot->mode, slot->period_ms, slot->cmd_id, slot->data_n);
/* 先 ACK,再延时落盘/发送,避免与组包叠栈导致复位 */
dengle_flash_save_defer();
dengle_imm_send_defer(slot_id);
return DENGLE_OK;
}
/*============================================================================*/
/* 查询导出(供 USB 0x74 / 0x75 */
/*============================================================================*/
static void dengle_write_u16_be(uint8_t *p, uint16_t v)
{
p[0] = (uint8_t)(v >> 8);
p[1] = (uint8_t)(v & 0xFF);
}
static void dengle_write_u32_be(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)(v >> 24);
p[1] = (uint8_t)(v >> 16);
p[2] = (uint8_t)(v >> 8);
p[3] = (uint8_t)(v & 0xFF);
}
/**
* @brief 打包单个槽位(与 0x70 设置 payload 同格式,便于上位机复用解析)
* enable=0 时仅输出 [slot][0]
* enable=1 时输出 [slot][1][period][cmd][data_n][data...]
* INT32 模式 data 仍按大端 int32 导出
*/
int dengle_sched_pack_slot(uint8_t slot_id, uint8_t *buf, uint16_t max_len)
{
const DengleSlot_t *slot;
uint16_t need;
uint8_t i;
if (!s_inited || !buf) {
return -1;
}
if (slot_id >= DENGLE_SLOT_MAX) {
return -1;
}
slot = &s_slots[slot_id];
if (!slot->enable) {
if (max_len < 2) {
return -1;
}
buf[0] = slot_id;
buf[1] = 0;
return 2;
}
if (slot->mode == DENGLE_MODE_INT32) {
need = 9 + (uint16_t)slot->data_n * 4;
} else {
need = 9 + slot->data_n;
}
if (max_len < need) {
return -1;
}
buf[0] = slot_id;
buf[1] = 1;
dengle_write_u32_be(&buf[2], slot->period_ms);
dengle_write_u16_be(&buf[6], slot->cmd_id);
buf[8] = slot->data_n;
if (slot->mode == DENGLE_MODE_INT32) {
for (i = 0; i < slot->data_n; i++) {
dengle_write_u32_be(&buf[9 + i * 4], (uint32_t)slot->data.i32[i]);
}
} else {
memcpy(&buf[9], slot->data.bytes, slot->data_n);
}
return (int)need;
}
int dengle_sched_pack_all(uint8_t *buf, uint16_t max_len)
{
uint16_t offset = 0;
uint8_t i;
int n;
if (!s_inited || !buf) {
return -1;
}
for (i = 0; i < DENGLE_SLOT_MAX; i++) {
n = dengle_sched_pack_slot(i, &buf[offset],
(uint16_t)(max_len - offset));
if (n <= 0) {
return -1;
}
offset = (uint16_t)(offset + n);
}
return (int)offset;
}