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
+48 -4
View File
@@ -40,6 +40,13 @@ static const iec104_comm_t s_comm_rom = {
IEC104_COMM_DEF_CA, IEC104_COMM_DEF_PORT
};
/* 落盘脏位(B7 形态,run_par/prot_set 同款):setter 成功置 1,
* iec104_comm_store_save() 写盘成功清 0。**不挂回调、不加事件位**(task-8
* D5 裁定的持久化面延续):defaultTask 每轮无条件调 store_save,无脏空转。
* 单字节标志:uiTask 写、defaultTask 读清——置 1 幂等、清零窗口最坏
* 多跑一轮 save,语义无损(run_par s_dirty 同一条竞态分析)。 */
static volatile uint8_t s_comm_dirty;
/* 键表(s_comm 布局内偏移,同 net_cfg/ftp_cfg 写法) */
static const cfg_key_def_t s_comm_keys[] = {
{ "ca", CFG_VAL_UINT, offsetof(iec104_comm_t, ca), 2u },
@@ -57,6 +64,7 @@ int iec104_comm_init(void) {
cfg_section_reg_t reg;
(void)memcpy(&s_comm, &s_comm_rom, sizeof(s_comm));
s_comm_dirty = 0u;
(void)memset(&reg, 0, sizeof(reg));
reg.name = IEC104_COMM_SEC;
@@ -100,27 +108,63 @@ uint16_t iec104_comm_port(void) {
/*
* 函数功能:写 ca 进 RAM 副本(守卫 = getter 同带;非法不提交)
* 入口参数:ca - 公共地址 uint16_t
* 返回值:0 提交;-1 带外(副本不动) int
* 限定条件:UI 保存交接路(task-9);落盘走 SYS_EVT_CFG_SAVE 整档外科 save
* 返回值:0 提交并置落盘脏;-1 带外(副本不动、不置脏) int
* 限定条件:UI 保存交接路(通讯设置页经 ui_data 通道六转进来)与宿主测试;
* 落盘 = defaultTask 每轮无条件 iec104_comm_store_save()(无事件位)
*/
int iec104_comm_ca_set(uint16_t ca) {
if ((ca < IEC104_COMM_CA_MIN) || (ca > IEC104_COMM_CA_MAX)) {
return -1;
}
s_comm.ca = ca;
s_comm_dirty = 1u;
return 0;
}
/*
* 函数功能:写 port 进 RAM 副本(守卫 = getter 同带;非法不提交)
* 入口参数:port - 监听端口 uint16_t
* 返回值:0 提交;-1 带外(副本不动) int
* 限定条件:同 iec104_comm_ca_set
* 返回值:0 提交并置落盘脏;-1 带外(副本不动、不置脏) int
* 限定条件:同 iec104_comm_ca_set(生效时刻 = 下一次重 bind 监听)
*/
int iec104_comm_port_set(uint16_t port) {
if ((port < IEC104_COMM_PORT_MIN) || (port > IEC104_COMM_PORT_MAX)) {
return -1;
}
s_comm.port = port;
s_comm_dirty = 1u;
return 0;
}
/*
* 函数功能:落盘脏探查(非消费)——页面保存行 WAIT/OK 的唯一判定源
* 返回值:1 有未落盘改动;0 干净 uint8_t
* 限定条件:无(任意上下文纯读)
* 函数说明:脏位是全段级(ca+port 一次 save 一起落):归零只证明
* 「这一档落盘了」,不证明「我这一项单独落盘」;104 侧无自动
* 写者(ca/port 不经 C_SE 整定),比 run_par 少一条「他人再改」
* 干扰,页面仍按「结果锁存」画(run_par 页同口径,别做两套)
*/
uint8_t iec104_comm_dirty_peek(void) {
return s_comm_dirty;
}
/*
* 函数功能:执行落盘(整档外科 save 成功后清脏;失败脏保持 = 下轮重试)
* 返回值:0 成功/无脏;-1 写盘失败 int
* 限定条件:**defaultTask(配置引擎属主)上下文**——run_par_store_save()
* 同一调用点旁挂,每轮无条件
* 函数说明:失败不打日志是刻意的(App/protocol 零日志惯例,本文件无 DBG):
* cfg_file_save 自身已有 DBG_ERROR 留痕 + 脏位保留 = 屏上 ERR 的
* 判定源,再打一条就是重复锚点(AGENTS 日志成本口径)
*/
int iec104_comm_store_save(void) {
if (s_comm_dirty == 0u) {
return 0;
}
if (cfg_file_save() != 0) {
return -1;
}
s_comm_dirty = 0u;
return 0;
}
+30 -7
View File
@@ -7,12 +7,15 @@
* 判定;初版曾寄身 iec104_pts.c 尾段,2026-10-26 lead 纠偏迁出)。
* 修改记录:
* 2026-10-26 DTU 项目组 创建(道G task-8);同日实现位置改配对拆分。
* 2026-10-27 DTU 项目组 道G task-9:脏位 + iec104_comm_dirty_peek() /
* store_save()(run_par B7 同款,通讯设置页与
* defaultTask 轮询的消费面)。
* 线程约定:注册/复位 = app_main_init 内核前(run_par_init / adc_cal_init 同款
* "先注册后 load"契约);load/save/poll = defaultTask(配置引擎属主,
* netTask/uiTask 不得直接触碰引擎);getter = 任意消费上下文(u16
* 半字对齐读天然原子,重载瞬间最坏 = 单字段新旧值之一);setter =
* UI 保存交接路(task-9,net_cfg_set_* 先例)。
* 生效语义:**改后重启生效**(docs/任务规划/UI补齐.md §2 第 5 行验收口径)——
* 生效语义:**改后重启生效**(通讯设置页验收口径,docs/任务规划/UI补齐.md §2)——
* iec104Task 启动时取一次 getter 填 link cfg;端口另在下次重 bind
* (拆链/网络代次重建)自然生效,不必整机重启。不挂段回调、不新增
* 事件位、不走 SYS_EVT_NET_RECONFIG(网络代次链语义不同;且 CH395F
@@ -34,9 +37,10 @@ extern "C" {
/* 段名(cfg_file 引擎注册用;≤ CFG_FILE_MAX_NAME_LEN) */
#define IEC104_COMM_SEC "comm"
/* 工厂默认:CA = 1(点表口径,V0.1 沿用);端口 = 2404(DL/T 634.5104 标准,
* 与 App/task/iec104_serv.h 的 IEC104_SERV_PORT 同值——该宏现仅作
* ui_data_feed 快照兼容出口保留,运行期真源 = 本段 getter) */
/* 工厂默认:CA = 1(点表口径,V0.1 沿用);端口 = 2404(DL/T 634.5104 标准)。
* 原同名宏 IEC104_SERV_PORT(App/task/iec104_serv.h)已随道G task-9 快照切
* getter 删除——本段 getter 自 task-8 起就是运行期唯一真源,本头文件是这两个
* 常量的唯一出口 */
#define IEC104_COMM_DEF_CA 1u
#define IEC104_COMM_DEF_PORT 2404u
@@ -79,8 +83,9 @@ uint16_t iec104_comm_port(void);
* 函数功能:写 ca 到 RAM 副本(守卫 = getter 合法带;非法值不提交)
* 入口参数:ca - 公共地址 1..65535 uint16_t
* 返回值:0 - 提交;-1 - 带外(副本不动) int
* 限定条件:调用方 = UI 保存交接路(task-9);落盘经 SYS_EVT_CFG_SAVE
* 整档外科 save(net_cfg_save_request 同款交接,本模块不写盘)
* 限定条件:调用方 = 通讯设置页(ui_data 通道六注入转 uiTask 上下文);
* 成功置落盘脏——写盘 = defaultTask 每轮无条件
* iec104_comm_store_save()(run_par B7 同款,无事件位、不写盘于本模块)
*/
int iec104_comm_ca_set(uint16_t ca);
@@ -88,10 +93,28 @@ int iec104_comm_ca_set(uint16_t ca);
* 函数功能:写 port 到 RAM 副本(守卫 = getter 合法带;非法值不提交)
* 入口参数:port - 监听端口 1024..65535 uint16_t
* 返回值:0 - 提交;-1 - 带外(副本不动) int
* 限定条件:同 iec104_comm_ca_set
* 限定条件:同 iec104_comm_ca_set(生效时刻 = 下一次重 bind 监听,改后不必重启)
*/
int iec104_comm_port_set(uint16_t port);
/*
* 函数功能:落盘脏探查(非消费)——页面保存行 ENTER/WAIT/OK 判定源
* 返回值:1 - 有已改未落盘;0 - 干净 uint8_t
* 限定条件:无(任意上下文纯读)
* 函数说明:脏位段级(ca+port 同档共落);页面按「结果锁存」画,与 run_par
* 页同一条纪律(ui_w_ytview 文件头)
*/
uint8_t iec104_comm_dirty_peek(void);
/*
* 函数功能:执行落盘(整档外科 save 成功清脏;失败脏保持下轮重试)
* 返回值:0 - 成功/无脏;-1 - 写盘失败(屏上 ERR 的判定源) int
* 限定条件:**defaultTask(配置引擎属主)上下文**;由 default_task.c 的
* run_par_store_save() 同点位每轮无条件调用(本模块不挂回调、
* 不设事件位——task-8 D5 口径的持久化面)
*/
int iec104_comm_store_save(void);
#ifdef __cplusplus
}
#endif