Files
STM32F4-Base/App/algo/run_par.h
T
wandering1 728d60675f 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,交台架道补。
2026-10-27 11:29:23 +08:00

200 lines
13 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.
/*
* 模块名称:运行参数段(L2/L3 整定层,「数据可信」卡 10)
* 模块功能:遥测死区(9 点)与通道标定系数覆盖(k/b 各 8 路)的**单一真源**:
* - 死区 = 本模块 int32 毫单位 RAM 副本(注册进 cfg 引擎 [run_par] 段,
* 工厂默认 = 「数据可信」卡 6 额定 0.5 % 档),104 消费侧
* (App/protocol/iec104/iec104_pts.c)每拍实时读取——无投递、无缓存
* 副本(卡 9 曾在 pts 侧留运行时副本 s_yc_db,卡 10 落地后收编于此,
* 双源必打架);
* - 系数覆盖 = **直通 adc_cal_set()**([adc_cal]/[adc_cal_ofs] 是系数
* 唯一真源,卡 4 已定;本模块只加 104 地址映射 + 落盘请求,不另存
* 一份系数——同 D1 裁"两处判断必打架"逻辑)。
* 104 地址映射(docs/算法/继电保护功能复现规划.md §5.1 运行参数类
* 0x8020H~0x8058H;逐项表原文"待补"(参考项目 CommPara_ConfigTable
* 不在仓),本表即卡 10 的落实,送检点表若另给权威地址只改常量段:
* 0x8030~0x8038 = 死区 UA/UB/UC/UN/IA/IB/IC/IN/FREQ(工程量 V/A/Hz);
* 0x8040~0x8047 = k 覆盖(µV/码);0x8048~0x804F = b 覆盖(码值,可负);
* 其余留白(未启用类别),**0x8027~0x802A 属 prot_set 告警段**,本表
* 永不让位(两表按精确地址分流,见 iec104_pts.c set 路由)。
* 持久化(B7 prot_set 先例同款):任一写成功 → 脏标志只置不清;
* iec104Task 主循环 peek → 置 SYS_EVT_RUN_PAR_SAVE → defaultTask
* (配置引擎属主)run_par_store_save() 调 cfg_file_save() 整档外科
* 保存(含 [run_par] 与刚被 adc_cal_set 改写的 [adc_cal]/[adc_cal_ofs]
* 副本)→ 成功清脏;失败留脏 = 下轮天然重试。本模块自身零文件 IO
* (属主纪律)。
* ⚠ 三份职责别混(道G task-43 第③项,此前只有意图式陈述):
* - **谁写 RAM**:系数/零点的唯一真源是 adc_cal 的引擎副本,而本模块那两支
* 语义 setter(run_par_cal_k_set / run_par_cal_b_set,task-7 落地)是**唯一
* "写值 + 置脏"成对发生**的入口;任何消费者绕过它们直调 adc_cal_set(),
* 值只进 RAM、脏标志不动 ⇒ 屏上"当前生效"是新的、盘上仍是旧的、重启即丢,
* 且该路径**没有错误返回**。这条形态现在可观测:adc_cal 侧
* set_ok 与落盘水位的差额 > 0,而 run_par_dirty_peek() 恒为 0。
* - **谁落盘 / 什么时机**:只有 defaultTask 的 run_par_store_save()(由
* iec104Task peek 后置的 SYS_EVT_RUN_PAR_SAVE 驱动);本模块零文件 IO。
* - **cfg_file_poll() 不是保存路径**:它的方向是**盘 → RAM 的重载**(外部
* 改档 → 沉降去抖 → 重载),拿它"兜住"未落盘的改值会把 FTP 侧的旧档倒灌
* 回 RAM,比不兜更坏。设计依据见 docs/算法/采集与算法层设计说明.md §10.1。
* 并发契约:引擎副本 int32 对齐字读写在 Cortex-M4 上原子;poll 重载 µs 级窗口
* 为「数据可信」§4.4 明载设计(adc_cal 同款)。写路径 = 104 执行段
* (iec104Task)与引擎读路径互不加锁,勿改成"读侧缓存"(缓存 = 新真源)。
* 纯算法:只用 stdint/stddef/math + cfg_file.h/adc_cal.h 头(注册/直通均为
* 接口级)→ 主机侧可单测(test/host/run_par,链真 cfg 引擎 + 真 adc_cal)。
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
* 作者:DTU 项目组
* 创建日期:2026-10-25(道D 卡 10)
* 修改记录:
* v1.0 2026-10-25 DTU 项目组 创建:[run_par] 段 + 104 地址映射 + 直通
* adc_cal + B7 式脏标志落盘请求
* v1.1 2026-10-27 DTU 项目组 道F task-7(遥测整定两页)配套:新增语义
* setter run_par_cal_k_set / run_par_cal_b_set
* (写值 + 置脏成对;104 的 k/b 两支同批改调它,
* 消除两份置脏逻辑)+ 合法域唯一出口
* run_par_db_range() + 系数粗守上限
* RUN_PAR_K_MAX_UV;毫→工程换算收进模块内单点
* s_milli_to_eng()(顺带把 task-43 第①项所需的
* "写值必置脏"这一半满足掉)
* v1.2 2026-10-27 DTU 项目组 道G task-43 第②③项:run_par_store_save() 成功路径
* 给标定层盖一次落盘水位(纯观测回执,不参与判定);
* 本头把"谁写 RAM / 谁落盘 / 什么时机 / poll 不是保存
* 路径"从意图式陈述改成逐条契约
*/
#ifndef RUN_PAR_H
#define RUN_PAR_H
#include <stdint.h>
#include "adc_cal.h" /* adc_semantic_t:公开语义 setter 的形参类型(同层 algo→algo,方向不逆)*/
#ifdef __cplusplus
extern "C" {
#endif
/* 死区/覆盖点数(死区 = YC 表行数:电气 8 + 频率;覆盖 = 语义电气 8 路) */
#define RUN_PAR_DB_N 9u
#define RUN_PAR_CH_N 8u
/* 系数 k 的**粗守**上限(µV/码):只为挡误写(如把 mV 当 µV 填),不是工况域;
* 真合法带在 adc_cal 执行段裁(µV/码 ×[0.5,2.0]),两层各说各的、互不掩盖。
* 公开它是因为 UI 整定页要给编辑器设上限——上限在这里,就不许页面再抄一份。*/
#define RUN_PAR_K_MAX_UV 1.0e9f
/*
* 函数功能:运行参数初始化——副本置工厂默认 + 注册 [run_par] 段进 cfg 引擎
* 入口参数:无
* 返 回 值:无
* 限定条件:**内核启动前**调用(app_main_init,adc_cal_init 之后);注册必须先
* 于 defaultTask 的 cfg_file_load(FTP 鉴权确立的契约,同 adc_cal 先例);幂等
* 函数说明:注册失败(段名重复/段数超限)打 [ERR] 后继续——工厂默认照常生效,
* 仅文件整定失效(adc_cal_init 同款降级)。
*/
void run_par_init(void);
/*
* 函数功能:读第 idx 路遥测死区(工程量)
* 入口参数:idx - 0..7 = 语义通道序(UA..IN),8 = 频率 uint8_t
* 返 回 值:死区 V / A / Hz;idx 越界返 0.0(调用方按"无死区"保守处理) float
* 限定条件:任意上下文(int32 单字读原子;104 COV 每拍调用)
* 函数说明:毫单位 → 浮点的换算在本模块内也只有一处(run_par.c 的
* s_milli_to_eng(),run_par_deadband 与 run_par_db_range 共用);
* 消费侧一律取本模块给的工程量,不要再各自除 1000。
*/
float run_par_deadband(uint8_t idx);
/*
* 函数功能:写第 idx 路死区(工程量,含范围守卫 + 脏置位)
* 入口参数:idx - 同上 uint8_t;db - 死区工程量(V/A/Hz,≥0) float
* 返 回 值:0 成功;-1 越界/越程/NaN(副本不动) int
* 限定条件:写者两门——**iec104Task**(104 C_SE 执行段)与 **uiTask**(遥测死区页
* 经 ui_data 通道五注入转达,道F task-7 起;ui_data.h 通道五表注:合法域
* 与"写值+置脏"必须原子发生在执行侧)。两门都只写 RAM 副本 + 置脏,
* 落盘统一收敛在 defaultTask(勿在 adcTask 调用;此句为 2026-10-27 措辞
* 收口——原写"UI 若整定需另行走属主路径"是 task-2 期遗留,task-7 已接线)
* 函数说明:合法域按点型分档——电压 0~50 V、电流 0~1 A、频率 0~5 Hz(上限 =
* 额定 × 二档裕量,守卫目的是挡误写不是限工况;见 .c 常量段)。
* 成功即置脏(落盘请求,见文件头持久化契约)。
*/
int run_par_deadband_set(uint8_t idx, float db);
/*
* 函数功能:104 运行参数地址读(C_RD 路由,先于 prot_set 扫描)
* 入口参数:addr - 通讯地址(0x8020~0x8058 段内) uint32_t;out - 工程量浮点
* (死区 V/A/Hz;k 覆盖 µV/码;b 覆盖 码值) float *
* 返 回 值:0 命中并写出;-1 地址不归本段(调用方继续走 prot_set) int
* 限定条件:iec104Task 上下文
* 函数说明:命中集 = 0x8030~0x8038 / 0x8040~0x8047 / 0x8048~0x804F 精确匹配;
* k/b 读回 = adc_cal **当前生效值**(与折算同视图:文件坏值被合法带
* 哨兵回退 ROM 时,回显的就是 ROM——主站看到的即下游实际吃到的)。
*/
int run_par_104_read(uint32_t addr, float *out);
/*
* 函数功能:104 运行参数地址写(C_SE 两段式路由,先于 prot_set 扫描)
* 入口参数:addr - 通讯地址 uint32_t;v - 工程量浮点 float;
* select - 1 = 选择段(只校验不落副本)/ 0 = 执行段 uint8_t
* 返 回 值:0 接受;-1 拒绝(越程/NaN/合法带外/非本段可写地址命中守卫);
* -2 地址不归本段(调用方继续走 prot_set) int
* 限定条件:iec104Task 上下文;执行段成功即置脏
* 函数说明:k/b 覆盖经 adc_cal_set() 合法带哨兵(µV/码 ×[0.5,2.0]、|b|≤8192,
* 越带 -1 → 104 侧 PN 否定确认,adc_cal 不留痕由**其自身**越带 [ERR]
* 机制负责——本模块直通不重复留痕);两段式与 prot_set 定值写同构。
*/
int run_par_104_write(uint32_t addr, float v, uint8_t select);
/*
* 函数功能:写某路系数 k(µV/码)**并在同一函数内置脏**——整定的语义写入口
* 入口参数:sem - 电气语义通道(adc_semantic_t 枚举序) adc_semantic_t;
* k_uv - 系数(µV/码,须 >0 且 <= RUN_PAR_K_MAX_UV) float
* 返回值:0 - RAM 副本已写且已置脏;-1 - 粗守或执行段拒(副本未动、不置脏) int
* 限定条件:整定路径上下文(iec104Task 的 C_SE 执行段;UI 侧由装配层
* App/app/ui_data_feed.c 经 ui_data_yt_ops_t 转进来)。勿在 adcTask 调用
* 函数说明:**写值与置脏成对,不许拆开**(lead 裁②):拆开就等于"屏上已改、
* 盘上永不落、且没有任何错误返回"。因此本模块**不提供**公开的
* mark_dirty——单独调它等于空承诺;零点 b 由本函数 RMW 取当前生效值。
*/
int run_par_cal_k_set(adc_semantic_t sem, float k_uv);
/*
* 函数功能:写某路零点偏移 b(码值)并置脏(与 k 同款语义,粗守 ±1e6 码)
* 入口参数:sem - 电气语义通道 adc_semantic_t;b_codes - 零点(码值) int32_t
* 返回值:0 - 已写并置脏;-1 - 粗守或执行段拒(副本未动、不置脏) int
* 限定条件:同 run_par_cal_k_set()
* 函数说明:k 侧 RMW 取当前生效值;±8192 真带由 adc_cal 执行段裁。消费者 =
* 104 的 0x8048~ 分支(改造后走这里),不是为将来预留的口
*/
int run_par_cal_b_set(adc_semantic_t sem, int32_t b_codes);
/*
* 函数功能:取第 idx 路死区的合法域(工程量)——**合法域的唯一出口**
* 入口参数:idx - 0..8 uint8_t;lo/hi - 输出下限/上限(V/A/Hz) float *
* 返回值:0 - 已写出;-1 - idx 越界或指针空(调用方不得回退成自造默认域) int
* 限定条件:任意上下文(纯常量读)
* 函数说明:存在的唯一理由:UI 整定页要给编辑器设 min/max,而这些上限的真源是
* 本模块的 RUN_PAR_DB_MAX_*;页面抄一份就是第二真源。给的是**粗守域**,
* 真提交仍由与 104 选择段同源的守卫裁,两条路不会漂移到两套数字。
*/
int run_par_db_range(uint8_t idx, float *lo, float *hi);
/*
* 函数功能:落盘脏标志探查(非消费)
* 入口参数:无
* 返 回 值:1 = 有已整定未落盘的改动;0 = 干净 uint8_t
* 限定条件:任意上下文(单字节);iec104_serv 主循环 peek 后投递事件(B7 同款)
*/
uint8_t run_par_dirty_peek(void);
/*
* 函数功能:执行落盘请求(cfg_file_save 整档外科保存 + 成功清脏)
* 入口参数:无
* 返 回 值:0 成功(或无脏可存);-1 写盘失败(脏保持,下轮重试) int
* 限定条件:**defaultTask(配置引擎属主)上下文**,由 SYS_EVT_RUN_PAR_SAVE 驱动
* 函数说明:save 走引擎唯一写闸(超长拒写/原子改名);本函数不清
* 引擎以外模块的脏语义([adc_cal] 副本值已由写路径就位)。
*/
int run_par_store_save(void);
#ifdef __cplusplus
}
#endif
#endif /* RUN_PAR_H */