P1和P2已开发完成并验证了大部分功能都没问题

This commit is contained in:
2026-09-07 18:50:35 +08:00
parent f4b0b3213c
commit 862ce10b90
18 changed files with 1525 additions and 370 deletions
+104 -195
View File
@@ -1,18 +1,33 @@
/* ================================================================
* jb_product.c — 用户层实现(请填补空白处
* 设备: 电子开关-加热垫 | 生成时间: 2026-09-04 10:30:37
* jb_product.c — 用户层实现(薄适配:只写 DP,不写业务算法
* 设备: 电子开关-加热垫 | 生成: 2026-09-04V1.0.0 → P1 接线)
*
* ══ 开发流程 ══
* 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供)
* 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用,无需 uartWrite
* 2. userInit() 设置 DP 默认值与外设初始化
* 3. userHandle() 用户数据采集
* 4. jbEventProcess() 处理 APP 下发的控制命令
* 5. 主动上报 / OTA 的网关应答在收到 REPORTCMD_V2 后调用 jbReportAckCheck 清除等待
* ================================================================
*/
* 发送由 SOC SDK 的 ble_send_data 承担,协议层已直接调用
* 2. userInit() 设置 DP 默认值
* 3. userHandle() 用户数据采集(本机无传感器,DP 由 app_function_poll 同步)
* 4. jbEventProcess() APP 下行:只写 gDevData 并转调 app_*,不在这里写业务
*
* ⚠ 规格书第 0 章第 2 条:**业务算法一律不进本文件**。
* 继电器 → app_relay / app_arbiter;定时存储 → app_timer_task
* 持久化 → app_nvm;出厂重置 → app_function。
* ⚠ DP0 上报**实际吸合态**:本文件的赋值只表达"手动请求"app_function_poll
* (20ms)会把它覆盖为过零后的真值,协议层差分上报读到的即实际态。
* ================================================================ */
#include "jb_product.h"
#include "app_relay.h"
#include "app_arbiter.h"
#include "app_linkage.h"
#include "app_timer_task.h"
#include "app_nvm.h"
#include "app_function.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[JB_PRD]"
#define LOG_INFO_ENABLE
#include "debug.h"
/* ════════════════════════════════════════════════════════════════
* 全局变量
@@ -28,16 +43,16 @@ void userInit(void)
{
memset(&gDevData, 0, sizeof(jb_data_point_t));
gDevData.heat_switch = 0; /* DP0 加热开关 0=关 1=开 */
gDevData.temp_control_switch = 0; /* DP1 温控开关 0=关 1=开 */
memset(gDevData.temp_control_time, 0, 4); /* DP2 温控时间 */
gDevData.mode = 0; /* MODE_0 (??) */
gDevData.min_temp = 0.0f; /* DP4 最低温度 (float) */
gDevData.max_temp = 0.0f; /* DP5 最高温度 (float) */
gDevData.current_temp = 0.0f; /* DP6 当前温度 (float) */
gDevData.linkage_delete = 0; /* DP7 联动删除 0=关 1=开 */
memset(gDevData.linkage_config, 0, 20); /* DP8 联动配置 */
gDevData.time_en = 0; /* DP9 定时使能 0=关 1=开 */
gDevData.heat_switch = 0; /* DP0 加热开关 0=关 1=开P1 起由 VM 恢复) */
gDevData.temp_control_switch = 0; /* DP1 温控开关(本版写忽略) */
memset(gDevData.temp_control_time, 0, 4); /* DP2 温控时间(本版写忽略) */
gDevData.mode = 0; /* DP3 模式(本版写忽略) */
gDevData.min_temp = 0.0f; /* DP4 最低温度(本版写忽略) */
gDevData.max_temp = 0.0f; /* DP5 最高温度(本版写忽略) */
gDevData.current_temp = 0.0f; /* DP6 当前温度WO,只收不下发) */
gDevData.linkage_delete = 0; /* DP7 联动删除(脉冲;P2 接) */
memset(gDevData.linkage_config, 0, 20); /* DP8 联动配置P2 接) */
gDevData.time_en = 0; /* DP9 定时使能P1 起由 VM 恢复) */
memset(gDevData.time_config1, 0, 8); /* DP10 定时配置1 */
memset(gDevData.time_config2, 0, 8); /* DP11 定时配置2 */
memset(gDevData.time_config3, 0, 8); /* DP12 定时配置3 */
@@ -45,58 +60,33 @@ void userInit(void)
memset(gDevData.time_config5, 0, 8); /* DP14 定时配置5 */
memset(gDevData.time_config6, 0, 8); /* DP15 定时配置6 */
memset(gDevData.time_config7, 0, 8); /* DP16 定时配置7 */
memset(gDevData.run_record, 0, 124); /* DP17 运行记录 */
gDevData.heartbeat = 0; /* DP18 心跳 0=关 1=开 */
memset(gDevData.run_record, 0, 124); /* DP17 运行记录ROP2 由 app_runrec 填) */
gDevData.heartbeat = 0; /* DP18 心跳(本版忽略) */
gDevData.fault_en = 0; /* DP19 设备故障使能 */
gDevData.fault_code = 0; /* DP20 设备故障 */
gDevData.fault_code = 0; /* DP20 设备故障RO */
gDevData.alarm_en = 0; /* DP21 设备告警使能 */
gDevData.alarm_code = 0; /* DP22 设备告警 */
gDevData.factory_reset = 0; /* DP23 恢复出厂设置 0=关 1=开 */
gDevData.alarm_code = 0; /* DP22 设备告警RO,保持 0 */
gDevData.factory_reset = 0; /* DP23 恢复出厂设置WO,写 1 触发) */
gDevData.reserva_dp1 = 0; /* DP24 预留DP1 */
gDevData.reserva_dp2 = 0; /* DP25 预留DP2 */
gDevData.reserva_dp3 = 0; /* DP26 预留DP3 */
}
/* ════════════════════════════════════════════════════════════════
* userHandle — 用户数据采集
* 协议层自动检测变化并上报(注意:此处是上报,不是下发控制)
* userHandle — 用户数据采集500ms 由 usr_timer_loop 调用)
* 本机无传感器:DP0 由 app_function_poll20ms)同步实际态;
* DP17 运行记录 P2 由 app_runrec 在任务上下文灌;其余无可上报采集
* ════════════════════════════════════════════════════════════════ */
void userHandle(void)
{
/* ── 可上报 DP (RO / RW): 用户数据采集 ── */
// gDevData.heat_switch = read_sensor(); /* DP0 加热开关 */
// gDevData.temp_control_switch = read_sensor(); /* DP1 温控开关 */
// memcpy(gDevData.temp_control_time, sensor_read(), 4); /* DP2 温控时间 */
// gDevData.mode = read_sensor(); /* DP3 模式 */
// gDevData.min_temp = 23.5f; /* DP4 最低温度 (float, scale=0.1), 直接填物理量 */
// gDevData.max_temp = 23.5f; /* DP5 最高温度 (float, scale=0.1), 直接填物理量 */
/* DP6 current_temp 为 WO:不下发采集、不上报;只在 jbEventProcess 收服务器写入 */
// gDevData.linkage_delete = read_sensor(); /* DP7 联动删除 */
// memcpy(gDevData.linkage_config, sensor_read(), 20); /* DP8 联动配置 */
// gDevData.time_en = read_sensor(); /* DP9 定时使能 */
// memcpy(gDevData.time_config1, sensor_read(), 8); /* DP10 定时配置1 */
// memcpy(gDevData.time_config2, sensor_read(), 8); /* DP11 定时配置2 */
// memcpy(gDevData.time_config3, sensor_read(), 8); /* DP12 定时配置3 */
// memcpy(gDevData.time_config4, sensor_read(), 8); /* DP13 定时配置4 */
// memcpy(gDevData.time_config5, sensor_read(), 8); /* DP14 定时配置5 */
// memcpy(gDevData.time_config6, sensor_read(), 8); /* DP15 定时配置6 */
// memcpy(gDevData.time_config7, sensor_read(), 8); /* DP16 定时配置7 */
// memcpy(gDevData.run_record, sensor_read(), 124); /* DP17 运行记录 */
// gDevData.fault_en = read_sensor(); /* DP19 设备故障使能 */
// gDevData.fault_code = read_sensor(); /* DP20 设备故障 */
// gDevData.alarm_en = read_sensor(); /* DP21 设备告警使能 */
// gDevData.alarm_code = read_sensor(); /* DP22 设备告警 */
// gDevData.reserva_dp1 = read_sensor(); /* DP24 预留DP1 */
// gDevData.reserva_dp2 = read_sensor(); /* DP25 预留DP2 */
// gDevData.reserva_dp3 = read_sensor(); /* DP26 预留DP3 */
/* 无本机温度/湿度采集。断连扫描温度(P2)只进联动引擎,不填 DP6 */
}
/* ════════════════════════════════════════════════════════════════
* jbEventProcess — 0x03 APP 控制事件处理(下行 / DP 赋值方向)
*
* 协议层自动解包 0x03 帧后调用本函数。
* info->dp_ids[] = 被控制的 DP ID 列表
* ctrl 已填入下发的值;复制到 gDevData 并执行动作
* ⚠ 只做两件事:DP 写入 gDevData + 转调 app_* 模块。业务算法一律
* 不进本文件(规格书第 0 章第 2 条)。
* ════════════════════════════════════════════════════════════════ */
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
{
@@ -109,197 +99,121 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
{
switch (info->dp_ids[i])
{
/* ── DP0 加热开关 (bool) ── */
/* ── DP0 加热开关:写 = 手动抢权,请求开/关(等过零)。
* gDevData 值会被 app_function_poll 同步为实际吸合态再上报 ── */
case DP_ID_HEAT_SWITCH:
if (ctrl->heat_switch)
{
gDevData.heat_switch = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.heat_switch = 0;
// TODO: 关 — 执行动作
}
gDevData.heat_switch = ctrl->heat_switch ? 1 : 0;
app_arbiter_manual_intervene(); /* 手动动作:联动/定时让位 */
app_relay_request(gDevData.heat_switch);
log_info("dp0 heat request=%d\n", gDevData.heat_switch);
break;
/* ── DP1 温控开关 (bool) ── */
case DP_ID_TEMP_CONTROL_SWITCH:
if (ctrl->temp_control_switch)
{
gDevData.temp_control_switch = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.temp_control_switch = 0;
// TODO: 关 — 执行动作
}
/* ── DP1~5:本版写忽略(规格书 0 章第 7 条 / 9 章)。
* 不更新 gDevData → 不动作、不回显、不落盘 ── */
case DP_ID_TEMP_CONTROL_SWITCH: /* DP1 温控开关 */
case DP_ID_TEMP_CONTROL_TIME: /* DP2 温控时间 */
case DP_ID_MODE: /* DP3 模式 */
case DP_ID_MIN_TEMP: /* DP4 最低温度 */
case DP_ID_MAX_TEMP: /* DP5 最高温度 */
break;
/* ── DP2 温控时间 (raw) ── */
case DP_ID_TEMP_CONTROL_TIME:
memcpy(gDevData.temp_control_time, ctrl->temp_control_time, 4);
// TODO: 根据 gDevData.temp_control_time 执行动作 (长度 4 字节)
break;
/* ── DP3 模式 (enum) ── */
case DP_ID_MODE:
gDevData.mode = ctrl->mode;
// TODO: 根据 ctrl->mode 执行动作
break;
/* ── DP4 最低温度 (float, scale=0.1) ── */
case DP_ID_MIN_TEMP:
gDevData.min_temp = ctrl->min_temp;
// TODO: 根据 ctrl->min_temp 执行动作 (已是浮点物理量)
break;
/* ── DP5 最高温度 (float, scale=0.1) ── */
case DP_ID_MAX_TEMP:
gDevData.max_temp = ctrl->max_temp;
// TODO: 根据 ctrl->max_temp 执行动作 (已是浮点物理量)
break;
/* ── DP6 当前温度 (float, WO, scale=0.1) ── */
/* ── DP6 当前温度(WO):服务器下发 → 联动用温度(10.2/8 章)。
* 协议层不打包不上报;联动引擎关时也只存 RAM ── */
case DP_ID_CURRENT_TEMP:
gDevData.current_temp = ctrl->current_temp;
/* 业务:转调 app_* 更新联动用温度;禁止在本文件写开停算法 */
app_linkage_set_connected_temp(ctrl->current_temp);
break;
/* ── DP7 联动删除 (bool) ── */
/* ── DP7 联动删除(脉冲,10.4):写 1 清配置并让出加热,
* 然后清回 0 再上报;写 0 = 无操作 ── */
case DP_ID_LINKAGE_DELETE:
if (ctrl->linkage_delete)
{
gDevData.linkage_delete = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.linkage_delete = 0;
// TODO: 关 — 执行动作
if (ctrl->linkage_delete) {
app_linkage_delete();
gDevData.linkage_delete = 0; /* 脉冲:清回 0 供上报 */
}
break;
/* ── DP8 联动配置 (raw) ── */
/* ── DP8 联动配置20B memcpy + 存 VM + 清手动作废 + 立刻重判(10.5)── */
case DP_ID_LINKAGE_CONFIG:
memcpy(gDevData.linkage_config, ctrl->linkage_config, 20);
// TODO: 根据 gDevData.linkage_config 执行动作 (长度 20 字节)
app_linkage_set_config();
break;
/* ── DP9 定时使能 (bool) ── */
/* ── DP9 定时使能:写 gDevData;引擎每 500ms 读 time_en11.2── */
case DP_ID_TIME_EN:
if (ctrl->time_en)
{
gDevData.time_en = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.time_en = 0;
// TODO: 关 — 执行动作
}
gDevData.time_en = ctrl->time_en ? 1 : 0;
app_timer_set_en(gDevData.time_en);
break;
/* ── DP10 定时配置1 (raw) ── */
/* ── DP10~16 定时配置8B 原样存 gDevDataByte1 模式忽略);
* 引擎按窗口起止/星期直接读这些字段 ── */
case DP_ID_TIME_CONFIG1:
memcpy(gDevData.time_config1, ctrl->time_config1, 8);
// TODO: 根据 gDevData.time_config1 执行动作 (长度 8 字节)
app_timer_set_config(1);
break;
/* ── DP11 定时配置2 (raw) ── */
case DP_ID_TIME_CONFIG2:
memcpy(gDevData.time_config2, ctrl->time_config2, 8);
// TODO: 根据 gDevData.time_config2 执行动作 (长度 8 字节)
app_timer_set_config(2);
break;
/* ── DP12 定时配置3 (raw) ── */
case DP_ID_TIME_CONFIG3:
memcpy(gDevData.time_config3, ctrl->time_config3, 8);
// TODO: 根据 gDevData.time_config3 执行动作 (长度 8 字节)
app_timer_set_config(3);
break;
/* ── DP13 定时配置4 (raw) ── */
case DP_ID_TIME_CONFIG4:
memcpy(gDevData.time_config4, ctrl->time_config4, 8);
// TODO: 根据 gDevData.time_config4 执行动作 (长度 8 字节)
app_timer_set_config(4);
break;
/* ── DP14 定时配置5 (raw) ── */
case DP_ID_TIME_CONFIG5:
memcpy(gDevData.time_config5, ctrl->time_config5, 8);
// TODO: 根据 gDevData.time_config5 执行动作 (长度 8 字节)
app_timer_set_config(5);
break;
/* ── DP15 定时配置6 (raw) ── */
case DP_ID_TIME_CONFIG6:
memcpy(gDevData.time_config6, ctrl->time_config6, 8);
// TODO: 根据 gDevData.time_config6 执行动作 (长度 8 字节)
app_timer_set_config(6);
break;
/* ── DP16 定时配置7 (raw) ── */
case DP_ID_TIME_CONFIG7:
memcpy(gDevData.time_config7, ctrl->time_config7, 8);
// TODO: 根据 gDevData.time_config7 执行动作 (长度 8 字节)
app_timer_set_config(7);
break;
/* ── DP18 心跳 (bool) ── */
/* ── DP17 运行记录:RO,禁止 APP 写(规格书第 15 章)── */
case DP_ID_RUN_RECORD:
break;
/* ── DP18 心跳:本版忽略 ── */
case DP_ID_HEARTBEAT:
if (ctrl->heartbeat)
{
gDevData.heartbeat = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.heartbeat = 0;
// TODO: 关 — 执行动作
}
break;
/* ── DP19 设备故障使能 (uint8) ── */
/* ── DP19/DP21 使能:原样存(P1 不做位级过滤)── */
case DP_ID_FAULT_EN:
gDevData.fault_en = ctrl->fault_en;
// TODO: 根据 ctrl->fault_en 执行动作
app_nvm_mark_dirty(); /* 使能要持久化(13 章"若有写入" */
break;
/* ── DP21 设备告警使能 (uint8) ── */
case DP_ID_ALARM_EN:
gDevData.alarm_en = ctrl->alarm_en;
// TODO: 根据 ctrl->alarm_en 执行动作
app_nvm_mark_dirty();
break;
/* ── DP23 恢复出厂设置 (bool) ── */
/* ── DP23 恢复出厂设置:清业务 VM(留配对)+ RAM 复位,
* 不断开不软复位(规格书第 14 章)── */
case DP_ID_FACTORY_RESET:
if (ctrl->factory_reset)
{
gDevData.factory_reset = 1;
// TODO: 开 — 执行动作
}
else
{
gDevData.factory_reset = ctrl->factory_reset ? 1 : 0;
if (gDevData.factory_reset) {
app_function_factory_reset();
gDevData.factory_reset = 0;
// TODO: 关 — 执行动作
log_info("dp23 factory reset\n");
}
break;
/* ── DP24 预留DP1 (uint32) ── */
/* ── DP24~26 预留:存 RAM,不实现动作 ── */
case DP_ID_RESERVA_DP1:
gDevData.reserva_dp1 = ctrl->reserva_dp1;
// TODO: 根据 ctrl->reserva_dp1 执行动作
break;
/* ── DP25 预留DP2 (uint32) ── */
case DP_ID_RESERVA_DP2:
gDevData.reserva_dp2 = ctrl->reserva_dp2;
// TODO: 根据 ctrl->reserva_dp2 执行动作
break;
/* ── DP26 预留DP3 (uint32) ── */
case DP_ID_RESERVA_DP3:
gDevData.reserva_dp3 = ctrl->reserva_dp3;
// TODO: 根据 ctrl->reserva_dp3 执行动作
break;
default:
@@ -313,14 +227,12 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
* 返回 0=接受升级(协议层应答 JB_OK + MaxDataLen
* 非 0=拒绝,返回值即错误码(JB_OTA_*)
*
* ⚠ SOC 侧 OTA 未实现:当前仅应答,不执行任何升级动作。 */
* ⚠ SOC 侧协议 OTA 未实现:拒绝接收,避免 APP 继续推数据。
* 按键 12s OTA 仍走 usr_app_ota_init。 */
int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
{
(void)fwVersion; (void)fwLength; (void)fwCrc32;
// TODO: 检查 Flash 空间与版本,决定是否接收升级
// 允许: 返回 JB_OK(协议层应答 JB_OK + MaxDataLen
// 拒绝: 返回对应 JB_OTA_* 错误码(如 JB_OTA_VER_LOW / JB_OTA_SPACE_ERR
return JB_OK;
return JB_OTA_STATE_ERR;
}
/* 0x32 OTA 数据(含分片序号)*/
@@ -328,35 +240,32 @@ void jbOnOtaData(const uint8_t *data, uint16_t len,
uint16_t shardIdx, uint16_t totalShards)
{
(void)data; (void)len; (void)shardIdx; (void)totalShards;
// TODO: 写入 Flash,累计 CRC32
// 全部数据接收完成后调用 jbSendOtaResult(JB_OK, mcuVersion)
/* TODO(P2): 写入 Flash,累计 CRC32;全部接收后调用 jbSendOtaResult */
}
/* 0x36 / 0x38 停止 OTA */
void jbOnOtaStop(void)
{
// TODO: 终止 OTA,释放资源
/* TODO(P2): 终止 OTA,释放资源 */
}
/* 任意 MCU→BLE 请求的 ACK 回调(cmd 标识是哪条请求的应答,result 为结果码)
- SOC 仅保留 OTA 主动请求应答: OTA完成 0x35 / 停止 0x39
- 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */
/* 任意 MCU→BLE 请求的 ACK 回调 */
void jbOnAck(uint8_t cmd, uint8_t result)
{
(void)cmd;
(void)result;
// TODO: switch(cmd){ case CMD_OTA_COMPLETE_ACK: gOtaDone = (result==JB_OK); break; case CMD_OTA_STOP_MCU_ACK: ... }
/* TODO(P2): switch(cmd){ case CMD_OTA_COMPLETE_ACK: ... } */
}
/* ════════════════════════════════════════════════════════════════
* 硬件抽象(按实际平台实现)
* ════════════════════════════════════════════════════════════════ */
/* ── 定时器 ── */
/* ── 定时器:由 usr_jb_main 的 20ms 节拍按 1ms 步进调用 jbTimerIrq() ── */
void jbTimerIrq(void)
{
timerMs++;
} /* 在 1ms 中断中调用本函数 */
}
uint32_t jbGetTimerCount(void)
{
+1 -1
View File
@@ -23,7 +23,7 @@
* ════════════════════════════════════════════════════════════════ */
#define PKT_HEAD_0 0x55
#define PKT_HEAD_1 0xAA
#define MAX_PACKAGE_LEN 256
#define MAX_PACKAGE_LEN 512
#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
#define DP_COUNT 27