diff --git a/.cubemx_survival_baseline.json b/.cubemx_survival_baseline.json
index bea7223d..fa05f149 100644
--- a/.cubemx_survival_baseline.json
+++ b/.cubemx_survival_baseline.json
@@ -5,6 +5,7 @@
"fault-handlers-3": "f3afab8d2be18794",
"fault-handler-enable": "be87fcd3a7926138",
"hardfault-report-impl": "f3afab8d2be18794",
+ "error-handler-degrade": "d02a92afda945c4b",
"stack-overflow-check": "d4735e3a265e16ee",
"malloc-failed-hook": "6b86b273ff34fce1",
"assert-not-deadloop": "d4a00718ab2bd071",
@@ -12,14 +13,21 @@
"stack-overflow-hook-impl": "a006edbd30d2c4d7",
"malloc-failed-hook-impl": "fcd7250e5cd6403b",
"stack-overflow-hook-helper": "cacd9fd028be9b5b",
- "watchdog-module": "3d908285b092c402",
+ "watchdog-module": "663f05caf22cf7d0",
"reset-cause-latch": "52fc71acbb0f1aed",
"iwdg-hal-driver": "4b902875a84554e9",
"hal-iwdg-enabled": "1a8ed6cff34f7ba6",
"ioc-keepusercode": "b5bea41b6c623f7c",
"ioc-iwdg-present": "d3a6e667048c95db",
"ioc-task-stacks": "3262f737d92dcbee",
- "adc1-hal-enabled": "1a8ed6cff34f7ba6"
+ "adc1-hal-enabled": "1a8ed6cff34f7ba6",
+ "adc1-calibration-call": "8d5f4b40fb218913",
+ "adc1-calibration-impl": "d33bc4323e243248",
+ "adc-tick-tim3-branch": "39c3548c37f6c4ae",
+ "ioc-adc-trig-t3trgo": "a9939de260ee46f8",
+ "ioc-adc-dma-continuous": "189126670d85d80a",
+ "adc1-dma-link": "397fce92dca3b728",
+ "adc1-dma-irq-priority": "beff08e91333229b"
},
"_note": "CubeMX 存活审计基线;改动用 --write 重建"
}
diff --git a/.dsh/skills/keil-build/SKILL.md b/.dsh/skills/keil-build/SKILL.md
index 048386a7..a84e07b2 100644
--- a/.dsh/skills/keil-build/SKILL.md
+++ b/.dsh/skills/keil-build/SKILL.md
@@ -35,9 +35,11 @@ whenToUse: 需要编译 Keil MDK 工程、验证代码改动是否 0 错误 0
硬件闭环只会把编译错误伪装成运行问题。
- **警告也算失败**:判 PASS 只认退出码 0,别只看"有没有 error:"。
- **`MDK-ARM/*.uvprojx` 每次构建后被 UV4 自动改写,改动一律不入库**:UV4 会把工程
- XML 归一化成本机风格(`` → `` 等)并把 `PackID` 降到本机 DFP 版本。
+ XML 归一化成本机风格(`` → ``、XML 声明加 `standalone="no"`)并把
+ `PackID` 降到**本机装的 DFP 版本**(属降级——提交后会把别人的 Pack 版本一起改掉)。
判断:`git diff` 里只见 XML 格式/`PackID` 差异而**无源文件挂载增删** →
`git checkout -- ` 丢弃;只有真的增删了源文件(grep 文件名计数变化)才提交。
+ 同目录的 `*.uvoptx` / `*.uvguix.*` 是 IDE 个人设置,已在 `.gitignore` 里,不会露出来。
- 报错定位读日志别只看终端回显:本仓库 = `MDK-ARM\build_log.txt`。
- **exit 2 先分清"真没设"还是"会话没继承"**(2026-10-10 实测):agent 的 shell 由
常驻服务派生,服务启动**之后**设置的用户级环境变量不会自动进来。查
diff --git a/.dsh/skills/lvgl-simulator/SKILL.md b/.dsh/skills/lvgl-simulator/SKILL.md
new file mode 100644
index 00000000..f4cb2e9b
--- /dev/null
+++ b/.dsh/skills/lvgl-simulator/SKILL.md
@@ -0,0 +1,52 @@
+---
+name: lvgl-simulator
+description: LVGL PC 模拟器构建/运行/自检入口:build_sim.bat 自动探测本机 VS、lvgl_sim.exe 无头截图与自检开关(--screenshot/--keys/--standby/--cfgtest/--logtest/--idle)、三套回归脚本、两份 lv_conf.h 同步门禁、构建报错速查(SDL2/lv_conf 找不到、全黑、键盘无响应)。
+whenToUse: 需要构建或运行模拟器、无头截图出图、跑模拟器自检或 UI 回归、修改 simulator/ 目录代码或 simulator/lv_conf.h、排查模拟器构建/运行异常时。UI 改动后的完整回归走 ui-verify skill。
+---
+
+# LVGL PC 模拟器(simulator/)
+
+PC 上编译运行与 MCU **同一份** UI 源码(CMake + MSVC + SDL2,x64,160×160 I1 单色),免烧录。
+**目录架构 / 完整坑与 FAQ → `simulator/README.md`;首次部署(SDL2 SDK)→ `docs/UI设计/LVGL模拟器部署与使用说明.md`。本 skill 只放操作入口。**
+
+## 构建与运行(仓库根执行)
+
+```powershell
+& "simulator\build_sim.bat" # 增量构建;vswhere 自动探测本机 VS(16/17/18),勿写死生成器
+& "simulator\build_sim.bat" -Clean # 全量重建(遇到奇怪问题先加它)
+.\simulator\build\Debug\lvgl_sim.exe # 窗口模式:↑↓←→ / Enter / Esc(纯键盘,无鼠标)
+```
+
+⚠ 窗口模式默认 **60 s 空闲自动退出**(防挂后台),`--idle <秒>` 覆盖。
+
+## 无头自检(退出码 0=PASS / 非 0=FAIL)
+
+| 命令(在 `simulator\build\Debug\` 下或用全路径) | 用途 |
+|---|---|
+| `lvgl_sim.exe --screenshot shot.bin` | 离屏渲染首帧 → `simulator/i1_to_png.py` 转 PNG 目检 |
+| `lvgl_sim.exe --keys "ud" shot.bin` | 注入键序后截图:`u`=↑ `d`=↓ `l`=← `r`=→ `e`=Enter `s`=Esc(`main.c` 实锤) |
+| `lvgl_sim.exe --standby shot.bin` | 待机页截图 |
+| `lvgl_sim.exe --cfgtest` | 配置引擎闭环:加载→轮询→外部改文件重载→外科式保存回验(在当前 CWD 产生 `sim_config.ini`) |
+| `lvgl_sim.exe --logtest` | syslog 后端闭环(定长记录/时间戳/轮转) |
+
+## UI 改动回归 → 用 `ui-verify` skill(勿在此零散手跑)
+
+5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 11 页黄金参考 + 往返池)→ `@build` → `coding-standard`。
+RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/check_remo_sink.py`。
+黄金参考 `tests/ref/` 是**本地生成物不入库**:换机后先 `check_content_pages.py --update` 生成,再 `regen_ref_png.py` 出目检 PNG。
+
+## 两条铁律
+
+- **改 `simulator/lv_conf.h` 触发 `lv-conf-sync` 门禁**:它与 MCU 端 `Lib/lv_conf.h` 必须保持同一特性子集(按**生效值**比对,未定义取 `lv_conf_internal.h` 兜底);白名单与四类判定 → `docs/代码规范/pre-commit使用说明.md` §2.15 / §5.5
+- **模拟器代码与固件同标准受检**(`cppcheck` / `coding-standard` / 乱码 / 改写型钩子 / `doc-links` 全覆盖 `simulator/`);**改动触及 `sim/` 任何后端,必须同步在 `docs/测试说明/模拟器差距清单.md` 登记一行**——模拟器跑过 ≠ 真机跑过
+
+## 构建/运行报错速查(详解 `simulator/README.md`「四、已知坑(构建/运行)」)
+
+| 现象 | 根因 |
+|---|---|
+| 找不到 `SDL2/SDL.h` | LVGL SDL 驱动写死该路径,头文件须有 `include/SDL2/` 副本(仓库已内置) |
+| 找不到 `lv_conf.h` | `LV_CONF_DIR`/`SDL2_INCLUDE_DIR` 必须在 `add_subdirectory(lvgl)` **之前**定义 |
+| 窗口全黑 | mono 主题未生效 / 缺 `lv_refr_now(NULL)` / 重复喂 tick(SDL 驱动内部已 `lv_tick_set_cb`) |
+| 画面正常但按键全无效 | 主循环**不得**调 `SDL_PollEvent`——LVGL SDL 驱动内部 5ms 定时器统一分发,先轮询会抢光事件 |
+| 中文显示空白 | 子集字体缺字 → **`font-regen` skill** 重生成 `ui_font_cn_12` |
+| 新加 UI `.c` 一端编不过 | 双端注册(uvprojx + CMakeLists)→ **`new-source-file` skill** |
diff --git a/.dsh/skills/new-machine-setup/SKILL.md b/.dsh/skills/new-machine-setup/SKILL.md
index b5b73ed0..72f28007 100644
--- a/.dsh/skills/new-machine-setup/SKILL.md
+++ b/.dsh/skills/new-machine-setup/SKILL.md
@@ -46,7 +46,7 @@ whenToUse: 换新机器/新克隆要把环境搭起来、构建或烧录报"环
(`env_check.py` 对这种情况报 WARN 并给注入命令。)
- 换机后 COM 口几乎必变:只改 `.serialtest/config.json` 的 `portName`,
然后 `serial_tool.py restart`(daemon 启动时缓存配置)。
-- 串口"拒绝访问"先查残留旧 daemon(AGENTS.md 串口助手节),不是驱动问题。
+- 串口"拒绝访问"先查残留旧 daemon(`serial-assistant` skill「陷阱」节),不是驱动问题。
## 其它项目怎么套
diff --git a/.dsh/skills/task-worktree/.gitignore b/.dsh/skills/task-worktree/.gitignore
new file mode 100644
index 00000000..c18dd8d8
--- /dev/null
+++ b/.dsh/skills/task-worktree/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/.dsh/skills/task-worktree/SKILL.md b/.dsh/skills/task-worktree/SKILL.md
new file mode 100644
index 00000000..b8c53f84
--- /dev/null
+++ b/.dsh/skills/task-worktree/SKILL.md
@@ -0,0 +1,111 @@
+---
+name: task-worktree
+description: 多任务并行开发的空间管理(git worktree 生命周期):new 开一棵工作树 + 任务分支、目录联接共享 .venv 与模拟器 SDK、build/bench 两类独占锁、done 预演合并回主干并审计并集残留。规格见 docs/环境与部署/并行任务工作树方案.md。
+whenToUse: 要同时推进多个任务、担心同分支同文档互相冲突;要为新任务开一个隔离空间;要把任务合并回主干;或遇到两个任务抢 COM6 / UV4、"改了文档被覆盖"、并集合并残留时。
+---
+
+# task-worktree — 多任务并行开发空间(短命令 `@task`)
+
+**执行体 = 本 skill 自带脚本** `.dsh/skills/task-worktree/scripts/task_worktree.py`
+(纯标准库,任意 python3;拷整个 skill 目录到别的项目即可用)。
+
+```powershell
+# cwd = 仓库根(或任意子目录)
+& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" <子命令> [参数]
+```
+
+退出码:`0`=成功 / `1`=操作失败 / `2`=前置或环境错误 / `3`=用法。
+
+## 固定命令
+
+| 命令 | 作用 |
+|---|---|
+| `new ` | 建分支 `task/-` + 工作树 + 共享联接 + 台账;打印新空间路径 |
+| `list` | 所有任务一览:分支 / 落后领先 / 脏文件数 / 台架状态 + 两个锁的归属 |
+| `status ` | 单任务详情(未提交清单、相对基线漂移、台架记录、已建联接) |
+| `sync ` | 在任务工作树里 `rebase` 追平主干,把冲突控制在任务分支上 |
+| `lock acquire\|release\|status ` | 独占资源锁 |
+| `done ` | 预演 → 合入主干 → 并集残留审计 → 重生成产物 → 门禁 → 清场 |
+| `abort ` | 放弃任务(工作树 + 分支一起删,**永久丢弃**) |
+
+`new` 常用开关:`--base <分支>`(默认 `v2-on-ui`)· `--needs-bench`(无台架 PASS 则 `done` 拒合)·
+`--lean`(跳过 Git LFS smudge)· `--no-bootstrap`(不建联接)。
+
+`done` 常用开关:`--dry-run`(只预演,零改动)· `--skip-build` · `--skip-gates` · `--force`。
+
+**环境变量**(高级):`DTU_WT_ROOT`(任务容器位置,默认仓库同级 `_wt/`)·
+`DTU_TASK_BASE`(默认基线分支,默认 `v2-on-ui`)·
+`DTU_INTEGRATION_WORKTREE`(集成点,默认主工作树——用于隔离演练/测试)。
+
+## 典型流程
+
+```powershell
+& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" new N21 dhara-isfree --needs-bench
+# → 把新会话的工作目录切到打印出来的 _wt/N21-dhara-isfree/,在那里干活
+
+& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" lock acquire bench --task N21
+# 烧录 / @test / 板级套件……
+& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" lock release bench --task N21 --bench-pass
+
+# 回到主工作树执行回收
+& $PY ".dsh\skills\task-worktree\scripts\task_worktree.py" done N21
+```
+
+## 铁律
+
+1. **`done` 只能在主工作树执行**——主干分支检出在主工作树,git 不允许同一分支在两处检出。
+2. **`done` 不 push**。它结束于本地合入 + 门禁全绿;推送需用户明确许可(项目既有纪律)。
+3. **`build` 与 `bench` 都必须持锁**:UV4 不能两个实例同时 `-b`;串口 COM6、ST-LINK、
+ 设备 IP `192.168.1.100` 各只有一份。**构建与烧录都不可并发。**
+4. **单写者文档**:`docs/任务规划/`(整个文件夹)、`AGENTS.md` 的任务表块、`MDK-ARM/*.uvprojx`、
+ `.pre-commit-config.yaml`、内存快照 —— 任务期**不在任务工作树里改**,留到 `done` 统一处理。
+5. **清场必须先摘联接**:脚本会先 `os.rmdir` 掉 `.venv` / `simulator/SDL2` 联接再
+ `git worktree remove`。**手工清场时千万别直接 `git worktree remove`**——git 可能递归进联接,
+ 把主工作树的 `.venv` 一起删掉。
+6. **并发上限 3 棵工作树**(每棵约 83 MB 检出)。
+7. **工作树放仓库同级 `_wt/`,绝不在仓库内部**——否则污染 `git status`、`pre-commit --all-files` 与全库搜索。
+
+## 陷阱(实测)
+
+- **联接不是符号链接**:本仓库 `core.symlinks=false`,git 里的 symlink 会被检出成
+ 「内容是目标路径的文本文件」。共享 `.venv` 只能用 Windows **目录联接**(`mklink /J`,
+ 不需要管理员权限),且联接必须建在版本控制之外。
+ 实测(2026-10-12):用 ctypes 直调 `DeviceIoControl(FSCTL_SET_REPARSE_POINT)`
+ 会被系统拒绝(Win32 4392),`mklink /J` 正常——所以脚本走后者,参数以列表交给
+ subprocess,含空格的路径(本项目 `DTU Code`)由 subprocess 自动加引号。
+- **联接建失败不中止 `new`**:只告警并打印手工补建命令,避免留下半成品任务空间;
+ 但该任务里跑不了 `.venv\Scripts\python.exe`,`@build` / `ci_local.py` 会找不到解释器。
+- **`done` 前主工作树必须干净**:否则未提交改动会被卷进合并。
+- **⚠ `done` 的门禁跑在 `@build` 之前 → 依赖 `.map` 的钩子会误报**(2026-10-12 实测):
+ `done` 的顺序是「合并 → 重生成 → **门禁(ci_local)** → `@build`」,而
+ `mem-budget`(N-27)/ `mem-numbers` 是拿**文档快照 vs `.map`** 比对 —— 门禁跑时
+ `.map` 还是**主工作树合并前**的旧产物,与刚合并进来的新台账**必然不一致** →
+ 误报 `[FAIL] 台账受管区与 .map 不一致`。`@build` 跑完后 `.map` 才是新的。
+ **处置**:门禁失败且只差这两项时,按提示在主工作树跑
+ `python scripts/ram_baseline.py --write` + `python scripts/check_mem_numbers.py --write`
+ 后追加提交,再重跑 `done`(它会检测到「已合入」直接清场)。
+- **⚠ 增量构建可能不重链 → `.map` 数字瞬态不一致**(2026-10-12 实测):
+ `@build` 若判定无需重链,`.map` 保持旧值,此时 `--write` 生成的快照是**瞬态值**
+ (实测同一份源码两次读到 223,560 与 223,724,差 164 B)。**以主工作树(集成点)
+ 的 `.map` 为准**;改完 RAM 相关代码后确认重链再刷新快照。
+- **合并冲突常落在「生成物」上**:`docs/内存管理/RAM使用分析与优化方案.md`(mem-numbers
+ 快照)在双方都改过时必冲突,且**两边都不能直接选**(各自基于不同基线)——
+ 正确做法是解冲突后用**合并后的实际构建**重新生成,不要挑一侧。
+- **并集合并残留**:双方都改过的文件(尤其 `.gitignore`)会被 git 自动并成**并集**,
+ 可能留下指向已删目录的死规则或重复条目。脚本会自动报「双方都改过」的文件清单 +
+ 连续重复行;**权威判据是 base / 两个 tip 三方对比**(`git show [:` 逐项 `grep -F`),
+ 别用 `tail -N` 目测。
+- **`.workbuddy-ai/` 刻意不共享**:记忆不随任务分叉,也不给联接递归删除的机会。
+ 任务工作树自己的记忆由 `done` / `abort` 在清场前归并回主工作树。
+- **`--lean` 只省磁盘不省时间**:实测全量 smudge 7.25 s / 83 MB,跳过 smudge 7.05 s / 45 MB,
+ 两者工作区都干净。默认走全量(参考图可用),磁盘紧张时才用 `--lean`。
+- **锁不绑定进程存活**:`lock acquire` 本身是短命进程,而真正用资源的烧录/构建在别的进程里跑。
+ 所以判「陈旧」只看**时效**(默认 6 小时,`LOCK_TTL_HOURS`)加显式 `--force`;pid 仅作排障记录。
+ 同任务重复 acquire 是幂等的(不会把自己锁死)。
+- **陈旧锁可自动接管**:超过 6 小时未释放即视为陈旧,下次 acquire 会接管并提示;活锁需 `--force`。
+
+## 与其它 skill 的衔接
+
+- 构建 = `keil-build` skill(`@build`);烧录/回读 = `stlink-flash` skill;串口闭环 = `serial-boot-test` skill
+- 新 `.c` 文件 = `new-source-file` skill(`uvprojx` 与 `CMakeLists.txt` 双端注册)
+- 完整机制与目录分配 → `docs/环境与部署/并行任务工作树方案.md`
diff --git a/.dsh/skills/task-worktree/scripts/task_worktree.py b/.dsh/skills/task-worktree/scripts/task_worktree.py
new file mode 100644
index 00000000..7a7ab99c
--- /dev/null
+++ b/.dsh/skills/task-worktree/scripts/task_worktree.py
@@ -0,0 +1,857 @@
+#!/usr/bin/env python3
+"""task-worktree — 多任务并行开发的空间管理(git worktree 生命周期)
+
+执行体 = 本脚本(纯标准库)。规格见 docs/环境与部署/并行任务工作树方案.md,
+用法与铁律见同目录 SKILL.md。
+
+退出码:0=成功 / 1=操作失败 / 2=前置或环境错误 / 3=用法错误
+"""
+
+import argparse
+import json
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import time
+from pathlib import Path
+
+# 输出统一 UTF-8,避免被控制台代码页(cp936)转码破坏
+for _stream in (sys.stdout, sys.stderr):
+ try:
+ _stream.reconfigure(encoding="utf-8")
+ except Exception:
+ pass
+
+EXIT_OK, EXIT_FAIL, EXIT_ENV, EXIT_USAGE = 0, 1, 2, 3
+
+BRANCH_PREFIX = "task/"
+MAX_CONCURRENT = 3
+LOCK_NAMES = ("build", "bench")
+LOCK_TTL_HOURS = 6.0 # 超过此持有时间即视为陈旧锁,下次 acquire 自动接管
+JUNCTION_SPECS = (
+ (".venv", "Python 环境"),
+ ("simulator/SDL2", "模拟器 SDL2 SDK"),
+)
+FILE_ATTRIBUTE_REPARSE_POINT = 0x400
+
+ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$")
+SLUG_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$")
+
+
+class Fail(Exception):
+ """操作失败(退出码 1)"""
+
+
+class EnvError(Exception):
+ """前置或环境错误(退出码 2)"""
+
+
+# --------------------------------------------------------------------- 仓库定位
+
+
+def _main_worktree_of(start: Path):
+ """借 git 反查「主工作树」路径——脚本可能被从任务工作树里调用,不能只靠 __file__"""
+ for extra in (["--path-format=absolute"], []):
+ r = subprocess.run(
+ ["git", "-C", str(start), "rev-parse", *extra, "--git-common-dir"],
+ capture_output=True, text=True, encoding="utf-8", errors="replace",
+ stdin=subprocess.DEVNULL,
+ )
+ if r.returncode == 0 and r.stdout.strip():
+ common = Path(r.stdout.strip())
+ if not common.is_absolute():
+ common = (start / common).resolve()
+ return common.parent if common.name == ".git" else common
+ return None
+
+
+_SCRIPT_ROOT = Path(__file__).resolve().parents[4]
+REPO_ROOT = _main_worktree_of(_SCRIPT_ROOT) or _SCRIPT_ROOT
+WT_ROOT = Path(os.environ.get("DTU_WT_ROOT") or (REPO_ROOT.parent / "_wt"))
+BASE_BRANCH = os.environ.get("DTU_TASK_BASE", "v2-on-ui")
+
+STATE_DIR = WT_ROOT / ".state"
+LOCKS_DIR = STATE_DIR / "locks"
+
+
+# --------------------------------------------------------------------- 基础工具
+
+
+def git(*args, cwd=None, check=True, env=None):
+ cmd = ["git"] + [str(a) for a in args]
+ full_env = dict(os.environ)
+ if env:
+ full_env.update(env)
+ r = subprocess.run(
+ cmd, cwd=str(cwd or REPO_ROOT), capture_output=True, text=True,
+ encoding="utf-8", errors="replace", stdin=subprocess.DEVNULL, env=full_env,
+ )
+ if check and r.returncode != 0:
+ raise Fail("git {} 失败(rc={})\n{}".format(
+ " ".join(cmd[1:]), r.returncode, (r.stdout + r.stderr).strip()))
+ return r
+
+
+def git_out(*args, **kw):
+ return git(*args, **kw).stdout.strip()
+
+
+def worktree_entries():
+ out = git_out("worktree", "list", "--porcelain")
+ entries, cur = [], None
+ for line in out.splitlines():
+ if line.startswith("worktree "):
+ if cur:
+ entries.append(cur)
+ cur = {"path": Path(line[len("worktree "):]), "branch": None}
+ elif cur is None:
+ continue
+ elif line.startswith("branch refs/heads/"):
+ cur["branch"] = line[len("branch refs/heads/"):]
+ if cur:
+ entries.append(cur)
+ return entries
+
+
+def main_worktree() -> Path:
+ """集成点(合并发生的地方)。默认 = 主工作树;可用环境变量覆盖,便于演练/测试。"""
+ override = os.environ.get("DTU_INTEGRATION_WORKTREE")
+ if override:
+ p = Path(override)
+ if not p.is_absolute():
+ p = (REPO_ROOT / p).resolve()
+ if not p.is_dir():
+ raise EnvError("DTU_INTEGRATION_WORKTREE 不存在:{}".format(p))
+ return p
+ entries = worktree_entries()
+ if not entries:
+ raise EnvError("无法解析 git worktree 列表")
+ return entries[0]["path"]
+
+
+def venv_python(tree: Path = None) -> Path:
+ """优先用该工作树自己的 .venv(任务树里是指向主树的联接),否则退回主工作树"""
+ for root in (tree, REPO_ROOT):
+ if root is None:
+ continue
+ p = Path(root) / ".venv" / "Scripts" / "python.exe"
+ if p.is_file():
+ return p
+ return Path(sys.executable)
+
+
+def run_script(script: str, args, cwd: Path, label: str, tail: int = 40) -> bool:
+ """跑项目脚本并回报。输出可能上千行,只回显尾部(结论与报错都在尾部)
+
+ ⚠ 脚本必须取自 **cwd 那棵树**,不能固定用 REPO_ROOT:
+ 项目脚本普遍以 `__file__` 反推仓库根(如 `scripts/ci_local.py`、
+ `scripts/check_registration.py`),若拿主树的脚本、却给集成树的 cwd,
+ 脚本仍会去校验主树——门禁就测错了对象(2026-10-12 实测踩到:
+ 报出主树才有的 iec104 未注册缺口,而集成树里根本没那些文件)。
+ """
+ script_path = Path(cwd) / script
+ if not script_path.is_file():
+ script_path = REPO_ROOT / script
+ cmd = [str(venv_python(Path(cwd))), str(script_path)] + [str(x) for x in args]
+ r = subprocess.run(
+ cmd, cwd=str(cwd), capture_output=True, text=True,
+ encoding="utf-8", errors="replace", stdin=subprocess.DEVNULL,
+ )
+ out = (r.stdout + r.stderr).strip()
+ print("[{}] {}".format("ok" if r.returncode == 0 else "!!", label))
+ if out:
+ lines = out.splitlines()
+ if len(lines) > tail:
+ print(" …(前 {} 行略)".format(len(lines) - tail))
+ lines = lines[-tail:]
+ print(" " + "\n ".join(lines))
+ return r.returncode == 0
+
+
+# --------------------------------------------------------------------- 台账
+
+
+def state_file(task_id: str) -> Path:
+ return STATE_DIR / (task_id + ".json")
+
+
+def load_state(task_id: str) -> dict:
+ p = state_file(task_id)
+ if not p.is_file():
+ raise EnvError("任务 {} 不存在(缺台账 {})".format(task_id, p))
+ return json.loads(p.read_text(encoding="utf-8"))
+
+
+def save_state(st: dict) -> None:
+ STATE_DIR.mkdir(parents=True, exist_ok=True)
+ state_file(st["id"]).write_text(
+ json.dumps(st, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
+
+
+def all_states():
+ if not STATE_DIR.is_dir():
+ return []
+ out = []
+ for p in sorted(STATE_DIR.glob("*.json")):
+ try:
+ out.append(json.loads(p.read_text(encoding="utf-8")))
+ except Exception:
+ continue
+ return out
+
+
+# --------------------------------------------------------------------- 独占锁
+
+
+def lock_age_hours(info) -> float:
+ try:
+ return max(0.0, (time.time() - float(info.get("epoch") or 0)) / 3600.0)
+ except (TypeError, ValueError):
+ return float("inf")
+
+
+def lock_path(name: str) -> Path:
+ return LOCKS_DIR / (name + ".lock")
+
+
+def lock_read(name: str):
+ p = lock_path(name)
+ if not p.is_file():
+ return None
+ try:
+ return json.loads(p.read_text(encoding="utf-8"))
+ except Exception:
+ return {"task": "?", "pid": 0, "epoch": 0, "at": "?"}
+
+
+def lock_acquire(name: str, task: str, wait: int = 0, force: bool = False, note: str = ""):
+ """抢占独占资源。
+
+ ⚠ 锁**不**绑定进程存活:`lock acquire` 本身是短命进程,而用锁的烧录/构建
+ 在另一个进程里跑。因此判「陈旧」只看**时效**(超过 LOCK_TTL_HOURS),
+ 外加显式 `--force`;pid 仅作记录便于排障。
+ """
+ LOCKS_DIR.mkdir(parents=True, exist_ok=True)
+ p = lock_path(name)
+ deadline = time.time() + max(0, wait)
+ while True:
+ try:
+ fd = os.open(str(p), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+ except FileExistsError:
+ info = lock_read(name) or {}
+ if info.get("task") == task:
+ return True, info
+ age = lock_age_hours(info)
+ if force or age > LOCK_TTL_HOURS:
+ print("[lock] 接管 {} 锁(原持有者 {},已持有 {:.1f} 小时{})".format(
+ name, info.get("task"), age, ",--force" if force else ",视为陈旧"))
+ try:
+ p.unlink()
+ except OSError:
+ pass
+ continue
+ if time.time() >= deadline:
+ return False, info
+ print("[lock] {} 被 {} 占用(已持有 {:.1f} 小时),等待…".format(
+ name, info.get("task"), age))
+ time.sleep(2)
+ else:
+ now = time.time()
+ payload = {
+ "name": name, "task": task, "pid": os.getpid(),
+ "host": os.environ.get("COMPUTERNAME", "?"),
+ "epoch": now,
+ "at": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now)),
+ "note": note,
+ }
+ os.write(fd, (json.dumps(payload, ensure_ascii=False) + "\n").encode("utf-8"))
+ os.close(fd)
+ return True, payload
+
+
+def lock_release(name: str, task=None, force: bool = False):
+ p = lock_path(name)
+ if not p.is_file():
+ return True, "锁 {} 未被持有".format(name)
+ info = lock_read(name) or {}
+ if not force and task and info.get("task") not in (task, None):
+ return False, "锁 {} 由 {} 持有,与 {} 不符;强制释放请加 --force".format(
+ name, info.get("task"), task)
+ try:
+ p.unlink()
+ except OSError as e:
+ return False, "释放锁 {} 失败:{}".format(name, e)
+ return True, "已释放锁 {}(原持有者 {})".format(name, info.get("task"))
+
+
+# --------------------------------------------------------------------- 目录联接
+
+
+def is_reparse(p: Path) -> bool:
+ try:
+ st = os.lstat(str(p))
+ except OSError:
+ return False
+ return bool(getattr(st, "st_file_attributes", 0) & FILE_ATTRIBUTE_REPARSE_POINT)
+
+
+def make_junction(link: Path, target: Path) -> None:
+ """创建 Windows 目录联接(不需要管理员权限,也不是符号链接)
+
+ 走 `mklink /J`——这是唯一不需要 SeCreateSymbolicLinkPrivilege 的目录链接形式。
+ ⚠ 实测(2026-10-12):直接用 ctypes 调 `DeviceIoControl(FSCTL_SET_REPARSE_POINT)`
+ 会被系统拒绝(Win32 4392 / ERROR_REPARSE_TAG_INVALID,缓冲区经 hexdump 核对无误),
+ 而 `mklink /J` 正常,故采用后者。参数以**列表**交给 subprocess,
+ 含空格的路径(本项目 `DTU Code`)由 subprocess 自动加引号,不经手写引号。
+ """
+ if os.name != "nt":
+ raise EnvError("目录联接是 Windows 专属能力;当前平台 {}".format(os.name))
+ if os.path.lexists(str(link)):
+ raise Fail("联接位置已被占用:{}".format(link))
+ if not target.exists():
+ raise Fail("联接源不存在:{}".format(target))
+ link.parent.mkdir(parents=True, exist_ok=True)
+ r = subprocess.run(
+ ["cmd", "/c", "mklink", "/J", str(link), str(target)],
+ capture_output=True, stdin=subprocess.DEVNULL,
+ )
+ if r.returncode != 0 or not is_reparse(link):
+ detail = (r.stdout or b"").decode("mbcs", "replace").strip()
+ raise Fail("创建目录联接失败:{} -> {}\n rc={} {}".format(
+ link, target, r.returncode, detail))
+
+
+def remove_junction(link: Path) -> bool:
+ """只摘掉联接本身,绝不触碰目标内容(os.rmdir = RemoveDirectoryW)"""
+ if not is_reparse(link):
+ return False
+ try:
+ os.rmdir(str(link))
+ return True
+ except OSError as e:
+ print("[warn] 摘除联接失败 {}:{}".format(link, e))
+ return False
+
+
+def bootstrap(wt: Path):
+ """建立共享联接。单个联接失败只告警不中止——避免留下半成品任务空间"""
+ made, failed = [], []
+ for rel, desc in JUNCTION_SPECS:
+ target = REPO_ROOT / rel
+ if not target.exists():
+ print("[skip] {}({})不存在,跳过联接".format(rel, desc))
+ continue
+ try:
+ make_junction(wt / rel, target)
+ except (Fail, EnvError) as e:
+ failed.append(rel)
+ print("[WARN] {} 联接失败:{}".format(rel, e))
+ continue
+ made.append(rel)
+ print("[ok] 联接 {} -> {}".format(rel, target))
+ if failed:
+ print()
+ print("!! {} 个共享联接未建成;该任务里跑 `.venv\\Scripts\\python.exe` 会找不到解释器。".format(len(failed)))
+ for rel in failed:
+ print(" 手工补:cmd /c mklink /J \"{}\\{}\" \"{}\\{}\"".format(wt, rel, REPO_ROOT, rel))
+ return made
+
+
+# --------------------------------------------------------------------- 子进程脚本
+
+
+def regen(cwd: Path) -> None:
+ run_script("scripts/gen_task_table.py", ["--write"], cwd, "重生成 FreeRTOS 任务表")
+ if list((Path(cwd) / "MDK-ARM").glob("**/*.map")):
+ run_script("scripts/check_mem_numbers.py", ["--write"], cwd, "同步内存/Flash 快照")
+ else:
+ print("[skip] 未找到 .map,跳过内存快照同步(需先 @build)")
+
+
+def run_gates(cwd: Path, skip_build: bool) -> bool:
+ ok = run_script("scripts/ci_local.py", [], cwd, "L0 + L0.5 本地等价 CI")
+ if skip_build:
+ print("[skip] 按 --skip-build 跳过 @build")
+ return ok
+ got, info = lock_acquire("build", "__done__", wait=900)
+ if not got:
+ print("!! build 锁被 {} 占用(UV4 不能并发构建),跳过 @build".format(info.get("task")))
+ return False
+ try:
+ if not run_script(".dsh/skills/keil-build/scripts/keil_build.py", [], cwd,
+ "@build(0 错 0 警)"):
+ ok = False
+ finally:
+ lock_release("build", "__done__", force=True)
+ return ok
+
+
+# --------------------------------------------------------------------- 合并辅助
+
+
+def write_merge_message(st: dict, base: str, cwd: Path) -> Path:
+ raw = git_out("log", "--format=%s", "{}..{}".format(base, st["branch"]), cwd=cwd)
+ subjects = [s.strip() for s in raw.splitlines() if s.strip()]
+ title = "merge: {} 合入 {}".format(st["branch"], base)
+ if len(title) > 100:
+ title = title[:97] + "..."
+ lines = [title, "", "任务 {}({} 个提交)经 task-worktree 回收:".format(st["id"], len(subjects))]
+ for s in subjects:
+ lines.append("- " + (s[:73] + "..." if len(s) > 76 else s))
+ if st.get("note"):
+ note = st["note"]
+ lines.append("")
+ lines.append("备注:" + (note[:73] + "..." if len(note) > 76 else note))
+ fd, path = tempfile.mkstemp(prefix="taskwt-merge-", suffix=".txt")
+ os.close(fd)
+ Path(path).write_text("\n".join(lines) + "\n", encoding="utf-8", newline="\n")
+ return Path(path)
+
+
+def audit_residue(base_before: str, branch: str, cwd: Path) -> None:
+ """并集合并残留审计:双方都改过的文件会被 git 自动并成并集"""
+ mb = git_out("merge-base", base_before, branch, cwd=cwd)
+ task_files = set(filter(None, git_out("diff", "--name-only", mb, branch, cwd=cwd).splitlines()))
+ base_files = set(filter(None, git_out("diff", "--name-only", mb, base_before, cwd=cwd).splitlines()))
+ both = sorted(task_files & base_files)
+ if not both:
+ print("[ok] 没有「双方都改过」的文件,无并集残留风险")
+ return
+ print("以下 {} 个文件双方都改过,git 会自动并成并集,需人工核对:".format(len(both)))
+ for f in both[:30]:
+ print(" - " + f)
+ if len(both) > 30:
+ print(" …(其余 {} 个略)".format(len(both) - 30))
+ hits = []
+ for f in both:
+ p = cwd / f
+ if not p.is_file():
+ continue
+ try:
+ lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
+ except OSError:
+ continue
+ dups = [i + 1 for i in range(1, len(lines))
+ if lines[i] == lines[i - 1] and lines[i].strip()]
+ if dups:
+ hits.append((f, dups))
+ if hits:
+ print()
+ print("!! 连续重复行(并集合并残留嫌疑):")
+ for f, dups in hits:
+ print(" - {} 第 {} 行".format(f, ", ".join(str(d) for d in dups[:8])))
+ else:
+ print("[ok] 未检测到连续重复行")
+ print("提示:权威判据是 base / 两个 tip 三方对比(git show ][:),别只看结果文件。")
+
+
+def harvest_memory(st: dict) -> None:
+ """任务工作树会被删除,先把它自己的记忆归并回主工作树,避免丢上下文"""
+ src = Path(st["path"]) / ".workbuddy-ai" / "memory"
+ if not src.is_dir():
+ return
+ dst_dir = REPO_ROOT / ".workbuddy-ai" / "memory"
+ dst_dir.mkdir(parents=True, exist_ok=True)
+ for f in sorted(src.glob("*.md")):
+ text = f.read_text(encoding="utf-8", errors="replace").strip()
+ if not text:
+ continue
+ dst = dst_dir / f.name
+ marker = "## [task-worktree 归并自任务 {} / {}]".format(st["id"], st["slug"])
+ with dst.open("a", encoding="utf-8", newline="\n") as fh:
+ fh.write("\n" + marker + "\n\n" + text + "\n")
+ print("[ok] 记忆归并 {} -> {}".format(f.name, dst))
+
+
+def teardown_worktree(st: dict, force: bool = False, base: str = None) -> None:
+ wt = Path(st["path"])
+ base = base or st.get("base")
+ # 1) 先摘联接——绝不能让 git 递归进联接去删目标内容(.venv / SDL2 会被一起删掉)
+ for rel in st.get("junctions", []):
+ if remove_junction(wt / rel):
+ print("[ok] 已摘除联接 {}".format(rel))
+ # 2) 归并任务侧记忆
+ if wt.is_dir():
+ harvest_memory(st)
+ # 3) 移除工作树
+ r = git("worktree", "remove", *(["--force"] if force else []), str(wt), check=False)
+ if r.returncode != 0:
+ raise Fail("移除工作树失败:{}\n(提示:先处理未跟踪文件,或加 --force)".format(
+ (r.stdout + r.stderr).strip()))
+ # 4) 删分支。⚠ 不用 `git branch -d`——它按**环境 HEAD** 判断是否已合并,
+ # 集成点被 DTU_INTEGRATION_WORKTREE 改走时会误判。改为显式核对 base。
+ if force or git("merge-base", "--is-ancestor", st["branch"], base,
+ check=False).returncode == 0:
+ r = git("branch", "-D", st["branch"], check=False)
+ if r.returncode != 0:
+ print("[warn] 删除分支 {} 失败:{}".format(
+ st["branch"], (r.stdout + r.stderr).strip()))
+ else:
+ print("[ok] 已删除分支 {}".format(st["branch"]))
+ else:
+ print("[warn] 分支 {} 未并入 {},保留不删(确认可弃再 `git branch -D {}`)".format(
+ st["branch"], base, st["branch"]))
+ state_file(st["id"]).unlink(missing_ok=True)
+
+
+# --------------------------------------------------------------------- 子命令
+
+
+def cmd_new(a) -> int:
+ if not ID_RE.match(a.task_id):
+ raise EnvError("任务 ID 非法:{}(字母数字开头,可用 . _ -)".format(a.task_id))
+ if not SLUG_RE.match(a.slug):
+ raise EnvError("slug 非法:{}(小写字母数字与连字符)".format(a.slug))
+ branch = "{}{}-{}".format(BRANCH_PREFIX, a.task_id, a.slug)
+ wt = WT_ROOT / "{}-{}".format(a.task_id, a.slug)
+
+ if state_file(a.task_id).is_file():
+ raise EnvError("任务 {} 已存在({})".format(a.task_id, state_file(a.task_id)))
+ if os.path.lexists(str(wt)):
+ raise EnvError("目录已存在:{}".format(wt))
+ if git("rev-parse", "--verify", "--quiet", "refs/heads/" + branch, check=False).returncode == 0:
+ raise EnvError("分支已存在:{}".format(branch))
+ if git("rev-parse", "--verify", "--quiet", a.base, check=False).returncode != 0:
+ raise EnvError("基线分支不存在:{}".format(a.base))
+
+ active = [s for s in all_states() if Path(s["path"]).is_dir()]
+ if len(active) >= MAX_CONCURRENT and not a.force:
+ raise EnvError("已有 {} 个活跃任务(上限 {});先 done/abort,或加 --force".format(
+ len(active), MAX_CONCURRENT))
+
+ WT_ROOT.mkdir(parents=True, exist_ok=True)
+ print("[1/3] 建工作树(分支 {},基线 {}{})…".format(
+ branch, a.base, ",跳过 LFS smudge" if a.lean else ""))
+ git("worktree", "add", "-b", branch, str(wt), a.base,
+ env={"GIT_LFS_SKIP_SMUDGE": "1"} if a.lean else None)
+
+ print("[2/3] 建立共享联接…")
+ junctions = [] if a.no_bootstrap else bootstrap(wt)
+
+ st = {
+ "id": a.task_id, "slug": a.slug, "branch": branch, "path": str(wt),
+ "base": a.base, "created_at": time.strftime("%Y-%m-%d %H:%M:%S"),
+ "junctions": junctions, "needs_bench": bool(a.needs_bench),
+ "bench_pass": False, "note": a.note or "", "lean": bool(a.lean),
+ }
+ save_state(st)
+ print("[3/3] 台账已写入 {}".format(state_file(a.task_id)))
+ print()
+ print("下一步:把新会话的工作目录切到")
+ print(" " + str(wt))
+ print("在该目录里干活、跑 @build 与 ci_local.py(并行安全);")
+ print("要动台架先 `lock acquire bench --task {}`,完事 `lock release bench --task {} --bench-pass`。".format(
+ a.task_id, a.task_id))
+ print("回收用 `done {}`(**必须在主工作树执行**)。".format(a.task_id))
+ return EXIT_OK
+
+
+def cmd_list(a) -> int:
+ states = all_states()
+ if not states:
+ print("(没有任务空间。用 `new ` 建一个)")
+ return EXIT_OK
+ print("{:<12} {:<32} {:>5} {:>5} {:>4} {}".format(
+ "任务", "分支", "落后", "领先", "脏", "台架"))
+ for st in states:
+ wt = Path(st["path"])
+ if not wt.is_dir():
+ print("{:<12} {:<32} {:>5} {:>5} {:>4} {}".format(
+ st["id"], st["branch"], "-", "-", "-", "[工作树缺失]"))
+ continue
+ ab = git_out("rev-list", "--left-right", "--count",
+ "{}...{}".format(st["base"], st["branch"]), cwd=wt, check=False).split()
+ behind, ahead = (ab[0], ab[1]) if len(ab) == 2 else ("?", "?")
+ dirty = len(git_out("status", "--porcelain", cwd=wt, check=False).splitlines())
+ bench = "PASS" if st.get("bench_pass") else ("需测" if st.get("needs_bench") else "-")
+ print("{:<12} {:<32} {:>5} {:>5} {:>4} {}".format(
+ st["id"], st["branch"], behind, ahead, dirty, bench))
+ print()
+ for n in LOCK_NAMES:
+ info = lock_read(n)
+ if not info:
+ print("锁 {:<6} 空闲".format(n))
+ else:
+ print("锁 {:<6} {} 持有(已 {:.1f} 小时,于 {})".format(
+ n, info.get("task"), lock_age_hours(info), info.get("at")))
+ return EXIT_OK
+
+
+def cmd_status(a) -> int:
+ st = load_state(a.task_id)
+ wt = Path(st["path"])
+ print("任务 {} ({})".format(st["id"], st["slug"]))
+ print("分支 {} ← 基线 {}".format(st["branch"], st["base"]))
+ print("工作树 {}{}".format(wt, "" if wt.is_dir() else " [缺失]"))
+ print("建立于 {}".format(st.get("created_at")))
+ if st.get("note"):
+ print("备注 {}".format(st["note"]))
+ if wt.is_dir():
+ dirty = git_out("status", "--porcelain", cwd=wt, check=False).splitlines()
+ print("未提交 {} 项".format(len(dirty)))
+ for line in dirty[:10]:
+ print(" " + line)
+ ab = git_out("rev-list", "--left-right", "--count",
+ "{}...{}".format(st["base"], st["branch"]), cwd=wt, check=False).split()
+ if len(ab) == 2:
+ print("相对基线 落后 {} / 领先 {} 个提交".format(ab[0], ab[1]))
+ print("台架 {}{}".format(
+ "需要," if st.get("needs_bench") else "不涉及,",
+ "已记录 PASS" if st.get("bench_pass") else "尚无 PASS 记录"))
+ if st.get("junctions"):
+ print("联接 {}".format(", ".join(st["junctions"])))
+ return EXIT_OK
+
+
+def cmd_sync(a) -> int:
+ st = load_state(a.task_id)
+ wt = Path(st["path"])
+ base = a.base or st["base"]
+ if not wt.is_dir():
+ raise EnvError("工作树不存在:{}".format(wt))
+ if git_out("status", "--porcelain", cwd=wt, check=False):
+ raise Fail("任务工作树有未提交改动,先提交或 stash 再 sync")
+ r = git("rebase", base, cwd=wt, check=False)
+ if r.returncode != 0:
+ print((r.stdout + r.stderr).strip())
+ raise Fail("rebase 有冲突:在 {} 里解决后 `git rebase --continue`,放弃用 `git rebase --abort`".format(wt))
+ print("[ok] 任务 {} 已 rebase 到 {}".format(st["id"], base))
+ return EXIT_OK
+
+
+def cmd_lock(a) -> int:
+ if a.action == "status":
+ for n in LOCK_NAMES:
+ info = lock_read(n)
+ if not info:
+ print("{:<6} 空闲".format(n))
+ else:
+ age = lock_age_hours(info)
+ mark = " [陈旧,可被自动接管]" if age > LOCK_TTL_HOURS else ""
+ print("{:<6} {} 持有(pid={},于 {},已 {:.1f} 小时,备注 {}){}".format(
+ n, info.get("task"), info.get("pid"), info.get("at"), age,
+ info.get("note") or "-", mark))
+ return EXIT_OK
+
+ if a.name not in LOCK_NAMES:
+ raise EnvError("未知资源 {}(可用:{})".format(a.name, " / ".join(LOCK_NAMES)))
+
+ if a.action == "acquire":
+ if not a.task:
+ raise EnvError("acquire 需要 --task ")
+ got, info = lock_acquire(a.name, a.task, wait=a.wait, force=a.force, note=a.note or "")
+ if not got:
+ print("获取失败:{} 被 {} 占用(pid={},于 {})".format(
+ a.name, info.get("task"), info.get("pid"), info.get("at")))
+ return EXIT_FAIL
+ print("已获取 {} 锁(task={} pid={})".format(a.name, a.task, os.getpid()))
+ return EXIT_OK
+
+ if a.bench_pass:
+ if not a.task:
+ raise EnvError("--bench-pass 需要 --task ")
+ st = load_state(a.task)
+ st["bench_pass"] = True
+ save_state(st)
+ print("[ok] 已为任务 {} 记录台架 PASS".format(a.task))
+ ok, msg = lock_release(a.name, a.task, force=a.force)
+ print(msg)
+ return EXIT_OK if ok else EXIT_FAIL
+
+
+def cmd_done(a) -> int:
+ st = load_state(a.task_id)
+ wt = Path(st["path"])
+ branch = st["branch"]
+ base = a.base or st["base"]
+ main = main_worktree()
+
+ print("=== 前置检查 ===")
+ if not wt.is_dir():
+ raise EnvError("工作树不存在:{}".format(wt))
+ if git_out("status", "--porcelain", cwd=wt, check=False):
+ raise Fail("任务工作树有未提交改动,先提交")
+ if git_out("status", "--porcelain", cwd=main, check=False):
+ raise Fail("主工作树不干净({})——合并会把未提交改动卷进来".format(main))
+ cur = git_out("rev-parse", "--abbrev-ref", "HEAD", cwd=main)
+ if cur != base:
+ raise Fail("主工作树当前在 {},不是集成分支 {};先切过去再 done".format(cur, base))
+ for n in LOCK_NAMES:
+ info = lock_read(n)
+ if info:
+ raise Fail("独占资源 {} 仍被 {} 占用(已 {:.1f} 小时),先 release".format(
+ n, info.get("task"), lock_age_hours(info)))
+
+ ahead = int(git_out("rev-list", "--count", "{}..{}".format(base, branch), cwd=wt) or "0")
+ already = git("merge-base", "--is-ancestor", branch, base, cwd=main, check=False).returncode == 0
+ if ahead == 0 and not already:
+ raise Fail("任务分支相对 {} 没有新提交,无需合并".format(base))
+ if already and ahead == 0:
+ print("[i] {} 已合入 {},跳过合并,直接清场".format(branch, base))
+ print("=== 清场 ===")
+ teardown_worktree(st, force=a.force, base=base)
+ print("\n完成(分支此前已合入)。")
+ return EXIT_OK
+
+ if st.get("needs_bench") and not st.get("bench_pass"):
+ raise Fail("任务标记 --needs-bench 但无台架 PASS 记录;跑完 @test 后执行 "
+ "`lock release bench --task {} --bench-pass`".format(st["id"]))
+ print("[ok] 前置检查通过(领先 {} 共 {} 个提交)".format(base, ahead))
+
+ print("=== 合并预演(git merge-tree,不改动任何东西)===")
+ r = git("merge-tree", "--write-tree", base, branch, cwd=main, check=False)
+ if r.returncode != 0:
+ print((r.stdout + r.stderr).strip())
+ raise Fail("预演发现冲突:到任务工作树里 `git merge {}` 解决并提交后重跑 done".format(base))
+ tree = (r.stdout.splitlines() or ["?"])[0].strip()
+ print("[ok] 无冲突,预演结果树 {}".format(tree))
+ if a.dry_run:
+ print()
+ print("--dry-run:到此为止,未做任何改动。")
+ return EXIT_OK
+
+ print("=== 合入主干({},--no-ff)===".format(main))
+ base_before = git_out("rev-parse", "HEAD", cwd=main)
+ msgfile = write_merge_message(st, base, wt)
+ r = git("merge", "--no-ff", "--no-edit", branch, "-F", str(msgfile), cwd=main, check=False)
+ msgfile.unlink(missing_ok=True)
+ if r.returncode != 0:
+ raise Fail("合并失败:\n{}".format((r.stdout + r.stderr).strip()))
+ print((r.stdout or "").strip())
+ base_after = git_out("rev-parse", "HEAD", cwd=main)
+
+ print("=== 合并残留审计 ===")
+ audit_residue(base_before, branch, cwd=main)
+
+ print("=== 重生成受管产物 ===")
+ regen(main)
+
+ if a.skip_gates:
+ print("=== 门禁(--skip-gates 已跳过)===")
+ gates_ok = True
+ else:
+ print("=== 门禁 ===")
+ gates_ok = run_gates(main, skip_build=a.skip_build)
+
+ if not gates_ok:
+ print()
+ print("!! 门禁未全绿:工作树与分支**保留**,未清场。")
+ print(" 主干已含合并提交 {}".format(base_after[:12]))
+ print(" 可选:a) 在主工作树修正后追加提交(推荐)")
+ print(" b) 回退本次合并:git -C \"{}\" reset --hard {}".format(main, base_before[:12]))
+ return EXIT_FAIL
+
+ print("=== 清场 ===")
+ teardown_worktree(st, force=a.force, base=base)
+ print()
+ print("完成。主干 {} 现为 {}。".format(base, base_after[:12]))
+ print("push 需人工许可,本次未推送。")
+ return EXIT_OK
+
+
+def cmd_abort(a) -> int:
+ st = load_state(a.task_id)
+ wt = Path(st["path"])
+ if wt.is_dir():
+ dirty = git_out("status", "--porcelain", cwd=wt, check=False).splitlines()
+ ahead = int(git_out("rev-list", "--count",
+ "{}..{}".format(st["base"], st["branch"]), cwd=wt,
+ check=False) or "0")
+ if (dirty or ahead) and not a.force:
+ print("任务 {} 有 {} 项未提交改动、{} 个未合并提交,abort 会**永久丢弃**:".format(
+ st["id"], len(dirty), ahead))
+ for line in dirty[:20]:
+ print(" " + line)
+ raise Fail("确认丢弃请加 --force")
+ teardown_worktree(st, force=True)
+ print("\n已放弃任务 {}(工作树与分支均已删除)".format(st["id"]))
+ return EXIT_OK
+
+
+# --------------------------------------------------------------------- 入口
+
+
+def build_parser():
+ ap = argparse.ArgumentParser(
+ prog="task_worktree.py",
+ description="多任务并行开发的空间管理(git worktree 生命周期)")
+ sub = ap.add_subparsers(dest="cmd")
+
+ p = sub.add_parser("new", help="新建任务空间")
+ p.add_argument("task_id")
+ p.add_argument("slug")
+ p.add_argument("--base", default=BASE_BRANCH, help="基线分支(默认 %(default)s)")
+ p.add_argument("--lean", action="store_true",
+ help="跳过 Git LFS smudge(省约 38MB,参考图变成指针文件)")
+ p.add_argument("--no-bootstrap", action="store_true", help="不建共享联接")
+ p.add_argument("--needs-bench", action="store_true",
+ help="需台架验证;无 PASS 记录则 done 拒绝合并")
+ p.add_argument("--note", default="")
+ p.add_argument("--force", action="store_true", help="忽略并发上限")
+ p.set_defaults(func=cmd_new)
+
+ p = sub.add_parser("list", help="列出所有任务空间")
+ p.set_defaults(func=cmd_list)
+
+ p = sub.add_parser("status", help="单个任务详情")
+ p.add_argument("task_id")
+ p.set_defaults(func=cmd_status)
+
+ p = sub.add_parser("sync", help="rebase 追平主干")
+ p.add_argument("task_id")
+ p.add_argument("--base", default=None)
+ p.set_defaults(func=cmd_sync)
+
+ p = sub.add_parser("lock", help="独占资源锁(build / bench)")
+ p.add_argument("action", choices=["acquire", "release", "status"])
+ p.add_argument("name", nargs="?", choices=list(LOCK_NAMES))
+ p.add_argument("--task", default=None)
+ p.add_argument("--wait", type=int, default=0, help="等待秒数(0=不等待)")
+ p.add_argument("--force", action="store_true")
+ p.add_argument("--note", default="")
+ p.add_argument("--bench-pass", action="store_true",
+ help="release bench 时顺带为该任务记录台架 PASS")
+ p.set_defaults(func=cmd_lock)
+
+ p = sub.add_parser("done", help="回收:预演 → 合入主干 → 审计 → 重生成 → 门禁 → 清场")
+ p.add_argument("task_id")
+ p.add_argument("--base", default=None)
+ p.add_argument("--dry-run", action="store_true", help="只预演,不做任何改动")
+ p.add_argument("--skip-build", action="store_true", help="跳过 @build")
+ p.add_argument("--skip-gates", action="store_true", help="跳过全部门禁")
+ p.add_argument("--force", action="store_true", help="清场时强制移除工作树")
+ p.set_defaults(func=cmd_done)
+
+ p = sub.add_parser("abort", help="放弃任务(不合并)")
+ p.add_argument("task_id")
+ p.add_argument("--force", action="store_true")
+ p.set_defaults(func=cmd_abort)
+ return ap
+
+
+def main(argv=None) -> int:
+ ap = build_parser()
+ a = ap.parse_args(argv)
+ if not getattr(a, "func", None):
+ ap.print_help()
+ return EXIT_USAGE
+ if not (REPO_ROOT / ".git").exists():
+ print("!! 无法定位仓库根(推导为 {})".format(REPO_ROOT), file=sys.stderr)
+ return EXIT_ENV
+ try:
+ return a.func(a)
+ except EnvError as e:
+ print("前置/环境错误:{}".format(e), file=sys.stderr)
+ return EXIT_ENV
+ except Fail as e:
+ print("失败:{}".format(e), file=sys.stderr)
+ return EXIT_FAIL
+ except KeyboardInterrupt:
+ print("已中断", file=sys.stderr)
+ return EXIT_FAIL
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/.dsh/skills/ui-verify/SKILL.md b/.dsh/skills/ui-verify/SKILL.md
index 370e4d2f..10c1b735 100644
--- a/.dsh/skills/ui-verify/SKILL.md
+++ b/.dsh/skills/ui-verify/SKILL.md
@@ -1,13 +1,18 @@
---
name: ui-verify
description: UI 改动后的回归验证:5 步门禁(模拟器构建、菜单像素回归、内容页黄金参考、Keil 构建、代码规范)。
-whenToUse: Drivers/BSP/UI、字体 ui_font_cn_12.c / ui_cn_chars.txt、菜单/内容页被修改之后,或用户要求"验证 UI / 跑 UI 回归 / UI 改动检查"时。
+whenToUse: Drivers/BSP/UI、字体 ui_font_cn_12.c / ui_cn_chars.txt、菜单/内容页被修改的前后(写码前先读「两条实证坑」节),或用户要求"验证 UI / 跑 UI 回归 / UI 改动检查"时。
---
# UI 回归验证
UI 相关改动完成后必须跑完 5 个门禁再提交。完整说明见 `docs/UI设计/UI_自动化测试流程.md`。
+## 两条实证坑(写码前先读,模拟器照不出来)
+
+- **I1 绘制缓冲含 8B palette 头**:布局 `[palette 8B][pixel data]`,但 LVGL 容量校验只看 `stride×h` → 只给 `stride×h` 会把末 8 字节写到缓冲外。`ui_port.c` 按 `stride×h + palette` 供给并在 flush 时 `+palette` → `docs/UI设计/UI_设计架构.md` §1.4 第 8 条
+- **MCU 端必须显式应用 mono 主题**:`lv_theme_mono_init(disp, true, ...)` + `lv_display_set_theme()` + 对活动屏幕再 `lv_theme_apply()`,否则活动屏仍是默认浅灰而内容恒用白色 → **白底白字整屏纯白**(远端"纯白屏"根因)→ `docs/UI设计/UI_设计架构.md` §1.4 第 9 条
+
## 步骤(仓库根目录执行,python 一律用 .venv)
```powershell
diff --git a/.gitignore b/.gitignore
index 8896ca94..4d0dd662 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,12 @@ docs/images/*.png
# Serial assistant runtime state (daemon log/meta, local runtime artifacts)
.dsh/skills/serial-assistant/scripts/state/
+# 记忆工具(Mnemon)本地状态:项目文档索引 documents/ + 运行时记忆
+# runtime/MEMORY.md、USER.md、memories.json。**只留本机、不进版本库**
+# (2026-10-11 定):记忆是按机器/按人累积的私有状态,入库会让每次记忆
+# 写入都变成提交抖动,还可能带出本机绝对路径与个人偏好。
+.mnemon/
+
# CI 运行时产物(.gitea/workflows/ci.yml 的 S3 步骤在 runner 工作区落盘的日志;
# 零依赖存档方案——落在工作区目录而非上传 artifact,故必须忽略以免误提交)
ci-artifacts/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ace93544..8aceebb4 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,11 +2,18 @@
# 详见 docs/代码规范/pre-commit使用说明.md
default_install_hook_types: [pre-commit, commit-msg]
+# 顶层 exclude 只挡**第三方与生成物**。
+# ⚠ 2026-10-11 修正:原先在这里整目录挡 `simulator/` 是错的——`simulator/` 下 21 个
+# tracked 文件**全是本项目自研代码**(模拟器入口 main.c、平台后端 sim/*.c、回归脚本
+# tests/*.py、两份配置与 README)。整目录豁免 = 自研代码落在所有钩子之外(连
+# lv-conf-sync 都拦不到);实测把 simulator/ 纳入 cppcheck 后,立刻揪出
+# sim_syslog.c 一处真实的空指针解引用。真正的生成物/第三方(`simulator/build/`、
+# `simulator/SDL2/`、`simulator/tests/ref/`、`simulator/shot*.{bin,png}`、`__pycache__/`)
+# **已在 .gitignore 里**,永远不会进 git 索引 → 无需在此重复豁免,故直接删掉该条。
exclude: |
(?x)^(
\.opencode/node_modules/|
Lib/lvgl/|
- simulator/|
MDK-ARM/RTE/
)
@@ -147,17 +154,18 @@ repos:
# 5) 文档一致性门禁(D-7)
# 存在性:md 互链 / 反引号路径是否指向真实文件(防迁移/删除后残留死链)
+ # 章节:`§N.M` 必须能在目标文档的编号标题里找到(--sections-strict:自引用失效也拦)
- id: doc-links
name: 文档链接存在性 (D-3/D-7)
- entry: python scripts/check_doc_links.py
+ entry: python scripts/check_doc_links.py --sections-strict
language: system
files: \.md$
# 外部资料(芯片手册转录)链接形态不受控;第三方目录整体排除
+ # (`simulator/` 已不再排除:其下 3 个 README 是本项目文档,2026-10-11)
exclude: |
(?x)^(
\.opencode/node_modules/|
Lib/lvgl/|
- simulator/|
MDK-ARM/RTE/|
docs/芯片手册/
)
@@ -177,6 +185,27 @@ repos:
files: ^(docs/内存管理/.*\.md|scripts/check_mem_numbers\.py|App/.*\.c|App/.*\.h|Src/.*\.c|Inc/.*\.h)$
pass_filenames: false
+ # 5d) 两份 lv_conf.h 特性子集一致性(2026-10-11 加,随 F-1/F-2/F-3 落地)
+ # 背景:`Lib/lv_conf.h`(MCU)与 `simulator/lv_conf.h`(PC)是两份独立文件,
+ # 此前长期漂移——模拟器早已 I1-only,MCU 端却让 11 个 `LV_DRAW_SW_SUPPORT_*`、
+ # 5 个未用控件类、`LV_USE_THEME_SIMPLE`、`LV_USE_FLEX/GRID` 靠 lv_conf_internal
+ # 默认值隐式生效 → 固件白链 **64,576 B 死代码**,且模拟器 golden 回归跑的是
+ # 另一套特性子集(等于验证不到固件实际编译的配置)。
+ # 口径:比对**生效值**(文件未定义则取 lv_conf_internal.h 的兜底默认),
+ # 并抓"写了 LVGL 不认的宏名"(`LV_USE_THEME_BASIC` 同类,实测两端共 26 处)
+ # 与同文件重复定义。规则正文见 docs/UI设计/UI_设计架构.md §1.4 第 10 条。
+ # ⚠ 触发范围(2026-10-11 两次修正):本钩子由**两端任一配置**或脚本自身触发。
+ # 原先顶层 exclude 整目录挡掉 `simulator/`,导致"只改 simulator/lv_conf.h"
+ # 的提交拦不到;已删该条(见文件顶部 exclude 注释),故这里可直接匹配
+ # `simulator/lv_conf.h`。(pre-commit 4.x 已移除 `always: true`,
+ # 实测对 local 钩子只告警并跳过——不能靠它兜底。)
+ - id: lv-conf-sync
+ name: 两份 lv_conf.h 特性子集一致性
+ entry: python scripts/check_lv_conf_sync.py
+ language: system
+ files: ^(Lib/lv_conf\.h|simulator/lv_conf\.h|scripts/check_lv_conf_sync\.py)$
+ pass_filenames: false
+
# 6) CubeMX 重生成存活审计(2026-09-29 加)
# 背景:.ioc 的 KeepUserCode 只保护 USER CODE 标记区;标记之外的手工改动
# 重生成即被覆盖,且多数不报编译错(崩溃采集被打回空 while(1)、加固宏被删)。
@@ -232,16 +261,178 @@ repos:
files: ^(App/cfg/cfg_file\.(c|h)|App/cfg/cfg_file_store\.h|App/log/sys_log\.h|test/host/cfg_file/.*|test/host/common/unit_test_harness\.py)$
pass_filenames: false
+ # 7d) 相量层(L2)主机侧单元测试(2026-10-10 加)
+ # 背景:App/algo/ 为**纯算法**层(无寄存器、无 RTOS、无外设依赖),是本项目
+ # 首个可完全离线验证的算法层。板端**无信号源**,无法注入已知幅值/相位/频率的
+ # 标准波形,故"读数 = 设定值"这一核心判据在板上不可验;本钩子用 MSVC 离线编译
+ # 真实源文件(#include 纳入,无需桩)+ 合成任意波形,把系数表、幅值/相位、
+ # 直流与谐波抑制、频偏敏感度、滑动窗、双区交换一次性钉死。
+ # files 含被测的两个源与其头,以及 harness 本身。
+ - id: phasor-unit-test
+ name: 相量层单测(主机侧)
+ entry: python test/host/phasor/run_unit_test.py
+ language: system
+ files: ^(App/algo/(phasor|sample_ring)\.(c|h)|test/host/phasor/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7e) ADC 输入层(L1)主机侧单元测试(2026-10-10 加)
+ # 背景:adc_input.c 的核心是"通道号 <-> Rank 顺序 <-> 语义"三者映射的正确性;
+ # 板端无信号源,无法注入已知电压验证"读到的是哪只脚"(映射说明 §8)。本钩子用
+ # MSVC 离线编译真实源文件(#include 纳入)+ HAL/TPAFE 可注入桩:给每个 Rank
+ # 注入互不相同的码值以**逐通道钉死映射**,并一次钉死 HAL 启动失败 / 转换超时 /
+ # 参数错等真机难复现的分支。
+ # files 含被测源与其头、两个桩头所在目录,以及 harness 本身。
+ - id: adc-input-unit-test
+ name: ADC 输入层单测(主机侧)
+ entry: python test/host/adc_input/run_unit_test.py
+ language: system
+ files: ^(App/algo/adc_input\.(c|h)|test/host/adc_input/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7f) 卷锁(vol_lock)主机侧单元测试(2026-10-10 加)
+ # 背景:vol_lock 的**核心不变量是 fail-open**——互斥量创建失败时
+ # `vol_lock_wait()` 必须**返回 1 且绝不阻塞**,因为它的调用者 defaultTask 是
+ # 看门狗喂狗点,一旦在此阻塞即整机复位。板端该路径**只在内存耗尽时才可能走到**,
+ # 无法人为复现;本钩子用 MSVC 离线编译真实源文件(#include 纳入)+ CMSIS 互斥桩,
+ # 把"创建失败 -> 直接放行"、"创建成功 -> 正常加解锁"、"未持锁释放"、
+ # 超时参数透传等分支一次性钉死。
+ # files 含被测源与其头,以及 harness 本身。
+ - id: vol-lock-unit-test
+ name: 卷锁单测(主机侧)
+ entry: python test/host/vol_lock/run_unit_test.py
+ language: system
+ files: ^(App/fs/vol_lock\.(c|h)|test/host/vol_lock/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7g) 时间同步(time_sync)主机侧单元测试(2026-10-10 加)
+ # 背景:time_sync.c 是 TCP 时间同步协议状态机,也是 N-17 号看门狗风险的**全部
+ # 逻辑贡献者**——"listen 重建 -> 800ms~1s 心跳间隔"由本模块的分片延时/退避/
+ # 结算等待组成,板端只能看到"是否复位"这一结果,看不到各段延时是否如设计。本钩子
+ # 用 MSVC 离线编译真实源文件(#include 纳入)+ 可脚本化的网络/事件桩(含 longjmp
+ # 跳出 for(;;) 任务循环),把分片喂狗节奏、监听退避、报文重组(半包/粘连/EOF/真错/
+ # 预算耗尽/tick 回绕)、重听门控逐条钉死。
+ # files 含被测源与其头、被测的真实看门狗头,以及 harness 本身。
+ - id: time-sync-unit-test
+ name: 时间同步单测(主机侧)
+ entry: python test/host/time_sync/run_unit_test.py
+ language: system
+ files: ^(App/sync/time_sync\.(c|h)|App/util/task_watchdog\.h|test/host/time_sync/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7h) ADC 采样有效性校验(L1-6)主机侧单元测试(2026-10-11 加)
+ # 背景:adc_health.c 是"静默/丢帧/序号倒退/错误计数增量"的判定层——这些
+ # 故障在健康台架上**不可复现**(复现 = 拔芯片/改驱动),板端闭环只能证明
+ # "健康时不误报",证不了"故障时必报";100ms 门限、tick 回绕、事件边沿
+ # 语义等边界只有合成计数序列能精确构造。本钩子用 MSVC 离线编译真实源文件
+ # (#include 纳入,纯逻辑零桩)把 11 组判据一次性钉死。
+ # files 含被测源与其头、adc_task.c(告警编排的消费方),以及 harness 本身。
+ - id: adc-health-unit-test
+ name: ADC 有效性校验单测(主机侧)
+ entry: python test/host/adc_health/run_unit_test.py
+ language: system
+ files: ^(App/algo/adc_health\.(c|h)|App/task/adc_task\.c|test/host/adc_health/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7i) 保护定值模型(P1 / L5-1 载体)主机侧单元测试(2026-10-11 加)
+ # 定值是保护动作的"输入宪法":范围校验、整表装载 all-or-nothing、
+ # 校验和封存-自检-闭锁(参考 tagPRS dwTagSum/dwTagSumN 模式)必须在
+ # 任何元件接线之前钉死——板端无法穷举越界定值注入,主机侧毫秒级全跑。
+ - id: prot-set-unit-test
+ name: 保护定值模型单测(主机侧)
+ entry: python test/host/prot_set/run_unit_test.py
+ language: system
+ files: ^(App/algo/prot_set\.(c|h)|test/host/prot_set/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7j) 保护元件框架(L3 P1 子集)主机侧单元测试(2026-10-11 加)
+ # 判据核(滞回/定时限/欠量方向/闭锁联动/时基回绕)是纯逻辑,
+ # 板端复现需要标准源 + 故障注入,主机侧用合成测量序列即可穷举
+ # 1.2×/0.9× 验收口径(规划 §8)。
+ - id: prot-elm-unit-test
+ name: 保护元件框架单测(主机侧)
+ entry: python test/host/prot_elm/run_unit_test.py
+ language: system
+ files: ^(App/algo/prot_elm\.(c|h)|App/algo/prot_set\.(c|h)|App/algo/phasor\.(c|h)|test/host/prot_elm/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7k) SHA-256 主机侧单元测试(2026-10-11 加,N-19 FTP 鉴权)
+ # sha256 是 FTP pwd_hash 校验的地基——哈希算错是"正确口令被拒 /
+ # 错误口令放行"的静默语义错误,板端只见 530/230 看不出根因;
+ # FIPS 180-4 标准向量(空串/abc/56B 补位跨界/1MB)+ hex_eq 非法输入
+ # 在主机侧毫秒级钉死。零平台依赖,无桩。
+ - id: sha256-unit-test
+ name: SHA-256 单测(主机侧)
+ entry: python test/host/sha256/run_unit_test.py
+ language: system
+ files: ^(App/util/sha256\.(c|h)|test/host/sha256/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7l) FTP 鉴权状态机主机侧单元测试(2026-10-11 加,N-19)
+ # 锁定 60s/解锁/成功清零/tick 32 位回绕是"防口令爆破"契约核心,
+ # 板端每验一条要真等 60s、回绕要跑满 49.7 天;lftpd_auth 纯函数
+ # 设计(tick 参数注入)就是为了让这些分支主机侧毫秒级穷举。
+ - id: ftp-auth-unit-test
+ name: FTP 鉴权状态机单测(主机侧)
+ entry: python test/host/ftp_auth/run_unit_test.py
+ language: system
+ files: ^(Drivers/BSP/NET/lftpd/lftpd_auth\.(c|h)|App/util/sha256\.(c|h)|test/host/ftp_auth/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
+ # 7m) IEC 60870-5-104 被控站协议栈主机侧单元测试(2026-10-12 加,B4)
+ # APCI 重组/STARTDT/总召分片/COV/k-w-t1~t3/遥控定值两段式/C_CS/错误
+ # 应答——link 层 tick 注入 + pts 数据源 vtable 抽象就是为 L0.5 全离线
+ # 穷举设计(A-4 约束 1);板端复现这些分支要真主站+真等待。
+ # 触发面含数据源依赖(prot_set/prot_elm/phasor/sample_ring)——测试
+ # 直接 #include 这些生产源,签名变了要立刻在这里暴露。
+ - id: iec104-unit-test
+ name: IEC104 协议栈单测(主机侧)
+ entry: python test/host/iec104/run_unit_test.py
+ language: system
+ files: ^(App/protocol/iec104/iec104_(asdu|pts|link)\.(c|h)|App/algo/(prot_set|prot_elm|phasor|sample_ring|adc_input)\.(c|h)|test/host/iec104/.*|test/host/common/unit_test_harness\.py)$
+ pass_filenames: false
+
# 8) 测试套件开关门禁(2026-10-01 加)
# 背景:test_config.h 的 TEST_SUITE_* 决定固件里编入哪个测试套件,
# 全部注释 = 产品构建。但此前**无任何门禁检查**——实测已提交的 HEAD 里
# TEST_SUITE_CH395F 就是开启状态(.map 含 34 处 test 代码),即仓库里
# 躺着的是测试固件。本钩子抓这个静默漏网:默认要求产品构建,跑套件回归
# 确需提交测试构建时用 `--allow-test-build` / ALLOW_TEST_BUILD=1 显式放行。
- # 详见 docs/待处理内容.md T-1
+ # 详见 docs/任务规划/登记簿.md T-1
- id: test-suite-switch
name: 测试套件开关=产品构建
entry: python scripts/check_test_suite.py
language: system
files: ^test/board/test_config\.h$
pass_filenames: false
+
+ # 9) 双端注册反向校验(2026-10-12 加,登记簿 N-23 / 审查报告 N-06 残余)
+ # 背景:新 .c 必须手动注册两处(`MDK-ARM/*.uvprojx` + `simulator/CMakeLists.txt`)
+ # 才会被编译;历史上 `App/task/rs485_task.c`、`Drivers/BSP/NET/lftpd/lftpd_log.c`
+ # **两处都没注册** = 写了却从不参与任何一端编译的**死代码**,误导读者以为功能已生效。
+ # 本钩子反向核对两份注册表:①存在但未进 uvprojx ②存在但未进 CMakeLists
+ # (MCU-only 豁免见脚本 SIM_EXEMPT,显式清单+每行理由,不整目录一刀切)
+ # ③注册了但文件不存在(幽灵注册)。
+ # 存量缺口(rs485_task/lftpd_log)已进脚本 KNOWN_GAPS 白名单 → WARN 不拦,
+ # 只对新缺口 FAIL,故钩子可立即上线不拦现有提交(`--strict` 供审计关白名单)。
+ # ⚠ pre-commit 4.x 已移除 `always: true` → files 必须覆盖所有触发面:
+ # 任意 .c + uvprojx + CMakeLists.txt + 脚本自身(脚本自身改动也值得复跑)。
+ - id: registration-check
+ name: 双端注册反向校验 (uvprojx + CMakeLists)
+ entry: python scripts/check_registration.py
+ language: system
+ files: (\.c$|\.uvprojx$|CMakeLists\.txt$|^scripts/check_registration\.py$)
+ pass_filenames: false
+ # 10) RAM 预算 92% 阈值门禁(2026-10-12 加,登记簿 N-27 前半 / 审查报告 §5.5 三件套)
+ # 背景:报告 §5.5 第 1 条要求"超阈值即失败 + Top-10 附表"——此前 mem-numbers 只防
+ # "文档数字漂移",**没有任何机械检查拦"占用超阈值"**;台账三件套(ram_baseline.py +
+ # ram_baseline.tsv + docs/内存管理/RAM预算台账.md)落地后,本钩子把 92% 红线机械化。
+ # 逻辑:RW_IRAM1/RW_CCM 任一 >92% → FAIL + Top-10 RW+ZI 对象表;台账受管区漂移 → FAIL
+ # (提示 --write);无 .map → 自适应跳过(同 mem-numbers 在 CI 的行为,盲区登记 A-5 残余)。
+ # ⚠ pre-commit 4.x 无 `always: true` → files 覆盖所有能改变 RAM 的触发面:
+ # 全部自研代码目录 + lv_conf.h + 测试板配置 + 脚本/tsv/台账自身 + 本配置文件。
+ - id: mem-budget
+ name: RAM 预算 92% 阈值门禁 (N-27)
+ entry: python scripts/ram_baseline.py --gate
+ language: system
+ files: ^(App/|Drivers/BSP/|Src/|Inc/|Lib/lv_conf\.h|test/board/|scripts/ram_baseline\.|docs/内存管理/|\.pre-commit-config\.yaml$)
+ pass_filenames: false
diff --git a/.serialtest/config.json b/.serialtest/config.json
index 4f89d139..59a7b3ef 100644
--- a/.serialtest/config.json
+++ b/.serialtest/config.json
@@ -7,7 +7,7 @@
"fw_marker": "fw build: ",
"readback_cmd": ".venv\\Scripts\\python.exe .dsh\\skills\\stlink-flash\\scripts\\stlink_flash.py cmp {hex}",
- "required": "App init OK;Network Init;net init: OK",
+ "required": "App init OK;Network Init;net init: OK;protect task start;i104: listening on 2404;[STACK] protect;[STACK] iec104;[STACK] heap",
"forbidden": "FAIL;ERROR;HardFault;[ERR]",
"boot_marker": "reset cause:",
"max_boot_count": 1,
diff --git a/AGENTS.md b/AGENTS.md
index 46a93876..c46a9c43 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,62 +1,53 @@
# STM32F4-Base
-STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2);**仅 Keil MDK-ARM v5 (ARMCC, C99) 工具链**;FreeRTOS V10.3.1 / CMSIS-RTOS2;192KB SRAM / 1MB Flash。
+**工业级嵌入式 DTU 平台固件**:STM32F407ZGTx @168MHz,集成以太网(CH395F)、SPI NAND 存储(GD5F)、电气量采集(TPAFE5160 + 片内 ADC)、RS-485、FTP 文件服务,自带远程显示(UI 推流)、墙钟对时、看门狗自恢复、运行/自检记录——既是可独立部署的现场设备,也是**可复用的平台底座**(保护测控等后续产品在此底座上补齐)。产品基线 → `docs/产品需求/产品需求设计文档.md` §2.1。
+
+**仅 Keil MDK-ARM v5 (ARMCC, C99) 工具链**;FreeRTOS V10.3.1 / CMSIS-RTOS2;192KB SRAM / 1MB Flash。
**任何代码修改后必须先 `@build` 验证(0 错误 0 警告)再提交;硬件闭环用 `@test`。**
-## 铁律:源码编辑与编码
+> 本文件 = agent 入口:宏观概况 + 每次都在场的铁律在这里,细节在 `docs/` 与 skills(文末索引)。同一事实只在一处详述(`docs/文档规范.md` 同此要求)。
-- 改代码**只用 Edit 工具**(精确子串替换);批量替换用 Edit 的 replaceAll。**绝不用 `Get-Content`/`Set-Content`/`cmd` 重写含中文的文件**——整文件重写会按系统默认编码重解码 → `U+FFFD` 乱码且不可逆
-- **字符串字面量必须纯 ASCII**(2026-09-15 实测):本机 ARMCC 对**无 BOM** 源文件按 CP936 解读,字符串内含**奇数个**汉字时末尾高字节吞掉结束引号 → 编译错(偶数个汉字侥幸通过);中文只写注释(注释不参与解析)。带 BOM 的文件(`Drivers/BSP/UI/*`、`App/task/ui_task.c`、`App/cfg/cfg_file.c`、`Lib/lv_conf.h`)经 BOM 按 UTF-8 解码,不受此条限制
-- 确需脚本级重写:工程源码为 UTF-8(多数文件无 BOM,UI 系带 BOM),按原样同编码回写、保持 BOM 状态;拿不准就别碰
-- 发现乱码:先统计 `EF BF BD` 字节数确认真损坏(勿信终端/Read 渲染);**不要急著 `git checkout`**(会丢弃全部未提交工作)——先用 Edit 只修坏行
-- **Edit 工具会剥掉文件原有的 UTF-8 BOM**(2026-09-18 实测):带 BOM 文件(如 `App/cfg/cfg_file.c`)被 Edit 后 BOM 静默消失 → ARMCC 改按 CP936 解码 → 中文字符串字面量报 `#870-D: invalid multibyte character sequence`(**只有重编译该文件才暴露,历史"0 警告"不代表安全**)。改完带中文字面量的文件须核对 BOM 并按原样补回(只在文件头前缀 3 字节 `EF BB BF`,勿整文件重写)
-- **行尾策略已写死进 `.gitattributes`(2026-10-09)**:`* text=auto eol=lf`(仓库 blob 与工作区统一 LF)+ `*.bat`/`*.cmd` 强制 CRLF(cmd.exe 对 LF-only 的 goto/标签不可靠)+ PNG/TTF/DLL/LIB 等显式 `binary` + `*.jpg` 走 LFS 且 `-text`(该条必须排在 `*` 之后才能覆盖)。**不要再依赖 `core.autocrlf`,也不要把工作区文件"改回 CRLF"**——CubeMX 重生成写的就是 LF;此前每次重生成后 40+ 个第三方文件在 `git status` 里显示"已修改"(内容归一化后一致),根因就是行尾由每台机器各自的 autocrlf 决定。第三方/生成文件的"文件末尾空行"已一次性对齐工具输出(`53bd9af`),**勿再用改写型钩子去修剪**(已在 `trailing-whitespace`/`end-of-file-fixer` 豁免)
+## 项目概况
-## 构建与验证
+**现状一句话**:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置)**已实现并台架验收**;采集主线已闭环到业务消费者(L1→L2 相量→L3 保护判据 protectTask→104 遥信,B5);**RS-485 采集尚未接线**(B2 按用户决策冻结,见 `docs/任务规划/暂缓-RS485.md`)——当前最大功能缺口,接线节奏受 E-59 门禁约束(见「平台约束」)。
-- `@build` = **`keil-build` skill**:`& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py`(仓库根目录执行;纯标准库,任意 python3 亦可)。退出码 0 = 0 错误 0 警告(PASS)、1 = 构建失败(打印报错行)、2 = UV4 未找到/无效、3 = 用法错误。UV4 解析链 `--uv4` > `.serialtest/config.json` 的 `uv4` > `$DSH_KEIL_UV4` > `$DTU_KEIL_UV4` > PATH(**显式设了但无效 = 立即报错、不静默回退**);日志 `MDK-ARM/build_log.txt`。⚠ **原 `MDK-ARM/build.bat` 已于 2026-10-10 删除**(判定口径同款,但无超时保护、UV4 只认 `DTU_KEIL_UV4`、失败时 dump 整份日志);`test/run_all_suites.py` 已改调本 skill。换机必设 `DTU_KEIL_UV4`,见 `docs/环境与部署/本地工具路径环境变量方案.md`
-- `@test` / `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` 一键闭环(2026-09-20 起为纯 Python 标准库实现,替代 PowerShell 版;`& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py`):开串口+清日志 → 烧录(**`stlink-flash` skill**:`stlink_flash.py flash` = `-P -Rst -Run`;`flash_cmd` 默认取 `.serialtest/config.json`,本仓库已指向本 skill)→ **固件身份戳**(从刚烧进去的 hex 里抠 `fw build:
+
+ net_listener.c
+ 1
+ ..\Drivers\BSP\NET\net_listener.c
+
net_cfg.c
1
..\Drivers\BSP\NETCFG\net_cfg.c
+
+ ftp_cfg.c
+ 1
+ ..\Drivers\BSP\NETCFG\ftp_cfg.c
+
nand_ftl.c
1
@@ -1533,6 +1608,11 @@
1
..\Drivers\BSP\NET\lftpd\lftpd.c
+
+ lftpd_auth.c
+ 1
+ ..\Drivers\BSP\NET\lftpd\lftpd_auth.c
+
lftpd_inet.c
1
diff --git a/MDK-ARM/ftp_abort_probe.py b/MDK-ARM/ftp_abort_probe.py
index 914b6477..bdfeff75 100644
--- a/MDK-ARM/ftp_abort_probe.py
+++ b/MDK-ARM/ftp_abort_probe.py
@@ -31,7 +31,9 @@ def rd(sock):
def raw_login(ip, port, timeout=10.0):
s = socket.create_connection((ip, port), timeout=timeout)
rd(s) # 220 banner
- s.sendall(b"USER anonymous\r\n"); rd(s)
+ # N-19: anonymous login is rejected (530) -> use the factory default account
+ s.sendall(b"USER admin\r\n"); rd(s)
+ s.sendall(b"PASS admin\r\n"); rd(s)
s.sendall(b"TYPE I\r\n"); rd(s)
return s
@@ -50,7 +52,7 @@ def followup_ok(ip, port, size=4096):
data = bytes(random.getrandbits(8) for _ in range(size))
want = hashlib.sha256(data).hexdigest()
try:
- f = FTP(); f.connect(ip, port, timeout=15.0); f.login("anonymous", "x")
+ f = FTP(); f.connect(ip, port, timeout=15.0); f.login("admin", "admin")
f.storbinary("STOR /ab_ok.bin", io.BytesIO(data))
buf = bytearray(); f.retrbinary("RETR /ab_ok.bin", buf.extend)
try:
diff --git a/README.md b/README.md
index 18837c12..13436d06 100644
--- a/README.md
+++ b/README.md
@@ -75,11 +75,13 @@ main.c
| testTask | 2048 words | BelowNormal | 硬件测试入口 |
| uiTask | 4096 words | BelowNormal | LVGL UI 界面 |
| timeSyncTask | 512 words | Normal | 墙钟时间同步 |
-| adcTask | 512 words | Normal | TPAFE5160 采样(1ms 节拍 + 原子快照) |
+| adcTask | 640 words | AboveNormal | TPAFE5160 采样(TIM3 1kHz 事件驱动 + 原子快照 + STM32 ADC DMA) |
+| iec104Task | 640 words | Normal | |
+| protectTask | 512 words | Normal | |
> 上表与 `Src/freertos.c` 的 `osThreadAttr_t` 一致(2026-09-25 校准);改动栈大小前先读
-> `docs/内存管理/RAM使用分析与优化方案.md` 的定栈规则与 `docs/待处理内容.md` 的内存预算项。
+> `docs/内存管理/RAM使用分析与优化方案.md` 的定栈规则与 `docs/任务规划/基线与待确认.md` 的内存预算项。
### 任务启动顺序管理
@@ -163,7 +165,7 @@ STM32F4-Base/
│ └── time_sync_server.py # 时间同步推送服务
├── docs/ # 项目文档(全量索引见 docs/README.md)
│ ├── README.md # 文档索引:目录结构 + 推荐阅读顺序 + 全量清单
-│ ├── 待处理内容.md # 统一待办登记簿
+│ ├── 任务规划/ # 统一待办登记簿(README 排序权威 + 拆分文件)
│ ├── 架构设计/ # 项目架构总览 / 存储架构 / 启动顺序 / 配图流程 / 引用关系图
│ ├── 子系统设计/ # 配置 / 网络 / 墙钟 / 日志 / 运行自检记录 设计说明
│ ├── 网络/ # BSD Socket API 使用指南 + 内部函数调用关系
diff --git a/STM32F407-Demo.ioc b/STM32F407-Demo.ioc
index 34a59a7c..54b08b4c 100644
--- a/STM32F407-Demo.ioc
+++ b/STM32F407-Demo.ioc
@@ -9,7 +9,9 @@ ADC1.Channel-7\#ChannelRegularConversion=ADC_CHANNEL_6
ADC1.Channel-8\#ChannelRegularConversion=ADC_CHANNEL_7
ADC1.Channel-9\#ChannelRegularConversion=ADC_CHANNEL_14
ADC1.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4
-ADC1.IPParameters=Rank-1\#ChannelRegularConversion,master,Channel-1\#ChannelRegularConversion,SamplingTime-1\#ChannelRegularConversion,NbrOfConversionFlag,ScanConvMode,Rank-2\#ChannelRegularConversion,Channel-2\#ChannelRegularConversion,SamplingTime-2\#ChannelRegularConversion,Rank-3\#ChannelRegularConversion,Channel-3\#ChannelRegularConversion,SamplingTime-3\#ChannelRegularConversion,Rank-4\#ChannelRegularConversion,Channel-4\#ChannelRegularConversion,SamplingTime-4\#ChannelRegularConversion,Rank-5\#ChannelRegularConversion,Channel-5\#ChannelRegularConversion,SamplingTime-5\#ChannelRegularConversion,Rank-6\#ChannelRegularConversion,Channel-6\#ChannelRegularConversion,SamplingTime-6\#ChannelRegularConversion,Rank-7\#ChannelRegularConversion,Channel-7\#ChannelRegularConversion,SamplingTime-7\#ChannelRegularConversion,Rank-8\#ChannelRegularConversion,Channel-8\#ChannelRegularConversion,SamplingTime-8\#ChannelRegularConversion,Rank-9\#ChannelRegularConversion,Channel-9\#ChannelRegularConversion,SamplingTime-9\#ChannelRegularConversion,NbrOfConversion,ClockPrescaler
+ADC1.DMAContinuousRequests=ENABLE
+ADC1.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T3_TRGO
+ADC1.IPParameters=Rank-1\#ChannelRegularConversion,master,Channel-1\#ChannelRegularConversion,SamplingTime-1\#ChannelRegularConversion,NbrOfConversionFlag,ScanConvMode,Rank-2\#ChannelRegularConversion,Channel-2\#ChannelRegularConversion,SamplingTime-2\#ChannelRegularConversion,Rank-3\#ChannelRegularConversion,Channel-3\#ChannelRegularConversion,SamplingTime-3\#ChannelRegularConversion,Rank-4\#ChannelRegularConversion,Channel-4\#ChannelRegularConversion,SamplingTime-4\#ChannelRegularConversion,Rank-5\#ChannelRegularConversion,Channel-5\#ChannelRegularConversion,SamplingTime-5\#ChannelRegularConversion,Rank-6\#ChannelRegularConversion,Channel-6\#ChannelRegularConversion,SamplingTime-6\#ChannelRegularConversion,Rank-7\#ChannelRegularConversion,Channel-7\#ChannelRegularConversion,SamplingTime-7\#ChannelRegularConversion,Rank-8\#ChannelRegularConversion,Channel-8\#ChannelRegularConversion,SamplingTime-8\#ChannelRegularConversion,Rank-9\#ChannelRegularConversion,Channel-9\#ChannelRegularConversion,SamplingTime-9\#ChannelRegularConversion,NbrOfConversion,ClockPrescaler,DMAContinuousRequests,ExternalTrigConv
ADC1.NbrOfConversion=9
ADC1.NbrOfConversionFlag=1
ADC1.Rank-1\#ChannelRegularConversion=1
@@ -35,13 +37,24 @@ ADC1.master=1
CAD.formats=[]
CAD.pinconfig=Dual
CAD.provider=Component Search Engine
+Dma.ADC1.6.Direction=DMA_PERIPH_TO_MEMORY
+Dma.ADC1.6.FIFOMode=DMA_FIFOMODE_DISABLE
+Dma.ADC1.6.Instance=DMA2_Stream4
+Dma.ADC1.6.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
+Dma.ADC1.6.MemInc=DMA_MINC_ENABLE
+Dma.ADC1.6.Mode=DMA_CIRCULAR
+Dma.ADC1.6.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
+Dma.ADC1.6.PeriphInc=DMA_PINC_DISABLE
+Dma.ADC1.6.Priority=DMA_PRIORITY_LOW
+Dma.ADC1.6.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode
Dma.Request0=USART1_TX
Dma.Request1=USART1_RX
Dma.Request2=SPI2_RX
Dma.Request3=SPI2_TX
Dma.Request4=SPI1_RX
Dma.Request5=SPI1_TX
-Dma.RequestsNb=6
+Dma.Request6=ADC1
+Dma.RequestsNb=7
Dma.SPI1_RX.4.Direction=DMA_PERIPH_TO_MEMORY
Dma.SPI1_RX.4.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.SPI1_RX.4.Instance=DMA2_Stream0
@@ -109,7 +122,7 @@ FREERTOS.Queues01=netMsgQueue,8,net_msg_t,0,Dynamic,NULL,NULL
FREERTOS.Tasks01=defaultTask,24,1024,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;netTask,24,960,StartNetTask,Default,NULL,Dynamic,NULL,NULL;ftpTask,24,1024,StartFtpTask,As weak,NULL,Dynamic,NULL,NULL;testTask,16,2048,start_test_task,As weak,NULL,Dynamic,NULL,NULL;uiTask,16,4096,StartUiTask,Default,NULL,Dynamic,NULL,NULL
FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2
FREERTOS.configENABLE_FPU=1
-FREERTOS.configTOTAL_HEAP_SIZE=49152
+FREERTOS.configTOTAL_HEAP_SIZE=57344
FREERTOS.configUSE_MALLOC_FAILED_HOOK=1
File.Version=6
GPIO.groupedBy=Group By Peripherals
@@ -121,7 +134,7 @@ Mcu.CPN=STM32F407ZGT6
Mcu.Family=STM32F4
Mcu.IP0=ADC1
Mcu.IP1=DMA
-Mcu.IP10=TIM6
+Mcu.IP10=TIM3
Mcu.IP11=UART4
Mcu.IP12=UART5
Mcu.IP13=USART1
@@ -204,7 +217,7 @@ Mcu.Pin65=PE1
Mcu.Pin66=VP_FREERTOS_VS_CMSIS_V2
Mcu.Pin67=VP_IWDG_VS_IWDG
Mcu.Pin68=VP_SYS_VS_tim7
-Mcu.Pin69=VP_TIM6_VS_ClockSourceINT
+Mcu.Pin69=VP_TIM3_VS_ClockSourceINT
Mcu.Pin7=PA4
Mcu.Pin8=PA5
Mcu.Pin9=PA6
@@ -217,9 +230,10 @@ MxDb.Version=DB.6.0.181
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.DMA1_Stream3_IRQn=true\:7\:0\:true\:false\:true\:true\:false\:true\:true
NVIC.DMA1_Stream4_IRQn=true\:7\:0\:true\:false\:true\:true\:false\:true\:true
-NVIC.DMA2_Stream0_IRQn=true\:7\:0\:true\:false\:true\:true\:false\:true\:true
-NVIC.DMA2_Stream2_IRQn=true\:9\:0\:true\:false\:true\:true\:false\:true\:true
+NVIC.DMA2_Stream0_IRQn=true\:7\:0\:true\:false\:true\:false\:false\:true\:true
+NVIC.DMA2_Stream2_IRQn=true\:9\:0\:true\:false\:true\:false\:false\:true\:true
NVIC.DMA2_Stream3_IRQn=true\:7\:0\:true\:false\:true\:true\:false\:true\:true
+NVIC.DMA2_Stream4_IRQn=true\:6\:0\:true\:false\:true\:false\:false\:true\:true
NVIC.DMA2_Stream7_IRQn=true\:9\:0\:true\:false\:true\:true\:false\:true\:true
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false
NVIC.EXTI9_5_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true\:true
@@ -236,7 +250,7 @@ NVIC.SavedPendsvIrqHandlerGenerated=true
NVIC.SavedSvcallIrqHandlerGenerated=true
NVIC.SavedSystickIrqHandlerGenerated=true
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:false\:true\:false\:true\:false
-NVIC.TIM6_DAC_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true\:true
+NVIC.TIM3_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true\:true
NVIC.TIM7_IRQn=true\:15\:0\:true\:false\:true\:false\:false\:true\:true
NVIC.TimeBase=TIM7_IRQn
NVIC.TimeBaseIP=TIM7
@@ -604,7 +618,7 @@ ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
-ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_USART1_UART_Init-USART1-false-HAL-true,5-MX_SPI2_Init-SPI2-false-HAL-true,6-MX_SPI1_Init-SPI1-false-HAL-true,7-MX_I2C1_Init-I2C1-false-HAL-true,8-MX_UART4_Init-UART4-false-HAL-true,9-MX_UART5_Init-UART5-false-HAL-true,10-MX_USART2_UART_Init-USART2-false-HAL-true,11-MX_USART3_UART_Init-USART3-false-HAL-true,12-MX_IWDG_Init-IWDG-false-HAL-true,13-MX_ADC1_Init-ADC1-false-HAL-true
+ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_USART1_UART_Init-USART1-false-HAL-true,5-MX_SPI2_Init-SPI2-false-HAL-true,6-MX_SPI1_Init-SPI1-false-HAL-true,7-MX_I2C1_Init-I2C1-false-HAL-true,8-MX_UART4_Init-UART4-false-HAL-true,9-MX_UART5_Init-UART5-false-HAL-true,10-MX_USART2_UART_Init-USART2-false-HAL-true,11-MX_USART3_UART_Init-USART3-false-HAL-true,12-MX_IWDG_Init-IWDG-false-HAL-true,13-MX_ADC1_Init-ADC1-false-HAL-true,14-MX_TIM3_Init-TIM3-false-HAL-true
RCC.48MHZClocksFreq_Value=84000000
RCC.AHBFreq_Value=168000000
RCC.APB1CLKDivider=RCC_HCLK_DIV4
@@ -670,10 +684,12 @@ SPI2.Direction=SPI_DIRECTION_2LINES
SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler
SPI2.Mode=SPI_MODE_MASTER
SPI2.VirtualType=VM_MASTER
-TIM6.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
-TIM6.IPParameters=Prescaler,Period,AutoReloadPreload
-TIM6.Period=999
-TIM6.Prescaler=83
+TIM3.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
+TIM3.IPParameters=Prescaler,Period,AutoReloadPreload,TIM_MasterSlaveMode,TIM_MasterOutputTrigger
+TIM3.Period=999
+TIM3.Prescaler=83
+TIM3.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
+TIM3.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_ENABLE
UART4.IPParameters=VirtualMode
UART4.VirtualMode=Asynchronous
UART5.IPParameters=VirtualMode
@@ -690,7 +706,7 @@ VP_IWDG_VS_IWDG.Mode=IWDG_Activate
VP_IWDG_VS_IWDG.Signal=IWDG_VS_IWDG
VP_SYS_VS_tim7.Mode=TIM7
VP_SYS_VS_tim7.Signal=SYS_VS_tim7
-VP_TIM6_VS_ClockSourceINT.Mode=Enable_Timer
-VP_TIM6_VS_ClockSourceINT.Signal=TIM6_VS_ClockSourceINT
+VP_TIM3_VS_ClockSourceINT.Mode=Internal
+VP_TIM3_VS_ClockSourceINT.Signal=TIM3_VS_ClockSourceINT
board=custom
rtos.0.ip=FREERTOS
diff --git a/Src/adc.c b/Src/adc.c
index cc5cf0d1..97621a1a 100644
--- a/Src/adc.c
+++ b/Src/adc.c
@@ -25,6 +25,7 @@
/* USER CODE END 0 */
ADC_HandleTypeDef hadc1;
+DMA_HandleTypeDef hdma_adc1;
/* ADC1 init function */
void MX_ADC1_Init(void)
@@ -48,11 +49,11 @@ void MX_ADC1_Init(void)
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
- hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
- hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
+ hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
+ hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 9;
- hadc1.Init.DMAContinuousRequests = DISABLE;
+ hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
@@ -141,7 +142,15 @@ void MX_ADC1_Init(void)
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
-
+ /* STM32F4 ADC 上电**自校准**:RM0090 §13.3.6 / AN4976 要求的必需步骤,CubeMX 不生成,
+ * 且本仓库的 HAL 是裁剪子集、**没有** HAL_ADCEx_Calibration_Start()(已核实
+ * stm32f4xx_hal_adc_ex.c/.h 均无该函数)→ 用本文件 USER CODE BEGIN 1 里的等价实现。
+ * 必须在首次转换之前完成,否则 ADC 存在失调误差(未校准的偏移可达数十 LSB)。
+ * 放 USER CODE 区 = L2,重生成不丢;由 cubemx-survival 审计项 adc1-calibration 兜底。 */
+ if (adc1_calibrate() != 0)
+ {
+ Error_Handler();
+ }
/* USER CODE END ADC1_Init 2 */
}
@@ -182,6 +191,25 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(ADC17_UB_GPIO_Port, &GPIO_InitStruct);
+ /* ADC1 DMA Init */
+ /* ADC1 Init */
+ hdma_adc1.Instance = DMA2_Stream4;
+ hdma_adc1.Init.Channel = DMA_CHANNEL_0;
+ hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
+ hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
+ hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
+ hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+ hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+ hdma_adc1.Init.Mode = DMA_CIRCULAR;
+ hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
+ hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
+ if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
+
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
@@ -215,6 +243,8 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
HAL_GPIO_DeInit(ADC17_UB_GPIO_Port, ADC17_UB_Pin);
+ /* ADC1 DMA DeInit */
+ HAL_DMA_DeInit(adcHandle->DMA_Handle);
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
@@ -222,6 +252,64 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
}
/* USER CODE BEGIN 1 */
+/* CR2.CAL(bit 3)。⚠ 本仓库的 CMSIS 设备头 stm32f407xx.h 是裁剪版,**未定义**
+ * ADC_CR2_CAL(已核实:CR2 只有 ADON/CONT/DMA/DDS/EOCS/ALIGN/JEXTSEL/JEXTEN/
+ * JSWSTART/EXTSEL/EXTEN/SWSTART,没有 CAL)
+ * → 按 RM0090 在此补齐,命名避开 CMSIS 前缀以免将来重定义冲突。 */
+#define ADC1_CAL_BIT (0x1UL << 3U)
+/* 断电/校准等待上限(ms)。CAL 实测在数十 µs 内清零,10 ms 是 100 倍以上余量 */
+#define ADC1_CAL_TIMEOUT_MS (10U)
+/* ADON 0→1 后的稳定等待(µs),同 HAL 的 ADC_STAB_DELAY_US 口径 */
+#define ADC1_STAB_DELAY_US (20U)
+/*
+ * 函数功能:ADC1 上电自校准(等价于 HAL_ADCEx_Calibration_Start,本仓库 HAL 无此函数)
+ * 入口参数:无(操作 ADC1 寄存器)
+ * 返 回 值:0 - 校准完成;-1 - 等待超时(ADON 未清零 / CAL 未清零)
+ * 限定条件:MX_ADC1_Init() 之后、**首次转换之前**;内核启动前可调用
+ * (只用 HAL_GetTick 与寄存器,不触碰任何 CMSIS/FreeRTOS 对象)
+ * 函数说明:RM0090 §13.3.6 时序——① ADC 必须处于关闭态(清 ADON 并等其生效);
+ * ② 置 ADON 上电,等 Tstab;③ 置 CAL 启动自校准;④ 完成后**硬件自动**清 CAL,
+ * 等其清零即完成。校准值存在 ADC 内部偏置寄存器,此后所有转换都带上校正。
+ * 缺这一步的后果不是"读不到"而是"读得有偏"——正是 §7.2 那类
+ * adc1_err/异常值的候选根因之一,见 docs/硬件平台/ADC通道映射说明.md。
+ */
+int adc1_calibrate(void)
+{
+ uint32_t tickstart;
+
+ /* 1) 前置条件:ADC 必须关闭 */
+ CLEAR_BIT(ADC1->CR2, ADC_CR2_ADON);
+ tickstart = HAL_GetTick();
+ while ((ADC1->CR2 & ADC_CR2_ADON) != 0U)
+ {
+ if ((HAL_GetTick() - tickstart) > ADC1_CAL_TIMEOUT_MS)
+ {
+ return -1;
+ }
+ }
+
+ /* 2) 上电(ADON 0→1),等待稳定后再启动校准 */
+ SET_BIT(ADC1->CR2, ADC_CR2_ADON);
+ {
+ __IO uint32_t wait_loop_index = (ADC1_STAB_DELAY_US * (SystemCoreClock / 1000000U));
+ while (wait_loop_index != 0U)
+ {
+ wait_loop_index--;
+ }
+ }
+
+ /* 3) 启动自校准,等硬件把 CAL 清零 */
+ SET_BIT(ADC1->CR2, ADC1_CAL_BIT);
+ tickstart = HAL_GetTick();
+ while ((ADC1->CR2 & ADC1_CAL_BIT) != 0U)
+ {
+ if ((HAL_GetTick() - tickstart) > ADC1_CAL_TIMEOUT_MS)
+ {
+ return -1;
+ }
+ }
+ return 0;
+}
/* USER CODE END 1 */
diff --git a/Src/dma.c b/Src/dma.c
index 9b224df3..0404dcef 100644
--- a/Src/dma.c
+++ b/Src/dma.c
@@ -59,6 +59,9 @@ void MX_DMA_Init(void)
/* DMA2_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
+ /* DMA2_Stream4_IRQn interrupt configuration */
+ HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 6, 0);
+ HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);
/* DMA2_Stream7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
diff --git a/Src/freertos.c b/Src/freertos.c
index 5877bdf0..31d2fbe1 100644
--- a/Src/freertos.c
+++ b/Src/freertos.c
@@ -39,6 +39,8 @@
#include "sys_events.h"
#include "default_task.h"
#include "adc_task.h" /* B1(2026-10-10):ADC 采集消费者(手写任务,见 Variables 区) */
+#include "iec104_serv.h" /* B4(2026-10-12):IEC 104 上行服务(手写任务,见 Variables 区) */
+#include "protect_task.h" /* B5(2026-10-12):保护判据节拍任务(手写任务,同上) */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -113,10 +115,19 @@ static int hook_ptr_ok(const void *p)
* 靠审计 WARN 提示),建监听 → net_bind → CH395F → 日志尾。2026-10-10 整改后
* 静态最坏链 1024 B(整改前 1736 B)、实测峰值 616 B;⚠ 余量 1024 B **正好**
* 等于 ISR 底线 → 本任务链上再加任何调用(或新日志行)都必须先抬栈。
- * adcTask 512 words:本区下方手工定义(同上,不在 `.ioc` 里)。B1(2026-10-10)
- * ADC 采集消费者:链上仅 tpafe5160_start_conv_irq + tpafe5160_snapshot(临界区
- * 原子拷贝)+ task_wd_beat + 周期 DBG_INFO(每 10s 一条)。栈值以
- * `scripts/stack_report.py` 实测最坏链为准,改动前后各跑一次。
+ * adcTask 640 words:本区下方手工定义(同上,不在 `.ioc` 里)。B1(2026-10-10)
+ * ADC 采集消费者;L2 相量(2026-10-11)+ L1-6 有效性校验(同日)后链上为
+ * adc_input_poll + sample_ring/phasor + adc_health_report(DBG_ERROR +
+ * syslog_post 回显尾)。静态最坏链 1080 B,512 words 时余 968 B < 1024 B
+ * ISR 底线 → 抬至 640 words(2.5 KB,堆余量 min_ever 7152→≈6640 B,可承受)。
+ * 栈值以 `scripts/stack_report.py` 实测最坏链为准,改动前后各跑一次。
+ * iec104Task 640 words:B4(2026-10-12)104 上行服务(2404 监听 + link 状态
+ * 机)。link/pts 缓冲全静态;stack_report 实测静态最坏链 1456 B(拆链
+ * 重建监听 → net_bind → SPI 故障 → 日志尾,日志主导)。512 words 时余
+ * 592 B < 1024 B ISR 底线 → 抬至 640(2.5 KB)。
+ * protectTask 512 words:B5(2026-10-12)保护判据节拍(prot_elm_run 消费者
+ * + L5-4 采样闭锁)。stack_report 实测静态最坏链 872 B(float 判据 +
+ * phasor 快照 + 日志尾),余 1176 B / 57% ≥ ISR 底线 → 512 保持。
*/
/* 存储测试变量(g_storage_stats / s_perf_buf)已迁至 test/board/storage_test_task.c */
osThreadId_t timeSyncTaskHandle;
@@ -125,11 +136,38 @@ const osThreadAttr_t timeSyncTask_attributes = {
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
-/* B1(2026-10-10):ADC 采集消费者(1ms 采样节拍 + 原子快照 + 帧统计)。
- * 手写于此而非 .ioc(同 timeSyncTask:不在 Tasks01 里 → 重生成会丢,靠审计 WARN 提示)。 */
+/* B1(2026-10-10):ADC 采集消费者(TIM6 1 kHz 采样节拍 + 原子快照 + 帧统计)。
+ * 手写于此而非 .ioc(同 timeSyncTask:不在 Tasks01 里 → 重生成会丢,靠审计 WARN 提示)。
+ * ⚠ 优先级 AboveNormal(Task #32 P1-5 方案 a,2026-10-10):消费改由 TIM6 ISR 事件驱动后
+ * 提优先级,使"被唤醒 → 立即抢占其余 Normal 任务",唤醒延迟固定为 µs 级。
+ * 代价:本任务单拍最坏实测 adcm≈2.1 ms 会在 AboveNormal 上挤占 net/ftp/default/timeSync,
+ * 其中 ADC 的 2 ms 忙等(App/algo/adc_input.c)尤其敏感 → 硬化方向见
+ * docs/子系统设计/ADC采样节拍设计说明.md §10.4。 */
osThreadId_t adcTaskHandle;
const osThreadAttr_t adcTask_attributes = {
.name = "adcTask",
+ .stack_size = 640 * 4,
+ .priority = (osPriority_t) osPriorityAboveNormal,
+};
+/* B4(2026-10-12):IEC 60870-5-104 被控站服务(2404 监听 + 链路状态机消费)。
+ * 手写于此而非 .ioc(同 timeSyncTask/adcTask:不在 Tasks01 里 → 重生成会丢,
+ * 靠审计 WARN 提示)。640 words:stack_report 实测静态最坏链 1456 B(拆链→
+ * 重建监听→net_bind→SPI 故障→日志尾,日志主导);512 words 时余 592 B
+ * < 1024 B ISR 底线 → 抬至 640(2.5 KB,CCM 堆 min_ever 再降 512 B,可承受)。 */
+osThreadId_t iec104TaskHandle;
+const osThreadAttr_t iec104Task_attributes = {
+ .name = "iec104Task",
+ .stack_size = 640 * 4,
+ .priority = (osPriority_t) osPriorityNormal,
+};
+/* B5(2026-10-12):保护判据节拍(prot_elm_run 消费者 + L5-4 采样闭锁)。
+ * 手写于此而非 .ioc(同上)。512 words:stack_report 实测静态最坏链 872 B
+ * (float 判据 + phasor 快照 + 日志尾),余 1176 B / 57% ≥ ISR 底线 → 保持。
+ * 优先级 Normal:V0.1 无出口硬件(规划 R7),判据是 20ms 软实时,不需要
+ * adcTask 的 AboveNormal;规划 §4 的"1ms/高"随 P2 瞬时值元件再评。 */
+osThreadId_t protectTaskHandle;
+const osThreadAttr_t protectTask_attributes = {
+ .name = "protectTask",
.stack_size = 512 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
@@ -313,6 +351,12 @@ void MX_FREERTOS_Init(void) {
* (内核启动前)完成;本任务只做运行期采样。 */
adcTaskHandle = osThreadNew(adc_task_func, NULL, &adcTask_attributes);
+ /* B4(2026-10-12):IEC 60870-5-104 上行服务任务。任务体内先等 NET_READY
+ * 屏障(100ms 分片 + 每片喂心跳,timeSyncTask 先例),再建 2404 监听并
+ * 100ms 循环服务;纳入 IWDG required_mask(TASK_WD_IEC104,任何构建都创建)。 */
+ iec104TaskHandle = osThreadNew(iec104_serv_task, NULL, &iec104Task_attributes);
+ protectTaskHandle = osThreadNew(protect_task, NULL, &protectTask_attributes);
+
/* P2-13(2026-10-04):产品构建(未选任何 TEST_SUITE_*)不需要 testTask。
* 上方生成区按 .ioc FREERTOS.Tasks01 无条件创建它,其 2048 words(8KB) 栈 + TCB
* 取自 CCM 堆;而产品构建下 start_test_task() 仅打印一行后 osDelay(1000) 永久空转,
diff --git a/Src/main.c b/Src/main.c
index 3881ad93..3829c6a7 100644
--- a/Src/main.c
+++ b/Src/main.c
@@ -42,6 +42,7 @@
#include "app_main.h"
#include "sys_clock.h"
#include "task_watchdog.h" /* task_wd_tick_probe():round 8b 时间源存活性取证 */
+#include "adc_task.h" /* adc_tick_irq_notify():TIM6 节拍 → 唤醒 adcTask(P1-5 方案 a) */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -56,6 +57,11 @@
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
+/* N-20(2026-10-12):本文件唯一的 DBG_* 调用点是 Error_Handler;其帧经
+ * MX_*_Init 调用链叠在 MSP 上,而 MSP 仅 1KB(startup Stack_Size 0x400)
+ * → 行缓冲收紧到 128 B(本文件最长行 = Error_Handler 行 ≈78 B < 128,不截断)。
+ * 日志成本口径见 dbg_log.h 的 DBG_LINE_BUF_MAX 注释。 */
+#define DBG_LINE_BUF_MAX 128U
#define DBG_TAG "[MAIN]"
#include "dbg_log.h"
/* USER CODE END PM */
@@ -132,7 +138,7 @@ int main(void)
MX_USART3_UART_Init();
MX_IWDG_Init();
MX_ADC1_Init();
- MX_TIM6_Init();
+ MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_Delay(100);
@@ -246,13 +252,22 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
task_wd_tick_probe();
}
- /* 1 kHz 采样节拍:TIM6 更新中断内脉冲 CONVST(仅两次 GPIO 写,亚 µs)。
- * TIM6 与 BUSY EXTI(EXTI9_5_IRQn) 同为抢占优先级 6 → 互不抢占,
+ /* 1 kHz 采样节拍:TIM3 更新事件内脉冲 CONVST(仅两次 GPIO 写,亚 µs),
+ * 再唤醒 adcTask 消费这一拍(Task #32 P1-5 方案 a:ISR 只发唤醒,计算全留在任务)。
+ * 同一个更新事件还经 TRGO 硬件触发 STM32 ADC1 一轮 9 通道转换(DMA 自动搬运)
+ * → 两片转换器由同一硬件事件驱动,采样时刻对齐。
+ * TIM3 与 BUSY EXTI(EXTI9_5_IRQn) 同为抢占优先级 6 → 互不抢占,
* 读 RD 时序不会被下一次 CONVST 脉冲打断。详见
- * docs/子系统设计/ADC采样节拍设计说明.md。放 USER CODE 区(L2)。 */
- if (htim->Instance == TIM6)
+ * docs/子系统设计/ADC采样节拍设计说明.md §10。放 USER CODE 区(L2)。
+ * ⚠ 节拍源 v2.0 由 TIM6 改为 TIM3:F4 上 ADC1 的外部触发清单里没有 TIM6 TRGO
+ * (TIM6/TIM7 的 TRGO 只接 DAC),详见 App/app/app_main.c 步骤 6 注释。
+ * ⚠ 唤醒必须走 adc_tick_irq_notify(),**不要**在这里直接 osThreadFlagsSet:
+ * TIM3 在 app_main_init() 就启动,远早于建任务与 osKernelStart,
+ * "消费者就绪"门在 adc_task.c 里(同本文件 TIM7 分支 2026-10-02 的实测坑)。 */
+ if (htim->Instance == TIM3)
{
tpafe5160_start_conv_irq();
+ adc_tick_irq_notify();
}
/* USER CODE END Callback 1 */
}
@@ -264,11 +279,26 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
- /* User can add his own implementation to report the HAL error return state */
- __disable_irq();
- while (1)
- {
- }
+ /* N-20 统一降级(2026-10-12):旧形态 __disable_irq()+while(1) = 单处外设初始化
+ * 失败即整机挂死,只剩 IWDG 兜底且无任何自检日志。现:打 1 条自检日志 +
+ * NVIC_SystemReset()(与 4 个 Fault 处理同款自恢复;IWDG 是最后兜底而非唯一
+ * 出路;复位归因 SFTRSTF → 开机日志 "software (NVIC_SystemReset)")。
+ * ⚠ 不得 __disable_irq():日志必须能走串口。
+ * 调用方识别(为何不打地址):本函数无参数、调用点全在 CubeMX 生成区(不可加标签);
+ * ⚠ __builtin_return_address(0) **不可用**——AC5 前端接受但编成库函数调用,
+ * MicroLIB 无该符号 → 链接错 L6218E(2026-10-12 台架实证,勿再尝试);
+ * ARMCC __asm 只有整函数形态且取不到 C 符号、带不动 C 日志(看门狗设计说明 §10.4)。
+ * → 子系统定位靠启动日志锚点:失败点在"最后一条成功日志"与 main() 的
+ * MX_*_Init 顺序(GPIO→DMA→USART1→SPI2→SPI1→I2C1→UART4→UART5→USART2→
+ * USART3→IWDG→ADC1→TIM3→app_main_init)之间。
+ * 日志路径内核前安全:调用点全部在 osKernelStart 之前,控制台写者未注册
+ * → con_append 走 dbg_console_write_raw(DWT/计数有界轮询,huart1 未就绪
+ * 即返回),sys_clock_get_str 未初始化时打占位串——全程无内核 API。
+ * 日志成本:仅这 1 条(≈90 B Flash;行缓冲已在本文件 PM 区收到 128 B)。
+ * 重生成存活:函数体在 USER CODE Error_Handler_Debug 区(L2,KeepUserCode
+ * 逐字保留);另登记 check_cubemx_survival.py 审计项兜底(L3)。 */
+ DBG_ERROR("boot init failure, resetting (locate MX_*_Init via boot order)");
+ NVIC_SystemReset();
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
diff --git a/Src/stm32f4xx_it.c b/Src/stm32f4xx_it.c
index bb0d0ac4..f898764c 100644
--- a/Src/stm32f4xx_it.c
+++ b/Src/stm32f4xx_it.c
@@ -238,13 +238,14 @@ __asm void UsageFault_Handler(void)
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
+extern DMA_HandleTypeDef hdma_adc1;
extern DMA_HandleTypeDef hdma_spi1_rx;
extern DMA_HandleTypeDef hdma_spi1_tx;
extern DMA_HandleTypeDef hdma_spi2_rx;
extern DMA_HandleTypeDef hdma_spi2_tx;
extern SPI_HandleTypeDef hspi1;
extern SPI_HandleTypeDef hspi2;
-extern TIM_HandleTypeDef htim6;
+extern TIM_HandleTypeDef htim3;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern UART_HandleTypeDef huart4;
@@ -398,6 +399,20 @@ void EXTI9_5_IRQHandler(void)
/* USER CODE END EXTI9_5_IRQn 1 */
}
+/**
+ * @brief This function handles TIM3 global interrupt.
+ */
+void TIM3_IRQHandler(void)
+{
+ /* USER CODE BEGIN TIM3_IRQn 0 */
+
+ /* USER CODE END TIM3_IRQn 0 */
+ HAL_TIM_IRQHandler(&htim3);
+ /* USER CODE BEGIN TIM3_IRQn 1 */
+
+ /* USER CODE END TIM3_IRQn 1 */
+}
+
/**
* @brief This function handles SPI1 global interrupt.
*/
@@ -496,20 +511,6 @@ void UART5_IRQHandler(void)
/* USER CODE END UART5_IRQn 1 */
}
-/**
- * @brief This function handles TIM6 global interrupt, DAC1 and DAC2 underrun error interrupts.
- */
-void TIM6_DAC_IRQHandler(void)
-{
- /* USER CODE BEGIN TIM6_DAC_IRQn 0 */
-
- /* USER CODE END TIM6_DAC_IRQn 0 */
- HAL_TIM_IRQHandler(&htim6);
- /* USER CODE BEGIN TIM6_DAC_IRQn 1 */
-
- /* USER CODE END TIM6_DAC_IRQn 1 */
-}
-
/**
* @brief This function handles TIM7 global interrupt.
*/
@@ -566,6 +567,20 @@ void DMA2_Stream3_IRQHandler(void)
/* USER CODE END DMA2_Stream3_IRQn 1 */
}
+/**
+ * @brief This function handles DMA2 stream4 global interrupt.
+ */
+void DMA2_Stream4_IRQHandler(void)
+{
+ /* USER CODE BEGIN DMA2_Stream4_IRQn 0 */
+
+ /* USER CODE END DMA2_Stream4_IRQn 0 */
+ HAL_DMA_IRQHandler(&hdma_adc1);
+ /* USER CODE BEGIN DMA2_Stream4_IRQn 1 */
+
+ /* USER CODE END DMA2_Stream4_IRQn 1 */
+}
+
/**
* @brief This function handles DMA2 stream7 global interrupt.
*/
diff --git a/Src/tim.c b/Src/tim.c
index aa07ef25..1ebeb097 100644
--- a/Src/tim.c
+++ b/Src/tim.c
@@ -24,78 +24,85 @@
/* USER CODE END 0 */
-TIM_HandleTypeDef htim6;
+TIM_HandleTypeDef htim3;
-/* TIM6 init function */
-void MX_TIM6_Init(void)
+/* TIM3 init function */
+void MX_TIM3_Init(void)
{
- /* USER CODE BEGIN TIM6_Init 0 */
+ /* USER CODE BEGIN TIM3_Init 0 */
- /* USER CODE END TIM6_Init 0 */
+ /* USER CODE END TIM3_Init 0 */
+ TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
- /* USER CODE BEGIN TIM6_Init 1 */
+ /* USER CODE BEGIN TIM3_Init 1 */
- /* USER CODE END TIM6_Init 1 */
- htim6.Instance = TIM6;
- htim6.Init.Prescaler = 83;
- htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
- htim6.Init.Period = 999;
- htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
- if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
+ /* USER CODE END TIM3_Init 1 */
+ htim3.Instance = TIM3;
+ htim3.Init.Prescaler = 83;
+ htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
+ htim3.Init.Period = 999;
+ htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+ htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
+ if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
- if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
+ sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
+ if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
- /* USER CODE BEGIN TIM6_Init 2 */
+ sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
+ sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
+ if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
+ {
+ Error_Handler();
+ }
+ /* USER CODE BEGIN TIM3_Init 2 */
- /* USER CODE END TIM6_Init 2 */
+ /* USER CODE END TIM3_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
- if(tim_baseHandle->Instance==TIM6)
+ if(tim_baseHandle->Instance==TIM3)
{
- /* USER CODE BEGIN TIM6_MspInit 0 */
+ /* USER CODE BEGIN TIM3_MspInit 0 */
- /* USER CODE END TIM6_MspInit 0 */
- /* TIM6 clock enable */
- __HAL_RCC_TIM6_CLK_ENABLE();
+ /* USER CODE END TIM3_MspInit 0 */
+ /* TIM3 clock enable */
+ __HAL_RCC_TIM3_CLK_ENABLE();
- /* TIM6 interrupt Init */
- HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 6, 0);
- HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
- /* USER CODE BEGIN TIM6_MspInit 1 */
+ /* TIM3 interrupt Init */
+ HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
+ HAL_NVIC_EnableIRQ(TIM3_IRQn);
+ /* USER CODE BEGIN TIM3_MspInit 1 */
- /* USER CODE END TIM6_MspInit 1 */
+ /* USER CODE END TIM3_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
- if(tim_baseHandle->Instance==TIM6)
+ if(tim_baseHandle->Instance==TIM3)
{
- /* USER CODE BEGIN TIM6_MspDeInit 0 */
+ /* USER CODE BEGIN TIM3_MspDeInit 0 */
- /* USER CODE END TIM6_MspDeInit 0 */
+ /* USER CODE END TIM3_MspDeInit 0 */
/* Peripheral clock disable */
- __HAL_RCC_TIM6_CLK_DISABLE();
+ __HAL_RCC_TIM3_CLK_DISABLE();
- /* TIM6 interrupt Deinit */
- HAL_NVIC_DisableIRQ(TIM6_DAC_IRQn);
- /* USER CODE BEGIN TIM6_MspDeInit 1 */
+ /* TIM3 interrupt Deinit */
+ HAL_NVIC_DisableIRQ(TIM3_IRQn);
+ /* USER CODE BEGIN TIM3_MspDeInit 1 */
- /* USER CODE END TIM6_MspDeInit 1 */
+ /* USER CODE END TIM3_MspDeInit 1 */
}
}
diff --git a/docs/README.md b/docs/README.md
index 7cc7d007..cca2ac08 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -11,7 +11,7 @@
```
docs/
├── README.md 本索引(阅读顺序 + 全量清单)
-├── 待处理内容.md 统一待办登记簿(高频入口,保留在顶层)
+├── 任务规划/ 统一待办登记簿(README 排序权威 + 6 个拆分文件)
├── 产品需求/ 产品需求设计文档(面向产品的需求基线)
├── 架构设计/ 全局架构与设计方法论
├── 子系统设计/ 各功能子系统的设计说明
@@ -53,7 +53,7 @@ docs/
- **应用/驱动开发** → §4 架构设计 → §5 子系统设计 → §7 问题记录(**改驱动前必读对应 Trap Records**)
- **测试/验证** → §6 测试说明 → §9.2 环境与部署
- **UI 开发** → §8 UI 设计 → §6 测试说明(串口助手、UI 自动化测试流程)
-- **接手/评审** → §9.1 评审报告 → §9.3 待处理内容(统一待办登记簿)
+- **接手/评审** → §9.1 评审报告 → §9.3 任务规划(统一待办登记簿,见 `任务规划/README.md`)
---
@@ -88,8 +88,9 @@ docs/
| [`系统墙钟时间维护说明.md`](子系统设计/系统墙钟时间维护说明.md) | `sys_clock`:RTC 基准、tick 回绕防护、30 天惰性再基准化 |
| [`控制台单写者日志设计说明.md`](子系统设计/控制台单写者日志设计说明.md) | 控制台日志单写者模型(避免多任务交错输出) |
| [`运行与自检记录设计说明.md`](子系统设计/运行与自检记录设计说明.md) | `/log/run.log`·`selftest.log` 定长记录与待机显示 |
-| [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5) |
-| [`ADC采样节拍设计说明.md`](子系统设计/ADC采样节拍设计说明.md) | **ADC 采集链路采样节拍**:现状(任务周期软触发)→ 目标(TIM6 更新中断触发 CONVST)的方案选型、CubeMX 配置规格、代码改造清单、时序/优先级分析与验收判据;**规划中(未实现)** |
+| [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5)。**§11 = 任务级饥饿的诊断方法与 `[PROF]` 实测基线**(Task #32 沉淀:判读口径、量化归因、三条脆弱点、明确不做的四条) |
+| [`ADC采样节拍设计说明.md`](子系统设计/ADC采样节拍设计说明.md) | **ADC 采集链路采样节拍**:TIM3 1 kHz 更新事件同时触发 TPAFE CONVST 与(经 TRGO)STM32 ADC1 + DMA 乒乓缓冲;方案选型、CubeMX 配置规格、代码改造清单、时序/优先级分析、验收判据与实测剖面;**已落地并上板闭环** |
+| [`IEC104上行设计说明.md`](子系统设计/IEC104上行设计说明.md) | **B4 上行规约**:IEC 60870-5-104 被控站(TCP 2404)范围/点表(V0.1 有效子集)/链路流程(k/w/t1~t3、总召、COV、遥控两段式、定值)/netTask 接入决策/预算与测试方案;协议核心纯逻辑 + L0.5 全栈单测 |
| [`网络/BSD_Socket_API_使用指南.md`](网络/BSD_Socket_API_使用指南.md) | **上层视图**:BSD Socket API 公开语义、示例、常见问题 |
| [`网络/BSD_Socket_API_函数调用关系.md`](网络/BSD_Socket_API_函数调用关系.md) | **内部视图**:netTask / 消息队列 / L3 挂起 / 重试对账的调用关系与设计意图 |
| [`网络/Net层阻塞模型架构设计.md`](网络/Net层阻塞模型架构设计.md) | **等待模型**:pending 请求架构、五路完成语义、设计边界与性能特征 |
@@ -124,7 +125,11 @@ docs/
|---|---|
| [`看门狗单元测试说明.md`](测试说明/看门狗单元测试说明.md) | Task Watchdog 判定状态机(位图 / 掩码 / 节流 / tick 回绕),39 用例 |
| [`系统时钟单元测试说明.md`](测试说明/系统时钟单元测试说明.md) | sys_clock 时间换算 / 星期 Zeller / 30 天惰性再基准化 / RTC 故障降级,100 断言点 |
-| [`配置文件单元测试说明.md`](测试说明/配置文件单元测试说明.md) | cfg_file INI 解析 / 值编解码 / load-save-poll 契约 / 超长守卫,139 断言点(含抓出的外科手术保存死码 P0) |
+| [`配置文件单元测试说明.md`](测试说明/配置文件单元测试说明.md) | cfg_file INI 解析 / 值编解码 / load-save-poll 契约 / 超长守卫,149 断言点(含抓出的外科手术保存死码 P0) |
+| [`ADC输入层单元测试说明.md`](测试说明/ADC输入层单元测试说明.md) | adc_input 语义通道映射 / ADC1 顺序扫描(**Rank↔通道判据**)/ poll 契约 / HAL 故障路径,70 用例 |
+| [`相量层单元测试说明.md`](测试说明/相量层单元测试说明.md) | phasor 全周傅氏系数表 / 幅值·相位 / 谐波抑制与提取 / 频偏 / 采样环 / 双区交换,57 用例 |
+| [`卷锁单元测试说明.md`](测试说明/卷锁单元测试说明.md) | vol_lock 创建 / 获取 / 释放契约(**fail-open 不变量**:创建失败仍返回 1 且零阻塞)/ 幂等 / 超时透传,33 断言点 |
+| [`时间同步单元测试说明.md`](测试说明/时间同步单元测试说明.md) | time_sync 分片喂狗延时 / 监听退避 / 时间包重组 / 任务主循环(**N-17 的全部逻辑贡献者**),80 断言点 |
**模块测试指南**(动手写测试前先读)
@@ -172,6 +177,7 @@ docs/
| [`RAM使用分析与优化方案.md`](内存管理/RAM使用分析与优化方案.md) | **内存主文档**:占用分析、优化方案、定栈规则 |
| [`内存内容分配策略.md`](内存管理/内存内容分配策略.md) | 各内存区(SRAM/CCM)的分配策略 |
| [`存储栈RAM优化调研.md`](内存管理/存储栈RAM优化调研.md) | 存储栈 RAM 优化调研与结论 |
+| [`RAM预算台账.md`](内存管理/RAM预算台账.md) | **预算台账**:子系统配额、CCM 分配、趋势基线与 92% 门禁(N-27 三件套,2026-10-12) |
| `data/` | `ram_report_*.md` / `stack_report_*.md` 快照数据(由 `scripts/` 生成) |
### 7.3 问题记录(**改驱动前必读**)
@@ -207,19 +213,20 @@ docs/
| 文档 | 内容 |
|---|---|
-| [`问题记录/项目审查与优化建议报告.md`](问题记录/项目审查与优化建议报告.md) | 项目全景审查:文档/代码/架构/可靠性问题清单(D-1~D-7、N-xx、Px-xx)——**体系层评审报告,非待办池**(待办见 §9.3 `待处理内容.md`) |
+| [`问题记录/项目审查与优化建议报告.md`](问题记录/项目审查与优化建议报告.md) | 项目全景审查:文档/代码/架构/可靠性问题清单(未闭环项:D-1、N-xx、Px-xx;**已修复项按「修复即销项」删除**,去处见其变更记录)——**体系层评审报告,非待办池**(待办见 `任务规划/README.md`) |
### 9.2 环境与部署
| 文档 | 内容 |
|---|---|
| [`项目部署环境要求.md`](环境与部署/项目部署环境要求.md) | 开发机软件环境、目标板资源预算、现场部署与验收步骤 |
+| [`并行任务工作树方案.md`](环境与部署/并行任务工作树方案.md) | **多任务并行开发机制**:`git worktree` 隔离、目录与共享/独占分配(junction 规则)、`task-worktree`(`@task`)SKILL 命令面、单写者文档与合并残留审计、台架锁;**已落地并端到端实测**(见其 §11) |
### 9.3 台账与外部资料
| 位置 | 内容 |
|---|---|
-| [`待处理内容.md`](待处理内容.md) | **统一待办登记簿**——审计缺陷(P-x)+ 台架/审计观察(N/S/T-x)+ 测试与架构专项(S1x/A-x)+ 任务排序(阶段 A/B/C/D)+ 诊断基线;只保留未闭环项,完成即删 |
+| [`任务规划/`](任务规划/README.md) | **统一待办登记簿(文件夹)**——`README.md` = 排序权威(波次 W1~W7)+ 编号空间 + 维护约定;`W1-采集保护收尾.md` = 当前波次任务卡(里程碑 × 并行道 × 五字段);`登记簿.md` = N/S/T-x 长期观察;`排期详述.md` / `暂缓-RS485.md` / `测试与架构专项.md` / `基线与待确认.md` = 波内详表 / 冻结簇 / 专项 / 基线;只保留未闭环项,完成即删 |
| `硬件平台/` | 网表详细分析报告与语义图(V0.0 / V0.1);**[`ADC通道映射说明.md`](硬件平台/ADC通道映射说明.md)——V0.1 十七路 ADC 语义/网名/通道号权威映射表(固件取数、UI 显示、自检注入以此为准)** |
| `芯片手册/` | 外部芯片数据手册转录(CH395F / GD5F2GQ5UE / SD2506 / TPAFE5160 / RTL8305NBI 等) |
| `images/` | 文档技术配图(`.svg` 产物,由 `tools/arch_diagram/`、`tools/refgraph/` 生成) |
@@ -242,19 +249,19 @@ docs/
## 11. 已知文档问题(待清理)
-> 来源:《项目审查与优化建议报告》D-3/D-7。
+> 来源:《项目审查与优化建议报告》D-3/D-7(两条已销项,正文已移入 [`文档规范.md`](文档规范.md) 与 pre-commit 门禁)。
> **D-3 / D-6 / D-7 已处理完毕**(2026-09-25):全库链接存在性检查通过(66 文档 0 失效)、任务表与
> `Src/freertos.c` 一致、文档规范已建立。下表保留**仍需跟踪**的项。
| 问题 | 位置 | 说明 |
|---|---|---|
-| 指向未实现工具 | `UI_界面制作流程.md`、`UI_自动化测试流程.md` → `tools/ui_mock/` | 文档承诺了未实现的 UI 渲染/比对工具(其引用已按 §3.3 标注「已删除」状态词) |
-| 产物缺失 | `内存管理/RAM使用分析与优化方案.md` → `scripts/ram_baseline.tsv` | 方案写了、工具未建(引用已加 `[计划]` 标记) |
+| 指向未实现工具 | `UI_界面制作流程.md`、`UI_自动化测试流程.md` → `tools/ui_mock/` | 该目录**始终不存在**;golden 回归实际落在 `simulator/tests/`(四脚本)。引用待改指,见 `docs/问题记录/项目审查与优化建议报告.md` §6.1 |
+| ~~产物缺失~~ | ~~`内存管理/RAM使用分析与优化方案.md` → `scripts/ram_baseline.tsv`~~ | ✅ 已建(2026-10-12,N-27 前半):`ram_baseline.py` + `ram_baseline.tsv` + [`RAM预算台账.md`](内存管理/RAM预算台账.md) + `mem-budget` 钩子 |
| ~~目录名含空格~~ | ~~`UI 设计/`~~ | ✅ 已修复(2026-09-25,D-6):改名为 `UI设计/`,全仓库 67 处引用同步;命名约定见 [`文档规范.md`](文档规范.md) §1 |
| ~~缺一致性门禁~~ | — | ✅ 已落地(2026-09-25/26,D-7 三档齐):`scripts/check_doc_links.py`(存在性)+ `scripts/gen_task_table.py`(数值)+ `scripts/check_mem_numbers.py`(体积),接入 pre-commit `doc-links` / `task-table` / `mem-numbers` 钩子 |
| ~~`docs/文档规范.md` 未创建~~ | — | ✅ 已创建(2026-09-25):[`文档规范.md`](文档规范.md) |
-> 完整清单与建议见 [`问题记录/项目审查与优化建议报告.md`](问题记录/项目审查与优化建议报告.md) §3.3。
+> 未闭环的文档类问题见 [`问题记录/项目审查与优化建议报告.md`](问题记录/项目审查与优化建议报告.md) §3.1(体量与 D-1)与 §3.4(复核后仅剩三条:D-1、语义断链、元信息漂移)。
---
@@ -262,9 +269,12 @@ docs/
| 日期 | 变更 | 说明 |
|---|---|---|
+| 2026-10-12 | 新增环境与部署 | 新建 `docs/环境与部署/并行任务工作树方案.md`(**多任务并行开发机制规格**:`git worktree` 隔离、目录与共享/独占分配、`task-worktree` / `@task` skill 设计、单写者文档与合并残留审计、台架锁;**已落地并端到端实测**);本索引 §9.2 补登记 |
+| 2026-10-11 | 功能 | N-19 FTP 鉴权:`/config.ini` 新增 `[ftp]` 段(`user` / `pwd_hash` = SHA-256(user‖password) / `write`)+ 恒定时间比较 + 连续 5 次失败锁 60 s + 未登录命令门;出厂默认 admin/admin,默认口令在用有启动告警。新增 `App/util/sha256.*`、`Drivers/BSP/NETCFG/ftp_cfg.*`、`Drivers/BSP/NET/lftpd/lftpd_auth.*`;主机侧单测 `test/host/sha256/`(13 例)+ `test/host/ftp_auth/`(36 例),pre-commit 钩子 26→28;台架脚本 `test/host/ftp_auth_bench.py`。文档同步:`docs/子系统设计/网络配置说明.md`、`docs/架构设计/STM32F4-Base存储架构说明.md`、`docs/代码规范/pre-commit使用说明.md`、`docs/产品需求/产品需求设计文档.md` |
+| 2026-10-11 | 文档调整 | `AGENTS编写指南.md` 新建后**同日删除**(要点已沉淀入 `AGENTS.md` 宏观重构与本索引变更记录,指南类内容不单独成文);撤销 §1 目录树与 §7.1b 登记;同日 `AGENTS.md` 完成宏观重构(补项目概况/目录地图、平台约束分组、边界块) |
| 2026-10-10 | 新增子系统设计 | 新建 `docs/子系统设计/ADC采样节拍设计说明.md`(ADC 采集链路 1 kHz 硬件定时器触发的 CubeMX 配置规格与代码改造清单,B1 遗留项规划);本索引 §5.1 补登记 |
| 2026-10-10 | 新增算法专题 | 新建 `docs/算法/继电保护功能复现规划.md`(保护链路复现总规划);本索引 §1 目录树补 `算法/`、§5 拆为 5.1 功能子系统 / 5.2 算法专题并登记 `算法/` 两篇文档 |
-| 2026-10-05 | 新增产品需求文档 | 新建 `docs/产品需求/产品需求设计文档.md`(面向产品的需求基线);本索引新增 §3 产品需求、§2 推荐阅读顺序置顶、§1 目录树补 `产品需求/`;后续章节号顺延(架构设计 §4 … 变更记录 §12) |
+| 2026-10-05 | 新增产品需求文档 | 新建 `docs/产品需求/产品需求设计文档.md`(面向产品的需求基线);本索引新增 §3 产品需求、§2 推荐阅读顺序置顶、§1 目录树补 `产品需求/`;后续章节号顺延(本文 §4 … 本文 §12) |
| 2026-09-25 | 目录重组 + 索引建立 | 顶层 16 篇散落 md 归入 5 个新目录(`架构设计/` `子系统设计/` `网络/` `审查记录/` `环境与部署/`);全仓库 90 处引用同步;新建本索引(D-5) |
| 2026-09-25 | D-6 目录去空格 | `docs/UI 设计/` → `docs/UI设计/`;全仓库 67 处引用同步(含 URL 编码形态 `UI%20设计`);命名约定写入 §10 |
| 2026-09-25 | D-7 文档-代码一致性门禁 | 新建 `scripts/check_doc_links.py`(链接存在性,含行内代码豁免/就近状态词/`[计划]` 标记)、`scripts/gen_task_table.py`(任务表由 `Src/freertos.c` 生成/校验,锚点 `TASK_TABLE:BEGIN/END`);接入 pre-commit `doc-links`/`task-table` 钩子;全库 66 文档 0 失效 |
diff --git a/docs/UI设计/RemoDispBus协议与接收设计说明.md b/docs/UI设计/RemoDispBus协议与接收设计说明.md
index 46c53abd..cfab465a 100644
--- a/docs/UI设计/RemoDispBus协议与接收设计说明.md
+++ b/docs/UI设计/RemoDispBus协议与接收设计说明.md
@@ -239,6 +239,23 @@ if (n > 0) {
2. **最小帧长判据统一**:`remo_handle` 以 `n < 5` 拦截(最短合法帧 5 字节),两端一致——旧 MCU `n < 6` 会丢 5 字节 KEEPLIVE/INIT 帧的隐患已随统一消除。
3. **断线重连统一**:`ui_net_sink_poll` 在客户端断开后用 `net_get_sock(g_listen) == NULL` 判监听是否失效(MCU 单连接模型下监听随客户端一起失效),失效即 `bind_listen()` 重建——两端一致,并修复了 MCU 单连接模型断线后无法重连的隐患。
4. **协议兼容**:线格式与命令语义不变,任一端都能与 DTU-RemoteLCD 参考客户端互通;双端行为一致已由 e2e 回归(`simulator/tests/check_remo_sink.py`,0/25600 位差异)与双端构建(Keil + CMake 均 0 错误 0 警告)验证。
+5. **驱动者是宿主,不是协议自己**:`ui_net_sink_poll()` 由宿主周期调用——MCU 端目前 = **uiTask 主循环 10 ms**,模拟器 = `main.c` 主循环。协议本身不含节拍、不阻塞。
+
+> ✅ **门控已落地(2026-10-10)**:`ui_net_sink_poll()` 现在**先查后动**——`net_can_accept()` /
+> `net_can_read()` 纯 RAM 读控制块判"有没有活",无活直接返回,不再每 10 ms 发一次
+> `net_accept`(原 100 msg/s 队列压力,与 FTP 抢深度 8 的队列)。门控判"无数据"时仍
+> **每 32 轮无条件 recv 一次**兜底(`readable` 只由 RECV_OK 中断置位,中断丢失时纯门控
+> 会把数据永久藏住)。模拟器后端恒返回"就绪",行为逐位不变。
+>
+> ⚠ **残留暴露(已知,刻意不处理)**:门控只消除"无谓的往返",消除不了"往返本身无上界"——
+> 客户端在连且 netTask 拥塞时,3204 B 的 LCDMEM 应答仍可能让宿主任务等最长 ≈4 s。
+> "把推流搬出 uiTask(独立任务 + 帧 seqlock)"已完整评估并**决定不做**:实测刷新最坏周期
+> (`uip` ≈61 ms)是 **LVGL 帧周期地板**、与网络无关,搬任务不会让刷新变快;成本却含
+> **必改 `.ioc`** 的重生成与全套回归,收益只剩极端并发下的观感隔离。若日后它成为实际问题,
+> 正确方向是先治 netTask 排队公平性(登记簿 N-4 二期),不是把 UI 搬走。
+> 观测手段:`[PROF]` 的 `uis`(往返最坏)与 `uisn`(此刻仍在推流段内多久)——
+> **`uisn` 持续非 0 就是这一格真发生的铁证**;字段语义与判读见
+> [`../子系统设计/看门狗设计说明.md`](../子系统设计/看门狗设计说明.md) §11.4。
---
diff --git a/docs/UI设计/UI_界面制作流程.md b/docs/UI设计/UI_界面制作流程.md
index 3f4bd9da..1d82867a 100644
--- a/docs/UI设计/UI_界面制作流程.md
+++ b/docs/UI设计/UI_界面制作流程.md
@@ -1,7 +1,7 @@
# UI 界面制作流程(参考图 → LVGL)
> **用途**:规范"参考图 → 设计规格 → LVGL 代码 → 双端验证"的完整管线,规定每一步的**动作、产物、放行条件(gate)**,保证过程可重复、可交接。
-> **适用**:所有 UI 新界面 / 界面大改;小改动走 §10 快速通道。
+> **适用**:所有 UI 新界面 / 界面大改;小改动走「小改动快速通道」一节(见文末第十节)。
> **配套文档**:`UI_设计架构.md`(系统如何工作)、`菜单页面设计.md`(菜单/导航行为与数据流设计)、`LVGL模拟器部署与使用说明.md`(模拟器)、`UI_中文字体生成与配置.md`(字体生成)、`UI_自动化测试流程.md`(P2/P3 回归门禁的可执行版本:脚本 + 池水位常量 + 黄金参考更新)。
> **首个实例**:界面 01 主界面(根页),规格 `mockups/01_主界面_说明.md`(本文所有示例数字均取自该试点)。
@@ -24,7 +24,7 @@ flowchart TD
P3 -- "Gate: @build 0 错 0 警 + @test PASS + 上机目检通过" --> P4(["P4 归档]
→ 变更记录/台账 + 随代码提交(代码+字体+字集+规格+文档)"])
```
-> 小改动(文案/间距/纯交互/字体重生成)走 §10 快速通道,无需全流程。
+> 小改动(文案/间距/纯交互/字体重生成)走「小改动快速通道」一节(见文末第十节),无需全流程。
### 1.1 各阶段职责分工
@@ -160,7 +160,7 @@ flowchart TD
| 黑/白背景 | `lv_style_set_bg_color` + `lv_style_set_bg_opa` | 1bpp 仅纯黑/纯白 |
| 文本 | `lv_label_create` + `lv_label_set_text_static` | 静态字符串(C5) |
| 文字对齐 | 标签定位 / `lv_label_set_long_mode` | 默认左对齐(见 §3.6 推论) |
-| 选中反色 | `LV_STATE_CHECKED` 样式(bg/文字色互换) | 条目对象**自身**带 CHECKED(防白底白字,架构 §5.5) |
+| 选中反色 | `LV_STATE_CHECKED` 样式(bg/文字色互换) | 条目对象**自身**带 CHECKED(防白底白字,`UI_设计架构.md` §5.5) |
| 隐藏状态 | `LV_OBJ_FLAG_HIDDEN` | 页面/状态切换 |
| 圆角/阴影/透明/渐变 | **禁止**(C8) | 规格里就不该有 |
@@ -175,8 +175,8 @@ flowchart TD
### 4.3 翻译纪律
1. **逐参数翻译**:代码中每个 px 值必须能在规格"布局参数表"中找到出处;禁止"看着办"
2. 样式沿用现有 `s_style_flat` 模式(静态样式、一次性初始化、后加优先覆盖主题)
-3. 焦点对象恒为**页面容器**;条目不入输入组(架构 §5.4)
-4. 容器**永不删除**(indev 释放事件 use-after-free,架构 §5.2);只建/毁框体(行自绘、无对象)
+3. 焦点对象恒为**页面容器**;条目不入输入组(`UI_设计架构.md` §5.4)
+4. 容器**永不删除**(indev 释放事件 use-after-free,`UI_设计架构.md` §5.2);只建/毁框体(行自绘、无对象)
5. 编码纪律(C6):Edit 工具精确替换;文件保持 UTF-8 BOM 不变
6. 注释按代码规范 V1.0:文件头模块块 + 函数头块(功能/参数/返回/限定/说明)
@@ -241,7 +241,7 @@ flowchart TD
### 5.4 Gate(进入 P3)
- [ ] 模拟器出图 `shot.png` 与规格参数表逐项一致、与参考图结构一致(§5.2,忽略 5.3)
-- [ ] 导航行为符合键位语义表(架构 §3.4)
+- [ ] 导航行为符合键位语义表(`UI_设计架构.md` §3.4)
- [ ] 自动化回归 PASS:`check_menu_rows.py` + `check_content_pages.py`(`UI_自动化测试流程.md`)
---
@@ -307,17 +307,17 @@ flowchart TD
| 用了字体子集外的字 | 该字显示空白 | P0 字符集核对 + P1 字体重生成 |
| 字体缺字形(如宋体无 ▶) | 该字符显示成方框 □ | `make_font.py` 已内置 `merge_arrow_glyph()` 自动补回;其余缺字形同法(从有该字的字体提取位图合并,§4.4) |
| 新 .c 只注册了 Keil(或只改了 CMake) | 单端编译缺函数/静默缺失 | 双端注册(C9) |
-| 模拟器主循环调 `SDL_PollEvent` | 键盘全部无响应 | 事件只由 LVGL SDL 驱动内部轮询(架构 §5.7) |
-| 选中行"白底白字" | 选中文字不可见 | 历史坑(2026-09-02):子 label 自身 card 样式总命中、继承不到父选中色;现行行自绘(绘制时给色、无样式继承)已根除(架构 §5.5) |
-| 按键处理里删除焦点容器 | use-after-free 崩溃 | 容器持久不删(架构 §5.2) |
+| 模拟器主循环调 `SDL_PollEvent` | 键盘全部无响应 | 事件只由 LVGL SDL 驱动内部轮询(`UI_设计架构.md` §5.7) |
+| 选中行"白底白字" | 选中文字不可见 | 历史坑(2026-09-02):子 label 自身 card 样式总命中、继承不到父选中色;现行行自绘(绘制时给色、无样式继承)已根除(`UI_设计架构.md` §5.5) |
+| 按键处理里删除焦点容器 | use-after-free 崩溃 | 容器持久不删(`UI_设计架构.md` §5.2) |
| LVGL 池爆 40KB | 对象创建失败/画面残缺 | §4.5 预算 + 按需建毁 |
| 极性配反 | 黑屏/白屏/不可读 | 模拟器 mono 主题 `dark_mode` 参数 + MCU 端背景色一致 |
| 步进宽接近/超出格宽的标签居中 | 亚像素错位/不对称 | 左对齐 + 字体侧边距(§3.6) |
-| 关闭了 `LV_DRAW_SW_COMPLEX`(省代码) | `lv_line` 斜线不绘制(如主界面顶部"浅 V"两腿消失,只剩水平线) | `LV_DRAW_SW_COMPLEX` 两端显式 =1;`LV_DRAW_SW_CIRCLE_CACHE_SIZE` 须非零(默认 4,否则零长数组 C2466)(架构 §关键约束 7) |
+| 关闭了 `LV_DRAW_SW_COMPLEX`(省代码) | `lv_line` 斜线不绘制(如主界面顶部"浅 V"两腿消失,只剩水平线) | `LV_DRAW_SW_COMPLEX` 两端显式 =1;`LV_DRAW_SW_CIRCLE_CACHE_SIZE` 须非零(默认 4,否则零长数组 C2466)(`UI_设计架构.md` §关键约束 7) |
| 斜线用细 `line_width`(如 2px) | 1bpp 阈值吃掉斜线边缘,腿偏细、比设计稿细 | 斜线加粗(腿 3px),或按段拆 `lv_line` 分粗细(如"浅 V"两腿 3 / 水平线 2) |
| 规格引入翻译不出来的效果 | P1 翻译不出来 | 1bpp 禁区(C8),P0 阶段就拦下 |
| UI 源文件(UTF-8 BOM)里 `snprintf` 格式串含中文 | 模拟器 cl.exe 报 C4819(代码页 936 误读 UTF-8 字节) | 格式化输出用 `lv_snprintf`(LVGL 自带,双端一致),勿用标准库 `snprintf` |
-| 常驻样式用动态 `lv_style_t` + set_prop | LVGL 池永久占用:首次 set_prop 分配 `values_and_props` 且永不释放(x64 实测 88 B/个) | 常驻样式一律常量样式:`LV_STYLE_CONST_INIT(var, props[])` + `lv_style_const_prop_t[]`(属性存 Flash、零池分配,见架构 §5.6) |
+| 常驻样式用动态 `lv_style_t` + set_prop | LVGL 池永久占用:首次 set_prop 分配 `values_and_props` 且永不释放(x64 实测 88 B/个) | 常驻样式一律常量样式:`LV_STYLE_CONST_INIT(var, props[])` + `lv_style_const_prop_t[]`(属性存 Flash、零池分配,见`UI_设计架构.md` §5.6) |
---
diff --git a/docs/UI设计/UI_自动化测试流程.md b/docs/UI设计/UI_自动化测试流程.md
index e1f8d4ba..ab1a2b7a 100644
--- a/docs/UI设计/UI_自动化测试流程.md
+++ b/docs/UI设计/UI_自动化测试流程.md
@@ -25,7 +25,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警
| 步 | 脚本/命令 | 门禁内容 | 预期输出 |
|---|---|---|---|
-| 1 | `simulator/tests/check_menu_rows.py` | 菜单 8 状态:选中行白带 / 非选行黑底白字 / 框体 1px 边框 / 往返像素一致(S3==S1、S6==S1、S7==S2)+ S0 池基线 | `PASS: 8 状态全部断言通过(8 图,池基线 4784 B)` |
+| 1 | `simulator/tests/check_menu_rows.py` | 菜单 8 状态:选中行白带 / 非选行黑底白字 / 框体 1px 边框 / 往返像素一致(S3==S1、S6==S1、S7==S2)+ S0 池基线 | `PASS: 8 状态全部断言通过(8 图,池基线 5368 B)` |
| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,50 条,独立于 `ref/`)→ ② 11 页截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 | `PASS: 11 内容页 + 往返池水位全部通过`(文本断言先于像素比对,失败即中止) |
| 3 | `keil-build` skill(`keil_build.py`) | MCU 端编译(UI 源码双端共用,改一端必须过另一端) | 退出码 0 = BUILD_OK |
| 4 | `scripts/check_coding_standard.py` | 代码规范 V1.0 机械检查 | PASS |
@@ -44,8 +44,12 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警
| 常量 | 所在脚本 | 当前值 | 含义 |
|---|---|---|---|
-| `MENU_POOL_BASELINE` | check_menu_rows.py | **4784 B** | 菜单根框建完的 LVGL 池 used(x64 模拟器) |
-| `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | **4864 B** | 开页-关页往返后水位 = 基线 + 一次性残留 80 B |
+| `MENU_POOL_BASELINE` | check_menu_rows.py | **5368 B** | 菜单根框建完的 LVGL 池 used(x64 模拟器) |
+| `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | **5448 B** | 开页-关页往返后水位 = 基线 + 一次性残留 80 B |
+
+> 水位历史:4784 →(2026-09-14 待机模块常驻 +656)5440 / 5520 →(2026-10-11 裁剪 LVGL 控件类 −72)5368 / 5448。
+> 后一次漂移已核实为**有意且更优**:像素逐字节未变(11 页黄金参考在裁剪**前**生成、裁剪后全部一致),
+> 且一次性残留仍是 80 B(5448 − 5368)→ 泄漏特征未变,只是 `lv_theme_apply` 不再为已不存在的控件类登记样式条目。
**漂移处理**:菜单/页面结构或样式变更导致门禁失败时,先确认新值合理(残留分解见
`菜单页面设计.md` §9.5),再更新脚本常量并在本文变更记录登记。**禁止为过门禁盲目改常量**——
@@ -102,3 +106,4 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警
| 2026-09-10 | 新增 `simulator/tests/regen_ref_png.py`(由 `ref/*.bin` 批量重生成目检 PNG,默认 `--scale 6 --invert`);`ref/*.png` 加入 `.gitignore`(本地调试产物不入库);§四 补"重生成目检 PNG"步骤(修复"刷了 `.bin` 参考但 `.png` 仍是旧渲染 → 看似乱码") |
| 2026-09-10 | `simulator/tests/ref/` **整目录不入库**(`.bin` 黄金参考 + `.png` 目检图均为本地生成物):`git rm --cached` 原有 3 个 `.bin`,`.gitignore` 改为忽略整目录;首次/换机后先 `--update` 生成参考;`--update` 先跑文本断言防误刷;§四 重写 |
| 2026-09-13 | 管线同步:`UI_界面制作流程.md` 移除 HTML 样机阶段(五阶段 → 四阶段 P0~P4),本文定位改为"P2/P3 门禁的可执行版本";§六 关系描述更新为 P0 规格 → P1 编码 → P2 本文回归 → P3 上机目检 → P4 归档;移除 `tools/ui_mock/` 引用 |
+| 2026-10-11 | **裁剪 LVGL 死代码(F-1/F-2/F-3)时的门禁记录**:先用**未裁剪**配置 `--update` 生成 11 页黄金参考,再切到裁剪配置重编重跑 → **逐字节全部一致**(菜单 8 状态像素一致),证明裁剪零视觉影响。池水位门禁随之 5440/5520 → **5368/5448**(−72 B;一次性残留仍 80 B,故非泄漏变化,见 §三 水位历史)。顺带揪出并修掉 `Lib/lv_conf.h` 与 `simulator/lv_conf.h` 的**长期漂移**(模拟器早已 I1-only、MCU 端未裁)→ 规则见 `UI_设计架构.md` §1.4 第 10 条,量化见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4 |
diff --git a/docs/UI设计/UI_设计架构.md b/docs/UI设计/UI_设计架构.md
index b5bb1b5e..0795abe7 100644
--- a/docs/UI设计/UI_设计架构.md
+++ b/docs/UI设计/UI_设计架构.md
@@ -23,7 +23,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓
| `Drivers/BSP/UI/ui_net_sink.c/h` | **共用** | RemoDispBus 服务端(收键 + 回送帧缓冲),MCU/PC 双端同一份;平台后端 `net_socket` + `ui_net_sink_backend`(见 RemoDispBus 协议文档) |
| `Drivers/BSP/UI/ui_font_cn_12.c` | **共用** | 中文 1bpp 子集字体(`lv_font_conv` 生成物) |
| `App/task/ui_task.c/h` | MCU | UI 任务:等 NET_READY → 初始化 → 10ms 刷新循环 |
-| `Lib/lv_conf.h` | MCU | LVGL 配置(1bpp / 40KB 内存池 / 默认字体) |
+| `Lib/lv_conf.h` | MCU | LVGL 配置(1bpp / 40KB 内存池 / 默认字体);⚠ **必须与 `simulator/lv_conf.h` 同一特性子集**,见 §1.4 第 10 条 |
| `simulator/`(main.c / sim/ / lv_conf.h / CMakeLists.txt) | PC | SDL2 模拟器(替代 ui_port,共用全部 UI 源码);支持 `--screenshot` / `--keys ` 无头截图验证 |
| `tools/font_gen/`(extract_cn.py / make_font.py)+ `ui_cn_chars.txt` | 工具 | 字符集提取 / 字体生成 |
| `tools/menu_gen/gen_menu.py` + `ui_menu_tree.txt` | 工具 | 菜单声明源 → 生成 ui_menu_tab.c/h |
@@ -43,7 +43,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓
### 1.4 硬约束(改 UI 必须遵守)
-1. **RAM 紧张**:新增页面/控件/缓冲前必须核算 LVGL 池(40KB)与任务栈余量(见 `待处理内容.md`)
+1. **RAM 紧张**:新增页面/控件/缓冲前必须核算 LVGL 池(40KB)与任务栈余量(见 `docs/任务规划/基线与待确认.md` §4 内存预算)
2. **新增汉字必须重新生成字体**(`tools/font_gen/` 两步),否则显示空白
3. 文本一律用静态字符串(`lv_label_set_text_static`),不 strdup
4. UI 相关文件为 **UTF-8 带 BOM**(`Drivers/BSP/UI/*`、`ui_task.c`、`Lib/lv_conf.h`),其余源码无 BOM;禁止 PowerShell 整文件重写(编码铁律,见 AGENTS.md)
@@ -64,6 +64,15 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓
`lv_display_set_theme()` + 对**活动屏幕**再 `lv_theme_apply()`(实测 `bg_before=F5F5F5` → `bg_after=000000`)。
另外 I1 缓冲头 8 B 调色板 LVGL 不替调用方初始化(上电全 0 = 两项透明黑),须显式写 `[0]=黑 [1]=白`;
渲染器本身按亮度阈值置位(`lv_draw_sw_blend_to_i1.c`:亮 → 位=1),与客户端"位=1 即亮"的约定一致。
+10. **两份 `lv_conf.h` 必须是同一特性子集(2026-10-11 定;F-1/F-2/F-3 落地时暴露的漂移)**:`Lib/lv_conf.h`(MCU)与 `simulator/lv_conf.h`(PC)是**两份独立文件**,此前已漂移——模拟器早已 I1-only,MCU 端却让 11 个 `LV_DRAW_SW_SUPPORT_*`、5 个未用控件类、`LV_USE_THEME_SIMPLE`、`LV_USE_FLEX/GRID` 全靠 `lv_conf_internal` 默认值隐式生效,把用不到的代码整份链进镜像 = **实测 64,576 B 死代码(旧镜像的 23.5%)**。现两端对齐为同一子集:
+ * 绘制核只留 `LV_DRAW_SW_SUPPORT_I1`(显示是 1bpp;字形 A8 位图是作为 **mask** 传入、混合按**目的**格式分派,故不需要 A8/L8/AL88);
+ * 控件全关(`label`/`button`/`buttonmatrix`/`bar`/`textarea`/`image`/`keyboard`)——全仓(`Drivers/BSP/UI`、`App`、`simulator`)**零控件构造函数**,只用 `lv_obj_create` + `lv_theme_mono_init`;
+ * `LV_USE_THEME_SIMPLE 0`:它是把上述控件类拉进镜像的**锚点**(`lv_display_create()` 会强链 `lv_theme_simple_init`);⚠ 9.3 **没有** `LV_USE_THEME_BASIC`,旧配置里那条是无效项,已删;
+ * `LV_USE_FLEX 0` / `LV_USE_GRID 0`:全仓无 `lv_obj_set_flex_*` / `lv_obj_set_grid_*` 调用。
+ ⚠ 裁剪后 ARMCC 报 `#177-D`(上游 9.3.0 在 `lv_draw_sw_blend_to_i1.c` 漏了 `lv_color16_from_u16` 的定义处守卫)→ 本仓已按使用点补 `#if LV_DRAW_SW_SUPPORT_RGB565_SWAPPED`(原型 + 定义两处),升级 LVGL 时若上游已修可去掉。
+ ⚠ 将来引入控件型页面(U2 原型库)时**两端同步**逐项打开并复跑 `ui-verify`。量化与验证记录见 `../内存管理/RAM使用分析与优化方案.md` §2.6.4。
+ ✅ **已入门禁**(2026-10-11):`scripts/check_lv_conf_sync.py` = pre-commit 钩子 `lv-conf-sync`。比对的是**生效值**(文件未定义则取 `lv_conf_internal.h` 兜底默认),并抓"写了 LVGL 不认的宏名"与同文件重复定义。首次跑即揪出两端共 **26 个 9.3 不存在的选项**(LVGL 8 / 9.4 模板残留,与 `LV_USE_THEME_BASIC` 同类)、**3 处同文件重复定义**、**10 项 MCU 端靠默认值隐式生效的开关**(含默认 =1 的 `LV_USE_OBSERVER` / `LV_USE_DRAW_PXP` / `LV_USE_IMAGEBUTTON`)——已全部清理/显式固化。
+ ✅ **门禁触发范围**(2026-10-11):`lv-conf-sync` 的 `files:` 同时匹配 `Lib/lv_conf.h`、**`simulator/lv_conf.h`** 与脚本自身 → **两端任一改动都会跑**。此前顶层 `exclude` 整目录挡掉 `simulator/`,模拟器侧改动拦不到;已删该条并把 `simulator/` 下自研代码整体纳入 pre-commit(cppcheck 当天即揪出 `sim_syslog.c` 一处真实空指针解引用),详见 `../代码规范/pre-commit使用说明.md` §5.5。
---
diff --git a/docs/images/引用关系图.dot b/docs/images/引用关系图.dot
index 5440d533..792167f0 100644
--- a/docs/images/引用关系图.dot
+++ b/docs/images/引用关系图.dot
@@ -2,6 +2,18 @@ digraph refgraph_full {
graph [rankdir=LR, bgcolor="white", labelloc=t, label="STM32F4-Base 项目文件引用关系图(项目代码视图)", fontname="Microsoft YaHei", fontsize=18];
node [fontname="Microsoft YaHei", fontsize=10, shape=box, style="rounded,filled"];
edge [fontname="Microsoft YaHei", fontsize=8];
+ "App/algo/adc_health.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/adc_health.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/adc_input.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/adc_input.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/phasor.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/phasor.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/prot_elm.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/prot_elm.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/prot_set.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/prot_set.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/sample_ring.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/algo/sample_ring.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/app/app_cfg.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/app/app_main.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/app/app_main.h" [fillcolor="#dbeafe", color="#1d4ed8"];
@@ -16,20 +28,34 @@ digraph refgraph_full {
"App/log/console_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/log/sys_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/log/sys_log_mcu.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/protocol/iec104/iec104_asdu.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/protocol/iec104/iec104_asdu.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/protocol/iec104/iec104_link.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/protocol/iec104/iec104_link.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/protocol/iec104/iec104_pts.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/protocol/iec104/iec104_pts.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/sync/time_sync.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/sync/time_sync.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/adc_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/adc_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/default_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/default_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/task/iec104_serv.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/task/iec104_serv.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/net_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/net_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/task/protect_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/task/protect_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/rs485_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/rs485_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/ui_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/task/ui_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/util/sha256.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/util/sha256.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/util/sys_clock.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/util/sys_clock.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/util/task_prof.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "App/util/task_prof.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/util/task_watchdog.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"App/util/task_watchdog.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/CH395F/ch395f.c" [fillcolor="#dbeafe", color="#1d4ed8"];
@@ -39,12 +65,15 @@ digraph refgraph_full {
"Drivers/BSP/GD5F2GQ5UE/nand_ftl.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/GD5F2GQ5UE/nand_ftl.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NET/lftpd/lftpd.h" [fillcolor="#f3f4f6", color="#9ca3af"];
+ "Drivers/BSP/NET/lftpd/lftpd_auth.c" [fillcolor="#f3f4f6", color="#9ca3af"];
"Drivers/BSP/NET/net_config.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NET/net_select.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NET/net_select.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NET/net_socket.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NET/net_socket.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NET/net_types.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "Drivers/BSP/NETCFG/ftp_cfg.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "Drivers/BSP/NETCFG/ftp_cfg.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NETCFG/net_cfg.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/NETCFG/net_cfg.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Drivers/BSP/RS485/rs485.c" [fillcolor="#dbeafe", color="#1d4ed8"];
@@ -114,6 +143,7 @@ digraph refgraph_full {
"Inc/spi.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Inc/stm32f4xx_hal_conf.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Inc/stm32f4xx_it.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "Inc/tim.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Inc/usart.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"Lib/FatFs/diskio.h" [fillcolor="#f3f4f6", color="#9ca3af"];
"Lib/FatFs/ff.h" [fillcolor="#f3f4f6", color="#9ca3af"];
@@ -136,6 +166,7 @@ digraph refgraph_full {
"Src/stm32f4xx_hal_timebase_tim.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Src/stm32f4xx_it.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Src/system_stm32f4xx.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "Src/tim.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"Src/usart.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"simulator/SDL2/SDL2-2.32.10/include/SDL.h" [fillcolor="#f3f4f6", color="#9ca3af"];
"simulator/dbg_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
@@ -148,6 +179,8 @@ digraph refgraph_full {
"simulator/sim/sim_port.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"simulator/sim/sim_syslog.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"simulator/sim/ui_net_sink_backend_sim.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/board/ch395f_reuse_bench.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/board/ch395f_reuse_bench.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/board/ch395f_test.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/board/ch395f_test_task.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/board/ch395f_test_task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
@@ -164,18 +197,65 @@ digraph refgraph_full {
"test/board/test_common.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/board/test_config.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/board/test_config.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/adc_health/adc_health_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/adc_input/adc_input_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/adc_input/stub/adc.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/adc_input/stub/tpafe5160.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/cfg_file/cfg_file_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/cfg_file/stub/dbg_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/ftp_auth/ftp_auth_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/iec104/iec104_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/phasor/phasor_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/prot_elm/prot_elm_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/prot_set/prot_set_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/sha256/sha256_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/sys_clock/stub/FreeRTOS.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/sys_clock/stub/dbg_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/sys_clock/stub/main.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/sys_clock/stub/task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/sys_clock/sys_clock_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/FreeRTOS.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/cmsis_os2.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/dbg_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/net_socket.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/sys_clock.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/sys_events.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/stub/task.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/time_sync/time_sync_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/vol_lock/stub/cmsis_os.h" [fillcolor="#dbeafe", color="#1d4ed8"];
+ "test/host/vol_lock/vol_lock_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/watchdog/stub/dbg_log.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/watchdog/stub/iwdg.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/watchdog/stub/main.h" [fillcolor="#dbeafe", color="#1d4ed8"];
"test/host/watchdog/watchdog_test.c" [fillcolor="#dbeafe", color="#1d4ed8"];
"外部/系统头文件" [shape=ellipse, fillcolor="#fef3c7", color="#d97706", fontsize=12];
+ "App/algo/adc_health.c" -> "App/algo/adc_health.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/adc_health.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/adc_input.c" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/adc_input.c" -> "Drivers/BSP/TPAFE5160/tpafe5160.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/adc_input.c" -> "Inc/adc.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/adc_input.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/adc_input.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/phasor.c" -> "App/algo/phasor.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/phasor.c" -> "App/algo/sample_ring.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/phasor.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/phasor.h" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/phasor.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/prot_elm.c" -> "App/algo/phasor.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/prot_elm.c" -> "App/algo/prot_elm.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/prot_elm.c" -> "App/algo/prot_set.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/prot_elm.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/prot_elm.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/prot_set.c" -> "App/algo/prot_set.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/prot_set.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/sample_ring.c" -> "App/algo/sample_ring.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/sample_ring.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/algo/sample_ring.h" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "App/algo/sample_ring.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/app/app_main.c" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "App/app/app_main.c" -> "App/algo/phasor.h" [color="#2563eb", penwidth=1.0];
+ "App/app/app_main.c" -> "App/algo/prot_elm.h" [color="#2563eb", penwidth=1.0];
+ "App/app/app_main.c" -> "App/algo/prot_set.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.c" -> "App/app/app_cfg.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.c" -> "App/app/app_main.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.c" -> "App/log/sys_log.h" [color="#2563eb", penwidth=1.0];
@@ -188,7 +268,9 @@ digraph refgraph_full {
"App/app/app_main.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.c" -> "Inc/main.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.c" -> "Inc/stm32f4xx_it.h" [color="#2563eb", penwidth=1.0];
+ "App/app/app_main.c" -> "Inc/tim.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.c" -> "Inc/usart.h" [color="#2563eb", penwidth=1.0];
+ "App/app/app_main.c" -> "test/board/test_config.h" [color="#2563eb", penwidth=1.0];
"App/app/app_main.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/app/sys_events.h" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h" [color="#94a3b8", penwidth=0.7];
"App/cfg/cfg_file.c" -> "App/cfg/cfg_file.h" [color="#2563eb", penwidth=1.0];
@@ -222,6 +304,22 @@ digraph refgraph_full {
"App/log/sys_log_mcu.c" -> "Lib/FatFs/ff.h" [color="#94a3b8", penwidth=0.7];
"App/log/sys_log_mcu.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h" [color="#94a3b8", penwidth=0.7];
"App/log/sys_log_mcu.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/protocol/iec104/iec104_asdu.c" -> "App/protocol/iec104/iec104_asdu.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_asdu.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/protocol/iec104/iec104_asdu.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/protocol/iec104/iec104_link.c" -> "App/protocol/iec104/iec104_asdu.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_link.c" -> "App/protocol/iec104/iec104_link.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_link.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/protocol/iec104/iec104_link.h" -> "App/protocol/iec104/iec104_pts.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_link.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/protocol/iec104/iec104_pts.c" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_pts.c" -> "App/algo/phasor.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_pts.c" -> "App/algo/prot_elm.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_pts.c" -> "App/algo/prot_set.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_pts.c" -> "App/protocol/iec104/iec104_asdu.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_pts.c" -> "App/protocol/iec104/iec104_pts.h" [color="#2563eb", penwidth=1.0];
+ "App/protocol/iec104/iec104_pts.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/protocol/iec104/iec104_pts.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/sync/time_sync.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
"App/sync/time_sync.c" -> "App/sync/time_sync.h" [color="#2563eb", penwidth=1.0];
"App/sync/time_sync.c" -> "App/util/sys_clock.h" [color="#2563eb", penwidth=1.0];
@@ -230,7 +328,15 @@ digraph refgraph_full {
"App/sync/time_sync.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
"App/sync/time_sync.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/sync/time_sync.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/task/adc_task.c" -> "App/algo/adc_health.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/algo/phasor.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/algo/sample_ring.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/log/console_log.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/log/sys_log.h" [color="#2563eb", penwidth=1.0];
"App/task/adc_task.c" -> "App/task/adc_task.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/util/task_prof.h" [color="#2563eb", penwidth=1.0];
+ "App/task/adc_task.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
"App/task/adc_task.c" -> "Drivers/BSP/TPAFE5160/tpafe5160.h" [color="#2563eb", penwidth=1.0];
"App/task/adc_task.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
"App/task/adc_task.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h" [color="#94a3b8", penwidth=0.7];
@@ -239,6 +345,7 @@ digraph refgraph_full {
"App/task/default_task.c" -> "App/fs/vol_lock.h" [color="#2563eb", penwidth=1.0];
"App/task/default_task.c" -> "App/log/sys_log.h" [color="#2563eb", penwidth=1.0];
"App/task/default_task.c" -> "App/task/default_task.h" [color="#2563eb", penwidth=1.0];
+ "App/task/default_task.c" -> "App/util/task_prof.h" [color="#2563eb", penwidth=1.0];
"App/task/default_task.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
"App/task/default_task.c" -> "Drivers/BSP/GD5F2GQ5UE/nand_ftl.h" [color="#2563eb", penwidth=1.0];
"App/task/default_task.c" -> "Drivers/BSP/NETCFG/net_cfg.h" [color="#2563eb", penwidth=1.0];
@@ -249,9 +356,20 @@ digraph refgraph_full {
"App/task/default_task.c" -> "Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h" [color="#94a3b8", penwidth=0.7];
"App/task/default_task.c" -> "Middlewares/Third_Party/FreeRTOS/Source/include/task.h" [color="#94a3b8", penwidth=0.7];
"App/task/default_task.c" -> "test/board/test_config.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "App/protocol/iec104/iec104_link.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "App/protocol/iec104/iec104_pts.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "App/task/iec104_serv.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "App/util/sys_clock.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "Drivers/BSP/NET/net_socket.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
+ "App/task/iec104_serv.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h" [color="#94a3b8", penwidth=0.7];
+ "App/task/iec104_serv.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/task/net_task.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
"App/task/net_task.c" -> "App/log/sys_log.h" [color="#2563eb", penwidth=1.0];
"App/task/net_task.c" -> "App/task/net_task.h" [color="#2563eb", penwidth=1.0];
+ "App/task/net_task.c" -> "App/util/task_prof.h" [color="#2563eb", penwidth=1.0];
"App/task/net_task.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
"App/task/net_task.c" -> "Drivers/BSP/CH395F/ch395f.h" [color="#2563eb", penwidth=1.0];
"App/task/net_task.c" -> "Drivers/BSP/NET/net_select.h" [color="#2563eb", penwidth=1.0];
@@ -261,6 +379,13 @@ digraph refgraph_full {
"App/task/net_task.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h" [color="#94a3b8", penwidth=0.7];
"App/task/net_task.c" -> "test/board/test_config.h" [color="#2563eb", penwidth=1.0];
"App/task/net_task.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/task/protect_task.c" -> "App/algo/adc_health.h" [color="#2563eb", penwidth=1.0];
+ "App/task/protect_task.c" -> "App/algo/prot_elm.h" [color="#2563eb", penwidth=1.0];
+ "App/task/protect_task.c" -> "App/algo/prot_set.h" [color="#2563eb", penwidth=1.0];
+ "App/task/protect_task.c" -> "App/task/protect_task.h" [color="#2563eb", penwidth=1.0];
+ "App/task/protect_task.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
+ "App/task/protect_task.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
+ "App/task/protect_task.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h" [color="#94a3b8", penwidth=0.7];
"App/task/rs485_task.c" -> "App/task/rs485_task.h" [color="#2563eb", penwidth=1.0];
"App/task/rs485_task.c" -> "Drivers/BSP/RS485/rs485.h" [color="#2563eb", penwidth=1.0];
"App/task/rs485_task.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
@@ -268,6 +393,7 @@ digraph refgraph_full {
"App/task/ui_task.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "App/task/ui_task.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "App/util/sys_clock.h" [color="#2563eb", penwidth=1.0];
+ "App/task/ui_task.c" -> "App/util/task_prof.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "Drivers/BSP/UI/ui_menu.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "Drivers/BSP/UI/ui_net_sink.h" [color="#2563eb", penwidth=1.0];
@@ -278,6 +404,9 @@ digraph refgraph_full {
"App/task/ui_task.c" -> "test/board/net_latency_test.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "test/board/test_config.h" [color="#2563eb", penwidth=1.0];
"App/task/ui_task.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/util/sha256.c" -> "App/util/sha256.h" [color="#2563eb", penwidth=1.0];
+ "App/util/sha256.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/util/sha256.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/util/sys_clock.c" -> "App/util/sys_clock.h" [color="#2563eb", penwidth=1.0];
"App/util/sys_clock.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
"App/util/sys_clock.c" -> "Inc/main.h" [color="#2563eb", penwidth=1.0];
@@ -286,6 +415,11 @@ digraph refgraph_full {
"App/util/sys_clock.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/util/sys_clock.h" -> "Drivers/BSP/SD2506/sd2506.h" [color="#2563eb", penwidth=1.0];
"App/util/sys_clock.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/util/task_prof.c" -> "App/log/console_log.h" [color="#2563eb", penwidth=1.0];
+ "App/util/task_prof.c" -> "App/util/task_prof.h" [color="#2563eb", penwidth=1.0];
+ "App/util/task_prof.c" -> "Inc/main.h" [color="#2563eb", penwidth=1.0];
+ "App/util/task_prof.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "App/util/task_prof.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"App/util/task_watchdog.c" -> "App/log/console_log.h" [color="#2563eb", penwidth=1.0];
"App/util/task_watchdog.c" -> "App/log/sys_log.h" [color="#2563eb", penwidth=1.0];
"App/util/task_watchdog.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
@@ -341,7 +475,12 @@ digraph refgraph_full {
"Drivers/BSP/NET/net_socket.h" -> "Middlewares/Third_Party/FreeRTOS/Source/include/task.h" [color="#94a3b8", penwidth=0.7];
"Drivers/BSP/NET/net_types.h" -> "Drivers/BSP/NET/net_config.h" [color="#2563eb", penwidth=1.0];
"Drivers/BSP/NET/net_types.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "Drivers/BSP/NETCFG/ftp_cfg.c" -> "App/cfg/cfg_file.h" [color="#2563eb", penwidth=1.0];
+ "Drivers/BSP/NETCFG/ftp_cfg.c" -> "Drivers/BSP/NETCFG/ftp_cfg.h" [color="#2563eb", penwidth=1.0];
+ "Drivers/BSP/NETCFG/ftp_cfg.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "Drivers/BSP/NETCFG/ftp_cfg.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"Drivers/BSP/NETCFG/net_cfg.c" -> "App/cfg/cfg_file.h" [color="#2563eb", penwidth=1.0];
+ "Drivers/BSP/NETCFG/net_cfg.c" -> "Drivers/BSP/NETCFG/ftp_cfg.h" [color="#2563eb", penwidth=1.0];
"Drivers/BSP/NETCFG/net_cfg.c" -> "Drivers/BSP/NETCFG/net_cfg.h" [color="#2563eb", penwidth=1.0];
"Drivers/BSP/NETCFG/net_cfg.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
"Drivers/BSP/NETCFG/net_cfg.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
@@ -474,13 +613,17 @@ digraph refgraph_full {
"Inc/stm32f4xx_hal_conf.h" -> "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h" [color="#94a3b8", penwidth=0.7];
"Inc/stm32f4xx_hal_conf.h" -> "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h" [color="#94a3b8", penwidth=0.7];
"Inc/stm32f4xx_hal_conf.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "Inc/tim.h" -> "Inc/main.h" [color="#2563eb", penwidth=1.0];
"Inc/usart.h" -> "Inc/main.h" [color="#2563eb", penwidth=1.0];
"Src/adc.c" -> "Inc/adc.h" [color="#2563eb", penwidth=1.0];
"Src/dma.c" -> "Inc/dma.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "App/sync/time_sync.h" [color="#2563eb", penwidth=1.0];
+ "Src/freertos.c" -> "App/task/adc_task.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "App/task/default_task.h" [color="#2563eb", penwidth=1.0];
+ "Src/freertos.c" -> "App/task/iec104_serv.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "App/task/net_task.h" [color="#2563eb", penwidth=1.0];
+ "Src/freertos.c" -> "App/task/protect_task.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "Drivers/BSP/CH395F/ch395f.h" [color="#2563eb", penwidth=1.0];
"Src/freertos.c" -> "Drivers/BSP/NET/lftpd/lftpd.h" [color="#94a3b8", penwidth=0.7];
@@ -498,6 +641,7 @@ digraph refgraph_full {
"Src/i2c.c" -> "Inc/i2c.h" [color="#2563eb", penwidth=1.0];
"Src/iwdg.c" -> "Inc/iwdg.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "App/app/app_main.h" [color="#2563eb", penwidth=1.0];
+ "Src/main.c" -> "App/task/adc_task.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "App/util/sys_clock.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "Drivers/BSP/CH395F/ch395f.h" [color="#2563eb", penwidth=1.0];
@@ -515,6 +659,7 @@ digraph refgraph_full {
"Src/main.c" -> "Inc/iwdg.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "Inc/main.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "Inc/spi.h" [color="#2563eb", penwidth=1.0];
+ "Src/main.c" -> "Inc/tim.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "Inc/usart.h" [color="#2563eb", penwidth=1.0];
"Src/main.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h" [color="#94a3b8", penwidth=0.7];
"Src/main.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
@@ -526,6 +671,7 @@ digraph refgraph_full {
"Src/stm32f4xx_it.c" -> "Inc/stm32f4xx_it.h" [color="#2563eb", penwidth=1.0];
"Src/stm32f4xx_it.c" -> "Inc/usart.h" [color="#2563eb", penwidth=1.0];
"Src/system_stm32f4xx.c" -> "Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h" [color="#94a3b8", penwidth=0.7];
+ "Src/tim.c" -> "Inc/tim.h" [color="#2563eb", penwidth=1.0];
"Src/usart.c" -> "Inc/usart.h" [color="#2563eb", penwidth=1.0];
"simulator/dbg_log.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"simulator/main.c" -> "App/cfg/cfg_file.h" [color="#2563eb", penwidth=1.0];
@@ -554,6 +700,10 @@ digraph refgraph_full {
"simulator/sim/ui_net_sink_backend_sim.c" -> "Drivers/BSP/UI/ui_port.h" [color="#2563eb", penwidth=1.0];
"simulator/sim/ui_net_sink_backend_sim.c" -> "Lib/lvgl/lvgl.h" [color="#94a3b8", penwidth=0.7];
"simulator/sim/ui_net_sink_backend_sim.c" -> "simulator/sim/sim_port.h" [color="#2563eb", penwidth=1.0];
+ "test/board/ch395f_reuse_bench.c" -> "Drivers/BSP/CH395F/ch395f.h" [color="#2563eb", penwidth=1.0];
+ "test/board/ch395f_reuse_bench.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
+ "test/board/ch395f_reuse_bench.c" -> "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h" [color="#94a3b8", penwidth=0.7];
+ "test/board/ch395f_reuse_bench.c" -> "test/board/ch395f_reuse_bench.h" [color="#2563eb", penwidth=1.0];
"test/board/ch395f_test.h" -> "test/board/test_common.h" [color="#2563eb", penwidth=1.0];
"test/board/ch395f_test.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/board/ch395f_test_task.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
@@ -632,12 +782,44 @@ digraph refgraph_full {
"test/board/test_config.h" -> "test/board/sd2506_test.h" [color="#2563eb", penwidth=1.0];
"test/board/test_config.h" -> "test/board/storage_test.h" [color="#2563eb", penwidth=1.0];
"test/board/test_config.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/adc_health/adc_health_test.c" -> "App/algo/adc_health.c" [color="#2563eb", penwidth=1.0];
+ "test/host/adc_health/adc_health_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/adc_input/adc_input_test.c" -> "App/algo/adc_input.c" [color="#2563eb", penwidth=1.0];
+ "test/host/adc_input/adc_input_test.c" -> "App/algo/adc_input.h" [color="#2563eb", penwidth=1.0];
+ "test/host/adc_input/adc_input_test.c" -> "Drivers/BSP/TPAFE5160/tpafe5160.h" [color="#2563eb", penwidth=1.0];
+ "test/host/adc_input/adc_input_test.c" -> "Inc/adc.h" [color="#2563eb", penwidth=1.0];
+ "test/host/adc_input/adc_input_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/adc_input/stub/adc.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/adc_input/stub/tpafe5160.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/cfg_file/cfg_file_test.c" -> "App/cfg/cfg_file.c" [color="#2563eb", penwidth=1.0];
"test/host/cfg_file/cfg_file_test.c" -> "App/cfg/cfg_file_store.h" [color="#2563eb", penwidth=1.0];
"test/host/cfg_file/cfg_file_test.c" -> "App/log/sys_log.h" [color="#2563eb", penwidth=1.0];
"test/host/cfg_file/cfg_file_test.c" -> "Drivers/BSP/dbg_log.h" [color="#2563eb", penwidth=1.0];
"test/host/cfg_file/cfg_file_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/cfg_file/stub/dbg_log.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/ftp_auth/ftp_auth_test.c" -> "App/util/sha256.c" [color="#2563eb", penwidth=1.0];
+ "test/host/ftp_auth/ftp_auth_test.c" -> "Drivers/BSP/NET/lftpd/lftpd_auth.c" [color="#94a3b8", penwidth=0.7];
+ "test/host/ftp_auth/ftp_auth_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/iec104/iec104_test.c" -> "App/algo/phasor.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "App/algo/prot_elm.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "App/algo/prot_set.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "App/algo/sample_ring.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "App/protocol/iec104/iec104_asdu.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "App/protocol/iec104/iec104_link.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "App/protocol/iec104/iec104_pts.c" [color="#2563eb", penwidth=1.0];
+ "test/host/iec104/iec104_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/phasor/phasor_test.c" -> "App/algo/phasor.c" [color="#2563eb", penwidth=1.0];
+ "test/host/phasor/phasor_test.c" -> "App/algo/sample_ring.c" [color="#2563eb", penwidth=1.0];
+ "test/host/phasor/phasor_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/prot_elm/prot_elm_test.c" -> "App/algo/phasor.c" [color="#2563eb", penwidth=1.0];
+ "test/host/prot_elm/prot_elm_test.c" -> "App/algo/prot_elm.c" [color="#2563eb", penwidth=1.0];
+ "test/host/prot_elm/prot_elm_test.c" -> "App/algo/prot_set.c" [color="#2563eb", penwidth=1.0];
+ "test/host/prot_elm/prot_elm_test.c" -> "App/algo/sample_ring.c" [color="#2563eb", penwidth=1.0];
+ "test/host/prot_elm/prot_elm_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/prot_set/prot_set_test.c" -> "App/algo/prot_set.c" [color="#2563eb", penwidth=1.0];
+ "test/host/prot_set/prot_set_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/sha256/sha256_test.c" -> "App/util/sha256.c" [color="#2563eb", penwidth=1.0];
+ "test/host/sha256/sha256_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/sys_clock/stub/FreeRTOS.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/sys_clock/stub/dbg_log.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/sys_clock/stub/main.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
@@ -650,6 +832,27 @@ digraph refgraph_full {
"test/host/sys_clock/sys_clock_test.c" -> "Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h" [color="#94a3b8", penwidth=0.7];
"test/host/sys_clock/sys_clock_test.c" -> "Middlewares/Third_Party/FreeRTOS/Source/include/task.h" [color="#94a3b8", penwidth=0.7];
"test/host/sys_clock/sys_clock_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/time_sync/stub/FreeRTOS.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/time_sync/stub/cmsis_os2.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/time_sync/stub/dbg_log.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/time_sync/stub/net_socket.h" -> "test/host/time_sync/stub/FreeRTOS.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/stub/net_socket.h" -> "test/host/time_sync/stub/task.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/stub/net_socket.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/time_sync/stub/sys_clock.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/time_sync/stub/sys_events.h" -> "test/host/time_sync/stub/cmsis_os2.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/stub/task.h" -> "test/host/time_sync/stub/FreeRTOS.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/time_sync_test.c" -> "App/app/sys_events.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/time_sync_test.c" -> "App/sync/time_sync.c" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/time_sync_test.c" -> "App/util/sys_clock.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/time_sync_test.c" -> "App/util/task_watchdog.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/time_sync_test.c" -> "Drivers/BSP/NET/net_socket.h" [color="#2563eb", penwidth=1.0];
+ "test/host/time_sync/time_sync_test.c" -> "Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h" [color="#94a3b8", penwidth=0.7];
+ "test/host/time_sync/time_sync_test.c" -> "Middlewares/Third_Party/FreeRTOS/Source/include/task.h" [color="#94a3b8", penwidth=0.7];
+ "test/host/time_sync/time_sync_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/vol_lock/stub/cmsis_os.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
+ "test/host/vol_lock/vol_lock_test.c" -> "App/fs/vol_lock.c" [color="#2563eb", penwidth=1.0];
+ "test/host/vol_lock/vol_lock_test.c" -> "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h" [color="#94a3b8", penwidth=0.7];
+ "test/host/vol_lock/vol_lock_test.c" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/watchdog/stub/dbg_log.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/watchdog/stub/iwdg.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
"test/host/watchdog/stub/main.h" -> "外部/系统头文件" [color="#f59e0b", penwidth=0.6, style=dashed];
diff --git a/docs/images/引用关系图.json b/docs/images/引用关系图.json
index 9e36ca10..e5d8a2cf 100644
--- a/docs/images/引用关系图.json
+++ b/docs/images/引用关系图.json
@@ -1,6 +1,54 @@
{
"title": "STM32F4-Base 项目文件引用关系图(项目代码视图)",
"nodes": [
+ {
+ "path": "App/algo/adc_health.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/adc_health.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/adc_input.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/adc_input.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/phasor.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/phasor.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/prot_elm.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/prot_elm.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/prot_set.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/prot_set.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/sample_ring.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/algo/sample_ring.h",
+ "class": "fp"
+ },
{
"path": "App/app/app_cfg.h",
"class": "fp"
@@ -57,6 +105,30 @@
"path": "App/log/sys_log_mcu.c",
"class": "fp"
},
+ {
+ "path": "App/protocol/iec104/iec104_asdu.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/protocol/iec104/iec104_asdu.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/protocol/iec104/iec104_link.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/protocol/iec104/iec104_link.h",
+ "class": "fp"
+ },
+ {
+ "path": "App/protocol/iec104/iec104_pts.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/protocol/iec104/iec104_pts.h",
+ "class": "fp"
+ },
{
"path": "App/sync/time_sync.c",
"class": "fp"
@@ -81,6 +153,14 @@
"path": "App/task/default_task.h",
"class": "fp"
},
+ {
+ "path": "App/task/iec104_serv.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/task/iec104_serv.h",
+ "class": "fp"
+ },
{
"path": "App/task/net_task.c",
"class": "fp"
@@ -89,6 +169,14 @@
"path": "App/task/net_task.h",
"class": "fp"
},
+ {
+ "path": "App/task/protect_task.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/task/protect_task.h",
+ "class": "fp"
+ },
{
"path": "App/task/rs485_task.c",
"class": "fp"
@@ -105,6 +193,14 @@
"path": "App/task/ui_task.h",
"class": "fp"
},
+ {
+ "path": "App/util/sha256.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/util/sha256.h",
+ "class": "fp"
+ },
{
"path": "App/util/sys_clock.c",
"class": "fp"
@@ -113,6 +209,14 @@
"path": "App/util/sys_clock.h",
"class": "fp"
},
+ {
+ "path": "App/util/task_prof.c",
+ "class": "fp"
+ },
+ {
+ "path": "App/util/task_prof.h",
+ "class": "fp"
+ },
{
"path": "App/util/task_watchdog.c",
"class": "fp"
@@ -153,6 +257,14 @@
"path": "Drivers/BSP/NET/lftpd/lftpd.h",
"class": "tp"
},
+ {
+ "path": "Drivers/BSP/NET/lftpd/lftpd_auth.c",
+ "class": "tp"
+ },
+ {
+ "path": "Drivers/BSP/NET/lftpd/lftpd_auth.h",
+ "class": "tp"
+ },
{
"path": "Drivers/BSP/NET/lftpd/lftpd_inet.c",
"class": "tp"
@@ -161,10 +273,6 @@
"path": "Drivers/BSP/NET/lftpd/lftpd_io.c",
"class": "tp"
},
- {
- "path": "Drivers/BSP/NET/lftpd/lftpd_log.c",
- "class": "tp"
- },
{
"path": "Drivers/BSP/NET/lftpd/lftpd_string.c",
"class": "tp"
@@ -213,6 +321,14 @@
"path": "Drivers/BSP/NET/net_types.h",
"class": "fp"
},
+ {
+ "path": "Drivers/BSP/NETCFG/ftp_cfg.c",
+ "class": "fp"
+ },
+ {
+ "path": "Drivers/BSP/NETCFG/ftp_cfg.h",
+ "class": "fp"
+ },
{
"path": "Drivers/BSP/NETCFG/net_cfg.c",
"class": "fp"
@@ -669,6 +785,10 @@
"path": "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_system.h",
"class": "tp"
},
+ {
+ "path": "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_tim.h",
+ "class": "tp"
+ },
{
"path": "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usart.h",
"class": "tp"
@@ -805,6 +925,10 @@
"path": "Inc/stm32f4xx_it.h",
"class": "fp"
},
+ {
+ "path": "Inc/tim.h",
+ "class": "fp"
+ },
{
"path": "Inc/usart.h",
"class": "fp"
@@ -4789,6 +4913,10 @@
"path": "Src/system_stm32f4xx.c",
"class": "fp"
},
+ {
+ "path": "Src/tim.c",
+ "class": "fp"
+ },
{
"path": "Src/usart.c",
"class": "fp"
@@ -5537,6 +5665,14 @@
"path": "simulator/sim/ui_net_sink_backend_sim.c",
"class": "fp"
},
+ {
+ "path": "test/board/ch395f_reuse_bench.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/board/ch395f_reuse_bench.h",
+ "class": "fp"
+ },
{
"path": "test/board/ch395f_test.h",
"class": "fp"
@@ -5601,6 +5737,22 @@
"path": "test/board/test_config.h",
"class": "fp"
},
+ {
+ "path": "test/host/adc_health/adc_health_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/adc_input/adc_input_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/adc_input/stub/adc.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/adc_input/stub/tpafe5160.h",
+ "class": "fp"
+ },
{
"path": "test/host/cfg_file/cfg_file_test.c",
"class": "fp"
@@ -5609,6 +5761,30 @@
"path": "test/host/cfg_file/stub/dbg_log.h",
"class": "fp"
},
+ {
+ "path": "test/host/ftp_auth/ftp_auth_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/iec104/iec104_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/phasor/phasor_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/prot_elm/prot_elm_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/prot_set/prot_set_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/sha256/sha256_test.c",
+ "class": "fp"
+ },
{
"path": "test/host/sys_clock/stub/FreeRTOS.h",
"class": "fp"
@@ -5629,6 +5805,46 @@
"path": "test/host/sys_clock/sys_clock_test.c",
"class": "fp"
},
+ {
+ "path": "test/host/time_sync/stub/FreeRTOS.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/stub/cmsis_os2.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/stub/dbg_log.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/stub/net_socket.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/stub/sys_clock.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/stub/sys_events.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/stub/task.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/time_sync/time_sync_test.c",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/vol_lock/stub/cmsis_os.h",
+ "class": "fp"
+ },
+ {
+ "path": "test/host/vol_lock/vol_lock_test.c",
+ "class": "fp"
+ },
{
"path": "test/host/watchdog/stub/dbg_log.h",
"class": "fp"
@@ -5651,6 +5867,150 @@
}
],
"edges": [
+ {
+ "from": "App/algo/adc_health.c",
+ "to": "App/algo/adc_health.h",
+ "kind": "fp-fp",
+ "include": "adc_health.h"
+ },
+ {
+ "from": "App/algo/adc_health.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/algo/adc_input.c",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
+ {
+ "from": "App/algo/adc_input.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "App/algo/adc_input.c",
+ "to": "Inc/adc.h",
+ "kind": "fp-fp",
+ "include": "adc.h"
+ },
+ {
+ "from": "App/algo/adc_input.c",
+ "to": "Drivers/BSP/TPAFE5160/tpafe5160.h",
+ "kind": "fp-fp",
+ "include": "tpafe5160.h"
+ },
+ {
+ "from": "App/algo/adc_input.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/algo/phasor.c",
+ "to": "App/algo/phasor.h",
+ "kind": "fp-fp",
+ "include": "phasor.h"
+ },
+ {
+ "from": "App/algo/phasor.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "math.h"
+ },
+ {
+ "from": "App/algo/phasor.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "App/algo/phasor.c",
+ "to": "App/algo/sample_ring.h",
+ "kind": "fp-fp",
+ "include": "sample_ring.h"
+ },
+ {
+ "from": "App/algo/phasor.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/algo/phasor.h",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
+ {
+ "from": "App/algo/prot_elm.c",
+ "to": "App/algo/prot_elm.h",
+ "kind": "fp-fp",
+ "include": "prot_elm.h"
+ },
+ {
+ "from": "App/algo/prot_elm.c",
+ "to": "App/algo/prot_set.h",
+ "kind": "fp-fp",
+ "include": "prot_set.h"
+ },
+ {
+ "from": "App/algo/prot_elm.c",
+ "to": "App/algo/phasor.h",
+ "kind": "fp-fp",
+ "include": "phasor.h"
+ },
+ {
+ "from": "App/algo/prot_elm.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "App/algo/prot_elm.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/algo/prot_set.c",
+ "to": "App/algo/prot_set.h",
+ "kind": "fp-fp",
+ "include": "prot_set.h"
+ },
+ {
+ "from": "App/algo/prot_set.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/algo/sample_ring.c",
+ "to": "App/algo/sample_ring.h",
+ "kind": "fp-fp",
+ "include": "sample_ring.h"
+ },
+ {
+ "from": "App/algo/sample_ring.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "App/algo/sample_ring.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/algo/sample_ring.h",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
{
"from": "App/app/app_main.c",
"to": "App/app/app_main.h",
@@ -5681,6 +6041,12 @@
"kind": "fp-fp",
"include": "usart.h"
},
+ {
+ "from": "App/app/app_main.c",
+ "to": "Inc/tim.h",
+ "kind": "fp-fp",
+ "include": "tim.h"
+ },
{
"from": "App/app/app_main.c",
"to": "Drivers/BSP/RS485/rs485.h",
@@ -5711,6 +6077,30 @@
"kind": "fp-fp",
"include": "tpafe5160.h"
},
+ {
+ "from": "App/app/app_main.c",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
+ {
+ "from": "App/app/app_main.c",
+ "to": "App/algo/phasor.h",
+ "kind": "fp-fp",
+ "include": "phasor.h"
+ },
+ {
+ "from": "App/app/app_main.c",
+ "to": "App/algo/prot_set.h",
+ "kind": "fp-fp",
+ "include": "prot_set.h"
+ },
+ {
+ "from": "App/app/app_main.c",
+ "to": "App/algo/prot_elm.h",
+ "kind": "fp-fp",
+ "include": "prot_elm.h"
+ },
{
"from": "App/app/app_main.c",
"to": "App/log/sys_log.h",
@@ -5723,6 +6113,12 @@
"kind": "fp-fp",
"include": "task_watchdog.h"
},
+ {
+ "from": "App/app/app_main.c",
+ "to": "test/board/test_config.h",
+ "kind": "fp-fp",
+ "include": "test_config.h"
+ },
{
"from": "App/app/app_main.c",
"to": "Drivers/BSP/dbg_log.h",
@@ -5975,6 +6371,102 @@
"kind": "fp-fp",
"include": "vol_lock.h"
},
+ {
+ "from": "App/protocol/iec104/iec104_asdu.c",
+ "to": "App/protocol/iec104/iec104_asdu.h",
+ "kind": "fp-fp",
+ "include": "iec104_asdu.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_asdu.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_asdu.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_link.c",
+ "to": "App/protocol/iec104/iec104_link.h",
+ "kind": "fp-fp",
+ "include": "iec104_link.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_link.c",
+ "to": "App/protocol/iec104/iec104_asdu.h",
+ "kind": "fp-fp",
+ "include": "iec104_asdu.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_link.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_link.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_link.h",
+ "to": "App/protocol/iec104/iec104_pts.h",
+ "kind": "fp-fp",
+ "include": "iec104_pts.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "App/protocol/iec104/iec104_pts.h",
+ "kind": "fp-fp",
+ "include": "iec104_pts.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "App/protocol/iec104/iec104_asdu.h",
+ "kind": "fp-fp",
+ "include": "iec104_asdu.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "App/algo/phasor.h",
+ "kind": "fp-fp",
+ "include": "phasor.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "App/algo/prot_elm.h",
+ "kind": "fp-fp",
+ "include": "prot_elm.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "App/algo/prot_set.h",
+ "kind": "fp-fp",
+ "include": "prot_set.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.c",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
+ {
+ "from": "App/protocol/iec104/iec104_pts.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
{
"from": "App/sync/time_sync.c",
"to": "外部/系统头文件",
@@ -6035,12 +6527,60 @@
"kind": "fp-tp",
"include": "cmsis_os.h"
},
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/algo/sample_ring.h",
+ "kind": "fp-fp",
+ "include": "sample_ring.h"
+ },
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/algo/phasor.h",
+ "kind": "fp-fp",
+ "include": "phasor.h"
+ },
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/algo/adc_health.h",
+ "kind": "fp-fp",
+ "include": "adc_health.h"
+ },
{
"from": "App/task/adc_task.c",
"to": "Drivers/BSP/TPAFE5160/tpafe5160.h",
"kind": "fp-fp",
"include": "tpafe5160.h"
},
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/util/task_watchdog.h",
+ "kind": "fp-fp",
+ "include": "task_watchdog.h"
+ },
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/log/console_log.h",
+ "kind": "fp-fp",
+ "include": "console_log.h"
+ },
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/log/sys_log.h",
+ "kind": "fp-fp",
+ "include": "sys_log.h"
+ },
+ {
+ "from": "App/task/adc_task.c",
+ "to": "App/util/task_prof.h",
+ "kind": "fp-fp",
+ "include": "task_prof.h"
+ },
{
"from": "App/task/adc_task.c",
"to": "Drivers/BSP/dbg_log.h",
@@ -6131,12 +6671,78 @@
"kind": "fp-fp",
"include": "task_watchdog.h"
},
+ {
+ "from": "App/task/default_task.c",
+ "to": "App/util/task_prof.h",
+ "kind": "fp-fp",
+ "include": "task_prof.h"
+ },
{
"from": "App/task/default_task.c",
"to": "App/fs/vol_lock.h",
"kind": "fp-fp",
"include": "vol_lock.h"
},
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "App/task/iec104_serv.h",
+ "kind": "fp-fp",
+ "include": "iec104_serv.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "App/protocol/iec104/iec104_link.h",
+ "kind": "fp-fp",
+ "include": "iec104_link.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "App/protocol/iec104/iec104_pts.h",
+ "kind": "fp-fp",
+ "include": "iec104_pts.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "Drivers/BSP/NET/net_socket.h",
+ "kind": "fp-fp",
+ "include": "net_socket.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "App/util/sys_clock.h",
+ "kind": "fp-fp",
+ "include": "sys_clock.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "App/app/sys_events.h",
+ "kind": "fp-fp",
+ "include": "sys_events.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "App/util/task_watchdog.h",
+ "kind": "fp-fp",
+ "include": "task_watchdog.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "Drivers/BSP/dbg_log.h",
+ "kind": "fp-fp",
+ "include": "dbg_log.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h",
+ "kind": "fp-tp",
+ "include": "cmsis_os2.h"
+ },
+ {
+ "from": "App/task/iec104_serv.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
{
"from": "App/task/net_task.c",
"to": "外部/系统头文件",
@@ -6203,12 +6809,60 @@
"kind": "fp-fp",
"include": "task_watchdog.h"
},
+ {
+ "from": "App/task/net_task.c",
+ "to": "App/util/task_prof.h",
+ "kind": "fp-fp",
+ "include": "task_prof.h"
+ },
{
"from": "App/task/net_task.c",
"to": "Drivers/BSP/dbg_log.h",
"kind": "fp-fp",
"include": "dbg_log.h"
},
+ {
+ "from": "App/task/protect_task.c",
+ "to": "App/task/protect_task.h",
+ "kind": "fp-fp",
+ "include": "protect_task.h"
+ },
+ {
+ "from": "App/task/protect_task.c",
+ "to": "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h",
+ "kind": "fp-tp",
+ "include": "cmsis_os2.h"
+ },
+ {
+ "from": "App/task/protect_task.c",
+ "to": "Drivers/BSP/dbg_log.h",
+ "kind": "fp-fp",
+ "include": "dbg_log.h"
+ },
+ {
+ "from": "App/task/protect_task.c",
+ "to": "App/algo/adc_health.h",
+ "kind": "fp-fp",
+ "include": "adc_health.h"
+ },
+ {
+ "from": "App/task/protect_task.c",
+ "to": "App/algo/prot_elm.h",
+ "kind": "fp-fp",
+ "include": "prot_elm.h"
+ },
+ {
+ "from": "App/task/protect_task.c",
+ "to": "App/algo/prot_set.h",
+ "kind": "fp-fp",
+ "include": "prot_set.h"
+ },
+ {
+ "from": "App/task/protect_task.c",
+ "to": "App/util/task_watchdog.h",
+ "kind": "fp-fp",
+ "include": "task_watchdog.h"
+ },
{
"from": "App/task/rs485_task.c",
"to": "App/task/rs485_task.h",
@@ -6305,12 +6959,42 @@
"kind": "fp-fp",
"include": "task_watchdog.h"
},
+ {
+ "from": "App/task/ui_task.c",
+ "to": "App/util/task_prof.h",
+ "kind": "fp-fp",
+ "include": "task_prof.h"
+ },
{
"from": "App/task/ui_task.c",
"to": "Drivers/BSP/dbg_log.h",
"kind": "fp-fp",
"include": "dbg_log.h"
},
+ {
+ "from": "App/util/sha256.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "App/util/sha256.c",
+ "to": "App/util/sha256.h",
+ "kind": "fp-fp",
+ "include": "sha256.h"
+ },
+ {
+ "from": "App/util/sha256.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "App/util/sha256.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
{
"from": "App/util/sys_clock.c",
"to": "App/util/sys_clock.h",
@@ -6365,6 +7049,36 @@
"kind": "fp-fp",
"include": "sd2506.h"
},
+ {
+ "from": "App/util/task_prof.c",
+ "to": "App/util/task_prof.h",
+ "kind": "fp-fp",
+ "include": "task_prof.h"
+ },
+ {
+ "from": "App/util/task_prof.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "App/util/task_prof.c",
+ "to": "Inc/main.h",
+ "kind": "fp-fp",
+ "include": "main.h"
+ },
+ {
+ "from": "App/util/task_prof.c",
+ "to": "App/log/console_log.h",
+ "kind": "fp-fp",
+ "include": "console_log.h"
+ },
+ {
+ "from": "App/util/task_prof.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
{
"from": "App/util/task_watchdog.c",
"to": "App/util/task_watchdog.h",
@@ -6851,6 +7565,18 @@
"kind": "tp-tp",
"include": "net_config.h"
},
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd.c",
+ "to": "Drivers/BSP/NET/lftpd/lftpd_auth.h",
+ "kind": "tp-tp",
+ "include": "lftpd_auth.h"
+ },
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd.c",
+ "to": "Drivers/BSP/NETCFG/ftp_cfg.h",
+ "kind": "tp-tp",
+ "include": "ftp_cfg.h"
+ },
{
"from": "Drivers/BSP/NET/lftpd/lftpd.c",
"to": "Lib/FatFs/ff.h",
@@ -6863,6 +7589,36 @@
"kind": "tp-ext",
"include": "stdint.h"
},
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd_auth.c",
+ "to": "外部/系统头文件",
+ "kind": "tp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd_auth.c",
+ "to": "外部/系统头文件",
+ "kind": "tp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd_auth.c",
+ "to": "Drivers/BSP/NET/lftpd/lftpd_auth.h",
+ "kind": "tp-tp",
+ "include": "lftpd_auth.h"
+ },
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd_auth.c",
+ "to": "App/util/sha256.h",
+ "kind": "tp-tp",
+ "include": "sha256.h"
+ },
+ {
+ "from": "Drivers/BSP/NET/lftpd/lftpd_auth.h",
+ "to": "外部/系统头文件",
+ "kind": "tp-ext",
+ "include": "stdint.h"
+ },
{
"from": "Drivers/BSP/NET/lftpd/lftpd_inet.c",
"to": "Drivers/BSP/NET/lftpd/private/lftpd_inet.h",
@@ -6947,36 +7703,6 @@
"kind": "tp-tp",
"include": "dbg_log.h"
},
- {
- "from": "Drivers/BSP/NET/lftpd/lftpd_log.c",
- "to": "Drivers/BSP/NET/lftpd/private/lftpd_log.h",
- "kind": "tp-tp",
- "include": "private/lftpd_log.h"
- },
- {
- "from": "Drivers/BSP/NET/lftpd/lftpd_log.c",
- "to": "外部/系统头文件",
- "kind": "tp-ext",
- "include": "stdio.h"
- },
- {
- "from": "Drivers/BSP/NET/lftpd/lftpd_log.c",
- "to": "外部/系统头文件",
- "kind": "tp-ext",
- "include": "stdlib.h"
- },
- {
- "from": "Drivers/BSP/NET/lftpd/lftpd_log.c",
- "to": "外部/系统头文件",
- "kind": "tp-ext",
- "include": "stdarg.h"
- },
- {
- "from": "Drivers/BSP/NET/lftpd/lftpd_log.c",
- "to": "Drivers/BSP/dbg_log.h",
- "kind": "tp-tp",
- "include": "dbg_log.h"
- },
{
"from": "Drivers/BSP/NET/lftpd/lftpd_string.c",
"to": "外部/系统头文件",
@@ -7019,6 +7745,36 @@
"kind": "tp-tp",
"include": "dbg_log.h"
},
+ {
+ "from": "Drivers/BSP/NETCFG/ftp_cfg.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "Drivers/BSP/NETCFG/ftp_cfg.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "Drivers/BSP/NETCFG/ftp_cfg.c",
+ "to": "Drivers/BSP/NETCFG/ftp_cfg.h",
+ "kind": "fp-fp",
+ "include": "ftp_cfg.h"
+ },
+ {
+ "from": "Drivers/BSP/NETCFG/ftp_cfg.c",
+ "to": "App/cfg/cfg_file.h",
+ "kind": "fp-fp",
+ "include": "cfg_file.h"
+ },
+ {
+ "from": "Drivers/BSP/NETCFG/ftp_cfg.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
{
"from": "Drivers/BSP/NETCFG/net_cfg.c",
"to": "外部/系统头文件",
@@ -7049,6 +7805,12 @@
"kind": "fp-fp",
"include": "cfg_file.h"
},
+ {
+ "from": "Drivers/BSP/NETCFG/net_cfg.c",
+ "to": "Drivers/BSP/NETCFG/ftp_cfg.h",
+ "kind": "fp-fp",
+ "include": "ftp_cfg.h"
+ },
{
"from": "Drivers/BSP/NETCFG/net_cfg.c",
"to": "Drivers/BSP/dbg_log.h",
@@ -8675,6 +9437,12 @@
"kind": "tp-tp",
"include": "stm32f4xx.h"
},
+ {
+ "from": "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_tim.h",
+ "to": "Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h",
+ "kind": "tp-tp",
+ "include": "stm32f4xx.h"
+ },
{
"from": "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usart.h",
"to": "Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h",
@@ -9167,6 +9935,12 @@
"kind": "fp-ext",
"include": "stm32f4xx_hal_mmc.h"
},
+ {
+ "from": "Inc/tim.h",
+ "to": "Inc/main.h",
+ "kind": "fp-fp",
+ "include": "main.h"
+ },
{
"from": "Inc/usart.h",
"to": "Inc/main.h",
@@ -39011,6 +39785,24 @@
"kind": "fp-fp",
"include": "default_task.h"
},
+ {
+ "from": "Src/freertos.c",
+ "to": "App/task/adc_task.h",
+ "kind": "fp-fp",
+ "include": "adc_task.h"
+ },
+ {
+ "from": "Src/freertos.c",
+ "to": "App/task/iec104_serv.h",
+ "kind": "fp-fp",
+ "include": "iec104_serv.h"
+ },
+ {
+ "from": "Src/freertos.c",
+ "to": "App/task/protect_task.h",
+ "kind": "fp-fp",
+ "include": "protect_task.h"
+ },
{
"from": "Src/gpio.c",
"to": "Inc/gpio.h",
@@ -39071,6 +39863,12 @@
"kind": "fp-fp",
"include": "spi.h"
},
+ {
+ "from": "Src/main.c",
+ "to": "Inc/tim.h",
+ "kind": "fp-fp",
+ "include": "tim.h"
+ },
{
"from": "Src/main.c",
"to": "Inc/usart.h",
@@ -39155,6 +39953,12 @@
"kind": "fp-fp",
"include": "task_watchdog.h"
},
+ {
+ "from": "Src/main.c",
+ "to": "App/task/adc_task.h",
+ "kind": "fp-fp",
+ "include": "adc_task.h"
+ },
{
"from": "Src/main.c",
"to": "Drivers/BSP/dbg_log.h",
@@ -39209,12 +40013,42 @@
"kind": "fp-tp",
"include": "stm32f4xx.h"
},
+ {
+ "from": "Src/tim.c",
+ "to": "Inc/tim.h",
+ "kind": "fp-fp",
+ "include": "tim.h"
+ },
{
"from": "Src/usart.c",
"to": "Inc/usart.h",
"kind": "fp-fp",
"include": "usart.h"
},
+ {
+ "from": "test/board/ch395f_reuse_bench.c",
+ "to": "test/board/ch395f_reuse_bench.h",
+ "kind": "fp-fp",
+ "include": "ch395f_reuse_bench.h"
+ },
+ {
+ "from": "test/board/ch395f_reuse_bench.c",
+ "to": "Drivers/BSP/CH395F/ch395f.h",
+ "kind": "fp-fp",
+ "include": "ch395f.h"
+ },
+ {
+ "from": "test/board/ch395f_reuse_bench.c",
+ "to": "Drivers/BSP/dbg_log.h",
+ "kind": "fp-fp",
+ "include": "dbg_log.h"
+ },
+ {
+ "from": "test/board/ch395f_reuse_bench.c",
+ "to": "Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal.h",
+ "kind": "fp-tp",
+ "include": "stm32f4xx_hal.h"
+ },
{
"from": "test/board/ch395f_test.h",
"to": "外部/系统头文件",
@@ -39695,6 +40529,78 @@
"kind": "fp-fp",
"include": "sd2506_test.h"
},
+ {
+ "from": "test/host/adc_health/adc_health_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/adc_health/adc_health_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/adc_health/adc_health_test.c",
+ "to": "App/algo/adc_health.c",
+ "kind": "fp-fp",
+ "include": "adc_health.c"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "Inc/adc.h",
+ "kind": "fp-fp",
+ "include": "adc.h"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "Drivers/BSP/TPAFE5160/tpafe5160.h",
+ "kind": "fp-fp",
+ "include": "tpafe5160.h"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "App/algo/adc_input.h",
+ "kind": "fp-fp",
+ "include": "adc_input.h"
+ },
+ {
+ "from": "test/host/adc_input/adc_input_test.c",
+ "to": "App/algo/adc_input.c",
+ "kind": "fp-fp",
+ "include": "adc_input.c"
+ },
+ {
+ "from": "test/host/adc_input/stub/adc.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/adc_input/stub/tpafe5160.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
{
"from": "test/host/cfg_file/cfg_file_test.c",
"to": "外部/系统头文件",
@@ -39749,6 +40655,192 @@
"kind": "fp-ext",
"include": "stdio.h"
},
+ {
+ "from": "test/host/ftp_auth/ftp_auth_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/ftp_auth/ftp_auth_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "test/host/ftp_auth/ftp_auth_test.c",
+ "to": "App/util/sha256.c",
+ "kind": "fp-fp",
+ "include": "sha256.c"
+ },
+ {
+ "from": "test/host/ftp_auth/ftp_auth_test.c",
+ "to": "Drivers/BSP/NET/lftpd/lftpd_auth.c",
+ "kind": "fp-tp",
+ "include": "lftpd_auth.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/protocol/iec104/iec104_asdu.c",
+ "kind": "fp-fp",
+ "include": "iec104_asdu.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/algo/prot_set.c",
+ "kind": "fp-fp",
+ "include": "prot_set.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/algo/sample_ring.c",
+ "kind": "fp-fp",
+ "include": "sample_ring.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/algo/phasor.c",
+ "kind": "fp-fp",
+ "include": "phasor.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/algo/prot_elm.c",
+ "kind": "fp-fp",
+ "include": "prot_elm.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/protocol/iec104/iec104_pts.c",
+ "kind": "fp-fp",
+ "include": "iec104_pts.c"
+ },
+ {
+ "from": "test/host/iec104/iec104_test.c",
+ "to": "App/protocol/iec104/iec104_link.c",
+ "kind": "fp-fp",
+ "include": "iec104_link.c"
+ },
+ {
+ "from": "test/host/phasor/phasor_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "math.h"
+ },
+ {
+ "from": "test/host/phasor/phasor_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/phasor/phasor_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/phasor/phasor_test.c",
+ "to": "App/algo/sample_ring.c",
+ "kind": "fp-fp",
+ "include": "sample_ring.c"
+ },
+ {
+ "from": "test/host/phasor/phasor_test.c",
+ "to": "App/algo/phasor.c",
+ "kind": "fp-fp",
+ "include": "phasor.c"
+ },
+ {
+ "from": "test/host/prot_elm/prot_elm_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/prot_elm/prot_elm_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/prot_elm/prot_elm_test.c",
+ "to": "App/algo/prot_set.c",
+ "kind": "fp-fp",
+ "include": "prot_set.c"
+ },
+ {
+ "from": "test/host/prot_elm/prot_elm_test.c",
+ "to": "App/algo/sample_ring.c",
+ "kind": "fp-fp",
+ "include": "sample_ring.c"
+ },
+ {
+ "from": "test/host/prot_elm/prot_elm_test.c",
+ "to": "App/algo/phasor.c",
+ "kind": "fp-fp",
+ "include": "phasor.c"
+ },
+ {
+ "from": "test/host/prot_elm/prot_elm_test.c",
+ "to": "App/algo/prot_elm.c",
+ "kind": "fp-fp",
+ "include": "prot_elm.c"
+ },
+ {
+ "from": "test/host/prot_set/prot_set_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/prot_set/prot_set_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/prot_set/prot_set_test.c",
+ "to": "App/algo/prot_set.c",
+ "kind": "fp-fp",
+ "include": "prot_set.c"
+ },
+ {
+ "from": "test/host/sha256/sha256_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/sha256/sha256_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "test/host/sha256/sha256_test.c",
+ "to": "App/util/sha256.c",
+ "kind": "fp-fp",
+ "include": "sha256.c"
+ },
{
"from": "test/host/sys_clock/sys_clock_test.c",
"to": "外部/系统头文件",
@@ -39833,6 +40925,162 @@
"kind": "fp-fp",
"include": "FreeRTOS.h"
},
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "setjmp.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "string.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h",
+ "kind": "fp-tp",
+ "include": "FreeRTOS.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "Middlewares/Third_Party/FreeRTOS/Source/include/task.h",
+ "kind": "fp-tp",
+ "include": "task.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "Drivers/BSP/NET/net_socket.h",
+ "kind": "fp-fp",
+ "include": "net_socket.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "App/util/sys_clock.h",
+ "kind": "fp-fp",
+ "include": "sys_clock.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "App/app/sys_events.h",
+ "kind": "fp-fp",
+ "include": "sys_events.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "App/util/task_watchdog.h",
+ "kind": "fp-fp",
+ "include": "task_watchdog.h"
+ },
+ {
+ "from": "test/host/time_sync/time_sync_test.c",
+ "to": "App/sync/time_sync.c",
+ "kind": "fp-fp",
+ "include": "time_sync.c"
+ },
+ {
+ "from": "test/host/time_sync/stub/FreeRTOS.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/cmsis_os2.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/dbg_log.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/net_socket.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/net_socket.h",
+ "to": "test/host/time_sync/stub/FreeRTOS.h",
+ "kind": "fp-fp",
+ "include": "FreeRTOS.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/net_socket.h",
+ "to": "test/host/time_sync/stub/task.h",
+ "kind": "fp-fp",
+ "include": "task.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/sys_clock.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/sys_events.h",
+ "to": "test/host/time_sync/stub/cmsis_os2.h",
+ "kind": "fp-fp",
+ "include": "cmsis_os2.h"
+ },
+ {
+ "from": "test/host/time_sync/stub/task.h",
+ "to": "test/host/time_sync/stub/FreeRTOS.h",
+ "kind": "fp-fp",
+ "include": "FreeRTOS.h"
+ },
+ {
+ "from": "test/host/vol_lock/vol_lock_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stddef.h"
+ },
+ {
+ "from": "test/host/vol_lock/vol_lock_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
+ {
+ "from": "test/host/vol_lock/vol_lock_test.c",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdio.h"
+ },
+ {
+ "from": "test/host/vol_lock/vol_lock_test.c",
+ "to": "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h",
+ "kind": "fp-tp",
+ "include": "cmsis_os.h"
+ },
+ {
+ "from": "test/host/vol_lock/vol_lock_test.c",
+ "to": "App/fs/vol_lock.c",
+ "kind": "fp-fp",
+ "include": "vol_lock.c"
+ },
+ {
+ "from": "test/host/vol_lock/stub/cmsis_os.h",
+ "to": "外部/系统头文件",
+ "kind": "fp-ext",
+ "include": "stdint.h"
+ },
{
"from": "test/host/watchdog/watchdog_test.c",
"to": "外部/系统头文件",
@@ -39889,6 +41137,37 @@
}
],
"unresolved": {
+ "App/algo/adc_health.h": [
+ "stdint.h"
+ ],
+ "App/algo/adc_input.c": [
+ "stddef.h"
+ ],
+ "App/algo/adc_input.h": [
+ "stdint.h"
+ ],
+ "App/algo/phasor.c": [
+ "math.h",
+ "stddef.h"
+ ],
+ "App/algo/phasor.h": [
+ "stdint.h"
+ ],
+ "App/algo/prot_elm.c": [
+ "stddef.h"
+ ],
+ "App/algo/prot_elm.h": [
+ "stdint.h"
+ ],
+ "App/algo/prot_set.h": [
+ "stdint.h"
+ ],
+ "App/algo/sample_ring.c": [
+ "stddef.h"
+ ],
+ "App/algo/sample_ring.h": [
+ "stdint.h"
+ ],
"App/app/app_main.h": [
"stdint.h"
],
@@ -39927,18 +41206,46 @@
"stdio.h",
"string.h"
],
+ "App/protocol/iec104/iec104_asdu.c": [
+ "string.h"
+ ],
+ "App/protocol/iec104/iec104_asdu.h": [
+ "stdint.h"
+ ],
+ "App/protocol/iec104/iec104_link.c": [
+ "string.h"
+ ],
+ "App/protocol/iec104/iec104_link.h": [
+ "stdint.h"
+ ],
+ "App/protocol/iec104/iec104_pts.c": [
+ "string.h"
+ ],
+ "App/protocol/iec104/iec104_pts.h": [
+ "stdint.h"
+ ],
"App/sync/time_sync.c": [
"string.h"
],
"App/sync/time_sync.h": [
"stdint.h"
],
+ "App/task/iec104_serv.c": [
+ "string.h"
+ ],
"App/task/net_task.c": [
"string.h"
],
"App/task/ui_task.c": [
"string.h"
],
+ "App/util/sha256.c": [
+ "string.h"
+ ],
+ "App/util/sha256.h": [
+ "stddef.h",
+ "stdint.h"
+ ],
"App/util/sys_clock.c": [
"stdio.h",
"string.h"
@@ -39946,6 +41253,12 @@
"App/util/sys_clock.h": [
"stdint.h"
],
+ "App/util/task_prof.c": [
+ "stdio.h"
+ ],
+ "App/util/task_prof.h": [
+ "stdint.h"
+ ],
"App/util/task_watchdog.c": [
"string.h"
],
@@ -39980,6 +41293,13 @@
"Drivers/BSP/NET/lftpd/lftpd.h": [
"stdint.h"
],
+ "Drivers/BSP/NET/lftpd/lftpd_auth.c": [
+ "stddef.h",
+ "string.h"
+ ],
+ "Drivers/BSP/NET/lftpd/lftpd_auth.h": [
+ "stdint.h"
+ ],
"Drivers/BSP/NET/lftpd/lftpd_inet.c": [
"string.h"
],
@@ -39989,11 +41309,6 @@
"stdlib.h",
"string.h"
],
- "Drivers/BSP/NET/lftpd/lftpd_log.c": [
- "stdarg.h",
- "stdio.h",
- "stdlib.h"
- ],
"Drivers/BSP/NET/lftpd/lftpd_string.c": [
"ctype.h",
"string.h"
@@ -40017,6 +41332,13 @@
"Drivers/BSP/NET/net_types.h": [
"stdint.h"
],
+ "Drivers/BSP/NETCFG/ftp_cfg.c": [
+ "stddef.h",
+ "string.h"
+ ],
+ "Drivers/BSP/NETCFG/ftp_cfg.h": [
+ "stdint.h"
+ ],
"Drivers/BSP/NETCFG/net_cfg.c": [
"stdbool.h",
"stdio.h",
@@ -41617,6 +42939,21 @@
"test/board/test_config.h": [
"stdint.h"
],
+ "test/host/adc_health/adc_health_test.c": [
+ "stdint.h",
+ "stdio.h"
+ ],
+ "test/host/adc_input/adc_input_test.c": [
+ "stddef.h",
+ "stdint.h",
+ "stdio.h"
+ ],
+ "test/host/adc_input/stub/adc.h": [
+ "stdint.h"
+ ],
+ "test/host/adc_input/stub/tpafe5160.h": [
+ "stdint.h"
+ ],
"test/host/cfg_file/cfg_file_test.c": [
"stdint.h",
"stdio.h",
@@ -41626,6 +42963,32 @@
"test/host/cfg_file/stub/dbg_log.h": [
"stdio.h"
],
+ "test/host/ftp_auth/ftp_auth_test.c": [
+ "stdio.h",
+ "string.h"
+ ],
+ "test/host/iec104/iec104_test.c": [
+ "stdint.h",
+ "stdio.h",
+ "string.h"
+ ],
+ "test/host/phasor/phasor_test.c": [
+ "math.h",
+ "stdint.h",
+ "stdio.h"
+ ],
+ "test/host/prot_elm/prot_elm_test.c": [
+ "stdint.h",
+ "stdio.h"
+ ],
+ "test/host/prot_set/prot_set_test.c": [
+ "stdint.h",
+ "stdio.h"
+ ],
+ "test/host/sha256/sha256_test.c": [
+ "stdio.h",
+ "string.h"
+ ],
"test/host/sys_clock/stub/FreeRTOS.h": [
"stdint.h"
],
@@ -41640,6 +43003,35 @@
"stdio.h",
"string.h"
],
+ "test/host/time_sync/stub/FreeRTOS.h": [
+ "stdint.h"
+ ],
+ "test/host/time_sync/stub/cmsis_os2.h": [
+ "stdint.h"
+ ],
+ "test/host/time_sync/stub/dbg_log.h": [
+ "stdio.h"
+ ],
+ "test/host/time_sync/stub/net_socket.h": [
+ "stdint.h"
+ ],
+ "test/host/time_sync/stub/sys_clock.h": [
+ "stdint.h"
+ ],
+ "test/host/time_sync/time_sync_test.c": [
+ "setjmp.h",
+ "stdint.h",
+ "stdio.h",
+ "string.h"
+ ],
+ "test/host/vol_lock/stub/cmsis_os.h": [
+ "stdint.h"
+ ],
+ "test/host/vol_lock/vol_lock_test.c": [
+ "stddef.h",
+ "stdint.h",
+ "stdio.h"
+ ],
"test/host/watchdog/stub/dbg_log.h": [
"stdio.h"
],
@@ -41655,12 +43047,12 @@
]
},
"stats": {
- "files_indexed": 1425,
- "includes_parsed": 5706,
- "resolved": 4637,
- "unresolved": 1069,
- "nodes_fp": 147,
- "nodes_tp": 1264,
+ "files_indexed": 1479,
+ "includes_parsed": 5878,
+ "resolved": 4749,
+ "unresolved": 1129,
+ "nodes_fp": 199,
+ "nodes_tp": 1266,
"ext_names": 351
},
"include_dirs": [
@@ -41680,11 +43072,13 @@
"Drivers/BSP/NETCFG",
"App",
"App/app",
+ "App/algo",
"App/cfg",
"App/fs",
"App/log",
"App/sync",
"App/task",
+ "App/protocol/iec104",
"App/util",
"Middlewares/Third_Party/FreeRTOS/Source/include",
"Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2",
diff --git a/docs/images/引用关系图.svg b/docs/images/引用关系图.svg
index ab0679a5..99b87f48 100644
--- a/docs/images/引用关系图.svg
+++ b/docs/images/引用关系图.svg
@@ -4,3947 +4,5165 @@
-