feat(UI/采集/104): 通讯设置页 + 开机账本 + adc_cal 可诊断化——三车道同树一次构建入库

三条道在同一棵树上交付,故合并成**一次权威构建**(@build 0 错 0 警,Code=235236 RO=26308 RW=1572
ZI=136924)后同笔入库;台账已按该构建回填(`ram_baseline.py --update` + `--write`、`chec
k_mem_numbers.py
 --write`),凭证与实际 .map 同源。归属如下:

· **通讯设置页**(道G task-9):新增 `Drivers/BSP/UI/ui_page_comm.{c,h}`(通道六注入,与死
区页同骨架同纪
  律),`#page=comm` 绑定使 bound **21 → 22**、内容页黄金 **47 → 53**(旧 47 页字节零变化,
新增六态含
  WAIT/OK 两张);`iec104_comm` 脏位 + store_save(**无事件位**)挂 defaultTask 同点位
轮询;`port_104` 由
  宏改走 getter、`IEC104_SERV_PORT` 删除;字体 +听/监(185 字全覆)。**实机链已闭环**:FTP 外改
[comm]
  的 ca/port → 复位 → 串口出现 `section 'comm' (2 keys)` 与 `i104: listening o
n 9004` → 还原 2404,全程无
  FAIL ⇒「重启即生效 + 端口真值」不是推断是实测。上限核对 6/8 段、2/16 键,超限现象由 TC-CONFIG-056/057
  钉死(注册 -1 + `comm init: FAIL` 锚 + getter 回 ROM),**不抬宏**。
· **开机账本**(道F task-12):新增 `App/util/boot_stats.{c,h}`——上电次数落 SD2506 用户
 SRAM(magic +
  小端计数 + 版本 + 异或校验),一次上电 = 1 读 + ≤1 写,**零 NAND、零卷锁、零配置段**;四态互斥
  UNKNOWN / FRESH / CONTINUED / RESTARTED(BLF=1 才算电池域掉过电),**读失败绝不回写**;
文案口径「自 RTC
  电池周期起」,**禁写终身次数**。复位原因优先级链从 `app_main.c` 的 if-else 收进一份真源(串口与屏幕同
  源);104 四个只增不清计数 + `iec104_serv_get_stats()` 留在 serv、未塞进 link(会被 open
/close 洗掉)。
  运行时长按用户裁定 = **只报本次开机以来**,零新增代码。新套件 `test/host/boot_stats` 21/21,五处注入
  **全部有牙**(含「漂一字节被校验和挡下而不是报 256 倍错账」)。
· **adc_cal 可诊断化**(道E task-43):三个 volatile 计数 + `adc_cal_get_stats()`
/ `adc_cal_note_persisted()`
  (水位**盖写非自增**,因为一次 save 覆盖整档,数 save 会留假未落盘);「差额 × 脏位」三态语义唯一落在
  `adc_cal.h`,其中「差额 > 0 且脏位恒 0」= 绕过语义 setter 的静默丢失;TC-ADCCAL-019~023。

三条**必须留档的自纠**(都是执行道自己报的,我不替他们润色):
① stdint 那条断链是**宿主套件抓到、通讯道先修**,采集道承认自己是重复添加者并删掉自己那份;
② 「exit 2 不是证据,断言变红才是」——采集道第一版反证注入了未声明标识符导致编译错,差点当成功;
③ 套件汇总行原写 `s_total/s_total`,失败时会打印「21/21 PASSED (failed=1)」这种自相矛盾的行,已改。

另修:`check_registration.py` 的 `SIM_EXEMPT` 加一行豁免 `App/util/boot_stats.c
`(依赖 I2C 与 RCC 位掩码,
MCU 专属;核过 `sys_clock.c`/`sha256.c` 同形态先例),注册反向校验 exit 1 → 0。发号 TC-IEC1
04-034、
TC-UIDATA-029 已实抬游标(`TC-IEC104-033→034`、`TC-UIDATA-026→029`)。

