fix(iot_mqtt): loop_data 陈旧快照 — 事件与缓存统一帧摄取 (同源)

现场: 长车离开后 event_report 正确, 但 loop_data 连续 34s/100+ 条
iscar:true 陈旧快照, 且冻结 diff=517 锁死 fast_mode 300ms 刷屏。

根因: 0xC0 帧两条竞争消费路径数据源分裂 —
  uart_srv→lup回调只喂事件不更新 _cached_sr;
  Step1 直读才更新缓存, 但赢得竞争概率实测 ~2%
→ 事件每帧必达而快照冻结, 出现'事件说车走了、快照说车还在'。

修复:
- 新增 iot_sensor_ingest(): 事件沿 + _cached_sr 刷新单一入口,
  回调与 Step1 两路汇入, 帧竞争不再影响数据新鲜度
- Step1 直读路径补 lup_verify_checksum (此前裸解析未校验)
This commit is contained in:
wangfq
2026-07-15 17:48:30 +08:00
parent a457b81916
commit 6284bca0cb
2 changed files with 59 additions and 9 deletions
@@ -150,12 +150,23 @@ static void iot_evt_feed(const LUP_SensorReport *sr) {
}
}
/* lup 传感回调: uart_srv 消费路径的帧从这里喂入 (已过校验) */
/* 统一帧摄取: 事件沿检测 + 刷新 loop_data 缓存 (单一数据源)
两条消费路径 (uart_srv→lup回调 / Step1直读) 都必须走这里。
教训(2026-07-15): 此前回调只喂事件、缓存只在 Step1 更新, 而 Step1 赢得
帧竞争的概率实测 <2% → loop_data 发布 34s 前的陈旧快照(车走后 iscar
仍 true), 且陈旧 diff 锁死 fast_mode 300ms 刷屏。事件与快照必须同源! */
static void iot_sensor_ingest(const LUP_SensorReport *sr) {
iot_evt_feed(sr); // 事件沿检测
memcpy(&_cached_sr, sr, sizeof(LUP_SensorReport)); // 刷新 loop_data 快照
_cached_sr_valid = 1;
}
/* lup 传感回调: uart_srv 消费路径的帧从这里喂入 (lup_process_frame 已过校验) */
static void iot_evt_sensor_cb(const uint8_t *pkg, uint16_t len) {
LUP_SensorReport sr;
memset(&sr, 0, sizeof(sr));
if (lup_parse_sensor_report(pkg, len, &sr) == 0) {
iot_evt_feed(&sr);
iot_sensor_ingest(&sr);
}
}
@@ -584,24 +595,27 @@ static void iot_process_recv(void) {
void iot_mqtt_publish_sensor(void) {
static uint8_t _last_car_state[4] = {0};
/*--- Step 1: 消费 0xC0 帧 → 事件沿检测 + 更新缓存 ---*/
/*--- Step 1: 消费 0xC0 帧 (Step1 直读路径, 竞争窗口小, 少数帧走此路) ---*/
/* 注意: 置于 READY/enable 检查之前 —— 断网/未使能期间事件照样入队,
重连后由 iot_evt_process() 补报 (协议 V1.04) */
重连后由 iot_evt_process() 补报 (协议 V1.04)
多数帧由 uart_srv→lup 回调路径摄取, 两路都汇入 iot_sensor_ingest */
if (g_pkg_uart_2.flag != 0
&& g_pkg_uart_2.pkg[0] == 0x7F
&& g_pkg_uart_2.pkg[3] == 0xC0) {
LUP_SensorReport sr;
int ret = -100;
memset(&sr, 0, sizeof(sr));
int ret = lup_parse_sensor_report(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &sr);
/* 直读路径未经过 lup_process_frame, 须自行校验 checksum */
if (lup_verify_checksum(g_pkg_uart_2.pkg, g_pkg_uart_2.offset) == 0) {
ret = lup_parse_sensor_report(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &sr);
}
InitPkgUart(&g_pkg_uart_2); // 读完即清, 不持有
if (ret == 0) {
iot_evt_feed(&sr); // 事件沿检测 (Step1 消费路径)
memcpy(&_cached_sr, &sr, sizeof(sr));
_cached_sr_valid = 1;
iot_sensor_ingest(&sr); // 统一摄取: 事件沿 + loop_data 缓存
} else {
PRINT("IOT: sensor parse failed (%d)\n", ret);
PRINT("IOT: sensor frame invalid (%d)\n", ret);
}
}