feat(屏板): 状态灯位映射 sb_led_map + 104 RUN 观测口(批 3);附 4-1 并发压测脚本
批 3(LOGO 按用户裁定出局,不实现、sb_proto 的 LOGO 组帧挂待删标注): - 新增 App/algo/sb_led_map.c/h:业务状态 → 屏板 LED 位掩码的唯一映射表 (纯逻辑,主机单测可跑)。ID1 运行心跳 500ms 半周期闪(与主板 LED 同拍)、 ID2 保护动作、ID3 104 链路 RUN、ID4 采样电平故障(不选 sticky——灯要能灭)、 ID5 保护启动 picked;bit0 与 ID 6~19 恒 0。这张表参考源码里没有,属自定, 出处 docs/产品需求/产品需求设计文档.md §5.16.3 ①。 - iec104_serv 新增 iec104_serv_link_run()(透传 link 状态机,不另存镜像量)。 - ui_task.c 帧组装块每拍现采四路状态源(均为任意上下文可读观测口)折算后 传 sb_link_frame_update——替换批 2 的恒 0 占位;掩码变了帧就重组, 复用 busy/dirty 机制,无新通路。 - 主机单测 test/host/sb_led_map 17/17(位语义/恒零位/闪烁相位/tick 回绕/ NULL 契约);注册豁免走 SIM_EXEMPT(模拟器无屏板串口)。 批 4 的 4-1 双通道并发回归(真屏板在线实测): - 新增 test/host/remolcd_pull_test.py:RemoDispBus 拉帧压测客户端 (INIT→LCDMEM 10 Hz、逐帧 XOR+长度校验、往返延迟统计、--dump PBM)。 - 实测:网络侧两轮 2200 拉帧零错零超时(min 35/avg 63/max 131 ms); 屏板侧 60 s 隔离窗口 rx +233 全收、drop 零新增、bad=0、off=0、无 OFFLINE——两侧互不拖。观察项 len=7(断帧尺寸≠6,累计 7 次)出现窗口 无法归因压测、隔离窗口零新增、有效帧零损失,已挂设计说明 §5.7 待查。 附带:sb_link.h frame_update 注释回填实测耗时(509/688 µs 取代旧估算)。 Keil 0 错误 0 警告;ci_local 全绿(SKIP=nested-comment,主树脚本另查 0 新发); Code=239,064 B / RW_IRAM1 65.5 % / RW_CCM 87.9 %;覆盖率 90.1 % / 44 套件。
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 模块名称:屏板状态灯位映射(sb_led_map)主机侧单元测试
|
||||
* 模块功能:在 PC 上离线验证 App/algo/sb_led_map.c 的位语义、恒零位、心跳闪烁
|
||||
* 相位与时基回绕行为。
|
||||
*
|
||||
* 被测对象:真实源文件 App/algo/sb_led_map.c(#include 原样纳入)。
|
||||
* 本层纯逻辑(仅 stdint.h),**无需任何桩**——与 adc_health 套件同形。
|
||||
*
|
||||
* 为什么值得单测(真机上验的是"灯亮没亮",这里验的是"亮的是不是那一位"):
|
||||
* 1. bit0 恒 0——屏板 ID 0 是硬件常亮电源灯,掩码 bit0 根本不被读;但一旦
|
||||
* 哪天有人往映射里塞 bit0,测试先拦住,而不是到现场发现"多一位没反应"。
|
||||
* 2. ID 6~19 恒 0——映射表之外的位不许被顺带点亮(掩码里出现未定义位 = 表被改
|
||||
* 而测试没跟上,这是这张"自定表"唯一的防漂移手段)。
|
||||
* 3. 心跳相位在 tick 回绕点不得连灭两个 500 ms 段(操作员眼里的"装置停了")。
|
||||
* 4. NULL 输入只跑心跳(心跳证明的是"代码活着",不该被空输入连坐熄灭)。
|
||||
*
|
||||
* 编号:TC-SBLEDMAP-001 起(小写子项 = 同一判据的另一面)。
|
||||
* 创建日期:2026-10-29(批 3 · 3-1)
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sb_led_map.h"
|
||||
|
||||
/* 被测生产源(include_dirs 里有 App/algo) */
|
||||
#include "sb_led_map.c"
|
||||
|
||||
static int s_pass;
|
||||
static int s_fail;
|
||||
|
||||
#define CHECK(cond, name) \
|
||||
do { \
|
||||
if (cond) { \
|
||||
s_pass++; \
|
||||
} else { \
|
||||
s_fail++; \
|
||||
printf("FAIL: %s (line %d)\n", (name), __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* 亮/灭心跳各取一个代表时刻(偶段 = 亮,见 sb_led_map.c) */
|
||||
#define T_ON (0U) /* [0,500) 偶段 → 亮 */
|
||||
#define T_OFF (500U) /* [500,1000) 奇段 → 灭 */
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-001 全零输入:只有心跳位,其余状态位全灭
|
||||
*/
|
||||
static void tc_zero_input(void)
|
||||
{
|
||||
sb_led_in_t in;
|
||||
uint32_t m;
|
||||
|
||||
in.prot_active = 0U;
|
||||
in.prot_pick = 0U;
|
||||
in.adc_flags = 0U;
|
||||
in.iec104_run = 0U;
|
||||
|
||||
m = sb_led_map_eval(&in, T_ON);
|
||||
CHECK(m == SB_LED_BIT_RUN, "TC-SBLEDMAP-001 zero input lights only the run blink (on phase)");
|
||||
|
||||
m = sb_led_map_eval(&in, T_OFF);
|
||||
CHECK(m == 0U, "TC-SBLEDMAP-001b zero input in the dark half-cycle is all-zero");
|
||||
}
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-002 每一路状态各自点亮且只点亮对应位(非零即真,不看具体值)
|
||||
*/
|
||||
static void tc_one_source_each(void)
|
||||
{
|
||||
sb_led_in_t in;
|
||||
uint32_t m;
|
||||
|
||||
in.prot_active = 0U;
|
||||
in.prot_pick = 0U;
|
||||
in.adc_flags = 0U;
|
||||
in.iec104_run = 0U;
|
||||
|
||||
in.prot_active = 0x00080000U; /* 随便哪个元件动作 */
|
||||
m = sb_led_map_eval(&in, T_OFF);
|
||||
CHECK(m == SB_LED_BIT_ALARM, "TC-SBLEDMAP-002 prot_active -> ID 2 only");
|
||||
in.prot_active = 0U;
|
||||
|
||||
in.prot_pick = 1U;
|
||||
m = sb_led_map_eval(&in, T_OFF);
|
||||
CHECK(m == SB_LED_BIT_PICKUP, "TC-SBLEDMAP-002b prot_pick -> ID 5 only");
|
||||
in.prot_pick = 0U;
|
||||
|
||||
in.adc_flags = 0x80000000U; /* 高位故障位同样算非零 */
|
||||
m = sb_led_map_eval(&in, T_OFF);
|
||||
CHECK(m == SB_LED_BIT_ADC_FAULT, "TC-SBLEDMAP-002c adc_flags -> ID 4 only");
|
||||
in.adc_flags = 0U;
|
||||
|
||||
in.iec104_run = 1U;
|
||||
m = sb_led_map_eval(&in, T_OFF);
|
||||
CHECK(m == SB_LED_BIT_COMM, "TC-SBLEDMAP-002d iec104_run -> ID 3 only");
|
||||
in.iec104_run = 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-003 多路同时:位可叠加、互不遮蔽(告警+通信同时真是合法工况)
|
||||
*/
|
||||
static void tc_stack(void)
|
||||
{
|
||||
sb_led_in_t in;
|
||||
uint32_t m;
|
||||
|
||||
in.prot_active = 3U;
|
||||
in.prot_pick = 3U;
|
||||
in.adc_flags = 1U;
|
||||
in.iec104_run = 1U;
|
||||
|
||||
m = sb_led_map_eval(&in, T_ON);
|
||||
CHECK(m == (SB_LED_BITS_DEFINED), "TC-SBLEDMAP-003 all sources + blink = exactly the defined set");
|
||||
|
||||
m = sb_led_map_eval(&in, T_OFF);
|
||||
CHECK(m == (SB_LED_BITS_DEFINED & ~SB_LED_BIT_RUN),
|
||||
"TC-SBLEDMAP-003b dark half-cycle drops only the run bit");
|
||||
}
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-004 恒零位:bit0 与 ID 6~19 在任何输入下不得出现
|
||||
*(bit0 屏板根本不读;ID 6~19 是保留位——掩码出现表外位 = 表被改而没走测试)
|
||||
*/
|
||||
static void tc_reserved_bits(void)
|
||||
{
|
||||
sb_led_in_t in;
|
||||
uint32_t t;
|
||||
int clean = 1;
|
||||
|
||||
in.prot_active = 0xFFFFFFFFU;
|
||||
in.prot_pick = 0xFFFFFFFFU;
|
||||
in.adc_flags = 0xFFFFFFFFU;
|
||||
in.iec104_run = 1U;
|
||||
|
||||
/* 扫 3 秒时基(6 个半周期)+ 边界时刻,全取定义集做差 */
|
||||
for (t = 0U; t < 3000U; t += 499U) {
|
||||
uint32_t m = sb_led_map_eval(&in, t);
|
||||
if ((m & ~SB_LED_BITS_DEFINED) != 0U) {
|
||||
clean = 0;
|
||||
}
|
||||
}
|
||||
CHECK(clean == 1, "TC-SBLEDMAP-004 no bit outside the defined set ever appears");
|
||||
CHECK((sb_led_map_eval(&in, T_ON) & 1U) == 0U, "TC-SBLEDMAP-004b bit0 stays 0 (screen board ignores it)");
|
||||
}
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-005 心跳相位:连续 8 个半周期亮灭交替,边界含等号(500 整 = 暗段起点)
|
||||
*/
|
||||
static void tc_blink_sequence(void)
|
||||
{
|
||||
int i;
|
||||
int prev = -1;
|
||||
int alt = 1;
|
||||
uint32_t m;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
uint32_t t = (uint32_t)i * SB_LED_BLINK_HALF_MS;
|
||||
int on;
|
||||
|
||||
m = sb_led_map_eval(NULL, t);
|
||||
on = ((m & SB_LED_BIT_RUN) != 0U) ? 1 : 0;
|
||||
if ((prev >= 0) && (on == prev)) {
|
||||
alt = 0;
|
||||
}
|
||||
prev = on;
|
||||
}
|
||||
CHECK(alt == 1, "TC-SBLEDMAP-005 run bit alternates every half-period");
|
||||
CHECK((sb_led_map_eval(NULL, 0U) & SB_LED_BIT_RUN) != 0U,
|
||||
"TC-SBLEDMAP-005b power-on first half-period is LIT (blink visible from t=0)");
|
||||
CHECK((sb_led_map_eval(NULL, SB_LED_BLINK_HALF_MS) & SB_LED_BIT_RUN) == 0U,
|
||||
"TC-SBLEDMAP-005c the boundary instant belongs to the dark half-period");
|
||||
CHECK((sb_led_map_eval(NULL, SB_LED_BLINK_HALF_MS - 1U) & SB_LED_BIT_RUN) != 0U,
|
||||
"TC-SBLEDMAP-005d last ms before the boundary still lit");
|
||||
}
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-006 tick 回绕:跨 0xFFFFFFFF 连续熄灭时长不得超过一个半周期
|
||||
*(相位由除法直接得出、不依赖"上次"状态。回绕把末段截短、下一段从"偶段=亮"
|
||||
* 重新开始——最坏也只是某段被截短,不会出现操作员眼里的"连灭 1 s = 装置停了"。
|
||||
* 1 ms 步进扫回绕前后各 1.5 个周期,量最长暗段。)
|
||||
*/
|
||||
static void tc_wraparound(void)
|
||||
{
|
||||
uint32_t t;
|
||||
int run = 0; /* 当前连续暗 ms 数 */
|
||||
int worst = 0;
|
||||
|
||||
/* now 从 0xFFFFFFFF-1500 走到 0xFFFFFFFF 再接 0..1500(模 2^32 连续) */
|
||||
for (t = 0xFFFFFFFEU - 1500U; t != 0xFFFFFFFFU; t++) {
|
||||
int dark = ((sb_led_map_eval(NULL, t) & SB_LED_BIT_RUN) == 0U) ? 1 : 0;
|
||||
|
||||
run = (dark != 0) ? (run + 1) : 0;
|
||||
if (run > worst) {
|
||||
worst = run;
|
||||
}
|
||||
}
|
||||
for (t = 0U; t <= 1500U; t++) {
|
||||
int dark = ((sb_led_map_eval(NULL, t) & SB_LED_BIT_RUN) == 0U) ? 1 : 0;
|
||||
|
||||
run = (dark != 0) ? (run + 1) : 0;
|
||||
if (run > worst) {
|
||||
worst = run;
|
||||
}
|
||||
}
|
||||
/* 半周期 500 ms + 1 ms 扫描步进余量 */
|
||||
CHECK(worst <= (int)SB_LED_BLINK_HALF_MS + 1,
|
||||
"TC-SBLEDMAP-006 dark run never exceeds one half-period across tick wraparound");
|
||||
}
|
||||
|
||||
/*
|
||||
* TC-SBLEDMAP-007 NULL 输入 = 全零输入 + 心跳照常(注释契约:心跳不被连坐)
|
||||
*/
|
||||
static void tc_null_input(void)
|
||||
{
|
||||
CHECK(sb_led_map_eval(NULL, T_ON) == SB_LED_BIT_RUN,
|
||||
"TC-SBLEDMAP-007 NULL input still blinks (liveness signal must not depend on inputs)");
|
||||
CHECK(sb_led_map_eval(NULL, T_OFF) == 0U,
|
||||
"TC-SBLEDMAP-007b NULL input lights nothing else");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("-- sb_led_map unit tests --\n");
|
||||
tc_zero_input();
|
||||
tc_one_source_each();
|
||||
tc_stack();
|
||||
tc_reserved_bits();
|
||||
tc_blink_sequence();
|
||||
tc_wraparound();
|
||||
tc_null_input();
|
||||
|
||||
printf("=== SB LED Map Tests: %d/%d PASSED ===\n", s_pass, s_pass + s_fail);
|
||||
return (s_fail == 0) ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user