门禁:id-check --strict 与 plain 均 rc=0、doc-links --all rc=0、注册校验 0、cppche
ck / coding-standard /
bom-drift 0;iec104 179/179、ui_data 295/295、boot_stats 21/21、adc_cal 11
7/117、run_par 67/67;菜单 8 状态
池 5368 不变、内容页 53/53。**已知遗留**:内容页数 47 → 53 的全仓文本同步尚未做(另卡跟踪),本笔只保
证代码与黄金一致;面板注键真机那一腿缺 CLI,交台架道补。
This commit is contained in:
2026-10-27 11:29:23 +08:00
parent 0ae9c10f24
commit 728d60675f
47 changed files with 2364 additions and 123 deletions
+75 -2
View File
@@ -25,6 +25,10 @@
* 见 s_param_ops);② 参数帧尾填 cal_block(未标定
* 闭锁徽标,三路口径同 protect_task.c,不用 eng)
* v1.3 2026-10-27 DTU 项目组 道F task-7:遥测整定注入表(ui_data_yt_ops_t)
* v1.4 2026-10-27 DTU 项目组 道G task-9:通讯参数注入表(ui_data_comm_ops_t,
* 通讯设置页);port_104 快照从 IEC104_SERV_PORT 宏
* 切 iec104_comm_port() 真源(宏同笔删除,交接口径见
* IEC104上行设计说明.md §8)
* 装配层实现 + 首轮懒绑(时序与通道四绑定等价,
* 见 feed_yt_bind_once 函数头);行数等值由编译期
* 守卫钉,量纲分档与合法域分档同源同序
@@ -66,7 +70,7 @@
#include "prot_elm.h" /* 元件动作态(遥信位图来源) */
#include "prot_set.h" /* 40 项定值 Q10 + 校验和 + 闭锁 + 越界位图 */
#include "sys_clock.h" /* 墙钟字符串 */
#include "iec104_serv.h" /* IEC104_SERV_PORT:实际监听端口(非协议层同名常量) */
#include "iec104_comm.h" /* 道G task-9:[comm] 真源 getter/setter/脏位;快照端口切 getter,IEC104_SERV_PORT 字面宏同笔删除 */
#include "iec104_pts.h" /* task-15 批4:遥信点数与单/双点性质(点表事实的唯一来源) */
/* 私有宏定义区 */
@@ -612,11 +616,74 @@ static const ui_data_yt_ops_t s_yt_ops = {
feed_yt_pending
};
/* ======================= 道G task-9:通讯参数注入表(ui_data_comm_ops_t)===
*
* 页面形状(两行的名/序)归 ui_page_comm.c;本层只转达值与合法带——
* lo/hi 现取 iec104_comm.h 带宏(唯一出口),值现取 getter(带内自愈),
* 装配层**不写第二份数值**(通道五同一条纪律)。
*/
/*
* 函数功能:读第 idx 行通讯参数现状(值 + 合法带)
* 入口参数:idx - UI_DATA_COMM_ROW_CA/_PORT uint8_t
* a_out - 行现状 ui_data_comm_row_t *
* 返回值:0 - 已整份写出;-1 - idx 越界(不猜、不顶替) int
* 限定条件:uiTask 页面绘制链(经 ui_data 通道六转发)
*/
static int feed_comm_row(uint8_t a_idx, ui_data_comm_row_t *a_out) {
if (a_idx == UI_DATA_COMM_ROW_CA) {
a_out->val = iec104_comm_ca();
a_out->lo = IEC104_COMM_CA_MIN;
a_out->hi = IEC104_COMM_CA_MAX;
return 0;
}
if (a_idx == UI_DATA_COMM_ROW_PORT) {
a_out->val = iec104_comm_port();
a_out->lo = IEC104_COMM_PORT_MIN;
a_out->hi = IEC104_COMM_PORT_MAX;
return 0;
}
return -1;
}
/*
* 函数功能:写第 idx 行通讯参数(守卫在 setter:带外拒、副本不动、不置脏)
* 返回值:0 - 已写并置脏;-1 - idx 越界或带外拒 int
* 限定条件:uiTask 按键链(编辑提交)
* 函数说明:a_val > 65535 不在此先裁——截断成 u16 会把 65536 洗成 0 再被
* setter 合法拒掉,等于替屏幕编了个假值;越界一律原样送 setter 拒
*/
static int feed_comm_set(uint8_t a_idx, uint32_t a_val) {
if (a_val > 65535uL) {
return -1;
}
if (a_idx == UI_DATA_COMM_ROW_CA) {
return iec104_comm_ca_set((uint16_t)a_val);
}
if (a_idx == UI_DATA_COMM_ROW_PORT) {
return iec104_comm_port_set((uint16_t)a_val);
}
return -1;
}
/* 函数功能:[comm] 段落盘脏位(与页面 WAIT/OK 同一枚 peek,不另发明) uint8_t */
static uint8_t feed_comm_pending(void) {
return iec104_comm_dirty_peek();
}
/* 通讯参数注入表(三件齐全;ui_data_comm_bind 拒残表) */
static const ui_data_comm_ops_t s_comm_ops = {
feed_comm_row,
feed_comm_set,
feed_comm_pending
};
/* 首轮懒绑一次:ui_data_feed_tick() 的首个调用点在 App/task/ui_task.c 里先于
* ui_menu_init()(首帧发布那次),与通道四 ui_data_maint_bind 的绑定时序等价;
* 不在 ui_task.c 直接绑是为了本批写域收在派发单列出的文件里。残表是真故障,
* 必须能在启动日志里看见,不能静默(通道四同口径) */
static uint8_t s_yt_bound;
static uint8_t s_comm_bound; /* 道G task-9 通讯参数表懒绑门(同款先置位防刷日志) */
/*
@@ -668,7 +735,7 @@ static void feed_fill_info(ui_data_info_t *p_snap, uint32_t tick_ms) {
p_snap->valid = 1u;
p_snap->uptime_s = tick_ms / 1000u;
p_snap->port_104 = (uint16_t)IEC104_SERV_PORT;
p_snap->port_104 = iec104_comm_port(); /* 真源 = [comm] RAM 副本(getter 带内恒成立,u16 读原子) */
p_snap->port_disp = (uint16_t)UI_NET_SINK_PORT;
feed_copy_str(p_snap->model, sizeof(p_snap->model), FEED_MODEL);
feed_copy_str(p_snap->sw_ver, sizeof(p_snap->sw_ver), FEED_SW_VER);
@@ -706,6 +773,12 @@ void ui_data_feed_tick(void) {
DBG_ERROR("UI yt bind: FAIL");
}
}
if (s_comm_bound == 0u) {
s_comm_bound = 1u; /* 道G task-9:同款一轮一绑;两表时序等价(先于 ui_menu_init) */
if (ui_data_comm_bind(&s_comm_ops) != 0) {
DBG_ERROR("UI comm bind: FAIL");
}
}
now = osKernelGetTickCount();