chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
"stack-overflow-hook-impl": "a006edbd30d2c4d7",
|
||||
"malloc-failed-hook-impl": "fcd7250e5cd6403b",
|
||||
"stack-overflow-hook-helper": "cacd9fd028be9b5b",
|
||||
"watchdog-module": "56bf2739f56a44e6",
|
||||
"watchdog-module": "2bb051d572be4149",
|
||||
"net-msgq-adopt": "a0f913e3f34c8e9e",
|
||||
"reset-cause-latch": "983927d587cb9f48",
|
||||
"iwdg-hal-driver": "4b902875a84554e9",
|
||||
|
||||
@@ -355,7 +355,7 @@ def main(argv=None):
|
||||
help="forbidden patterns, ';' separated (config 'forbidden')")
|
||||
ap.add_argument("--require-suite-summary", default=None,
|
||||
help="suite names that MUST appear as '=== <name>: N/M PASSED (failed=0) ===' "
|
||||
"with N>0, ';' separated (T-2). Empty = off")
|
||||
"with N>0, ';' separated. Empty = off")
|
||||
ap.add_argument("--require-selftest-pass", action="store_true", default=None,
|
||||
help="jointly judge /log/selftest.log (S7): missing/empty = PASS, "
|
||||
"any 64B record = FAIL")
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
- 读取编码:先按 UTF-8,解码失败回退系统 locale 编码(旧版 Get-Content 行为)
|
||||
- 控制台输出重配置为 UTF-8(errors=replace):任何 Windows 控制台代码页
|
||||
都只影响终端显示,不会导致本脚本崩溃
|
||||
- 汇总行(2026-10-01 加,T-2):纯 Required 子串匹配有个盲区——套件启动横幅
|
||||
- 汇总行(2026-10-01 加):纯 Required 子串匹配有个盲区——套件启动横幅
|
||||
命中即判 PASS,**套件是否跑到汇总行、断言数是否为 0 完全不可见**(套件中途
|
||||
挂死/复位、或 Phase 宏全关时会被误判为通过)。`--require-suite-summary`
|
||||
要求日志中出现 `=== <名称>: N/M PASSED (failed=0) ===` 且 **N > 0**。
|
||||
@@ -311,7 +311,7 @@ def verify(log_path, required=REQUIRED_DEFAULT, forbidden=FORBIDDEN_DEFAULT,
|
||||
"(run_boot_test.py adds it; --raw-log turns it off)")
|
||||
res["timing"] = timing
|
||||
|
||||
# 套件汇总行解析(T-2):抓"没跑到汇总行 / 断言数为 0"的盲区
|
||||
# 套件汇总行解析:抓"没跑到汇总行 / 断言数为 0"的盲区
|
||||
bad_summary = []
|
||||
if summary_list:
|
||||
parsed = {}
|
||||
@@ -407,7 +407,7 @@ def main(argv=None):
|
||||
help="forbidden patterns, ';' separated (case-insensitive)")
|
||||
ap.add_argument("--require-suite-summary", default="",
|
||||
help="suite names that MUST appear as '=== <name>: N/M PASSED (failed=0) ===' "
|
||||
"with N>0, ';' separated (T-2). Empty = off")
|
||||
"with N>0, ';' separated. Empty = off")
|
||||
ap.add_argument("--show-tail-lines", type=int, default=40,
|
||||
help="show last N log lines (0 = off)")
|
||||
ap.add_argument("--require-selftest-pass", action="store_true",
|
||||
|
||||
@@ -81,7 +81,7 @@ whenToUse: 要同时推进多个任务、担心同分支同文档互相冲突;
|
||||
- **`done` 前主工作树必须干净**:否则未提交改动会被卷进合并。
|
||||
- **⚠ `done` 的门禁跑在 `@build` 之前 → 依赖 `.map` 的钩子会误报**(2026-10-12 实测):
|
||||
`done` 的顺序是「合并 → 重生成 → **门禁(ci_local)** → `@build`」,而
|
||||
`mem-budget`(N-27)/ `mem-numbers` 是拿**文档快照 vs `.map`** 比对 —— 门禁跑时
|
||||
`mem-budget`(登记簿条目「缩堆迁 CCM 后半(暂缓)」)/ `mem-numbers` 是拿**文档快照 vs `.map`** 比对 —— 门禁跑时
|
||||
`.map` 还是**主工作树合并前**的旧产物,与刚合并进来的新台账**必然不一致** →
|
||||
误报 `[FAIL] 台账受管区与 .map 不一致`。`@build` 跑完后 `.map` 才是新的。
|
||||
**处置**:门禁失败且只差这两项时,按提示在主工作树跑
|
||||
|
||||
@@ -41,7 +41,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警
|
||||
| `MENU_POOL_BASELINE` | check_menu_rows.py | 5368 B | 菜单根框建完的 LVGL 池 used(x64 模拟器;2026-10-11 随 LVGL 控件裁剪由 4784 上移) |
|
||||
| `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | 5448 B | 开页-关页往返后 = 基线 + 80 B 一次性残留 |
|
||||
|
||||
⚠ 两个常量量的是池的 **`used`**,与 `LV_MEM_SIZE` 总大小**无关** → 改池大小(如 2026-10-15 R-9 的 40→16 KB)**不该动这两个数**;动了就是页面结构变化或泄漏。
|
||||
⚠ 两个常量量的是池的 **`used`**,与 `LV_MEM_SIZE` 总大小**无关** → 改池大小(如 2026-10-15 缩池 40→16 KB)**不该动这两个数**;动了就是页面结构变化或泄漏。
|
||||
|
||||
**禁止为过门禁盲目改常量**——无法解释的漂移 = 内存回归 bug。
|
||||
|
||||
|
||||
+12
-12
@@ -14,7 +14,7 @@
|
||||
# 构建产物、已 gitignore),无 `@build` 即无 `.map`。故本 workflow 在**检测不到
|
||||
# .map 时自动跳过它们**(见下方 "决定跳过哪些钩子" 步骤),有 .map 的 runner 照常跑。
|
||||
#
|
||||
# 🔴 体积门禁:盲区已关闭为**强制路径**(A-5 残余,2026-10-25;原 S14 只做到"声明")
|
||||
# 🔴 体积门禁:盲区已关闭为**强制路径**(2026-10-25 起;原先只做到"声明")
|
||||
# 跳过钩子只解释了"runner 没有 Keil",没解决**"没跑 = 通过"**:钩子在无 .map 时
|
||||
# 自适应 SKIP 并返回 0,于是 CI 全绿仍可能被读成"体积也守住了"。现由 L0 作业**最后
|
||||
# 一步** `python scripts/check_size_gate.py --ci` 在**不需要 Keil** 的前提下补两条判据:
|
||||
@@ -28,7 +28,7 @@
|
||||
# 模块 docstring,此处不重复):绝对数字由本地真实构建产生(runner 无 Keil 许可证)、
|
||||
# 编译器选项(uvprojx,UV4 每次改写且不入库)不在指纹范围内。
|
||||
#
|
||||
# 产物存档(S3,2026-10-02):两个 job 把关键输出 `Tee-Object` 到 `ci-artifacts/*.log`,
|
||||
# 产物存档与结果可见性(2026-10-02 起):两个 job 把关键输出 `Tee-Object` 到 `ci-artifacts/*.log`,
|
||||
# 并写 $GITHUB_STEP_SUMMARY(Gitea UI 直接可见)+ `::notice::` 汇总行。
|
||||
# **刻意不用 `actions/upload-artifact`**——内网可能拉不到外层 action(见 docs/CI/远程CI完整指南.md §5.3);
|
||||
# 零依赖方案下日志留在 runner 工作区,UI 通过 step-summary 即可排障。
|
||||
@@ -247,12 +247,12 @@ jobs:
|
||||
# mem-numbers / mem-budget fact source is the Keil build artifact .map
|
||||
# (gitignored). No .map => those hooks cannot run => skip them EXPLICITLY.
|
||||
#
|
||||
# A-5 (2026-10-25): two honesty fixes here.
|
||||
# 2026-10-25: two honesty fixes here.
|
||||
# 1) mem-budget used NOT to be listed, yet it self-skips inside the script
|
||||
# when .map is missing (exit 0) -- so on a Keil-less runner it reported
|
||||
# "Passed" while checking nothing. A gate that fakes green is worse than
|
||||
# a gate that is visibly skipped: skip it here so the report says Skipped.
|
||||
# 2) "skipped" alone is still the A-5 blind spot, so the compensation is a
|
||||
# 2) "skipped" alone is still the blind spot, so the compensation is a
|
||||
# real step, not only a warning: see the "size gate" step at the end of
|
||||
# this job (scripts/check_size_gate.py --ci, no Keil needed: attestation
|
||||
# parity + 92% re-check on the recorded numbers). Boundary and rationale
|
||||
@@ -266,7 +266,7 @@ jobs:
|
||||
Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
|
||||
} else {
|
||||
Write-Host "::warning::no .map found (no Keil build) -- SKIPPING mem-numbers and mem-budget, so absolute Flash/RAM numbers are NOT recomputed here."
|
||||
Write-Host "::warning::that gap is no longer silent: the final step of this job runs scripts/check_size_gate.py --ci (A-5 forced path: attestation parity + 92% budget re-check). Locally: scripts/ci_local.py step L0c."
|
||||
Write-Host "::warning::that gap is no longer silent: the final step of this job runs scripts/check_size_gate.py --ci (forced path: attestation parity + 92% budget re-check). Locally: scripts/ci_local.py step L0c."
|
||||
"value=mem-numbers,mem-budget" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding ascii
|
||||
"sizegate=off" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding ascii
|
||||
"> :warning: Volume gates (mem-numbers, mem-budget): **NOT RUN** -- no `.map` (no Keil build); compensated by the size-gate step below." |
|
||||
@@ -315,7 +315,7 @@ jobs:
|
||||
if: always()
|
||||
shell: powershell
|
||||
run: |
|
||||
# S3: zero-dependency artifact archival. We do NOT use
|
||||
# Result visibility: zero-dependency artifact archival. We do NOT use
|
||||
# actions/upload-artifact (inner networks may not fetch outer actions);
|
||||
# instead we keep the log on the runner AND surface its path + tail in
|
||||
# the step summary, so the UI is self-sufficient for triage.
|
||||
@@ -328,10 +328,10 @@ jobs:
|
||||
Write-Host "::notice::L0 artifacts kept on runner at: ci-artifacts\l0-pre-commit.log"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# A-5 残余的强制体积门禁(2026-10-25):不需要 Keil,也不需要 .map
|
||||
# 强制体积门禁(2026-10-25):不需要 Keil,也不需要 .map
|
||||
# 放在 L0 作业**最后**且 if: always()——L0 失败时这条结论同样要看得到,
|
||||
# 而它自己失败会让整个作业变红(这正是"强制"的含义)。
|
||||
- name: 体积门禁(A-5 强制路径:同码凭证 + 92% 红线,无需 Keil)
|
||||
- name: 体积门禁(强制路径:同码凭证 + 92% 红线,无需 Keil)
|
||||
if: always()
|
||||
shell: powershell
|
||||
env:
|
||||
@@ -342,8 +342,8 @@ jobs:
|
||||
#
|
||||
# WHY THIS STEP EXISTS: mem-numbers / mem-budget read the Keil .map and
|
||||
# self-skip (exit 0) without it, so "not measured" used to look like
|
||||
# "passed" -- that is exactly the A-5 blind spot ("CI all green does not
|
||||
# mean the size budget held"). Declaring it (S14) did not close it.
|
||||
# "passed" -- that is exactly the blind spot ("CI all green does not
|
||||
# mean the size budget held"). Merely declaring it did not close it.
|
||||
#
|
||||
# This step enforces a check that needs NO Keil and NO .map:
|
||||
# * parity -- scripts/size_attestation.json (a committed attestation
|
||||
@@ -383,7 +383,7 @@ jobs:
|
||||
$ErrorActionPreference = $saved
|
||||
|
||||
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "" -Encoding utf8
|
||||
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Volume gate (A-5 forced path)" -Encoding utf8
|
||||
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Volume gate (forced path)" -Encoding utf8
|
||||
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "size gate: rc=$rc mode=$label (attestation parity + 92% re-check; no Keil)" -Encoding utf8
|
||||
Write-Host "::notice::size gate: rc=$rc mode=$label (log: $log)"
|
||||
if ($rc -ne 0) {
|
||||
@@ -534,7 +534,7 @@ jobs:
|
||||
if: always()
|
||||
shell: powershell
|
||||
run: |
|
||||
# S3: same zero-dependency archival as L0.
|
||||
# Result visibility: same zero-dependency archival as L0.
|
||||
if (Test-Path 'ci-artifacts') {
|
||||
$logs = Get-ChildItem -Path 'ci-artifacts' -Filter '*.log' -ErrorAction SilentlyContinue
|
||||
foreach ($f in $logs) {
|
||||
|
||||
+24
-24
@@ -160,11 +160,11 @@ repos:
|
||||
Inc/
|
||||
)
|
||||
|
||||
# 5) 文档一致性门禁(D-7)
|
||||
# 5) 文档一致性门禁
|
||||
# 存在性:md 互链 / 反引号路径是否指向真实文件(防迁移/删除后残留死链)
|
||||
# 章节:`§N.M` 必须能在目标文档的编号标题里找到(--sections-strict:自引用失效也拦)
|
||||
- id: doc-links
|
||||
name: 文档链接存在性 (D-3/D-7)
|
||||
name: 文档链接存在性
|
||||
entry: python scripts/check_doc_links.py --sections-strict
|
||||
language: system
|
||||
files: \.md$
|
||||
@@ -177,23 +177,23 @@ repos:
|
||||
MDK-ARM/RTE/|
|
||||
docs/芯片手册/
|
||||
)
|
||||
# 数值:README/AGENTS 任务表须与 Src/freertos.c 一致(D-2/D-7)
|
||||
# 数值:README/AGENTS 任务表须与 Src/freertos.c 一致
|
||||
- id: task-table
|
||||
name: FreeRTOS 任务表一致性 (D-2/D-7)
|
||||
name: FreeRTOS 任务表一致性
|
||||
entry: python scripts/gen_task_table.py --check
|
||||
language: system
|
||||
files: ^(README\.md|AGENTS\.md|Src/freertos\.c)$
|
||||
pass_filenames: false
|
||||
# 体积:内存/Flash 数字须与最新 .map 一致(D-7 第三档)
|
||||
# 体积:内存/Flash 数字须与最新 .map 一致(体积档)
|
||||
# 只在内存文档或代码变动时触发;.map 未生成时脚本自身给出明确提示
|
||||
- id: mem-numbers
|
||||
name: 内存/Flash 数字一致性 (D-7 体积档)
|
||||
name: 内存/Flash 数字一致性(体积档)
|
||||
entry: python scripts/check_mem_numbers.py --scan
|
||||
language: system
|
||||
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 落地)
|
||||
# 5d) 两份 lv_conf.h 特性子集一致性(2026-10-11 加)
|
||||
# 背景:`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
|
||||
@@ -328,7 +328,7 @@ repos:
|
||||
pass_filenames: false
|
||||
|
||||
# 7g) 时间同步(time_sync)主机侧单元测试(2026-10-10 加)
|
||||
# 背景:time_sync.c 是 TCP 时间同步协议状态机,也是 N-17 号看门狗风险的**全部
|
||||
# 背景:time_sync.c 是 TCP 时间同步协议状态机,也是探针窗口心跳缺口那项看门狗风险的**全部
|
||||
# 逻辑贡献者**——"listen 重建 -> 800ms~1s 心跳间隔"由本模块的分片延时/退避/
|
||||
# 结算等待组成,板端只能看到"是否复位"这一结果,看不到各段延时是否如设计。本钩子
|
||||
# 用 MSVC 离线编译真实源文件(#include 纳入)+ 可脚本化的网络/事件桩(含 longjmp
|
||||
@@ -440,7 +440,7 @@ repos:
|
||||
files: ^(App/algo/freq\.(c|h)|App/algo/phasor\.(c|h)|App/algo/sample_ring\.(c|h)|test/host/freq/.*|test/host/common/unit_test_harness\.py)$
|
||||
pass_filenames: false
|
||||
|
||||
# 7k) SHA-256 主机侧单元测试(2026-10-11 加,N-19 FTP 鉴权)
|
||||
# 7k) SHA-256 主机侧单元测试(2026-10-11 加,FTP 鉴权)
|
||||
# sha256 是 FTP pwd_hash 校验的地基——哈希算错是"正确口令被拒 /
|
||||
# 错误口令放行"的静默语义错误,板端只见 530/230 看不出根因;
|
||||
# FIPS 180-4 标准向量(空串/abc/56B 补位跨界/1MB)+ hex_eq 非法输入
|
||||
@@ -452,7 +452,7 @@ repos:
|
||||
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)
|
||||
# 7l) FTP 鉴权状态机主机侧单元测试(2026-10-11 加,FTP 鉴权)
|
||||
# 锁定 60s/解锁/成功清零/tick 32 位回绕是"防口令爆破"契约核心,
|
||||
# 板端每验一条要真等 60s、回绕要跑满 49.7 天;lftpd_auth 纯函数
|
||||
# 设计(tick 参数注入)就是为了让这些分支主机侧毫秒级穷举。
|
||||
@@ -466,7 +466,7 @@ repos:
|
||||
# 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
|
||||
@@ -489,7 +489,7 @@ repos:
|
||||
files: ^(App/log/soe_rec\.(c|h)|App/log/soe_store\.(c|h)|test/host/soe/.*|test/host/common/unit_test_harness\.py)$
|
||||
pass_filenames: false
|
||||
|
||||
# 7n) dhara NAND 移植层(nand_ftl)主机侧单元测试(2026-10-15 加,N-21 / 报告 N-12+N-13)
|
||||
# 7n) dhara NAND 移植层(nand_ftl)主机侧单元测试(2026-10-15 加)
|
||||
# 背景:移植层六个 HAL 入口是**纯地址算术**,此前对越界页/块号(含
|
||||
# DHARA_PAGE_NONE 哨兵)**不做任何上界校验**——加偏移送进 SPI 命令后被地址位
|
||||
# 截断成某个真实页 → 静默读写/搬走无辜数据;而 is_free 旧实现只看页首 64 B,
|
||||
@@ -497,7 +497,7 @@ repos:
|
||||
# = 数据丢失。两类缺陷真机**极难复现**(要恰好撞上特定页形态 / 被污染的 meta),
|
||||
# 板端只能表现为"某次复位后丢数据",无从定位。
|
||||
# 本钩子用 MSVC 离线编译真实 nand_ftl.c(#include 纳入)+ **RAM 假芯片**
|
||||
# (32 块 x 64 页 x 2048 B):把"双编程"做成可数事件(N-12 探针)、记录全部
|
||||
# (32 块 x 64 页 x 2048 B):把"双编程"做成可数事件、记录全部
|
||||
# 访问次数,于是"越界不产生任何芯片访问""越界标坏块次数为 0"可机械断言;
|
||||
# 并内联一份"只看页首 64 B"的旧算法做对照,证明这组断言真能咬人。
|
||||
# 触发面含 dhara 本体(测试一并 #include map.c/journal.c 跑真链路场景)。
|
||||
@@ -531,7 +531,7 @@ repos:
|
||||
# TEST_SUITE_CH395F 就是开启状态(.map 含 34 处 test 代码),即仓库里
|
||||
# 躺着的是测试固件。本钩子抓这个静默漏网:默认要求产品构建,跑套件回归
|
||||
# 确需提交测试构建时用 `--allow-test-build` / ALLOW_TEST_BUILD=1 显式放行。
|
||||
# 详见 docs/任务规划/登记簿.md T-1
|
||||
# 详见 docs/任务规划/登记簿.md
|
||||
- id: test-suite-switch
|
||||
name: 测试套件开关=产品构建
|
||||
entry: python scripts/check_test_suite.py
|
||||
@@ -539,7 +539,7 @@ repos:
|
||||
files: ^test/board/test_config\.h$
|
||||
pass_filenames: false
|
||||
|
||||
# 9) 双端注册反向校验(2026-10-12 加,登记簿 N-23 / 审查报告 N-06 残余)
|
||||
# 9) 双端注册反向校验(2026-10-12 加,登记簿条目「rs485_task 白名单缺口」)
|
||||
# 背景:新 .c 必须手动注册两处(`MDK-ARM/*.uvprojx` + `simulator/CMakeLists.txt`)
|
||||
# 才会被编译;历史上 `App/task/rs485_task.c`、`Drivers/BSP/NET/lftpd/lftpd_log.c`
|
||||
# **两处都没注册** = 写了却从不参与任何一端编译的**死代码**,误导读者以为功能已生效。
|
||||
@@ -556,22 +556,22 @@ repos:
|
||||
language: system
|
||||
files: (\.c$|\.uvprojx$|CMakeLists\.txt$|^scripts/check_registration\.py$)
|
||||
pass_filenames: false
|
||||
# 10) RAM 预算 92% 阈值门禁(2026-10-12 加,登记簿 N-27 前半 / 审查报告 §5.5 三件套)
|
||||
# 10) RAM 预算 92% 阈值门禁(2026-10-12 加,登记簿条目「缩堆迁 CCM 后半(暂缓)」的台账/门禁那半 / 审查报告 §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 残余)。
|
||||
# (提示 --write);无 .map → 自适应跳过(同 mem-numbers 在 CI 的行为,盲区已登记)。
|
||||
# ⚠ pre-commit 4.x 无 `always: true` → files 覆盖所有能改变 RAM 的触发面:
|
||||
# 全部自研代码目录 + lv_conf.h + 测试板配置 + 脚本/tsv/台账自身 + 本配置文件。
|
||||
- id: mem-budget
|
||||
name: RAM 预算 92% 阈值门禁 (N-27)
|
||||
name: RAM 预算 92% 阈值门禁
|
||||
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
|
||||
|
||||
# 11) UI 字符集差集检查(2026-10-15 加,报告 §6.5 U4 残余 / §7 交付物清单"字符集差集检查"行)
|
||||
# 11) UI 字符集差集检查(2026-10-15 加,报告 §6.5 的 UI 字符集与像素回归门禁残余 / §7 交付物清单"字符集差集检查"行)
|
||||
# 背景:`ui_font_cn_12.c` 是**子集**字体,只含 `ui_cn_chars.txt` 列出的码位。
|
||||
# 生成链 UI 源码 --extract_cn--> ui_cn_chars.txt --make_font--> 字体 .c,其中
|
||||
# verify_coverage 只在**生成期**校验"txt ⊆ cmap";改完 UI 字符串忘了重跑
|
||||
@@ -621,7 +621,7 @@ repos:
|
||||
files: ^(tools/setview_check/|Drivers/BSP/UI/ui_w_setview\.(c|h)$)
|
||||
pass_filenames: false
|
||||
|
||||
# 12) UI 像素回归门禁(2026-10-20 加,W6 U4 收尾第三件 / 报告 §6.5 U4、§6.1 golden 行)
|
||||
# 12) UI 像素回归门禁(2026-10-20 加,W6 的 UI 字符集与像素回归门禁收尾第三件 / 报告 §6.5 同项、§6.1 golden 行)
|
||||
# 背景:golden 两脚本(check_menu_rows.py / check_content_pages.py)早已存在且一直在
|
||||
# 人工跑,但**从未进自动门禁**——"有基线"不等于"有门禁"(报告 §6.1 原文即自认此缺口)。
|
||||
# 形态选型依据实测:改一个 UI 源文件后 build_sim 增量重编 + 两条 golden 合计 **2~4 秒**,
|
||||
@@ -674,9 +674,9 @@ repos:
|
||||
# /形态/冻结/游标,逐项来自规范 §4 总表)+ `scripts/id_aliases.json`(旧号→新号,
|
||||
# 照搬 section_aliases.json 的成功范式)。
|
||||
# 判据要点:「定义」= 字面量出现在该族**属主登记处文档**里;于是 §5.5 的
|
||||
# 「销项 = 删行 + 清全库引用」只做一半,校验 3 立刻报(N-31/N-32 正是这条)。
|
||||
# 「销项 = 删行 + 清全库引用」只做一半,校验 3 立刻报(销项号被重新取走那类撞号正是这条)。
|
||||
# 盲区显式登记在 registry 的 `blind_spots` / `unmanaged_bare`:裸形同字母多义
|
||||
# (§1.2:P1 五义 / L1 四义 / U1 四义)与元器件位号同形(§4.4),机械全库判定
|
||||
# (§1.2:P1 五义 / L1 四义 / UI 状态机号四义)与元器件位号同形(§4.4),机械全库判定
|
||||
# 必然一边倒误报 → 裸形族只在属主文档内判越游标、文档外降告警;收口属 §9 阶段
|
||||
# 1-② 的迁移批次。**不是「检查通过」,是记在案上的盲区。**
|
||||
# ⚠ 当前 warn-only(§8 上线节奏:先清 backlog → 清零后 entry 加 --strict 拦提交,
|
||||
@@ -692,7 +692,7 @@ repos:
|
||||
files: \.(md|c|h|py|ya?ml|json|txt)$|^\.pre-commit-config\.yaml$
|
||||
pass_filenames: false
|
||||
|
||||
# 14) 分层方向门禁(2026-10-22 加,报告 §0.4 结构债 S-1 的"防扩大"那一半)
|
||||
# 14) 分层方向门禁(2026-10-22 加,结构债的"防扩大"那一半)
|
||||
# 背景:AGENTS.md 与 项目架构总览.md 早就写着"六层单向依赖",但**从来没有机械检查**
|
||||
# ——Drivers/BSP/ 里可以随手 include "cfg_file.h" 而无人报警。后果可测量:2026-09 审查
|
||||
# 报告记"NETCFG 一处",2026-10-22 实测已长到 **8 个文件 9 处**。边界的静默腐蚀只能
|
||||
@@ -711,7 +711,7 @@ repos:
|
||||
files: ^Drivers/BSP/|^scripts/check_layer_deps\.py$|^\.pre-commit-config\.yaml$
|
||||
pass_filenames: false
|
||||
|
||||
# 15) BSP 错误码与超时命名约定(报告 S-5 的机械检查那一半)。
|
||||
# 15) BSP 错误码与超时命名约定(机械检查那一半)。
|
||||
# 四项检查:公开 API 不得用 HAL_StatusTypeDef/bool 当错误通道(谓词 is_/has_/can_/valid
|
||||
# 不在此列);*_TIMEOUT 宏不得取负值(名字叫 TIMEOUT 其实是返回码);非 void 的公开 API
|
||||
# 必须有「返回值:」注释;**返回错误码的函数不得裸丢弃返回值**(接住或显式 (void)——实测
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
## 项目概况
|
||||
|
||||
**现状一句话**:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置)**已实现并台架验收**;采集主线已闭环到业务消费者(L1→L2 相量→L2.5 遥测快照→L3 保护判据 protectTask→104 遥信/遥测+UI,B5/批 2);⚠ **保护判据现处于「未标定闭锁」态**——产品构建下 `adc_cal_flags()` 的 `ADC_CAL_F_VALID` 未置位,`protect_task.c:98` 直接停走判据,等卡 12 两点标定写入 `[adc_cal] valid` 后自动解卡(用户裁定 2026-10-25:保持闭锁,不用万用表走捷径);**RS-485 采集尚未接线**(B2 按用户决策冻结,见 `docs/任务规划/暂缓-RS485.md`)——当前最大功能缺口,接线节奏受 E-59 门禁约束(见「平台约束」)。
|
||||
**现状一句话**:平台能力(网络 / 存储 / 时间 / UI / 日志 / 看门狗 / FTP / 配置)**已实现并台架验收**;采集主线已闭环到业务消费者(L1→L2 相量→L2.5 遥测快照→L3 保护判据 protectTask→104 遥信/遥测+UI,B5/批 2);⚠ **保护判据现处于「未标定闭锁」态**——产品构建下 `adc_cal_flags()` 的 `ADC_CAL_F_VALID` 未置位,`protect_task.c:98` 直接停走判据,等卡 12 两点标定写入 `[adc_cal] valid` 后自动解卡(用户裁定 2026-10-25:保持闭锁,不用万用表走捷径);**RS-485 采集尚未接线**(B2 按用户决策冻结,见 `docs/任务规划/暂缓-RS485.md`)——当前最大功能缺口,接线节奏受 **RS-485 接线门禁**约束(见「平台约束」)。
|
||||
|
||||
**数据主线**(能力视角;分层图与跨层依赖表 → `docs/架构设计/项目架构总览.md`):
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
## 提交门禁(pre-commit)
|
||||
|
||||
- 任一失败即拦截,共 **49 个钩子**(2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条;**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 23 个(`-unit-test` 后缀,2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par / ui-sink 七个)+ `test-coverage`(**覆盖率总闸**:跑全部 40 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,N-23/U4)+ `mem-budget`(RAM 92% 预算门禁,N-27)+ `font-charset`(UI 字符集差集检查,防缺字白框,U4)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 18 页黄金参考,U4)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3),报告 S-1)+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**),报告 S-5)
|
||||
- 任一失败即拦截,共 **49 个钩子**(2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条;**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败;**2026-10-26 方案 B 同批加了第二根**:不可判定引用**绝对数**超过 `undecidable_total_ceiling` 即失败,因为比值会被"把可判定的引用改成散文"稀释(详见 `docs/门禁说明/id-check.md` §7.6))+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 23 个(`-unit-test` 后缀,2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par / ui-sink 七个)+ `test-coverage`(**覆盖率总闸**:跑全部 40 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 18 页黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**))
|
||||
- **范围 = 项目代码**:第三方(`Lib/`、`Middlewares/`、CMSIS、HAL、`lftpd`、`dhara`、`MDK-ARM`)排除;`cppcheck`/`coding-standard` 另排除 `test/`,规范检查再排除 CubeMX 生成的 `Src/`/`Inc/`。**`simulator/` 已纳入全部钩子**(2026-10-11)——生成物只靠 `.gitignore` 挡,**不在 `exclude` 里写目录名**(教训见 `docs/代码规范/pre-commit使用说明.md` §5.5)
|
||||
- ⚠ **pre-commit 4.x 已移除 `always: true`**(实测对 `local` 钩子只告警 `Unexpected key(s)` 并跳过)→ 想"总是跑"的钩子**必须让 `files:` 覆盖到所有会触发它的文件**,没有兜底开关
|
||||
- **改写型钩子(`trailing-whitespace` + `end-of-file-fixer`)豁免 CubeMX/Keil 生成文件**(配置里 YAML 锚点 `&cubemx-generated`);⚠ **只读检查不受影响**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` / `mem-numbers` 仍覆盖 `Src/`、`Inc/`
|
||||
@@ -119,7 +119,7 @@
|
||||
- **ADC 取数只用 `tpafe5160_snapshot()`(临界区原子快照)**:旧的 `get_buf()`/`data_ready()`/`clear_ready()` 已从驱动**删除**(两步之间被抢占会拷回两帧混合数据;`s_ready` 三写者竞态)。消费者侧一律经 `adc_input_poll()` / `adc_input_raw()` **按语义**取数(不写死下标)→ `docs/子系统设计/ADC采样节拍设计说明.md`、`App/algo/README.md`
|
||||
- **F4 上 ADC1 的硬件触发源与 DMA 流是硬约束**:① ADC1 的外部触发清单**没有 TIM6/TIM7 TRGO**(F4 上它们只接 DAC)→ 同时驱动两片转换器必须用 **TIM3**(PSC 83 / ARR 999 → 1 kHz + TRGO_UPDATE);② ADC1 在 DMA2 **只挂 Stream0 或 Stream4** → 选 **Stream4** 才不挤走 SPI1/USART1 RX,且**换流会被重置为优先级 0**(违反"外设中断 ≥5")→ 改流后必须回 NVIC 核对 `Src/dma.c`;③ `DMAContinuousRequests` **必须 ENABLE**(关掉 = 一轮 9 通道只发一次请求,静默丢 8 路);④ DMA 目的缓冲必须在 SRAM(DMA2 不可达 CCM)→ `docs/硬件平台/ADC通道映射说明.md`
|
||||
- **STM32F4 ADC 上电必须自校准**:CubeMX **不生成**这一步,本仓裁剪版 HAL 没有 `HAL_ADCEx_Calibration_Start`、裁剪版 CMSIS 头没有 `ADC_CR2_CAL` → 按 RM0090 §13.3.6 手写 `adc1_calibrate()`(`Src/adc.c` 的 `USER CODE BEGIN 1` 区)+ `MX_ADC1_Init()` 末尾调用;缺它**不报任何错**,只是读数带失调
|
||||
- **接 ADC / RS-485 消费者前的强制门禁(E-59)**:先确认驱动契约已修(TPAFE E-44/E-45/E-58 ✅;RS485 E-36/E-37 ✅ 但 **E-50 单缓冲包撕裂、E-49 TX 超时拉 DE 未修 ❌** → `rs485_task` 仍被挡)+ 建任务前后各跑一次 `stack_report.py`
|
||||
- **接 ADC / RS-485 消费者前的强制门禁(RS-485 接线门禁)**:先确认驱动契约已修——TPAFE 三条 ✅(原子快照取数 / 帧序号取代就绪电平标志 / FRSTDATA 数据有效性校验);RS485 已修两条 ✅,但 **单缓冲包撕裂、TX 超时拉 DE 两条未修 ❌** → `rs485_task` 仍被挡。建任务前后各跑一次 `stack_report.py`
|
||||
|
||||
**存储与网络外设**
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
- **完整引脚表(含 SD2506 I2C1、SWD、调试口)→ `docs/产品需求/产品需求设计文档.md` §4.2**(权威,V0.1 口径;勿在本文重复抄表)
|
||||
- ⚠ **网名 `AINn` ≠ 板级 `ADCn` ≠ 转换器通道号**(17 路里 13 路置换)→ 取数**必须查** `docs/硬件平台/ADC通道映射说明.md`(TPAFE `buf[2]=IA`/`buf[5]=UN`/`buf[6]=UA`;网名 ADC17=PC4 用 `ADC_CHANNEL_14`,**不是** `ADC_CHANNEL_12`)
|
||||
- ⚠ **8 路电气量被分给两片转换器是 V0.1 硬件缺陷,不是有意分配**(跨转换器无共同采样触发 → 无同一时刻性、相位不确定)→ V0.1 上**禁止**跨转换器做相位/功率/功率因数/矢量运算;**V0.2 须把 8 路全接 TPAFE**(登记簿 N-18)
|
||||
- ⚠ **8 路电气量被分给两片转换器是 V0.1 硬件缺陷,不是有意分配**(跨转换器无共同采样触发 → 无同一时刻性、相位不确定)→ V0.1 上**禁止**跨转换器做相位/功率/功率因数/矢量运算;**V0.2 须把 8 路全接 TPAFE**(登记簿条目「双转换器无共同采样触发」)
|
||||
|
||||
## 模块约定
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
|
||||
## 文档与图形
|
||||
|
||||
- **登记簿类文档「完成即销项」**:`docs/任务规划/README.md` 这类待办登记簿,事项修复/关闭后**直接从表中删除该行**(不保留"已消化"行)——结论与证据应已沉淀进对应模块文档/问题记录。销项时必须:① 先核对目标文档确已含完整记录 ② 同步清理其他文档对该编号的引用 ③ 改完跑 `check_doc_links.py --all` + pre-commit。**新条目标识(用户裁定 2026-10-14)**:`#` 列填**语义短语**(如 `沉降窗 800ms 上限只留痕`),**不再新增字母+数字编号**——"编号不回收"只写规则却不留痕迹就不可执行(销项删行后仓库无痕迹,实测 `N-31` 于 2026-10-14 被复用撞号),语义标识自带唯一性;存量编号冻结沿用、**已随销项消失的号永久作废** → `docs/任务规划/README.md` §0
|
||||
- **登记簿类文档「完成即销项」**:`docs/任务规划/README.md` 这类待办登记簿,事项修复/关闭后**直接从表中删除该行**(不保留"已消化"行)——结论与证据应已沉淀进对应模块文档/问题记录。销项时必须:① 先核对目标文档确已含完整记录 ② 同步清理其他文档对该编号的引用 ③ 改完跑 `check_doc_links.py --all` + pre-commit。**新条目标识(用户裁定 2026-10-14)**:`#` 列填**语义短语**(如 `沉降窗 800ms 上限只留痕`),**不再新增字母+数字编号**——"编号不回收"只写规则却不留痕迹就不可执行(销项删行后仓库里毫无痕迹,2026-10-14 实测就发生过「销项号被重新取走」的撞号事故),语义标识自带唯一性;**存量编号(登记簿 N-x / S-x / T-x 与专项 A-x)已于 2026-10-26 全部转成语义短语**——旧号进 `scripts/id_aliases.json` 标 `retired-to-phrase`,不得再指;**已随销项消失的号永久作废**、不得复用 → `docs/任务规划/README.md` §0
|
||||
- 图形走 **`draw-diagram` skill**:精确布局图用 `tools/arch_diagram/gen_*.py` 生成器(产物放 `docs/images/`,脚本与 .svg 一并提交);拓扑图用 Mermaid;**勿手绘 ASCII 框图**。文件引用关系图走 `tools/refgraph`(`& 'tools\refgraph\gen_refgraph.bat'`)
|
||||
- **改名 / 挪目录一律走 `scripts/rename_ref.py`(先 dry-run,确认后 `--apply`)**:`git mv` + 自动改写全库引用(反引号引用 / Markdown 链接 / 图片 `../` 层级换算 / `#include` / `uvprojx` `<FilePath>` / `CMakeLists.txt`),落盘后自动跑 `check_doc_links --all` 复核——手工扫引用既慢又漏(一次改名历史上波及 10~30 文件)。引用章节用「`路径.md` §N.M」**紧邻写法**(`doc-links` 校验编号是否还在:紧邻/裸名/前缀/别名/自引用**全部拦提交**(钩子带 `--sections-strict`);每次跑打印「可判定比例」,`--section-todo` 出待改清单,昵称歧义就登记 `scripts/section_aliases.json` 或写清楚)→ `docs/文档规范.md` §3.5 / §3.6。**编号改名走 `scripts/rename_ids.py`**(映射表驱动、**显式 scope**、干跑默认、自动写 `scripts/id_aliases.json` 的 scoped 旧号→新号条目;`[示例]`/`[计划]` 行与变更记录表不改写;**绝不全局查找替换**——同一裸形字面量可能同时属多个族,全局替换等于造新撞号,实测 `S3` 三义)
|
||||
- 文档写作规范(编码/BOM/链接/表格/一致性钩子)→ `docs/文档规范.md`;全量索引与推荐阅读顺序 → `docs/README.md`
|
||||
@@ -174,7 +174,7 @@
|
||||
| 存储栈(FatFs / dhara / lftpd / 两条 FatFs 陷阱) | `docs/架构设计/STM32F4-Base存储架构说明.md` |
|
||||
| RAM / 栈预算 / 容量台账 | `docs/内存管理/RAM使用分析与优化方案.md`、`内存内容分配策略.md` |
|
||||
| **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(49 个钩子 → 26 篇索引 + 1 指针) |
|
||||
| 实体编号怎么写 / 前缀总表 / `id-check` 八项校验 | `docs/编号规范.md`(§4 总表、§8 门禁与实测基线) |
|
||||
| **要不要给某个东西发编号**(默认:不发,写语义短语)/ 允许用号的 8 个族 / `id-check` 八项校验 | `docs/编号规范.md`(§1 先问三句、§4.1 族总表〔穷举〕、§8 门禁与实测基线) |
|
||||
| 提交门禁通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式) | `docs/代码规范/pre-commit使用说明.md` §2 |
|
||||
| 代码规范 V1.0 / cppcheck 口径 | `docs/代码规范/嵌入式C语言代码规范_V1.0.md`、`cppcheck使用说明.md` |
|
||||
| 自动化测试 / 实机测试 / 板级套件 | `docs/测试说明/自动化测试流程.md`、`实机测试实施指南.md`、`board-test-suite` skill |
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(L5-2,码值域)→ `s
|
||||
## 3. 逐文件要点(只留"改代码时必须知道"的;机制与理由见设计文档)
|
||||
|
||||
> **改本目录前先记五条红线**:① 取数只经 `adc_input_raw()`,不写死下标;② **禁止跨转换器**做
|
||||
> 相位 / 功率 / 矢量运算(N-18);③ 帧值已是 L1.5 工程量,但 **未标定(`adc_cal_flags()` 无
|
||||
> 相位 / 功率 / 矢量运算(双转换器无共同采样触发);③ 帧值已是 L1.5 工程量,但 **未标定(`adc_cal_flags()` 无
|
||||
> VALID)时单位只是 ADC 引脚电压**,不是端子二次值——下游消费精度判据见「数据可信」§4.2 三态
|
||||
> 表;④ 工程量 → 帧的唯一折算点是 `adc_cal_apply()`,禁止旁路再乘系数;⑤ 本层不建任务、
|
||||
> 不等事件、不写日志(`adc_cal` 的 `[ERR]` 哨兵留痕是唯一例外,且每通道每开机一次),
|
||||
@@ -87,7 +87,7 @@ ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(L5-2,码值域)→ `s
|
||||
`freq_get() == 0 && freq_valid() == 1`。
|
||||
- 精度靠**自适应窗长**(窗长 = 泄漏纹波周期的整数倍);定长平均压不到 0.02 Hz,
|
||||
换窗长 = 换精度,二者不是独立旋钮。
|
||||
- 源切换即废相位基准重建(UA / IA 无共同相位基准,N-18)。
|
||||
- 源切换即废相位基准重建(UA / IA 无共同相位基准,即双转换器无共同采样触发)。
|
||||
- 判据 **J1~J6** 与实测口径 → `docs/算法/采集与算法层设计说明.md` §7.2(与 `test/host/freq/freq_test.c` 文件头同源)。
|
||||
|
||||
### 3.8 `adc_cal.c` / `.h` — L1.5 通道标定折算(「数据可信」卡 2 / 卡 4)
|
||||
@@ -129,7 +129,7 @@ ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(L5-2,码值域)→ `s
|
||||
### 4.1 挂载与模拟器边界
|
||||
- 九个 `.c` 已登记在 `MDK-ARM/STM32F407-Demo.uvprojx`;`../App/algo` 已在 `<IncludePath>` 内。
|
||||
- 本层**不进** `simulator/CMakeLists.txt`(UI 模拟器不跑采集链路):九个文件全部登记在
|
||||
`scripts/check_registration.py` 的 `SIM_EXEMPT`(附理由),`registration-check` 钩子反向校验(N-23)。
|
||||
`scripts/check_registration.py` 的 `SIM_EXEMPT`(附理由),`registration-check` 钩子反向校验(登记簿条目「rs485_task 白名单缺口」)。
|
||||
|
||||
### 4.2 主机侧单测(L0.5)——生产源文件以 `#include` 原样纳入
|
||||
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ static adc_cal_bcfg_t s_bcfg; /* 生效零点副本 */
|
||||
* (16 位 ±5 V 档),UB/UC/IB/IC/IN 走 STM32 ADC1;逐路对过
|
||||
* docs/硬件平台/ADC通道映射说明.md §3(IN=ADC11/STM32,勿再按分组顺序误写)。
|
||||
* ROM 表按**当前 V0.1 来源排布**显式书写(不查 desc 运行期派生),因为它是
|
||||
* 卡 1 定稿的存档事实;V0.2(N-18)改版时本表与 adc_input 映射表同步改,
|
||||
* 卡 1 定稿的存档事实;V0.2 改版(消除双转换器无共同采样触发)时本表与 adc_input 映射表同步改,
|
||||
* 上层语义与 API 不动。CLIPPED 位则**运行期按来源派生**(见 adc_cal_flags) */
|
||||
static const adc_cal_kcfg_t s_kcfg_rom = {
|
||||
/* idx0 UA / idx1 UB / idx2 UC / idx3 UN */
|
||||
@@ -272,7 +272,7 @@ uint32_t adc_cal_flags(adc_semantic_t sem) {
|
||||
f |= ADC_CAL_F_VALID;
|
||||
}
|
||||
/* CLIPPED 由**来源**派生(R5:STM32 侧单极性无偏置 → 负半周削顶)——
|
||||
* V0.2 改版(N-18,8 路全接 TPAFE)后本位自动恒 0,无需改码,
|
||||
* V0.2 改版(8 路全接 TPAFE,消除双转换器无共同采样触发)后本位自动恒 0,无需改码,
|
||||
* 对应「数据可信」§10「页面与 104 绑定零改动」的要求 */
|
||||
if (s_rom_k_stm32((unsigned)sem) != 0) {
|
||||
f |= ADC_CAL_F_CLIPPED;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 标定(卡 12)完成前不得把输出当二次值消费。TEMP 不参与线性标定
|
||||
* (决策 5),adc_cal_apply() 原样透传码值。
|
||||
* 本层**纯算法 + 纯数据**:不碰寄存器、不等事件;adc_cal_init() 内核前
|
||||
* 可调(注册进引擎必须先于 cfg_file_load,N-19 契约);apply/flags
|
||||
* 可调(注册进引擎必须先于 cfg_file_load,FTP 鉴权确立的契约);apply/flags
|
||||
* 任意上下文可读(cfg RAM 副本 4 字节对齐字读写在 Cortex-M4 上原子,
|
||||
* 与 defaultTask 的 load/poll 复位存在 µs 级混窗——设计接受,见 .c 头)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F);主机侧 L0.5 单测 test/host/adc_cal/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* ④ 错误计数增量:FRSTDATA 跳变 / HAL ADC·DMA 错误 / STM32 停摆计数。
|
||||
* ⚠ 本层**纯逻辑**:不碰寄存器、不取数、不打日志——输入是调用方
|
||||
* 采集的计数快照,输出是故障位;日志与自检上报由消费者
|
||||
* (`App/task/adc_task.c`)按位执行 → 主机侧可单测(L0.5,A-4)。
|
||||
* (`App/task/adc_task.c`)按位执行 → 主机侧可单测(L0.5)。
|
||||
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
* 权威口径:docs/硬件平台/ADC通道映射说明.md(2026-10-09 硬件确认的语义分配)。
|
||||
* index 含义:TPAFE 源 = 驱动 buf 下标 0..7;STM32 源 = ADC1 通道号 0..15。
|
||||
* ⚠ 消费侧一律经本表按语义取数,**不得写死下标**(V0.1 有 13/17 路被置换,
|
||||
* 网名编号与通道号不等价);V0.2 改版(N-18)只改本表内容。 */
|
||||
* 网名编号与通道号不等价);V0.2 改版(消除双转换器无共同采样触发)只改本表内容。 */
|
||||
static const adc_channel_desc_t k_adc_map[ADC_SEM_COUNT] = {
|
||||
[ADC_SEM_UA] = { ADC_SRC_TPAFE, 6U }, /* TPAFE buf[6](ch7) */
|
||||
[ADC_SEM_UB] = { ADC_SRC_STM32, (uint8_t)ADC_CHANNEL_14 }, /* PC4 */
|
||||
@@ -240,7 +240,7 @@ int adc_input_init(void) {
|
||||
* STM32 侧无新帧不影响 TPAFE 快照与返回值语义(其状态另由
|
||||
* adc_input_stm32_rc() / adc_input_stm32_seq() 暴露)。
|
||||
* ⚠ STM32 侧数据比 TPAFE 侧**滞后 1 拍**(乒乓双缓冲的固有延迟,
|
||||
* 9 路一致);按 N-18,V0.1 本就禁止跨转换器做矢量运算。
|
||||
* 9 路一致);因双转换器无共同采样触发,V0.1 本就禁止跨转换器做矢量运算。
|
||||
*/
|
||||
int adc_input_poll(void) {
|
||||
int r = tpafe5160_snapshot(s_tpafe_raw, (uint8_t)ADC_INPUT_TPAFE_CH_NUM);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* ① 语义通道映射表(L1-4)——权威口径 docs/硬件平台/ADC通道映射说明.md;
|
||||
* ② STM32 ADC1 9 通道采样(L1-3)——由 **TIM3 TRGO 硬件触发 + DMA 双缓冲
|
||||
* 搬运**,本层只消费、不触发、不等待。
|
||||
* V0.1 上 8 路电气量分属两片转换器(硬件缺陷 N-18):UA/UN/IA 走
|
||||
* V0.1 上 8 路电气量分属两片转换器(硬件缺陷:双转换器无共同采样触发):UA/UN/IA 走
|
||||
* TPAFE5160(16 位 / ±5 V 双极性),UB/UC/IB/IC/IN 走 STM32 ADC1
|
||||
* (12 位 / 0~3.3 V 单极性)。V0.2 改版(8 路电气量全部接 TPAFE)
|
||||
* 只改映射表内容,本层 API 与上层语义不动。
|
||||
@@ -47,7 +47,7 @@ typedef enum {
|
||||
ADC_SEM_COUNT /* 语义通道数(哨兵) */
|
||||
} adc_semantic_t;
|
||||
|
||||
/* 数据来源:V0.1 的 8 路电气量分属两片转换器(N-18) */
|
||||
/* 数据来源:V0.1 的 8 路电气量分属两片转换器(双转换器无共同采样触发) */
|
||||
typedef enum {
|
||||
ADC_SRC_TPAFE = 0, /* TPAFE5160:16 位、±5 V 双极性、8 通道同步 */
|
||||
ADC_SRC_STM32 /* STM32 ADC1:12 位、0~3.3 V 单极性、9 通道顺序扫描 */
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
* - 信号源优先级:UA 基波 → IA 基波(UA 无幅值时用 IA,台架只注电流亦可测频)。
|
||||
*
|
||||
* ⚠ 本层**纯算法**:不碰寄存器、不碰 RTOS、不自己取数(输入 = phasor 使用区)
|
||||
* → 主机侧可免桩单测(L0.5,落实登记簿 A-4)。
|
||||
* → 主机侧可免桩单测(L0.5)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-12(B8)
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
* 覆盖规划 §3.2:L2-1 系数表 / L2-2 实例 / L2-3 有效值输出 /
|
||||
* L2-6 每 1 ms 滑动窗 / L2-7 双区指针原子交换。
|
||||
* ⚠ 本层**纯算法**:不碰寄存器、不碰任务调度、不自己取数(输入由
|
||||
* `sample_ring` 提供)→ 主机侧可单测(L0.5,落实登记簿 A-4)。
|
||||
* `sample_ring` 提供)→ 主机侧可单测(L0.5)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-10
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* 同 phasor 实例表模式)。判据为**纯逻辑**(吃测量值 + 定值),
|
||||
* 主机侧可离线单测;出口/SOE(L4)与任务编排为后续。
|
||||
*
|
||||
* V0.1 有效实例(N-18:仅 TPAFE 三路):
|
||||
* V0.1 有效实例(双转换器无共同采样触发 → 仅 TPAFE 三路):
|
||||
* OC1/OC2/OC3 三段式相间过流(A 相 IA,L3-1)
|
||||
* OL 过负荷(A 相 IA,L3-3)
|
||||
* OV/UV 过压/欠压(UA,L3-4)
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* 入口参数:无
|
||||
* 返 回 值:无
|
||||
* 限定条件:**内核启动前**调用(app_main_init,adc_cal_init 之后);注册必须先
|
||||
* 于 defaultTask 的 cfg_file_load(N-19 契约,同 adc_cal 先例);幂等
|
||||
* 于 defaultTask 的 cfg_file_load(FTP 鉴权确立的契约,同 adc_cal 先例);幂等
|
||||
* 函数说明:注册失败(段名重复/段数超限)打 [ERR] 后继续——工厂默认照常生效,
|
||||
* 仅文件整定失效(adc_cal_init 同款降级)。
|
||||
*/
|
||||
@@ -118,7 +118,7 @@ uint8_t run_par_dirty_peek(void);
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 成功(或无脏可存);-1 写盘失败(脏保持,下轮重试) int
|
||||
* 限定条件:**defaultTask(配置引擎属主)上下文**,由 SYS_EVT_RUN_PAR_SAVE 驱动
|
||||
* 函数说明:save 走引擎唯一写闸(超长拒写/原子改名,E-01 契约);本函数不清
|
||||
* 函数说明:save 走引擎唯一写闸(超长拒写/原子改名);本函数不清
|
||||
* 引擎以外模块的脏语义([adc_cal] 副本值已由写路径就位)。
|
||||
*/
|
||||
int run_par_store_save(void);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 模块名称:采样环缓冲(L2 相量计算的输入)
|
||||
* 模块功能:按**语义通道**保存最近若干周波的采样值,供 L2 相量计算按窗取数。
|
||||
* V0.1 上 8 路电气量分属两片转换器(硬件缺陷 N-18),本层只做"按时序
|
||||
* V0.1 上 8 路电气量分属两片转换器(双转换器无共同采样触发),本层只做"按时序
|
||||
* 存/取",**不做任何跨通道运算**——同刻性由上游(L1)保证,本层不假设。
|
||||
* 容量取 **2 个周波(40 点)**,与参考系统 `CN_BUFSAM_CYCLE = 2`
|
||||
* (规划 §10.1)一致,为后续加长数据窗(1.5~2 周波,抗衰减直流,
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-10-04 王建锋 P2-12:如实标注三个 APP_*_ENABLE 为"未接线占位"(全库零消费点)
|
||||
* v1.2 2026-10-22 王建锋 S-4 收口(报告 S-4 / 登记簿 C5):删除 4 个零消费点的宏
|
||||
* v1.1 2026-10-04 王建锋 如实标注三个 APP_*_ENABLE 为"未接线占位"(全库零消费点)
|
||||
* v1.2 2026-10-22 王建锋 收口(登记簿 C5):删除 4 个零消费点的宏
|
||||
* (APP_NET_ENABLE / APP_ADC_ENABLE / APP_RS485_ENABLE /
|
||||
* APP_SYS_TIME_SRC)。留"看起来能裁剪、其实不生效"的宏比不留更糟:
|
||||
* 评审者会误信模块可裁剪,而关掉它们不会产生任何编译期效果。
|
||||
|
||||
+6
-6
@@ -30,7 +30,7 @@
|
||||
#include "prot_set.h" /* L3 定值模型:prot_set_init()(B5,内核前封存) */
|
||||
#include "prot_elm.h" /* L3 元件框架:prot_elm_init()(B5,解析相量索引) */
|
||||
#include "sys_log.h"
|
||||
#include "task_watchdog.h" /* task_wd_seg_report():round 8 复位不丢"进段打点"上报 */
|
||||
#include "task_watchdog.h" /* task_wd_seg_report():第 8 轮复位不丢"进段打点"上报 */
|
||||
#include "test_config.h" /* 套件开关:身份戳的构建变体串(FW_BUILD_VARIANT)依赖本头,
|
||||
* 改 TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS 必重编本 TU →
|
||||
* __DATE__/__TIME__ 随之刷新,见 app_main_init() 的戳注释 */
|
||||
@@ -131,7 +131,7 @@ int app_main_init(void) {
|
||||
|
||||
/* 日志引擎初始化(创建待发队列;须先于任何 syslog_post) */
|
||||
(void)syslog_mcu_init();
|
||||
/* 启动锚点(2026-10-02 S10):本函数在内核启动前执行、不创建任何 RTOS 对象,
|
||||
/* 启动锚点(2026-10-02 加):本函数在内核启动前执行、不创建任何 RTOS 对象,
|
||||
* 恒成功且无副作用;本行是"启动进度轨迹"的起点——之后的每一步都有结果日志,
|
||||
* 故某次启动卡死时,最后一行日志即可指向卡住的步骤。 */
|
||||
DBG_INFO("boot: syslog init OK");
|
||||
@@ -166,10 +166,10 @@ int app_main_init(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 0b) round 8 取证:上报上一轮 defaultTask 主循环的**最后进段**。
|
||||
/* 0b) 第 8 轮取证:上报上一轮 defaultTask 主循环的**最后进段**。
|
||||
* 轨迹槽在 .noinit(scatter UNINIT_CCM),IWDG/引脚复位不清 SRAM →
|
||||
* 误复位后开机第一时间可见 `PREVRUN seg=N`,直接指向主循环阻塞点
|
||||
* (round 7 观察"判定分支未执行"经 round 8b 更正:真因是 IWDG 超时被静默
|
||||
* (第 7 轮观察"判定分支未执行"经第 8b 轮更正:真因是 IWDG 超时被静默
|
||||
* 配成复位默认值,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。
|
||||
* 裸 UART 直发,且一次复位只报一次(内部清 magic)。 */
|
||||
task_wd_seg_report();
|
||||
@@ -178,7 +178,7 @@ int app_main_init(void) {
|
||||
* 1) 时间子系统初始化(尽早完成,后续所有日志带时间戳)
|
||||
* sd2506_init() 从 RTC 读取初始时间
|
||||
* sys_clock_init() 将 RTC 时间转为 Unix epoch,建立墙钟基准
|
||||
* S10(2026-10-02):sd2506_init 原先所有失败路径静默返回,是启动段
|
||||
* 启动段可观测性(2026-10-02 加):sd2506_init 原先所有失败路径静默返回,是启动段
|
||||
* 唯一无日志的初始化;现内部已逐路径打 DBG_ERROR,此处再补一行
|
||||
* 带错误码的结果回执,使 I2C 不通时能看到"在哪一步、什么错"。
|
||||
*/
|
||||
@@ -308,7 +308,7 @@ int app_main_init(void) {
|
||||
/* 6) 启动 1 kHz 采样节拍:TIM3 更新事件 → ① ISR 内脉冲 CONVST(Src/main.c)
|
||||
* ② 同一事件经 TRGO 硬件触发 STM32 ADC1 一轮 9 通道转换(DMA 自动搬运)。
|
||||
* ⚠ 必须在 tpafe5160_init() 成功之后启动,否则 ISR 会在驱动首帧自检
|
||||
* (E-44 有效性探针)期间乱发 CONVST。消费端 adcTask 退化为纯消费者。
|
||||
* (有效性探针)期间乱发 CONVST。消费端 adcTask 退化为纯消费者。
|
||||
* ⚠ 节拍源必须能触发 ADC1,故用 **TIM3**:F4 上 ADC1 的外部触发只认
|
||||
* TIM1/TIM2/TIM3/TIM4/TIM5/TIM8 的 CC 或 TRGO,**TIM6/TIM7 的 TRGO 只接
|
||||
* DAC**(查 CubeMX DB 与本仓库 stm32f4xx_hal_adc.h 的 EXTSEL 表证实)
|
||||
|
||||
@@ -149,7 +149,7 @@ static void feed_copy_str(char *p_dst, size_t dst_len, const char *a_src) {
|
||||
* 限定条件:uiTask 上下文,ui_data_feed_tick 首帧前调用
|
||||
* 函数说明:只认 harm == 1 的实例;一个语义通道有多个基波实例时取**首个**
|
||||
* (现状 k_inst[] 每通道最多一个基波,取首个即唯一解)。
|
||||
* UB/UC/IB/IC/IN 无实例(V0.1 硬件削顶,N-18)→ 留 -1,该路发 IV
|
||||
* UB/UC/IB/IC/IN 无实例(V0.1 硬件削顶:双转换器无共同采样触发)→ 留 -1,该路发 IV
|
||||
*/
|
||||
static void feed_map_phasor(void) {
|
||||
uint8_t n;
|
||||
|
||||
+15
-15
@@ -8,20 +8,20 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(P3:段键表引擎 + net_cfg 集成)
|
||||
* v1.1 2026-09-18 王建锋 P0-1:临时缓冲改经 cfg_store_alloc(MCU = FreeRTOS 堆),
|
||||
* v1.1 2026-09-18 王建锋 临时缓冲改经 cfg_store_alloc(MCU = FreeRTOS 堆),
|
||||
* 移除对 C 库 malloc 的全部依赖(MicroLIB C 堆仅 512B,
|
||||
* 原 4 处 KB 级 malloc 真机必然失败);增编译期堆峰值预算
|
||||
* 校验 + 失败上报自检记录
|
||||
* v1.2 2026-09-21 王建锋 E-01(历史审计 P0,属主文档已删除):超长文件守卫根治——poll 判定
|
||||
* v1.2 2026-09-21 王建锋 超长文件守卫根治——poll 判定
|
||||
* 前置到 cfg_store_stat 真实 st.size(旧检查位于截断后的
|
||||
* len 上恒假=死码,超长文件被截断当有效解析 → 界外静默
|
||||
* 回工厂默认 → 基线粘住 → 用户配置销毁链);save 输出上限
|
||||
* 由 CFG_OUT_MAX 收紧至 CFG_FILE_MAX_LEN;s_write_doc 写盘
|
||||
* 唯一闸口拒写超长文档;load/poll 超长处置补自检留痕
|
||||
* v1.3 2026-10-04 王建锋 P2-1:受管键行超 CFG_LINE_TEST_MAX 时拒绝保存——
|
||||
* v1.3 2026-10-04 王建锋 受管键行超 CFG_LINE_TEST_MAX 时拒绝保存——
|
||||
* 原实现 Pass1 静默跳过超长行,Pass2 遂在段头补写新 KV 且
|
||||
* 原文超长行原样保留 → 同一键出现两行(旧值残留、静默截断)
|
||||
* v1.4 2026-10-15 王建锋 E-16(历史审计 P2,属主文档已删除):生成文档边界余量收口——
|
||||
* v1.4 2026-10-15 王建锋 生成文档边界余量收口——
|
||||
* s_gen_document 两处 `off += snprintf(...)` 改检查式(截断即
|
||||
* 返回 -1,不再跳过缓冲末尾);注册拒超长键名(≤ 15 字符),
|
||||
* 使每行 112 字节余量成为可编译期验证的前提
|
||||
@@ -29,7 +29,7 @@
|
||||
* len 1/2/4 小端补码)——s_decode_int() 用 strtol + ERANGE +
|
||||
* 宽度上下界校验,s_encode() 带负号输出;负零点偏移从此可
|
||||
* 干净落盘(动因见 docs/任务规划/数据可信.md §4.4 与 §7 决策 3)
|
||||
* 函数说明:引擎状态非重入——调用方须收敛同一任务(defaultTask:poll/save,见 P1-3;
|
||||
* 函数说明:引擎状态非重入——调用方须收敛同一任务(defaultTask:poll/save;
|
||||
* 启动期 load 先于任务竞争);FS 卷并发由 FatFS REENTRANT 卷锁保证
|
||||
* (见《配置文件设计说明》D15 A+ 方案)
|
||||
*/
|
||||
@@ -304,7 +304,7 @@ static int s_encode(const cfg_key_def_t *p_key, const uint8_t *p_src, char *p_ds
|
||||
case CFG_VAL_STR: {
|
||||
size_t slen;
|
||||
|
||||
/* P3-1:len==0 时 `p_key->len - 1u` 回绕为 SIZE_MAX → memcpy 巨长越界。
|
||||
/* len==0 时 `p_key->len - 1u` 回绕为 SIZE_MAX → memcpy 巨长越界。
|
||||
* STR 字段至少 1 字节(含结尾 NUL),len==0 非法,直接拒绝 */
|
||||
if (p_key->len == 0u) {
|
||||
return -1;
|
||||
@@ -619,7 +619,7 @@ static int s_load_file(uint8_t **p_pbuf, size_t *p_len) {
|
||||
* len - 数据长度 size_t
|
||||
* 返回值:0 - 成功;-1 - 写失败或超长拒写 int
|
||||
* 限定条件:p_buf 非空;len ≤ CFG_FILE_MAX_LEN
|
||||
* 函数说明:E-01 纵深防御——超长文档一旦落盘,下次开机 load 守卫即判损坏、
|
||||
* 函数说明:纵深防御——超长文档一旦落盘,下次开机 load 守卫即判损坏、
|
||||
* 整份用户配置连同注释/未知段销毁,故在唯一出口硬性拦截,使
|
||||
* "引擎永不产出超长 config.ini"成为结构性质(调用者新增路径免疫)
|
||||
*/
|
||||
@@ -923,7 +923,7 @@ static int s_surgical(char *p_in, size_t in_len, char *p_out, size_t out_cap, si
|
||||
* i_sec - 段索引 int
|
||||
* 返回值:1 - 是受管键;0 - 不是(无 '=' 或键名不匹配) int
|
||||
* 限定条件:仅比较 '=' 前的键名(大小写无关、剥尾部空白),不解析值
|
||||
* 函数说明:P2-1 用于"超长行是否落在受管键上"的判定(值可能很长,无法拷入定长缓冲);
|
||||
* 函数说明:用于"超长行是否落在受管键上"的判定(值可能很长,无法拷入定长缓冲);
|
||||
* 非修改式,故可安全用于仍供 Pass2 原样保留的行
|
||||
*/
|
||||
static int s_line_key_managed(const char *p_line, int i_sec) {
|
||||
@@ -969,9 +969,9 @@ static int s_line_key_managed(const char *p_line, int i_sec) {
|
||||
/*
|
||||
* 函数功能:保存 Pass 1——定位段头行号与各受管键行号
|
||||
* 入口参数:p_in - 原文本(可修改) char *
|
||||
* 返回值:0 - 成功;-1 - 受管键行超 CFG_LINE_TEST_MAX(拒绝保存,见 P2-1) int
|
||||
* 返回值:0 - 成功;-1 - 受管键行超 CFG_LINE_TEST_MAX(拒绝保存) int
|
||||
* 限定条件:行末被 NUL 化且不复原——调用方须传一次性副本
|
||||
* 函数说明:P2-1(2026-10-04):受管键行超过 CFG_LINE_TEST_MAX 时**不能**静默跳过
|
||||
* 函数说明:受管键行超过 CFG_LINE_TEST_MAX 时**不能**静默跳过
|
||||
* ——Pass2 会因该行未登记而在段头补写新 KV,同时原文超长行原样保留,导致
|
||||
* 同一键出现两行(旧值残留 + 新值,解析取谁取决于行序)。故此处显式拒绝:
|
||||
* 返回 -1,调用方中止保存、原文件不动、自检留痕。非受管键的超长行照旧跳过
|
||||
@@ -1033,7 +1033,7 @@ static int s_surgical_pass1(char *p_in) {
|
||||
}
|
||||
}
|
||||
else if (i_cur >= 0) {
|
||||
/* 超长行(> CFG_LINE_TEST_MAX):若其键名为本段受管键 → 拒绝保存(P2-1);
|
||||
/* 超长行(> CFG_LINE_TEST_MAX):若其键名为本段受管键 → 拒绝保存;
|
||||
* 否则按未知内容放过(Pass2 原样保留) */
|
||||
if (s_line_key_managed(p_line, i_cur) != 0) {
|
||||
return -1;
|
||||
@@ -1165,7 +1165,7 @@ int cfg_file_load(void) {
|
||||
}
|
||||
|
||||
if (st.size > CFG_FILE_MAX_LEN) {
|
||||
/* 文件超长 = 损坏:留工厂默认 + 重建模板(E-01:补自检留痕——销毁
|
||||
/* 文件超长 = 损坏:留工厂默认 + 重建模板(补自检留痕——销毁
|
||||
* 用户文件属重大事件,仅串口 DBG_ERROR 在现场无调试器时不可追溯) */
|
||||
if (s_write_template() != 0) {
|
||||
DBG_ERROR("load: corrupt file, template write FAIL");
|
||||
@@ -1254,7 +1254,7 @@ int cfg_file_save(void) {
|
||||
(void)memcpy(p_in1, p_in, in_len + 1u);
|
||||
|
||||
if (s_surgical_pass1((char *)p_in1) != 0) {
|
||||
/* P2-1:受管键行超 CFG_LINE_TEST_MAX → 中止保存(原文件不动)。此类超长行
|
||||
/* 受管键行超 CFG_LINE_TEST_MAX → 中止保存(原文件不动)。此类超长行
|
||||
* 只可能来自手工编辑(代码生成的受管 KV 行恒 <127B);若强行外科保存会
|
||||
* 产生重复键行,故宁可拒存并留痕,也不静默写坏。 */
|
||||
cfg_store_free(p_in1);
|
||||
@@ -1276,7 +1276,7 @@ int cfg_file_save(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* E-01:输出上限由 CFG_OUT_MAX(5120) 收紧至 CFG_FILE_MAX_LEN(4096)——
|
||||
/* 输出上限由 CFG_OUT_MAX(5120) 收紧至 CFG_FILE_MAX_LEN(4096)——
|
||||
* 旧上限允许把 >4096B 文档合法写盘(= 下一颗雷:下次开机 load 守卫
|
||||
* 判损坏、整份用户文件连同注释/未知段一起销毁)。超限走下方既有
|
||||
* 溢出中止路径:原文件不动 + 自检上报,RAM 副本保留(可修复后重试) */
|
||||
@@ -1342,7 +1342,7 @@ void cfg_file_poll(void) {
|
||||
{
|
||||
int i;
|
||||
|
||||
/* E-01(2026-09-21 审计 P0):超长守卫必须基于 cfg_store_stat 的真实
|
||||
/* 2026-09-21 审计 P0:超长守卫必须基于 cfg_store_stat 的真实
|
||||
* st.size 且先于 s_load_file——s_load_file 读长钉死 CFG_FILE_MAX_LEN
|
||||
* (nread ≤ 4096 恒成立),旧检查写在读后的 len 上恒假(死码):
|
||||
* 超长文件被截断当有效解析 → 界外段/键静默回工厂默认 → 基线粘住 →
|
||||
|
||||
+7
-7
@@ -8,12 +8,12 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(P3:段键表引擎 + net_cfg 集成)
|
||||
* v1.1 2026-09-15 王建锋 属主任务由 netTask 改为 defaultTask(配置引擎单一属主,P1-3)
|
||||
* v1.2 2026-09-21 王建锋 E-01(历史审计 P0,属主文档已删除):超长守卫契约同步——poll 沉降
|
||||
* v1.1 2026-09-15 王建锋 属主任务由 netTask 改为 defaultTask(配置引擎单一属主)
|
||||
* v1.2 2026-09-21 王建锋 超长守卫契约同步——poll 沉降
|
||||
* 后先按真实 st.size 判超长(旧实现查截断后读长恒假)、
|
||||
* save 输出上限收紧至 CFG_FILE_MAX_LEN、写盘唯一闸口拒写
|
||||
* 超长;超长处置补自检留痕
|
||||
* v1.3 2026-10-15 王建锋 E-16(历史审计 P2,属主文档已删除):新增公开常量
|
||||
* v1.3 2026-10-15 王建锋 新增公开常量
|
||||
* CFG_FILE_MAX_NAME_LEN(段名/键名上限 15 字符)
|
||||
* v1.4 2026-10-25 DTU 项目组 数据可信卡 3:新增 CFG_VAL_INT 有符号十进制值类型
|
||||
* (len = 1/2/4,小端补码)——标定零点偏移等负值需有符号
|
||||
@@ -32,7 +32,7 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
|
||||
/* 配置文件长度上限(字节);超过 = 文件损坏,按出厂模板重建
|
||||
* (load 与 poll 同判,E-01:poll 在读取前基于真实 st.size 判定) */
|
||||
* (load 与 poll 同判:poll 在读取前基于真实 st.size 判定) */
|
||||
#define CFG_FILE_MAX_LEN 4096u
|
||||
|
||||
/* 引擎可注册段数上限 */
|
||||
@@ -103,7 +103,7 @@ int cfg_file_register(const cfg_section_reg_t *p_reg);
|
||||
* 2. 文件缺失 → 按当前 RAM 副本生成出厂模板写盘(首启建文件)
|
||||
* 3. 文件存在 → 解析:有效键写入 RAM 副本;无效键留工厂默认并留痕
|
||||
* 4. 文件超长(> CFG_FILE_MAX_LEN)= 损坏 → 工厂默认 + 重建模板
|
||||
* + 自检留痕(poll 检出运行期超长文件执行同一契约,E-01)
|
||||
* + 自检留痕(poll 检出运行期超长文件执行同一契约)
|
||||
*/
|
||||
int cfg_file_load(void);
|
||||
|
||||
@@ -116,7 +116,7 @@ int cfg_file_load(void);
|
||||
* 值原位改写,缺失段/键补写;文件缺失/损坏 → 按 RAM 副本重建
|
||||
* 完整文档(含文件头注释);写盘 = 临时文件 + 原子改名;输出超
|
||||
* CFG_FILE_MAX_LEN → 拒绝保存返回 -1(原文件不动 + 自检上报,
|
||||
* 引擎写盘闸口同拒,E-01)
|
||||
* 引擎写盘闸口同拒)
|
||||
*/
|
||||
int cfg_file_save(void);
|
||||
|
||||
@@ -129,7 +129,7 @@ int cfg_file_save(void);
|
||||
* 函数说明:(size, mtime 变更令牌) 相对基线变化且连续 CFG_FILE_SETTLE_CNT
|
||||
* 次轮询稳定 → 先查 st.size 超 CFG_FILE_MAX_LEN = 损坏 → 工厂默认
|
||||
* + 重建模板 + 自检留痕(不触发段回调:保留在线修复通道,重启
|
||||
* 收敛,E-01);否则重载全部段(各段复位工厂默认后解析);挂回调
|
||||
* 收敛);否则重载全部段(各段复位工厂默认后解析);挂回调
|
||||
* 段有差异 → 调用其回调(如触发网络重建);重载后刷新基线
|
||||
* (回调触发的写盘不引发二次重载)
|
||||
*/
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本
|
||||
* v1.1 2026-09-18 王建锋 增 cfg_store_alloc / cfg_store_free / cfg_store_heap_free:
|
||||
* 共享核心临时缓冲改走 FreeRTOS 堆(P0-1,MicroLIB C 堆仅 512B)
|
||||
* 共享核心临时缓冲改走 FreeRTOS 堆(MicroLIB C 堆仅 512B)
|
||||
* v1.2 2026-09-22 王建锋 原子写修复:rename 前先 unlink 已存在目标——本 FatFs
|
||||
* 移植 f_rename 不覆盖既有目标(FR_EXIST),旧实现一切
|
||||
* "目标存在的覆盖写"必然失败(E-01 台架验证暴露)
|
||||
* "目标存在的覆盖写"必然失败(台架验证暴露)
|
||||
* v1.3 2026-10-02 王建锋 四个后端函数纳入卷锁 try 语义(vol_lock_wait(0)):
|
||||
* 与 ftpTask 的 lftpd 文件操作互斥(并发 FTL 写 →
|
||||
* dhara journal 竞争实证);busy 返回 -1 由调用方
|
||||
@@ -22,7 +22,7 @@
|
||||
* FRESULT 里不存在的 FR_NO_SYSTEM,改成实际会返回的
|
||||
* FR_NOT_READY / FR_NO_FILESYSTEM 并写明"非 FR_OK 一律
|
||||
* 转 -1,不要枚举错误码集合"(无行为改动)
|
||||
* v1.4 2026-10-05 王建锋 原子写临时文件由 p_path 推导(P3-2):与目标同目录的
|
||||
* v1.4 2026-10-05 王建锋 原子写临时文件由 p_path 推导:与目标同目录的
|
||||
* 固定 8.3 名 config.TMP,不再硬编码卷根;目标恒为
|
||||
* "/config.ini" 时推导结果与旧值逐字节一致
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nre
|
||||
#define CFG_STORE_TMP_PATH_MAX 24u
|
||||
|
||||
/*
|
||||
* 函数功能:由目标路径推导同目录的临时文件路径(P3-2)
|
||||
* 函数功能:由目标路径推导同目录的临时文件路径
|
||||
* 入口参数:p_path - 目标文件路径 const char *
|
||||
* p_out - 输出缓冲 char *
|
||||
* cap - 缓冲容量 size_t
|
||||
@@ -144,7 +144,7 @@ int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len)
|
||||
if (p_buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
/* P3-2:临时文件由 p_path 推导(同目录 + 固定 8.3 名),不再硬编码卷根 */
|
||||
/* 临时文件由 p_path 推导(同目录 + 固定 8.3 名),不再硬编码卷根 */
|
||||
if (s_build_tmp_path(p_path, tmp_path, sizeof(tmp_path)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len)
|
||||
}
|
||||
/* 本 FatFs 移植的 f_rename 对已存在目标返回 FR_EXIST、不覆盖(ff.c
|
||||
* "new name already in use" 检查,上游同语义)——"目标存在的覆盖写"必须先
|
||||
* unlink 目标再 rename。2026-09-22 台架实证(E-01 验证暴露):缺本步时
|
||||
* unlink 目标再 rename。2026-09-22 台架实证:缺本步时
|
||||
* 一切非首启写(UI 保存/模板重建)静默失败,模拟器后端(POSIX rename
|
||||
* 覆盖语义)照不出。unlink→rename 非原子窗口掉电 = 配置文件暂失,
|
||||
* 开机 load 按"首启"重建工厂模板——与函数失败语义一致,丢失边界 =
|
||||
@@ -262,7 +262,7 @@ const char *cfg_store_tmp_path(void) {
|
||||
* 返回值:缓冲指针(内容未清零);失败返回 NULL void *
|
||||
* 限定条件:任务上下文调用(heap_4 内部临界区自护);释放必须用 cfg_store_free
|
||||
* 函数说明:MCU 端 MicroLIB C 堆仅 512B(startup Heap_Size=0x200),共享核心的 KB 级
|
||||
* 缓冲若走 C 库 malloc 必然返回 NULL,故统一由本接口取 FreeRTOS 堆(P0-1)
|
||||
* 缓冲若走 C 库 malloc 必然返回 NULL,故统一由本接口取 FreeRTOS 堆
|
||||
*/
|
||||
void *cfg_store_alloc(size_t size) {
|
||||
return pvPortMalloc(size);
|
||||
|
||||
+10
-10
@@ -8,17 +8,17 @@
|
||||
* 修改记录:
|
||||
* 2026-09-24 王建锋 创建(自 Src/usart.c USER CODE 区迁出,实现逻辑不变,
|
||||
* 仅变更文件位置:HAL 库生成文件不得包含应用代码)
|
||||
* 2026-10-02 王建锋 僵尸通道喂狗节奏保护(台架 gen-288 实证):round-3 的
|
||||
* 2026-10-02 王建锋 僵尸通道喂狗节奏保护(台架日志代次 288 实证):第 3 轮的
|
||||
* "单次 drain 预算 250ms" 只限单次调用——ORE 锁存在外设软复位后
|
||||
* 毫秒级重锁(PA10 悬空 + RX 使能),背靠背 drain 每轮再耗 100ms
|
||||
* 块超时 → 主循环周期拉到 450~500ms → 100ms 节流的喂狗抽样间隙
|
||||
* 超过 IWDG 最坏容限 1.7s → 停喂 → 误复位(且心跳位全员健康、
|
||||
* 无 stall 日志,纯节奏性饥饿)。修复:发送故障后立即结束本轮
|
||||
* drain 并进入 300ms 发送冷却(冷却期内 drain 直接让位主循环),
|
||||
* 保证喂狗节奏 ≤ ~300ms。根治(USART1 TX-only 灭 ORE 机制)
|
||||
* 另行推进(.ioc 结构性修改 + CubeMX 重生成,见 docs/任务规划/登记簿.md S-3)。
|
||||
* 2026-10-02 王建锋 round 8b 加固:发送超时判据改用 DWT CYCCNT(时间源无关)。
|
||||
* 台架 round 8a 取证(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)定位到:defaultTask
|
||||
* 保证喂狗节奏 ≤ ~300ms。根治(把 USART1 改成只发不收)已于 2026-10-26
|
||||
* 由用户裁定不做,本缓解即长期方案;机制详述 docs/子系统设计/控制台单写者日志设计说明.md §12。
|
||||
* 2026-10-02 王建锋 第 8b 轮加固:发送超时判据改用 DWT CYCCNT(时间源无关)。
|
||||
* 台架第 8a 轮取证(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)定位到:defaultTask
|
||||
* 冻结在 console 突发行内、无 STALL、复位为 IWDGRSTF。唯一自洽机理 =
|
||||
* HAL_UART_Transmit 的超时判据 `HAL_GetTick() - Tickstart` 依赖
|
||||
* TIM7 的 uwTick —— TIM7 一旦停摆,超时永不成立 → 写者死等 TXE →
|
||||
@@ -75,13 +75,13 @@
|
||||
#define DBG_CON_TX_TIMEOUT_MS 100U /* 单次发送超时:256B 块 @115200 标称 22ms;僵尸通道限时 + 自愈 */
|
||||
#define DBG_CON_DRAIN_BUDGET_MS 250U /* 单次 drain 连续发送上限:让位喂狗轮询(缺失容忍 800ms) */
|
||||
#define DBG_CON_FAULT_COOL_MS 300U /* 故障后发送冷却:ORE 锁存毫秒级重锁,背靠连发无益
|
||||
且拖长主循环周期 → 喂狗节奏饥饿(gen-288 实证);
|
||||
且拖长主循环周期 → 喂狗节奏饥饿(台架日志代次 288 实证);
|
||||
冷却期内 drain 只让位、不发送,下轮重试 */
|
||||
|
||||
/* ==== round 8b:与时间源无关的硬上界(DWT CYCCNT)====
|
||||
/* ==== 第 8b 轮:与时间源无关的硬上界(DWT CYCCNT)====
|
||||
* HAL_UART_Transmit 的超时判据是 `HAL_GetTick() - Tickstart > Timeout`,而 HAL 时基
|
||||
* = TIM7 的 uwTick。TIM7 停摆 → 超时永不成立 → 写者死等 TXE → 唯一喂狗者停摆 →
|
||||
* IWDG 误复位(round 8a 台架现场)。改用 DWT CYCCNT(内核硬件周期计数,不依赖任何
|
||||
* IWDG 误复位(第 8a 轮台架现场)。改用 DWT CYCCNT(内核硬件周期计数,不依赖任何
|
||||
* 中断/uwTick)做判据;DWT 使能失败时退回 HAL_UART_Transmit(旧行为,功能不退化)。 */
|
||||
#define CON_TX_TIMEOUT_CYC (SystemCoreClock / 10U) /* 发送硬上界 = 100ms 对应周期数 */
|
||||
/*
|
||||
@@ -293,7 +293,7 @@ void dbg_console_drain(void) {
|
||||
t0 = (uint32_t)HAL_GetTick();
|
||||
|
||||
/* 故障冷却:僵尸通道(ORE 重锁)秒内不自愈,冷却期内不发送——
|
||||
* 本轮 drain 直接让位主循环,保住喂狗节奏(gen-288 停喂误复位的
|
||||
* 本轮 drain 直接让位主循环,保住喂狗节奏(日志代次 288 停喂误复位的
|
||||
* 直接诱因 = 背靠连发的块超时链)。是否在冷却看 s_con_cooling 独立标志,
|
||||
* **不再拿 tick 值当哨兵**(2026-10-24 实测:故障恰好落在 tick==0 那一拍时,旧写法
|
||||
* 记出的 cool_until==0 等于没记冷却,下一轮立刻再耗一次 100ms 块超时)。
|
||||
@@ -403,7 +403,7 @@ void dbg_console_drain(void) {
|
||||
* 环满等待(vTaskDelay / DWT 判据)一律在临界区之外,区内绝不睡眠。
|
||||
* 写者自身记录日志时先排空缓冲,避免自等。
|
||||
* 等待上界优先用 DWT 真实时间(CON_APPEND_WAIT_CYC,时间源无关)——
|
||||
* tick 停摆时 vTaskDelay 本身也会停,故不能用它作唯一判据(round 8b)。
|
||||
* tick 停摆时 vTaskDelay 本身也会停,故不能用它作唯一判据(第 8b 轮加固)。
|
||||
*/
|
||||
static int con_append(const char *buf, uint16_t len) {
|
||||
uint32_t waited = 0U;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* 模块功能:定长 64B SOE(Sequence-of-Events)记录的构建与解析:
|
||||
* CP56Time2a 时标 + 记录类型 + 元件号 + 单调序号 + 值 + 校验和。
|
||||
* **零 I/O、零 OS 依赖**(仅 stdint/string)→ 主机侧 L0.5 离线单测
|
||||
* (落实登记簿 A-4 约束 1"协议/记录与硬件 I/O 分离")。
|
||||
* (分层约束:协议/记录与硬件 I/O 分离)。
|
||||
* 记录口径(docs/算法/继电保护功能复现规划.md §3.4 L4-4/L4-5,B9):
|
||||
* 落盘文件 = /log/soe.log(8.3 合规;FF_USE_LFN=0 约束),定长 64B 一条
|
||||
* (照 run.log 先例:count = filesize/64 O(1)、容量满删除重建)。
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ void syslog_drain(void);
|
||||
* 返回值:0 - 成功;-1 - 卷占用或写失败(证据可能回滚,**不阻断判定**) int
|
||||
* 限定条件:poller(defaultTask)上下文调用;卷锁一律 try(绝不阻塞,
|
||||
* 本上下文 = 看门狗喂狗点)
|
||||
* 函数说明:round 7 取证通道(设计见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:
|
||||
* 函数说明:第 7 轮取证通道(设计见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:
|
||||
* 误复位后 RAM 态随复位消失、stall 行经僵尸串口丢失,故证据必须落盘)。
|
||||
* 内部在释放卷锁后立刻追加一条 s_append(SYSLOG_RUN, ...) 写另一个
|
||||
* 文件——FTL 单页缓存语义为"写到不同 LPN 才把前一脏页刷进 NAND",
|
||||
|
||||
+19
-19
@@ -15,7 +15,7 @@
|
||||
* (本任务 = 看门狗喂狗点,绝不阻塞等锁)
|
||||
* v1.3 2026-10-02 王建锋 新增 sys_log_evidence():把看门狗停喂现场
|
||||
* 证据落 /log/wdev.log(掉电不丢)+ flush 诱导
|
||||
* (round 7 纯取证,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
* (第 7 轮纯取证,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
* v1.5 2026-10-24 王建锋 主机单测批次照出的缺陷一起修,本文件内 5 条
|
||||
* (A/B/C/D/F;E 在 App/cfg/cfg_file_store_mcu.c):
|
||||
* A 卷忙整批静默丢日志——syslog_drain 批量路径补
|
||||
@@ -34,10 +34,10 @@
|
||||
* 数组满各立 volatile 抑制位,与 B 的 drain 侧那位分
|
||||
* 开):卷被长期占用 + A 项回投让队列常驻满时,串口
|
||||
* 不再按投递频率刷 "queue full, drop"
|
||||
* v1.4 2026-10-04 王建锋 P2-9:syslog_mcu_mounted() 改为幂等 + 卷锁保护;
|
||||
* v1.4 2026-10-04 王建锋 syslog_mcu_mounted() 改为幂等 + 卷锁保护;
|
||||
* defaultTask 每 2s 复查,建 /log 曾失败时自动重试
|
||||
* (原"仅调用一次"→ 失败后 drain 永久 no-op)。
|
||||
* P2-10:s_read_raw() 纳入卷锁 try 语义,与写者
|
||||
* 读者 s_read_raw() 纳入卷锁 try 语义,与写者
|
||||
* s_append 的"容量满 unlink 重建"互斥,消除
|
||||
* "UI 读记录(FA_READ) 时文件被 unlink"竞态窗口。
|
||||
*/
|
||||
@@ -63,7 +63,7 @@ static const uint32_t s_caps[SYSLOG_COUNT] = { SYSLOG_RUN_MAX, SYSLOG_SELFTEST_M
|
||||
|
||||
#define SYSLOG_QUEUE_MAX 8u
|
||||
|
||||
/* N-24(2026-10-15,报告 N-16 写侧):一次排空的合并写入上限。
|
||||
/* 日志读写放大收口(2026-10-15,写侧):一次排空的合并写入上限。
|
||||
* 8 条 × 64 B = 512 B 静态缓冲(不吃任务栈),与队列深度 SYSLOG_QUEUE_MAX 同值
|
||||
* ——即"一轮排空最多一次 open/write/close"。 */
|
||||
#define SYSLOG_BATCH_MAX 8u
|
||||
@@ -84,13 +84,13 @@ typedef struct {
|
||||
} syslog_entry_t;
|
||||
|
||||
static syslog_entry_t s_preboot[SYSLOG_QUEUE_MAX]; /* 启动前待发数组(RTOS 前单生产者/后单消费者,免锁) */
|
||||
static syslog_entry_t s_batch_src[SYSLOG_BATCH_MAX]; /* 本批条目的原始来源(N-24:未落盘的尾部要回投队列,故必须留住 id + desc 副本);
|
||||
static syslog_entry_t s_batch_src[SYSLOG_BATCH_MAX]; /* 本批条目的原始来源(未落盘的尾部要回投队列,故必须留住 id + desc 副本);
|
||||
* 唯一写者 defaultTask,走静态不吃栈 */
|
||||
static volatile uint8_t s_preboot_cnt; /* 数组条目数 */
|
||||
static osMessageQueueId_t s_queue; /* 运行期消息队列(syslog_drain 首次调用时惰性创建) */
|
||||
static const osMessageQueueAttr_t s_queue_attr = { .name = "syslog" };
|
||||
static volatile uint8_t s_fs_ready; /* 卷挂载 + /log 目录就绪后置位 */
|
||||
static volatile uint8_t s_mount_fail_reported; /* P2-9:建目录失败日志去重(成功复位) */
|
||||
static volatile uint8_t s_mount_fail_reported; /* 建目录失败日志去重(成功复位) */
|
||||
/* B 项整改(2026-10-24):syslog_drain 批量写失败"整批回投"这条留痕的节流位——
|
||||
* 只在"从正常转入故障"的那一次打一条,成功一轮后复位。与 s_append_many 内部的
|
||||
* s_batch_fault 各管一层:那位压的是"这个文件写坏了",本位压的是"这批条目回投了"。
|
||||
@@ -108,7 +108,7 @@ static uint8_t s_requeue_reported[SYSLOG_COUNT];
|
||||
static volatile uint8_t s_post_qfull_reported; /* 队列满丢弃:已留痕? */
|
||||
static volatile uint8_t s_post_pboot_full_reported;/* 启动前数组满丢弃:已留痕? */
|
||||
|
||||
/* N-24(2026-10-15,报告 N-16 读侧):8 条滑动读窗口 + 写者代数。
|
||||
/* 日志读写放大收口(2026-10-15,读侧):8 条滑动读窗口 + 写者代数。
|
||||
* 窗口 512 B 走静态(不吃任务栈;defaultTask/uiTask 最坏链见 stack_report.py,
|
||||
* 预算余量见 docs/内存管理/RAM预算台账.md)。s_rd_gen 由写者自增、读者比对,
|
||||
* 对齐 32 位在 ARM 上原子,跨任务无需临界区。 */
|
||||
@@ -195,7 +195,7 @@ static int s_is_selfheal_code(FRESULT fr) {
|
||||
* p_rec - 输出缓冲(SYSLOG_REC_SIZE 字节,不可为 NULL) uint8_t *
|
||||
* 返回值:无
|
||||
* 限定条件:时间戳未就绪时落 "1970-01-01 08:00:00" 占位串
|
||||
* 函数说明:N-24(2026-10-15,报告 N-16)从 s_append 抽出,供"单条写"与
|
||||
* 函数说明:日志读写放大收口(2026-10-15)时从 s_append 抽出,供"单条写"与
|
||||
* "批量写"共用——两条路径必须产出逐字节相同的记录,否则 UI 按定长
|
||||
* 字段偏移解析会错位。
|
||||
*/
|
||||
@@ -327,7 +327,7 @@ static int s_append(syslog_id_t id, const char *desc) {
|
||||
* 返回值:0 - 成功;-1 - 写失败;-2 - 卷被占用(顺延,非故障);
|
||||
* -3 - 剩余容量不足 n 条,本批不能走快路径(交回按条慢路径做轮转) int
|
||||
* 限定条件:仅 defaultTask(唯一写者)调用;卷已挂载
|
||||
* 函数说明:N-24(2026-10-15,报告 N-16 写侧)。旧实现每条记录一轮
|
||||
* 函数说明:日志读写放大收口(2026-10-15,写侧)。旧实现每条记录一轮
|
||||
* f_open + f_write(64 B) + f_close,而 f_close 要回写目录项、f_write
|
||||
* 要对目标扇区做读-改-写 → 每条日志 2~4 次 NAND 页读写。本函数把
|
||||
* 同一次排空里属于同一文件的最多 8 条合并成一次 512 B 追加写。
|
||||
@@ -405,15 +405,15 @@ static int s_append_many(syslog_id_t id, const uint8_t *p_buf, uint32_t n) {
|
||||
* idx - 记录序号(0 起) uint32_t
|
||||
* p_rec- 输出缓冲(SYSLOG_REC_SIZE 字节,不可为 NULL) uint8_t *
|
||||
* 返回值:0 - 成功;-1 - 越界/读失败/卷忙 int
|
||||
* 函数说明:P2-10(2026-10-04):读者纳入卷锁(try),与写者 s_append 的
|
||||
* 函数说明:读者纳入卷锁(try),与写者 s_append 的
|
||||
* "容量满 unlink 重建"互斥——原实现读时无锁,写者 unlink 与读者
|
||||
* open/read 可交错,读到的可能是半删除态文件。try 语义(timeout=0):
|
||||
* 本路径可由 uiTask 调用,uiTask 在 required_mask 内、须每 100ms 喂
|
||||
* 心跳,绝不能阻塞等锁;busy = 本次读失败(UI 下轮刷新重读,非故障)。
|
||||
*
|
||||
* N-24(2026-10-15,报告 N-16 读侧):加**8 条滑动窗口**。旧实现每读
|
||||
* 日志读写放大收口(2026-10-15,读侧):加**8 条滑动窗口**。旧实现每读
|
||||
* 一条记录都要 open + lseek + read + close 一轮,UI 翻 20 条记录 =
|
||||
* 20 次目录项/ FAT 往返(报告 N-16 的"UI 翻记录逐条 open/seek/close")。
|
||||
* 20 次目录项/ FAT 往返(即"UI 翻记录逐条 open/seek/close"这笔开销)。
|
||||
* 记录定长 64 B,故一次 open + 一次 lseek + 一次 f_read 就能取回连续
|
||||
* 8 条(512 B),命中窗口时零 I/O 直接从 RAM 返回。API 与调用方
|
||||
* (ui_page_runlog / ui_page_selftest 的逐行取数)一律不变。
|
||||
@@ -424,7 +424,7 @@ static int s_append_many(syslog_id_t id, const uint8_t *p_buf, uint32_t n) {
|
||||
*/
|
||||
static int s_read_raw(syslog_id_t id, uint32_t idx, uint8_t *p_rec) {
|
||||
static FIL f; /* static:省 528 B 栈;open~close 全在卷锁区间内
|
||||
* (uiTask 与写者任务由 vol_lock 串行化,见上方 P2-10 说明) */
|
||||
* (uiTask 与写者任务由 vol_lock 串行化,见上方读者卷锁说明) */
|
||||
FRESULT fr;
|
||||
UINT br = 0u;
|
||||
uint32_t total;
|
||||
@@ -504,8 +504,8 @@ int syslog_mcu_init(void) {
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:defaultTask f_mount/f_mkfs 成功后调用;**可重复调用(幂等)**——
|
||||
* 产品运行期 defaultTask 每 2s 复查一次(见下 P2-9 说明)
|
||||
* 函数说明:P2-9(2026-10-04):原实现"仅调用一次",若此处建 /log 失败则
|
||||
* 产品运行期 defaultTask 每 2s 复查一次(见下方幂等说明)
|
||||
* 函数说明:原实现"仅调用一次",若此处建 /log 失败则
|
||||
* s_fs_ready 恒 0 → syslog_drain 永久 no-op(运行期日志与自检全部
|
||||
* 滞留/丢弃),且无任何重试。现改为幂等:s_fs_ready 已置位则立即
|
||||
* 返回;否则重试建目录。defaultTask 在 STORAGE_READY 置位后每 2s
|
||||
@@ -570,7 +570,7 @@ void syslog_drain(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* N-24(2026-10-15,报告 N-16 写侧):一批一批地排空——同一文件、最多
|
||||
/* 日志读写放大收口(2026-10-15,写侧):一批一批地排空——同一文件、最多
|
||||
* SYSLOG_BATCH_MAX 条合并成一次 open/write/close(旧实现每条一轮
|
||||
* open/write/close,每条日志 2~4 次 NAND 页读写)。
|
||||
* 取数规则:preboot 数组按下标"看而不删",整批落盘成功才真正删除;
|
||||
@@ -831,13 +831,13 @@ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把一条取证行追加到 /log/wdev.log(掉电不丢;round 7 纯取证)
|
||||
* 函数功能:把一条取证行追加到 /log/wdev.log(掉电不丢;第 7 轮纯取证)
|
||||
* 入口参数:p_desc - 取证描述行(调用方已格式化;不可为 NULL) const char *
|
||||
* 返回值:0 - 成功;-1 - 卷占用或写失败(本条证据丢失,不阻断判定) int
|
||||
* 限定条件:poller(defaultTask)上下文调用;卷锁一律 try(绝不阻塞)
|
||||
* 函数说明:
|
||||
* 为什么必须落盘:误复位后 RAM 态随复位消失、`[WD] task stall` 行经僵尸串口
|
||||
* 可能丢失(2026-10-02 台架 round 3~6 绕圈的判据缺口),故证据必须落到
|
||||
* 可能丢失(2026-10-02 台架第 3~6 轮绕圈的判据缺口),故证据必须落到
|
||||
* **掉电不丢**的盘上,复位后经 FTP RETR 回读。
|
||||
* 第二条写(旧称"flush 诱导"):写完 wdev.log 后立刻 s_append(SYSLOG_RUN, ...)
|
||||
* 再落一条 run.log。保留理由是**多一条独立证据**(run.log 有 UI 记录页与 FTP
|
||||
@@ -845,7 +845,7 @@ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) {
|
||||
* disk_ioctl(CTRL_SYNC) 已经调 ftl_flush_cache() 把 wdev.log 的脏页刷进 NAND
|
||||
* (nand_ftl.c 的 CTRL_SYNC 分支),所以"写另一个文件(不同 LPN)逼出前一脏页"
|
||||
* 从来不是必要条件。
|
||||
* ⚠ 2026-10-02 round 3~8 那次"复位后写入整段丢失"的根因是 dhara resume 锚错
|
||||
* ⚠ 2026-10-02 台架第 3~8 轮那次"复位后写入整段丢失"的根因是 dhara resume 锚错
|
||||
* checkpoint 使 journal 整段回退(docs/架构设计/STM32F4-Base存储架构说明.md
|
||||
* §5.4.1),不是这层缓存:单页缓存最多丢最后未跨页那笔(≤512 B),丢不出
|
||||
* "整段"。空闲周期 flush 兜底已接线(App/fs/vol_idle_sync.h:STORAGE_READY
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 模块名称:IEC 60870-5-104 ASDU 编解码(应用层,纯逻辑)
|
||||
* 模块功能:104 规约应用服务数据单元(ASDU)的类型/传送原因常量、组帧与解析。
|
||||
* **零 I/O、零平台依赖**(仅 stdint.h)→ 主机侧 L0.5 可离线单测
|
||||
* (登记簿 A-4 约束 1"协议解析与硬件 I/O 分离")。
|
||||
* (分层约束:协议解析与硬件 I/O 分离)。
|
||||
* 口径来源:docs/算法/继电保护功能复现规划.md §1.5(B0 产品定义)——
|
||||
* 公共地址 2 字节、传送原因 2 字节、信息体地址 IOA **3 字节**(101 为 2 字节);
|
||||
* 遥测上送**短浮点** M_ME_NC_1(系数 = 1);遥信双点上双点、其余单点;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* → clock_sync 回调)、试验(C_TS)与进程复位(C_RP,清快照)。
|
||||
* **零 I/O**:收 = on_rx 喂 TCP 流片段,发 = send_apdu 回调,
|
||||
* 时基 = 调用方注入 now_ms(tick ms,回绕安全:差值法)
|
||||
* → 全栈(asdu+pts+link)主机侧 L0.5 可离线跑(登记簿 A-4 约束 1)。
|
||||
* → 全栈(asdu+pts+link)主机侧 L0.5 可离线跑(分层约束)。
|
||||
* 参数(DL/T 634.5104 标准默认,§1.5 未另定):k=12 w=8 t1=15s t2=10s t3=20s。
|
||||
* 端口:TCP **2404**(§1.5 B0 定义;本 DTU = 被控站/服务器,主站连入)。
|
||||
* 线程约定:单实例单任务上下文(板端 = 104 服务在 netTask 消息循环内驱动,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* 2026-10-25 DTU 项目组 卡 9/卡 11(「数据可信」,道D):YC 板端源改读
|
||||
* L2 遥测快照(App/algo/meas.h,meas_get 无锁双区);
|
||||
* 削顶五路由快照品质 CLIPPED 驱动线上 IV(不再绑死 -1,
|
||||
* V0.2/N-18 零改动自动启用);0x401d 绑频率源(卡 11);
|
||||
* V0.2 消除双转换器无共同采样触发后零改动自动启用);0x401d 绑频率源(卡 11);
|
||||
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
|
||||
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
|
||||
* 单测 = test/host/iec104 TC-IEC104-025~028。
|
||||
@@ -52,8 +52,8 @@ typedef struct {
|
||||
* 频率点不适用"额定百分比"(额定附近 0.5 Hz 与旧值同量级,维持 0.5 并随
|
||||
* 卡 10 运行参数段可调)。自卡 9 起本表**只作工厂默认载体**:生效死区 =
|
||||
* App/algo/run_par.c 的 [run_par] 引擎副本(卡 10 真源收编,本层不再存副本);削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
|
||||
* 驱动线上 IV——V0.2(N-18)削顶消失后点位零改动自动启用(docs/任务规划/
|
||||
* 数据可信.md §10 的 N-18 行)。
|
||||
* 驱动线上 IV——V0.2 削顶消失后点位零改动自动启用(docs/任务规划/
|
||||
* 数据可信.md §10 的「双转换器无共同采样触发」行)。
|
||||
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
|
||||
* 数字按本域偏保守(迟报不漏报);标定落地后复核(「数据可信」§2.3 表
|
||||
* 第 2 行)。判定 = 死区连续 2 点确认(卡 9),见 iec104_pts.h COV 说明。 */
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* 模块功能:104 点表(遥测 YC / 遥信 YX / 遥控 YK / 定值)的定义、读取与变化检测
|
||||
* (COV:遥信变位 + 遥测死区)。**数据源经 vtable 注入**:板端绑
|
||||
* phasor/prot_elm/prot_set 真实源;主机单测绑假源 → 协议栈全链
|
||||
* (asdu+pts+link)可离线跑(登记簿 A-4 约束 1)。
|
||||
* (asdu+pts+link)可离线跑(分层约束:协议解析与硬件 I/O 分离)。
|
||||
* 点表口径(docs/算法/继电保护功能复现规划.md §1.5,验收 = 省电科院点表):
|
||||
* - YC:IOA 0x4011~0x4018 + 0x401d(频率),短浮点、系数 1;数据源 =
|
||||
* L2 遥测快照(App/algo/meas.h,「数据可信」卡 9/11):电气 8 路绑快照槽,
|
||||
* 有效性由快照品质驱动——V0.1 削顶路(docs/算法/继电保护功能复现规划.md
|
||||
* §1.6)快照置 CLIPPED → 本层映射为线上 IV(V0.2/N-18 后自动启用,绑定零
|
||||
* 改动,见 docs/任务规划/数据可信.md §10 行 N-18);0x401d 绑频率源,
|
||||
* §1.6)快照置 CLIPPED → 本层映射为线上 IV(V0.2 后自动启用,绑定零
|
||||
* 改动,见 docs/任务规划/数据可信.md §10「双转换器无共同采样触发」行);0x401d 绑频率源,
|
||||
* freq_valid()==0 → IV;
|
||||
* - YX:IOA 1~33(§1.5 表(3));双点 = 开关位置类(YX1~4,本期无硬件 → IV),
|
||||
* 保护动作类 YX15~23 映射 prot_elm 状态,闭锁类 YX31~33 映射 prot_set;
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
|
||||
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
|
||||
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
|
||||
* v1.2 2026-10-02 王建锋 round 8b 消抖:等待片长 200ms→50ms,并把各处长
|
||||
* v1.2 2026-10-02 王建锋 第 8b 轮消抖:等待片长 200ms→50ms,并把各处长
|
||||
* 等待(500/2000/200ms)统一走 time_sync_delay_fed()
|
||||
* 按 ≤50ms 分片、每片喂心跳——消除"心跳滑出两个连续
|
||||
* 100ms 抽样窗"的节拍抖动(round 7 台架 snap=0x17)
|
||||
* 100ms 抽样窗"的节拍抖动(第 7 轮台架 snap=0x17)
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
@@ -33,10 +33,10 @@
|
||||
#define TIME_SYNC_PORT 8888
|
||||
|
||||
/*
|
||||
* E-02 头阻塞修复 + L3 pending 化(timeSync 侧;lftpd 侧见 lftpd_inet.c):
|
||||
* 头阻塞修复 + L3 pending 化(timeSync 侧;lftpd 侧见 lftpd_inet.c):
|
||||
* 旧实现用单次 30s 阻塞 net_recv(默认 NET_RECV_TIMEOUT_MS)等时间包——等待期间
|
||||
* netTask(全部 socket 操作的唯一执行者)被独占,其余任务的网络操作(远端显示 /
|
||||
* FTP)整体停摆(历史审计 E-02,属主文档已删除)。
|
||||
* FTP)整体停摆(历史审计遗留问题)。
|
||||
* 2026-09-22 先落地分片有界读(5ms 片),同日 L3 pending 化后改为"挂起请求":
|
||||
* 每次等待 = 一次挂起登记(netTask 零占用,完成按主循环节拍检查),调用方
|
||||
* 单次超时 = 剩余总预算(总等待 ≤ TIME_SYNC_TOTAL_BUDGET_MS,语义与旧 30s
|
||||
@@ -47,7 +47,7 @@
|
||||
/* 看门狗分片等待:单次 net_recv_to 的等待片长上限。须显著小于看门狗抽样窗
|
||||
* (task_watchdog.c WD_POLL_PERIOD_MS=100ms)——片长 ≤50ms 时每片前喂的心跳
|
||||
* 必然落在同一个 100ms 抽样窗内,消除"心跳恰好滑过两个连续窗口"的边界抖动
|
||||
* (round 7 台架稳态 snap=0x17 即抽样窗 vs 心跳节拍的良性错位,round 8b 消抖)。
|
||||
* (第 7 轮台架稳态 snap=0x17 即抽样窗 vs 心跳节拍的良性错位,第 8b 轮消抖)。
|
||||
* 语义上仅把一次长等待拆成多次短等待,总预算不变。 */
|
||||
#define TIME_SYNC_WAIT_SLICE_MS 50U
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
* 函数说明:看门狗抽样窗为 100ms(task_watchdog.c WD_POLL_PERIOD_MS)。把任意长
|
||||
* 等待切成 ≤TIME_SYNC_WAIT_SLICE_MS 的短片、每片前喂心跳,使心跳必然
|
||||
* 落在单个抽样窗内——语义等价于一次长延时,仅把"心跳节奏"钉在窗内
|
||||
* (round 8b 消抖;不改变总延时预算)。
|
||||
* (第 8b 轮消抖;不改变总延时预算)。
|
||||
*/
|
||||
static void time_sync_delay_fed(uint32_t ms) {
|
||||
while (ms > 0U) {
|
||||
@@ -180,7 +180,7 @@ static void time_sync_handle_conn(int conn_sock) {
|
||||
DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
|
||||
}
|
||||
} else {
|
||||
/* E-02 收口(docs/网络/网络自愈架构设计.md §5.2 E13):本层放弃会话 → 主动拆除。
|
||||
/* 头阻塞收口(docs/网络/网络自愈架构设计.md §5.2 E13):本层放弃会话 → 主动拆除。
|
||||
* 挂起但 TCP 仍活的客户端(PC 内核自动 ACK keepalive,芯片僵尸判不死)
|
||||
* 会永久占住单连接监听;EOF 路径 drop 幂等返 0(net 层已 relisten)。
|
||||
* 模拟器多连接后端 conn≠ls,由 sim 实现按数据 fd 关闭。 */
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-10-05 王建锋 P3-11:注明"预留桩(未启用)",避免误认为已实现
|
||||
* v1.1 2026-10-05 王建锋 注明"预留桩(未启用)",避免误认为已实现
|
||||
* v2.0 2026-10-10 王建锋 B1:实现真实消费者(1ms 节拍 + 原子快照 + 帧统计 +
|
||||
* 看门狗心跳),由 Src/freertos.c 创建为 adcTask
|
||||
* v2.1 2026-10-10 王建锋 日志门控:首帧/周期统计仅在 dbg_console_writer_ready()
|
||||
@@ -75,7 +75,7 @@
|
||||
* 单调性/错误计数增量)——严重事件与故障电平上升沿
|
||||
* 打 DBG_ERROR + 每类每次开机一次 SYSLOG_SELFTEST;
|
||||
* 恢复打 DBG_INFO;心跳日志增 tgap(TPAFE 侧丢帧,
|
||||
* 与 adc1_gap 对称)。判定不在本文件(A-4:算法与
|
||||
* 与 adc1_gap 对称)。判定不在本文件(算法与
|
||||
* I/O 分离),本文件只做"消费计数 → 告警"的编排
|
||||
* v3.0 2026-10-21 王建锋 修复"任务初始化入口从未被调用":adc_task_func 进入
|
||||
* 循环之前先调 adc_task_init()。原实现下判定层状态靠
|
||||
|
||||
+12
-12
@@ -2,7 +2,7 @@
|
||||
* 模块名称:Default Application Task
|
||||
* 模块功能:主控任务(defaultTask)任务体实现:
|
||||
* ① 启动段:存储 bring-up(挂载 + 空卷自动建卷 + 卷健康探针两击 +
|
||||
* 自愈重格式化,E-31 失败后主循环 3×2s 有界重试)+ 网络配置加载
|
||||
* 自愈重格式化,挂载失败后主循环 3×2s 有界重试)+ 网络配置加载
|
||||
* + STORAGE_READY 屏障 + "保存网络配置"回调注册
|
||||
* ② 控制台单写者:dbg_console_writer_attach 注册 + 主循环 dbg_console_drain
|
||||
* 排空(串口唯一发送者,见 App/log/console_log.c 设计说明)
|
||||
@@ -65,7 +65,7 @@
|
||||
* 2026-10-12(B5):堆扩至 56KB(.ioc configTOTAL_HEAP_SIZE=57344,CCM 腾给
|
||||
* protectTask/iec104Task 栈),阈值**不随堆缩放**——它是绝对字节安全线。 */
|
||||
#define HEAP_LOW_WATER_B 3072u
|
||||
/* E-31 自愈策略(2026-09-21 审计 P0 修复):启动期存储挂载失败后,
|
||||
/* 存储自愈策略(2026-09-21 审计 P0 修复):启动期存储挂载失败后,
|
||||
* 主循环按 2s 周期重跑完整 bring-up 的次数上限;耗尽则维持无存储降级
|
||||
* (自检已上报、卷保留、不重格式化),等下次上电重新 resume */
|
||||
#define STORAGE_BRINGUP_RETRIES 3u
|
||||
@@ -106,7 +106,7 @@ static FRESULT storage_bring_up(FATFS *fs);
|
||||
* 只在内核运行期被调度,启动段允许使用 CMSIS 对象
|
||||
* 函数说明:启动段 = 存储 bring-up + 保存回调注册 + 控制台写者注册;
|
||||
* 主循环 = 事件等待(配置保存/控制台待发,50ms 兜底)→ 控制台排空
|
||||
* → 配置保存执行 → 2s 配置轮询(含 E-31 有界重试)→ LED 心跳
|
||||
* → 配置保存执行 → 2s 配置轮询(含存储自愈有界重试)→ LED 心跳
|
||||
* → syslog 落盘 → 栈水位观测
|
||||
*/
|
||||
void default_task_func(void *argument) {
|
||||
@@ -121,7 +121,7 @@ void default_task_func(void *argument) {
|
||||
* 完整 bring-up 序列(挂载 + 空卷自动建卷 + 健康探针 + 自愈重格式化
|
||||
* + 日志挂载 + 配置加载 + 就绪屏障)实现于 storage_bring_up()。
|
||||
*
|
||||
* E-31(2026-09-21 审计,P0 修复):dhara_map_resume 失败不再被当
|
||||
* 存储自愈(2026-09-21 审计,P0 修复):dhara_map_resume 失败不再被当
|
||||
* "新卷"静默重格式化(扫描中任何一次瞬态毛刺都会销毁全部用户数据)——
|
||||
* nand_ftl 仅把 DHARA_E_TOO_BAD(真空白)判为新卷,其余失败使
|
||||
* disk_initialize 失败 → f_mount 失败 → 下方自检上报并保留卷;
|
||||
@@ -131,7 +131,7 @@ void default_task_func(void *argument) {
|
||||
* 小写两击确认、FR_INVALID_NAME 不入重格式化集、写保护类只上报,
|
||||
* 详见该函数注释)。 */
|
||||
#if !defined(TEST_SUITE_STORAGE)
|
||||
/* E-31 自愈:存储 bring-up 剩余重试次数(0 = 已就绪或已耗尽);
|
||||
/* 存储自愈:存储 bring-up 剩余重试次数(0 = 已就绪或已耗尽);
|
||||
* 须在启动块之前声明(启动块失败路径写入、主循环读取) */
|
||||
uint32_t storage_retry = 0u;
|
||||
#endif
|
||||
@@ -146,12 +146,12 @@ void default_task_func(void *argument) {
|
||||
if (fr != FR_OK) {
|
||||
/* 存储不可写:自检故障仅能滞留待发队列(串口已告警) */
|
||||
syslog_post(SYSLOG_SELFTEST, "filesystem fault");
|
||||
/* E-31 自愈:启动主循环中的有界重试(每 2s 一次) */
|
||||
/* 存储自愈:启动主循环中的有界重试(每 2s 一次) */
|
||||
storage_retry = STORAGE_BRINGUP_RETRIES;
|
||||
}
|
||||
|
||||
/* 注册"保存网络配置"请求通知:UI(uiTask)投递 → 置事件 → 本任务立即执行写盘
|
||||
* (配置引擎属主 = 本任务;UI 保存不再直接触碰配置文件引擎,P1-3) */
|
||||
* (配置引擎属主 = 本任务;UI 保存不再直接触碰配置文件引擎) */
|
||||
net_cfg_set_save_req_cb(default_task_net_cfg_save_notify);
|
||||
#else
|
||||
/* STORAGE 套件构建:挂载/配置引擎/日志盘写全部跳过,存储由 testTask 独占 */
|
||||
@@ -177,9 +177,9 @@ void default_task_func(void *argument) {
|
||||
task_wd_start();
|
||||
|
||||
for (;;) {
|
||||
/* round 8 取证:进段打点——每步前记录段号到 .noinit 槽(复位不丢)。
|
||||
/* 第 8 轮取证:进段打点——每步前记录段号到 .noinit 槽(复位不丢)。
|
||||
* 误复位后开机 `PREVRUN seg=N` 即"上一轮主循环死在哪个调用点"
|
||||
* (round 7 初判"poller 自身停摆"经 round 8b 更正:真因是 IWDG 超时被静默
|
||||
* (第 7 轮初判"poller 自身停摆"经第 8b 轮更正:真因是 IWDG 超时被静默
|
||||
* 配成复位默认值,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。 */
|
||||
task_wd_seg(TASK_WD_SEG_POLL);
|
||||
/* 心跳抽样与喂狗判定(内部节流至 100ms;全员到位才喂,否则累计停喂) */
|
||||
@@ -240,7 +240,7 @@ void default_task_func(void *argument) {
|
||||
if ((cfg_poll_cnt++ % 40u) == 0u) {
|
||||
#if !defined(TEST_SUITE_STORAGE)
|
||||
task_wd_seg(TASK_WD_SEG_CFGPOLL);
|
||||
/* E-31 自愈:启动期挂载失败时,每 2s 重跑完整 bring-up——
|
||||
/* 存储自愈:启动期挂载失败时,每 2s 重跑完整 bring-up——
|
||||
* 瞬态毛刺(如 dhara resume 扫描的瞬时 SPI 故障)通常第二次
|
||||
* 即恢复;成功后由 storage_bring_up 补跑启动时跳过的
|
||||
* "存储就绪"序列(日志挂载/配置加载/屏障置位,均幂等);
|
||||
@@ -257,7 +257,7 @@ void default_task_func(void *argument) {
|
||||
syslog_post(SYSLOG_SELFTEST, "filesystem fault");
|
||||
}
|
||||
}
|
||||
/* P2-9:卷已就绪(STORAGE_READY)但日志后端未就绪(建 /log 曾失败,
|
||||
/* 卷已就绪(STORAGE_READY)但日志后端未就绪(建 /log 曾失败,
|
||||
* s_fs_ready 恒 0 → syslog_drain 永久 no-op)→ 2s 周期重试(幂等;
|
||||
* 就绪后 syslog_mcu_mounted 立即返回,仅一次 volatile 读)。 */
|
||||
if (sys_evtHandle != NULL
|
||||
@@ -410,7 +410,7 @@ static void stack_probe_poll(void) {
|
||||
* 下整体不参与编译(卷由 testTask 独占)
|
||||
* 函数说明:
|
||||
* 由 defaultTask 调用:启动期一次;若失败,主循环每 2s 重试至多
|
||||
* STORAGE_BRINGUP_RETRIES 次(E-31 自愈策略,2026-09-21):dhara_map_resume
|
||||
* STORAGE_BRINGUP_RETRIES 次(存储自愈策略,2026-09-21):dhara_map_resume
|
||||
* 失败分类修复后(nand_ftl.c 仅真空白判新卷,其余失败保留卷并返回
|
||||
* disk_initialize 失败),重试让瞬态毛刺(SPI 瞬时故障/扫描窗口抖动)
|
||||
* 在数秒内免重启恢复,成功时补跑启动期跳过的就绪序列(日志挂载/配置
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 模块功能:2404 监听 + 单连接 socket 收发 + 把字节流喂给 iec104_link 纯逻辑
|
||||
* 层;对时经 sys_clock_set_unix 落墙钟;数据源走 iec104_pts 默认源
|
||||
* (phasor / prot_elm / prot_set)。协议语义全部在 App/protocol/iec104,
|
||||
* 本文件只做 I/O 编排(A-4 约束 1)。
|
||||
* 本文件只做 I/O 编排(分层约束)。
|
||||
* 线程约定:iec104Task 独占(NET_READY 屏障后循环,100ms 节拍 + 看门狗心跳)。
|
||||
* 创建日期:2026-10-12(B4)
|
||||
*/
|
||||
@@ -33,7 +33,7 @@
|
||||
/* 兜底节拍:门控判"无数据"时每 32 轮无条件收一次(ui_net_sink RM 先例,
|
||||
* 防 RECV_OK 事件未收到把数据永久藏住);须为 2 的幂 */
|
||||
#define IEC104_LIVENESS_DIV 32u
|
||||
/* 连续发送失败阈值:同 ui_net_sink E-04(短写=对端收坏帧) */
|
||||
/* 连续发送失败阈值:同 ui_net_sink 先例(短写=对端收坏帧) */
|
||||
#define IEC104_SEND_FAIL_MAX 3u
|
||||
|
||||
/* ---------------- 服务状态(iec104Task 独占) ---------------- */
|
||||
@@ -59,7 +59,7 @@ static const iec104_link_cfg_t s_cfg = { 1u, s_send_apdu, s_clock_sync, s_epoch_
|
||||
* 入口参数:ctx 未用;apdu/len = 完整 APCI 帧
|
||||
* 返回值:无
|
||||
* 限定条件:s_client >= 0(在 link 调用栈内执行,即本任务上下文)
|
||||
* 函数说明:短写/失败累计(E-04 先例);连续 3 次 → 置 s_conn_dead,
|
||||
* 函数说明:短写/失败累计(ui_net_sink 先例);连续 3 次 → 置 s_conn_dead,
|
||||
* 由 s_service_link 在 link 调用返回后统一拆链(严禁在回调里拆)
|
||||
*/
|
||||
static void s_send_apdu(void *ctx, const uint8_t *apdu, uint16_t len) {
|
||||
@@ -191,7 +191,7 @@ static void s_service_link(uint32_t now) {
|
||||
* 返回值:无
|
||||
* 限定条件:NET_READY 已置位;s_cfg/s_link 已 init
|
||||
* 函数说明:门控(net_listener_armed/net_can_read 纯 RAM 读)+ 兜底节拍照
|
||||
* ui_net_sink I-1/I-2;EOF 时 client==listen 严禁 close(net 层
|
||||
* ui_net_sink 先例;EOF 时 client==listen 严禁 close(net 层
|
||||
* 已 auto_relisten,close 会连监听一起毁)
|
||||
*/
|
||||
static void s_serv_poll(void) {
|
||||
|
||||
+9
-9
@@ -11,12 +11,12 @@
|
||||
* v1.2 2026-07-21 王建锋 网络初始化移入本任务(net_init + TCP 监听)
|
||||
* v1.3 2026-07-22 王建锋 单连接模式重构,增加大文件/边界/KeepAlive 测试
|
||||
* v1.4 2026-09-14 王建锋 新增自检埋点:PHY 链路 3s 未建立 → sys_log "comms fault"
|
||||
* v1.5 2026-09-15 王建锋 配置加载移交 defaultTask(配置引擎属主,P1-3);
|
||||
* v1.5 2026-09-15 王建锋 配置加载移交 defaultTask(配置引擎属主);
|
||||
* 本任务只读 RAM 副本
|
||||
* v1.6 2026-09-15 王建锋 P1-6:入口加 CH395F 硬件层测试模式自挂起守卫
|
||||
* v1.6 2026-09-15 王建锋 入口加 CH395F 硬件层测试模式自挂起守卫
|
||||
* (基础线程无条件创建,该模式下不运行);
|
||||
* freertos.c USER CODE 区重复创建已删除
|
||||
* v1.7 2026-10-02 王建锋 P1-8:初始组网失败/重建失败 → 自检记录 + 30s 周期重试
|
||||
* v1.7 2026-10-02 王建锋 初始组网失败/重建失败 → 自检记录 + 30s 周期重试
|
||||
* (原实现:初始失败 = netTask 无限驻留;重建失败 = 静默丢配置)
|
||||
* v1.8 2026-10-10 王建锋 任务耗时测量埋点:net_poll() 单独计时——
|
||||
* 本任务每 ~1ms 分派一次消息队列,若驱动轮询被拉长,
|
||||
@@ -52,7 +52,7 @@
|
||||
* 函数说明:只置事件位;真正的重建在主循环内完成(CH395F SPI 归 netTask 独占,
|
||||
* UI 不得直接操作)
|
||||
*/
|
||||
/* P1-8(2026-10-02):重建失败延迟重试的截止 tick;0 = 无待重试 */
|
||||
/* 重建失败延迟重试的截止 tick;0 = 无待重试 */
|
||||
static uint32_t s_reinit_retry_at;
|
||||
|
||||
static void net_reconfig_request_cb(void) {
|
||||
@@ -186,7 +186,7 @@ void net_task_func(void const *arg) {
|
||||
* 2026-10-02 台架修复:10s 超时等待改 100ms 分片 + 每片喂心跳——原实现
|
||||
* 在等待期无心跳:若存储 bring-up 失败,本等待跑满 10s 超时,而 IWDG
|
||||
* 此时已启动(defaultTask 在 bring-up 后即启动),NET 位长期缺失 →
|
||||
* 必需掩码不可达 → 复位循环,E-31 的 2s 自愈重试根本没有机会。
|
||||
* 必需掩码不可达 → 复位循环,存储自愈的 2s 重试根本没有机会。
|
||||
* 分片喂位保证"存储未就绪"降级态下 NET 位持续新鲜。 */
|
||||
uint32_t rounds_left = 100u; /* 100 × 100ms = 原 10s 超时语义 */
|
||||
for (;;) {
|
||||
@@ -202,7 +202,7 @@ void net_task_func(void const *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
/* P1-8(2026-10-02):初始组网失败不自驻留——自检台账留痕 + 30s 周期重试
|
||||
/* 初始组网失败不自驻留——自检台账留痕 + 30s 周期重试
|
||||
* (1s 分片、每片喂心跳):线缆/芯片恢复即可当场完成组网;原 for(;;)osDelay
|
||||
* 会令看门狗 NET 位持续陈旧,并阻塞全部下游就绪屏障 */
|
||||
for (;;) {
|
||||
@@ -235,7 +235,7 @@ void net_task_func(void const *arg) {
|
||||
|
||||
/* UI 保存网口配置后请求立即应用:在此重建网络(新 IP/掩码/网关/MAC)。
|
||||
* 重建完成后代次号已递增,各 TCP 服务据此重建监听;期间连接会断开(预期)。
|
||||
* P2-11(2026-10-04):用 osEventFlagsWait(timeout=0) 原子"取走并清除"RECONFIG
|
||||
* 用 osEventFlagsWait(timeout=0) 原子"取走并清除"RECONFIG
|
||||
* 位,替换原 Get+Clear 两步;原写法在 Get 与 Clear 之间若有生产者在同一位上
|
||||
* Set,该次请求会被 Clear 一并吞掉(丢事件窗口)。 */
|
||||
if (sys_evtHandle != NULL) {
|
||||
@@ -247,7 +247,7 @@ void net_task_func(void const *arg) {
|
||||
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_READY);
|
||||
s_reinit_retry_at = 0U;
|
||||
} else {
|
||||
/* P1-8(2026-10-02):重建失败 → 自检留痕 + 30s 延迟重试(重事件位),
|
||||
/* 重建失败 → 自检留痕 + 30s 延迟重试(重事件位),
|
||||
* 新配置不再静默丢失(原实现仅一条 DBG_ERROR 后丢弃) */
|
||||
DBG_ERROR("netTask: re-init FAIL, retry in 30s");
|
||||
(void)syslog_post(SYSLOG_SELFTEST, "net reinit fail");
|
||||
@@ -255,7 +255,7 @@ void net_task_func(void const *arg) {
|
||||
}
|
||||
} else if ((s_reinit_retry_at != 0U)
|
||||
&& ((int32_t)((uint32_t)HAL_GetTick() - s_reinit_retry_at) >= 0)) {
|
||||
/* P1-8:30s 倒计时到 → 重新触发重建(符号差处理 tick 回绕) */
|
||||
/* 30s 倒计时到 → 重新触发重建(符号差处理 tick 回绕) */
|
||||
s_reinit_retry_at = 0U;
|
||||
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_RECONFIG);
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-08-24 王建锋 提取 net_stack_bring_up() 供硬件层/NET 层两种测试模式共用
|
||||
* v1.2 2026-09-15 王建锋 硬件层模式描述修正:netTask 恒创建(基础线程),
|
||||
* 该模式下自挂起不运行(P1-6 守卫)
|
||||
* 该模式下自挂起不运行(入口自挂起守卫)
|
||||
* v1.3 2026-10-21 王建锋 删除 net_task_init() 声明(空壳入口、无调用点)
|
||||
*/
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ static uint8_t s_prev_cal_lock; /* 上拍未标定闭锁态(边沿日志用
|
||||
* 返 回 值:1 - 闭锁(判据停走);0 - 放行 uint8_t
|
||||
* 限定条件:仅本任务上下文(adc_cal_flags 纯读,任意上下文可读)
|
||||
* 函数说明:判据 = UA/UN/IA 三路 VALID **全**置位——恰好是 V0.1 唯一有效的
|
||||
* 三个测量通道(N-18);UB/UC/IB/IC/IN 即使将来标定也另受 CLIPPED
|
||||
* 三个测量通道(双转换器无共同采样触发);UB/UC/IB/IC/IN 即使将来标定也另受 CLIPPED
|
||||
* 品质约束(M5 快照层处理),不掺和进本闭锁源。VALID 随 cfg
|
||||
* load/poll 与卡 12 标定自动翻转,本函数无自有状态(每拍现查,
|
||||
* 无陈旧标志位)。台架调试旁路 = 编译期 -DDTU_BENCH(§4.3 决策 1
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-10-05 王建锋 P3-11:注明"预留桩(未启用)",避免误认为已实现
|
||||
* v1.1 2026-10-05 王建锋 注明"预留桩(未启用)",避免误认为已实现
|
||||
*/
|
||||
|
||||
#include "rs485_task.h"
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
* (2026-10-25 实测 bit4 8 连击、uisn=154ms 仍在段内;
|
||||
* 按位连击判据 = docs/子系统设计/看门狗设计说明.md §5.3)。
|
||||
* 单个往返自身仍无上界——该上界与彻底解耦(事件驱动
|
||||
* accept)归登记簿 E-27/E-28(批 3),本票只做心跳续命。
|
||||
* accept)归登记簿「net 层跨上下文收口」(批 3),本票只做心跳续命。
|
||||
*/
|
||||
#include "ui_task.h"
|
||||
|
||||
@@ -295,7 +295,7 @@ void ui_task_func(void *argument) {
|
||||
* 一次心跳,救不了 → 彻底解耦须走事件驱动 accept,见文件头修改记录 v1.3。
|
||||
* v1.4 补充:sink 内每个往返**返回点**已各自补喂(set_beat_cb 注入),
|
||||
* 「单轮多个慢往返之和」不再构成缺席链;剩单往返 >800ms 才报——那是
|
||||
* netTask 自身停摆,按位判据抓对了人(上界归 E-27/E-28)。 */
|
||||
* netTask 自身停摆,按位判据抓对了人(上界归「net 层跨上下文收口」)。 */
|
||||
task_wd_beat(TASK_WD_UI);
|
||||
/* 推流轮询段单独 enter/exit:它内部经 net_accept → net_wait_result 走到
|
||||
* xTaskNotifyWait(portMAX_DELAY) 的**无限期阻塞**。若 uiTask 永久卡在这里,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权;标准向量由
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权;标准向量由
|
||||
* test/host/sha256 主机单测钉死)
|
||||
*/
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权:[ftp] pwd_hash 校验)
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权:[ftp] pwd_hash 校验)
|
||||
*/
|
||||
|
||||
#ifndef SHA256_H
|
||||
|
||||
+26
-26
@@ -8,17 +8,17 @@
|
||||
* 创建日期:2026-09-29
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-29 王建锋 创建:心跳位图 + 抽样判定 + IWDG 延后启动
|
||||
* v1.1 2026-10-02 王建锋 round 7 纯取证埋点:近 8 次抽样位图环 + 最大喂狗
|
||||
* v1.1 2026-10-02 王建锋 第 7 轮纯取证埋点:近 8 次抽样位图环 + 最大喂狗
|
||||
* 间隔统计 + 停喂现场落 /log/wdev.log(掉电不丢);
|
||||
* 全部在 TASK_WATCHDOG_UNIT_TEST 守卫内,判定状态机
|
||||
* 与主机侧单测逐字不变(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
* v1.2 2026-10-02 王建锋 round 8 取证:复位不丢的"进段打点"(.noinit 槽 +
|
||||
* PREVRUN 开机上报 + STALL 行附 seg)——round 7 结论为
|
||||
* v1.2 2026-10-02 王建锋 第 8 轮取证:复位不丢的"进段打点"(.noinit 槽 +
|
||||
* PREVRUN 开机上报 + STALL 行附 seg)——第 7 轮结论为
|
||||
* "poller 自身停摆",需定位 defaultTask 主循环阻塞点
|
||||
* v1.3 2026-10-02 王建锋 round 8b 取证:第二个 .noinit 槽"时间源存活性"
|
||||
* v1.3 2026-10-02 王建锋 第 8b 轮取证:第二个 .noinit 槽"时间源存活性"
|
||||
* (TIM7 ISR 每 1ms 写 uwTick/FreeRTOS tick/计数)+
|
||||
* TICKRUN 开机上报(含相对进段时刻的增量 duw/dfr)——
|
||||
* round 8a 定位到 console 通道后,需钉死是 TIM7(HAL tick)
|
||||
* 第 8a 轮定位到 console 通道后,需钉死是 TIM7(HAL tick)
|
||||
* 还是 SysTick(FreeRTOS tick) 停摆(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
*
|
||||
* v1.5 2026-10-25 DTU 项目组 **停喂判定按位分账**(大窗判读 F2):stall/停喂的唯一
|
||||
@@ -35,7 +35,7 @@
|
||||
* HAL_IWDG_Init() 时,一并丢掉了**唯一**写这两只寄存器的
|
||||
* 路径 → 实机停在复位默认值 PR=0/RLR=4095(≈349~512ms),
|
||||
* 比设计短约 5 倍 → 800ms 容忍分支成死代码、任何 >400ms
|
||||
* 心跳缺口直接 IWDG 误复位(round 3~8 全部"空 STOR 必
|
||||
* 心跳缺口直接 IWDG 误复位(第 3~8 轮全部"空 STOR 必
|
||||
* 复位"的共同根因)。SWD 现场改回设计值后同一用例 100% 通过
|
||||
*
|
||||
* 设计依据:docs/子系统设计/看门狗设计说明.md
|
||||
@@ -64,7 +64,7 @@
|
||||
#include "FreeRTOS.h" /* xTaskGetCurrentTaskHandle / TaskHandle_t(FTL 心跳路由) */
|
||||
#include "task.h"
|
||||
#include "lftpd.h" /* lftpd_task_wd_beat() 弱钩子声明(本文件提供强符号,见下方) */
|
||||
#include "sys_log.h" /* SYSLOG_RUN(flush 诱导落盘)+ sys_log_evidence()(round 7 取证落盘) */
|
||||
#include "sys_log.h" /* SYSLOG_RUN(flush 诱导落盘)+ sys_log_evidence()(第 7 轮取证落盘) */
|
||||
#include "console_log.h" /* dbg_console_tx_fault()(僵尸通道计数) */
|
||||
/* 测试构建可见性(2026-10-25 台架实测缺陷):wd_required_mask() 里那条
|
||||
* ENABLE_HW_LAYER_TESTS 分支要按"硬件层独占测试构建"剔除 netTask,而该宏定义在
|
||||
@@ -134,7 +134,7 @@ static uint32_t s_started; /* 1 = IWDG 已启动 */
|
||||
static uint32_t s_last_tick; /* 上次抽样时刻(100ms 节流基准) */
|
||||
static uint32_t s_stall_evidenced; /* 1 = 本停摆 episode 已落盘证据(门闩;见 task_wd_poll) */
|
||||
|
||||
/* ==== round 7 纯取证状态(仅 MCU 构建;判定状态机逐字不变)====
|
||||
/* ==== 第 7 轮纯取证状态(仅 MCU 构建;判定状态机逐字不变)====
|
||||
* 设计背景见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:误复位后 RAM 态随复位消失,
|
||||
* 故把"停喂现场"快照落盘(/log/wdev.log)供复位后回读定性。 */
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
@@ -145,7 +145,7 @@ static uint32_t s_last_feed_tick; /* 上次喂狗时刻(ms) *
|
||||
static uint32_t s_max_feed_gap; /* 观测到的最大喂狗间隔(ms) */
|
||||
static uint32_t s_feed_seen; /* 1 = 已记录首个喂狗时刻 */
|
||||
|
||||
/* ==== round 8 取证:复位不丢的"进段打点"槽(.noinit → SRAM 保留)====
|
||||
/* ==== 第 8 轮取证:复位不丢的"进段打点"槽(.noinit → SRAM 保留)====
|
||||
* IWDG/引脚复位不改变 SRAM 内容,故复位后仍能读出"上一轮 defaultTask 主循环
|
||||
* 最后走到哪一段"。magic 用于区分"本模块写过"与上电时的随机值。
|
||||
* 布局:[0]=magic [1]=段号 [2]=进段时刻(ms) [3]=循环序号
|
||||
@@ -155,7 +155,7 @@ static uint32_t s_feed_seen; /* 1 = 已记录首个喂狗时
|
||||
#define WD_SEG_MAGIC 0x57445347u /* 'WDSG' */
|
||||
__attribute__((section(".noinit"), zero_init)) volatile uint32_t s_wd_seg_trail[4];
|
||||
|
||||
/* ==== round 8b 取证:复位不丢的"时间源存活性"槽(.noinit → SRAM 保留)====
|
||||
/* ==== 第 8b 轮取证:复位不丢的"时间源存活性"槽(.noinit → SRAM 保留)====
|
||||
* 两个槽(均在 UNINIT_CCM,复位不清):
|
||||
* s_wd_tick_cur [0]=magic [1]=uwTick(最新) [2]=本轮 TIM7 中断计数 [3]=保留
|
||||
* —— TIM7 ISR 每 1ms 更新("本轮活动值")
|
||||
@@ -238,7 +238,7 @@ static uint32_t wd_required_mask(void) {
|
||||
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/*
|
||||
* 函数功能:记录喂狗时刻并累计最大喂狗间隔(round 7 纯取证)
|
||||
* 函数功能:记录喂狗时刻并累计最大喂狗间隔(第 7 轮纯取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:由 wd_platform_feed() 内调用(任意喂狗路径均经过)
|
||||
@@ -260,7 +260,7 @@ static void wd_feed_track(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把停喂现场落盘到 /log/wdev.log(round 7 纯取证)
|
||||
* 函数功能:把停喂现场落盘到 /log/wdev.log(第 7 轮纯取证)
|
||||
* 入口参数:snapshot - 本次抽样到的位图 uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:task_wd_poll 停喂分支(defaultTask 上下文)
|
||||
@@ -298,14 +298,14 @@ static void wd_stall_evidence(uint32_t snapshot) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== round 8 取证:复位不丢的"进段打点" ====================
|
||||
* 见 task_watchdog.h 的说明(round 7 初判"poller 自身停摆"经 round 8b 更正:
|
||||
/* ==================== 第 8 轮取证:复位不丢的"进段打点" ====================
|
||||
* 见 task_watchdog.h 的说明(第 7 轮初判"poller 自身停摆"经第 8b 轮更正:
|
||||
* 真因是 IWDG 超时被静默配成复位默认值,见 docs/子系统设计/看门狗设计说明.md
|
||||
* 2026-10-02 变更记录)→ 需定位 defaultTask 主循环冻结在哪个调用点。
|
||||
* 槽放 .noinit(scatter UNINIT_CCM):IWDG/引脚复位不改变 SRAM,复位后仍可读。 */
|
||||
|
||||
/*
|
||||
* 函数功能:记录"即将进入的主循环段"到复位不丢的 .noinit 槽(round 8 取证)
|
||||
* 函数功能:记录"即将进入的主循环段"到复位不丢的 .noinit 槽(第 8 轮取证)
|
||||
* 入口参数:seg - 段号(task_wd_seg_t) uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:defaultTask 主循环调用(任意上下文亦可,纯 SRAM 写)
|
||||
@@ -320,7 +320,7 @@ void task_wd_seg(uint32_t seg) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(round 8b 取证)
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(第 8b 轮取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:**必须在 HAL_Init() 之前调用**(main() 的 USER CODE BEGIN 1)
|
||||
@@ -346,7 +346,7 @@ void task_wd_tick_snapshot(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(round 8b 取证)
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(第 8b 轮取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:TIM7 中断上下文调用(HAL_TIM_PeriodElapsedCallback 内,每 1ms)
|
||||
@@ -363,13 +363,13 @@ void task_wd_tick_probe(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(round 8 取证)
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(第 8 轮取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:启动早期调用(内核启动前亦可,裸 UART 直发)
|
||||
* 函数说明:magic 命中才打一行 `PREVRUN seg=<段> seq=<序号> t=<ms>`(裸 UART,
|
||||
* 不依赖环形缓冲写者),随后清 magic(一次复位只报一次)。
|
||||
* round 8b:紧接着打 `TICKRUN uw= n= duw=`——uw 为 TIM7 最后一次记下的
|
||||
* 第 8b 轮:紧接着打 `TICKRUN uw= n= duw=`——uw 为 TIM7 最后一次记下的
|
||||
* HAL tick、n 为 TIM7 中断计数、duw = uw - t(时间源是否活到复位)。
|
||||
*/
|
||||
void task_wd_seg_report(void) {
|
||||
@@ -391,7 +391,7 @@ void task_wd_seg_report(void) {
|
||||
}
|
||||
s_wd_seg_trail[0] = 0u; /* 一次复位只报一次 */
|
||||
|
||||
/* round 8b:时间源存活性(取自开机快照 prev,增量相对上一行的 t = 最后进段时刻)。
|
||||
/* 第 8b 轮:时间源存活性(取自开机快照 prev,增量相对上一行的 t = 最后进段时刻)。
|
||||
* duw≈0 → TIM7 与 defaultTask 同刻停摆(HAL 时基停摆);duw≈2000+ → TIM7 活到
|
||||
* 复位(冻结另有阻塞点);n = 上一轮 TIM7 中断总数(≈该轮存活毫秒数)。 */
|
||||
if (s_wd_tick_prev[0] == WD_TICK_MAGIC) {
|
||||
@@ -487,7 +487,7 @@ void task_wd_poll(void) {
|
||||
s_last_mask = snapshot;
|
||||
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/* round 7 取证:留存近 8 次抽样位图(环形),停喂时随证据落盘 */
|
||||
/* 第 7 轮取证:留存近 8 次抽样位图(环形),停喂时随证据落盘 */
|
||||
s_snap_hist[s_snap_hist_idx] = snapshot;
|
||||
s_snap_hist_idx = (s_snap_hist_idx + 1u) % WD_SNAP_HIST_DEPTH;
|
||||
#endif
|
||||
@@ -524,7 +524,7 @@ void task_wd_poll(void) {
|
||||
s_miss_total++;
|
||||
if (s_miss_streak == 0u) {
|
||||
/* 复合缺席 episode 起点裸 UART 标记(每 episode 一条):证明 poller 仍在
|
||||
* 抽样、并给出**首次**缺席位图——定位谁先停跑(round 7 出身)。⚠ v1.5 起
|
||||
* 抽样、并给出**首次**缺席位图——定位谁先停跑(第 7 轮出身)。⚠ v1.5 起
|
||||
* 本行为**纯诊断**——复合边沿 ≠ 停喂,stall 判定权在下面的按位连击。 */
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
char m[96];
|
||||
@@ -549,7 +549,7 @@ void task_wd_poll(void) {
|
||||
(unsigned)wd_required_mask(),
|
||||
(unsigned long)s_miss_total);
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/* round 7 取证:把现场落 /log/wdev.log(掉电不丢)。
|
||||
/* 第 7 轮取证:把现场落 /log/wdev.log(掉电不丢)。
|
||||
* 串口 stall 行经僵尸通道可能丢失,故此处独立留盘上证据。
|
||||
* ⚠ 本 episode 只写**一次**(s_stall_evidenced 门闩;注意 worst 连击达
|
||||
* 上限后会**饱和**在容忍值,不能用它判"首次"):证据落盘经
|
||||
@@ -615,7 +615,7 @@ uint32_t task_wd_is_armed(void) {
|
||||
*/
|
||||
__weak void wd_platform_feed(void) {
|
||||
IWDG->KR = WD_IWDG_KEY_FEED; /* 重装计数器(喂狗) */
|
||||
wd_feed_track(); /* round 7 取证:记录喂狗时刻/最大间隔 */
|
||||
wd_feed_track(); /* 第 7 轮取证:记录喂狗时刻/最大间隔 */
|
||||
}
|
||||
|
||||
/* ==== 为什么 IWDG 参数必须在这里写(2026-10-02 台架实测,P0 根因)====
|
||||
@@ -627,10 +627,10 @@ __weak void wd_platform_feed(void) {
|
||||
* 实测读数:`IWDG->PR = 0`、`IWDG->RLR = 4095` → 实际超时 4×4096/LSI
|
||||
* ≈ 349~512ms,比设计的 1701~2498ms **短约 5 倍**。后果有二:
|
||||
* ① `WD_MISS_TOLERANCE`(8×100ms=800ms)**永远等不到**就被 IWDG 复位
|
||||
* → 停喂判定分支成为死代码(这正是 round 7 观察到"STALL 从未执行"
|
||||
* → 停喂判定分支成为死代码(这正是第 7 轮观察到"STALL 从未执行"
|
||||
* 的原因,当时误判为"poller 自身停摆");
|
||||
* ② 任何 >400ms 的心跳缺口都直接 IWDG 误复位。空 STOR(0 字节截断)
|
||||
* → dhara/FTL flush 使 NET/FTP/UI 心跳缺 ~400ms → 复位(round 3~8
|
||||
* → dhara/FTL flush 使 NET/FTP/UI 心跳缺 ~400ms → 复位(第 3~8 轮
|
||||
* 全部"空 STOR 必复位"的共同根因,P0-B 出口阻塞点)。
|
||||
* 实测验证:SWD 现场把 PR/RLR 改成 4/1249 后,同一个空 STOR 由 5/5 复位
|
||||
* 变为 100% 成功(`226 Closing data connection.`)。
|
||||
|
||||
+10
-10
@@ -85,10 +85,10 @@ uint32_t task_wd_miss_count(void); /* 累计未达标轮数 */
|
||||
uint32_t task_wd_is_armed(void); /* 1 = IWDG 已启动 */
|
||||
|
||||
/*
|
||||
* ==================== round 8 取证:复位不丢的"进段打点" ====================
|
||||
* 背景(2026-10-02 台架 round 7~8 取证,完整链条见
|
||||
* ==================== 第 8 轮取证:复位不丢的"进段打点" ====================
|
||||
* 背景(2026-10-02 台架第 7~8 轮取证,完整链条见
|
||||
* docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录):误复位时判定分支(STALL)从未
|
||||
* 执行,当时判为**喂狗点 task_wd_poll 自身停摆**(round 8b 已更正:真因是 IWDG 超时
|
||||
* 执行,当时判为**喂狗点 task_wd_poll 自身停摆**(第 8b 轮已更正:真因是 IWDG 超时
|
||||
* 被静默配成复位默认值),而非某个被监视任务饿死。RAM 态随复位消失、串口又可能来不及打,
|
||||
* 故需要一条**复位后仍可读**的"defaultTask 主循环最后走到哪一段"轨迹。
|
||||
*
|
||||
@@ -96,7 +96,7 @@ uint32_t task_wd_is_armed(void); /* 1 = IWDG 已启动 */
|
||||
* MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct 的 UNINIT_CCM)——IWDG/引脚复位
|
||||
* 不改变 SRAM 内容,故复位后仍能读出上一轮的最后进段。槽布局:
|
||||
* [0]=magic(校验是否为本模块写过)[1]=段号 [2]=进段时刻(ms) [3]=循环序号
|
||||
* round 8b 追加"时间源存活性"槽(同样在 .noinit):
|
||||
* 第 8b 轮追加"时间源存活性"槽(同样在 .noinit):
|
||||
* cur = [magic, uwTick, 本轮 TIM7 计数, 0](TIM7 ISR 每 1ms 更新)
|
||||
* prev = [magic, uwTick, 上一轮 TIM7 计数, 0](main() 在 HAL_Init 之前快照)
|
||||
* 注意:**仅 MCU 构建存在**(TASK_WATCHDOG_UNIT_TEST 下不编入,主机单测不受影响)。
|
||||
@@ -117,7 +117,7 @@ typedef enum {
|
||||
} task_wd_seg_t;
|
||||
|
||||
/*
|
||||
* 函数功能:记录"即将进入的主循环段"(round 8 取证;**仅 MCU 构建**)
|
||||
* 函数功能:记录"即将进入的主循环段"(第 8 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:seg - 段号(task_wd_seg_t)
|
||||
* 返回值:无
|
||||
* 限定条件:defaultTask 主循环调用(任意上下文亦可,纯 SRAM 写)
|
||||
@@ -126,20 +126,20 @@ typedef enum {
|
||||
void task_wd_seg(uint32_t seg);
|
||||
|
||||
/*
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(round 8 取证;**仅 MCU 构建**)
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(第 8 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:启动早期调用(内核启动前亦可,裸 UART 直发)
|
||||
* 函数说明:magic 命中才打一行 `PREVRUN seg=<段> seq=<序号> t=<ms>`(裸 UART,
|
||||
* 不依赖环形缓冲写者),随后清 magic(一次复位只报一次)。
|
||||
* round 8b:紧接着打一行 `TICKRUN uw= n= duw=`——uw 为 TIM7 在**上一轮**
|
||||
* 第 8b 轮:紧接着打一行 `TICKRUN uw= n= duw=`——uw 为 TIM7 在**上一轮**
|
||||
* 最后一次记下的 HAL tick、n 为上一轮 TIM7 中断总数、duw = uw - t
|
||||
* (相对进段时刻的增量),据此判定 TIM7 是否在冻结同刻停摆。
|
||||
*/
|
||||
void task_wd_seg_report(void);
|
||||
|
||||
/*
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(round 8b 取证;**仅 MCU 构建**)
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(第 8b 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:**必须在 HAL_Init() 之前调用**(main() 的 USER CODE BEGIN 1)
|
||||
@@ -150,13 +150,13 @@ void task_wd_seg_report(void);
|
||||
void task_wd_tick_snapshot(void);
|
||||
|
||||
/*
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(round 8b 取证;**仅 MCU 构建**)
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(第 8b 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:TIM7 中断上下文调用(`HAL_TIM_PeriodElapsedCallback` 内,每 1ms)
|
||||
* 函数说明:更新 cur 槽 = [magic, uwTick, 本轮 TIM7 中断计数, 0](纯 SRAM 写,无阻塞)。
|
||||
* 与 task_wd_seg 的"进段打点"配合,复位后由 task_wd_seg_report() 上报。
|
||||
* 背景(2026-10-02 台架 round 8b 取证,见 docs/子系统设计/看门狗设计说明.md
|
||||
* 背景(2026-10-02 台架第 8b 轮取证,见 docs/子系统设计/看门狗设计说明.md
|
||||
* 2026-10-02 变更记录):HAL_UART_Transmit 的超时判据
|
||||
* 依赖 uwTick,时间源停摆即死等——必须先把"TIM7 是否停摆"钉死再定向修复。
|
||||
* ⚠ **本函数内严禁调用任何 FreeRTOS/CMSIS-RTOS API**:TIM7 在 HAL_Init
|
||||
|
||||
+12
-12
@@ -8,9 +8,9 @@
|
||||
* 修改记录:
|
||||
* 2026-03-14 王建锋 创建初始版本,按代码规范重构
|
||||
* 2026-07-18 王建锋 改进write_send_buf/read_recv_buf使用SPI2 DMA批量传输
|
||||
* 2026-10-02 王建锋 E-34:3 处字节级 SPI 调用由 HAL_MAX_DELAY 改有限超时(100ms)
|
||||
* 2026-10-02 王建锋 3 处字节级 SPI 调用由 HAL_MAX_DELAY 改有限超时(100ms)
|
||||
* + 返回值检查 + 故障锁存(沿程只记一条日志);原 SPI 异常时调用方永久挂死
|
||||
* 2026-10-04 王建锋 P2-2:ch395f_spi_dma_transfer 由 void 改为返回状态(0 成功/-1 失败),
|
||||
* 2026-10-04 王建锋 ch395f_spi_dma_transfer 由 void 改为返回状态(0 成功/-1 失败),
|
||||
* DMA 初始化失败/超时中止均锁存通道故障;read_recv_buf 在 DMA 失败时不再把
|
||||
* rx_buf 内容拷入调用方缓冲(改零填充),消除"失败当成功"的静默数据损坏
|
||||
*/
|
||||
@@ -40,7 +40,7 @@
|
||||
#define CH395F_CHECK_EXIST_TEST_DATA 0x57U /* 芯片检测测试数据 */
|
||||
#define CH395F_RESET_DELAY_MS 20U /* 复位后等待延时(ms) */
|
||||
#define CH395F_INIT_TIMEOUT_MS 500U /* 初始化超时时间(ms) */
|
||||
#define CH395F_SPI_BYTE_TIMEOUT_MS 100U /* E-34:字节级SPI有限超时(原 HAL_MAX_DELAY) */
|
||||
#define CH395F_SPI_BYTE_TIMEOUT_MS 100U /* 字节级SPI有限超时(原 HAL_MAX_DELAY) */
|
||||
/* DMA缓冲大小使用ch395f.h中公开的CH395F_SPI_DMA_BUF_SIZE */
|
||||
#define CH395F_DMA_BUF_SIZE CH395F_SPI_DMA_BUF_SIZE
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
* 静态变量定义区 - DMA传输相关
|
||||
*/
|
||||
static volatile uint8_t s_spi2_dma_done = 0U; /* DMA传输完成标志 */
|
||||
static uint8_t s_spi_fault = 0U; /* E-34/P2-2:SPI 通道故障锁存(字节级或 DMA 级超时/出错置位,MCU复位清除) */
|
||||
static uint8_t s_spi_fault = 0U; /* SPI 通道故障锁存(字节级或 DMA 级超时/出错置位,MCU复位清除) */
|
||||
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t s_spi2_dma_tx_buf[CH395F_DMA_BUF_SIZE]; /* DMA发送缓冲区 */
|
||||
@@ -90,7 +90,7 @@ static void ch395f_spi_end(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:锁存一次 SPI 通道故障(E-34/P2-2:仅首条故障记日志)
|
||||
* 函数功能:锁存一次 SPI 通道故障(仅首条故障记日志)
|
||||
* 入口参数:kind - 故障类别标签("byte"/"dma-hal"/"dma-timeout") const char*
|
||||
* 返回值:无
|
||||
* 限定条件:无
|
||||
@@ -114,7 +114,7 @@ static void ch395f_spi_fault_set(const char *kind) {
|
||||
* 2. 阻塞等待DMA完成回调置标志位
|
||||
* 3. 超时(1秒)时强制中止SPI DMA并打印错误信息
|
||||
* 4. 传输完成后检查SPI错误码并清除溢出标志
|
||||
* 5. P2-2(2026-10-04):失败路径锁存通道故障并返回 -1,调用方据此
|
||||
* 5.(2026-10-04):失败路径锁存通道故障并返回 -1,调用方据此
|
||||
* 判定 rx_buf 内容不可信,不再"失败当成功"继续消费
|
||||
*/
|
||||
static int ch395f_spi_dma_transfer(uint16_t len) {
|
||||
@@ -209,7 +209,7 @@ void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
|
||||
uint8_t ch395f_write_cmd(uint8_t cmd) {
|
||||
uint8_t rx_data = 0xFFU;
|
||||
|
||||
/* 通过SPI发送命令并接收回显(E-34:有限超时+返回值检查;失败返回0xFF占位并锁存故障) */
|
||||
/* 通过SPI发送命令并接收回显(有限超时+返回值检查;失败返回0xFF占位并锁存故障) */
|
||||
if (HAL_SPI_TransmitReceive(&hspi2, &cmd, &rx_data, 1,
|
||||
CH395F_SPI_BYTE_TIMEOUT_MS) != HAL_OK) {
|
||||
ch395f_spi_fault_set("byte");
|
||||
@@ -226,7 +226,7 @@ uint8_t ch395f_write_cmd(uint8_t cmd) {
|
||||
uint8_t ch395f_write_data(uint8_t data) {
|
||||
uint8_t rx_data = 0xFFU;
|
||||
|
||||
/* 通过SPI发送数据并接收回显(E-34:有限超时+返回值检查) */
|
||||
/* 通过SPI发送数据并接收回显(有限超时+返回值检查) */
|
||||
if (HAL_SPI_TransmitReceive(&hspi2, &data, &rx_data, 1,
|
||||
CH395F_SPI_BYTE_TIMEOUT_MS) != HAL_OK) {
|
||||
ch395f_spi_fault_set("byte");
|
||||
@@ -244,7 +244,7 @@ uint8_t ch395f_read_data(void) {
|
||||
uint8_t tx_dummy = 0xFFU;
|
||||
uint8_t rx_data = 0xFFU;
|
||||
|
||||
/* 发送0xFF作为时钟源,读取芯片返回数据(E-34:有限超时+返回值检查) */
|
||||
/* 发送0xFF作为时钟源,读取芯片返回数据(有限超时+返回值检查) */
|
||||
if (HAL_SPI_TransmitReceive(&hspi2, &tx_dummy, &rx_data, 1,
|
||||
CH395F_SPI_BYTE_TIMEOUT_MS) != HAL_OK) {
|
||||
ch395f_spi_fault_set("byte");
|
||||
@@ -253,7 +253,7 @@ uint8_t ch395f_read_data(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:查询 SPI 通道故障锁存(E-34 / P2-2)
|
||||
* 函数功能:查询 SPI 通道故障锁存
|
||||
* 返回值:0 - 正常 1 - 曾发生 SPI 超时/错误(字节级或 DMA 级)
|
||||
* 限定条件:无
|
||||
* 函数说明:锁存式(不自动清除,MCU 复位前一直有效);上层据此判定
|
||||
@@ -1197,7 +1197,7 @@ void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||||
/* 数据同步屏障:确保memcpy写入对DMA可见 */
|
||||
__DSB();
|
||||
|
||||
/* CS低 → DMA批量传输 → CS高(P2-2:失败已由 transfer 锁存通道故障并记日志,
|
||||
/* CS低 → DMA批量传输 → CS高(失败已由 transfer 锁存通道故障并记日志,
|
||||
* 调用方经 ch395f_spi_byte_fault() 查询;此处无后续处理,失败即放弃本次发送) */
|
||||
ch395f_spi_begin();
|
||||
(void)ch395f_spi_dma_transfer(total);
|
||||
@@ -1250,7 +1250,7 @@ void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
|
||||
rc = ch395f_spi_dma_transfer(total);
|
||||
ch395f_spi_end();
|
||||
|
||||
/* P2-2(2026-10-04):DMA 失败时 rx_buf 内容不可信,禁止拷入调用方缓冲
|
||||
/* (2026-10-04):DMA 失败时 rx_buf 内容不可信,禁止拷入调用方缓冲
|
||||
* (否则把总线残留/半截数据当有效载荷 → 静默数据损坏);改零填充并返回,
|
||||
* 调用方据 ch395f_spi_byte_fault() 判定通道异常。 */
|
||||
if (rc != 0) {
|
||||
|
||||
@@ -307,7 +307,7 @@ uint8_t ch395f_write_data(uint8_t data);
|
||||
uint8_t ch395f_read_data(void);
|
||||
|
||||
/*
|
||||
* 函数功能:查询 SPI 通道故障锁存(E-34 / P2-2)
|
||||
* 函数功能:查询 SPI 通道故障锁存
|
||||
* 返回值:0 - 正常 1 - 曾发生 SPI 超时/错误(字节级或 DMA 级)
|
||||
* 限定条件:无
|
||||
* 函数说明:锁存式(MCU 复位前一直有效);上层据此判定"SPI 通道异常"
|
||||
@@ -584,7 +584,7 @@ uint16_t ch395f_get_recv_len(uint8_t sock);
|
||||
* 任何未配对 SINT 消费的读取都会吞掉其它 Socket 的事件通知。
|
||||
* **唯一读者 = net_poll(net_socket.c);测试/台架代码同样禁自行读取**
|
||||
* (用户裁定 2026-10-13)——等事件一律直读本 Socket ch395f_get_sock_int_status()。
|
||||
* 唯一例外:被测对象就是本 API 的板级用例(P1-06 寄存器断言 / Phase 12 L1
|
||||
* 唯一例外:被测对象就是本 API 的板级用例(寄存器断言 / Phase 12 L1
|
||||
* 时延基准,硬件层独占上下文、无并发消费者)。
|
||||
*/
|
||||
uint16_t ch395f_get_glob_int_status_all(void);
|
||||
|
||||
@@ -62,7 +62,7 @@ static volatile uint8_t s_spi1_dma_done;
|
||||
* 源/目的全部为文件作用域静态缓冲。 */
|
||||
|
||||
/* DMA 可达内存窗口 GD5F_DMA_RAM_BASE/END 与 CCM 窗口常量已上移到 gd5f2gq5ue.h
|
||||
* (N-26,2026-10-12:nand_ftl.c 的 init 期自检也要用),并带编译期守卫
|
||||
* (DMA 可达性三层防线,2026-10-12:nand_ftl.c 的 init 期自检也要用),并带编译期守卫
|
||||
* (白名单窗口与 CCM 永不相交、阈值契约)。此处只留语义提醒:
|
||||
* 窗口必须与分散加载 RW_IRAM1(0x20000000,128KB,SRAM1+SRAM2)一致;
|
||||
* CCM(0x10000000,64KB)DMA1/DMA2 物理不可达,RW_CCM 区任何缓冲都不得进 DMA。
|
||||
@@ -130,7 +130,7 @@ static int gd5f_private_bbt_find_best(void);
|
||||
static int gd5f_bbt_load(void);
|
||||
static int gd5f_bbt_save(void);
|
||||
|
||||
/* DMA 可达性守卫:实现见下方(N-26 起为公开 API,声明在 gd5f2gq5ue.h) */
|
||||
/* DMA 可达性守卫:实现见下方(随 DMA 可达性三层防线升为公开 API,声明在 gd5f2gq5ue.h) */
|
||||
|
||||
/* ======================== DMA 可达性守卫 ======================== */
|
||||
|
||||
@@ -174,7 +174,7 @@ int gd5f_dma_buf_ok(const void *p_buf, size_t size) {
|
||||
* GD5F_ERROR(-1) - SPI 传输真故障(不可重试,须上报自检)
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时。
|
||||
* E-53(2026-10-03,GD5F 错误通道统一):三处 HAL 调用的返回值**必须**检查
|
||||
* (2026-10-03,GD5F 错误通道统一):三处 HAL 调用的返回值**必须**检查
|
||||
* ——原实现全部丢弃,失败时 `status` 保持初值 0 → `(status & OIP) == 0`
|
||||
* 立即成立 → **把"操作完成"伪造出来**,后续命令在未就绪的芯片上执行,
|
||||
* 最终表现为"静默报成功但数据没落盘"。现区分两类失败:
|
||||
@@ -217,7 +217,7 @@ int32_t gd5f_wait_busy(uint32_t timeout_ms) {
|
||||
GD5F_CS_HIGH();
|
||||
|
||||
if (hs != HAL_OK) {
|
||||
/* E-53:SPI 真故障——绝不伪造成"操作完成"(详见函数说明) */
|
||||
/* SPI 真故障——绝不伪造成"操作完成"(详见函数说明) */
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
@@ -500,8 +500,8 @@ int32_t gd5f_check_ecc(void) {
|
||||
* 返回值:GD5F_OK - 成功;其它 - SPI 传输/等待失败错误码
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:先写使能,发 1Fh + 地址 + 数据,等待操作完成。
|
||||
* E-53(2026-10-03):HAL 传输与 wait_busy 的返回值必须检查并上报——
|
||||
* 原实现恒返回 GD5F_OK,"配置未生效"对上层不可见(E-10 同族问题)。
|
||||
* (2026-10-03):HAL 传输与 wait_busy 的返回值必须检查并上报——
|
||||
* 原实现恒返回 GD5F_OK,"配置未生效"对上层不可见(同族问题)。
|
||||
*/
|
||||
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
|
||||
uint8_t cmd[3];
|
||||
@@ -534,10 +534,10 @@ static int gd5f_set_feature(uint8_t addr, uint8_t data) {
|
||||
* 出口参数:p_data - 读到的字节输出指针 uint8_t* 不为 NULL
|
||||
* 返回值:GD5F_OK - 成功;GD5F_ERROR - SPI 传输失败
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:E-10(2026-10-03):init 需**回读** B0h 校验 ECC 使能位真正生效
|
||||
* 函数说明:(2026-10-03):init 需**回读** B0h 校验 ECC 使能位真正生效
|
||||
* (原实现只写不读,"init 返回 OK 但 ECC 未打开"→ 存储栈在无 ECC
|
||||
* 保护下静默降级)。与 gd5f_read_status 同构(后者固定 C0h),
|
||||
* 此处参数化地址;同样检查 HAL 返回值(E-53)。
|
||||
* 此处参数化地址;同样检查 HAL 返回值。
|
||||
*/
|
||||
static int gd5f_private_get_feature(uint8_t addr, uint8_t *p_data) {
|
||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||
@@ -600,7 +600,7 @@ int32_t gd5f_block_erase(uint32_t block_addr) {
|
||||
* 限定条件:SPI 已初始化,ECC 尚未使能
|
||||
* 函数说明:读取每个块首页(page 0)的 spare byte 0,
|
||||
* 若不为 0xFF 则为出厂坏块(符合 datasheet §12.4)。
|
||||
* E-42(2026-10-03,GD5F 错误通道统一):原实现丢弃 page_read /
|
||||
* (2026-10-03,GD5F 错误通道统一):原实现丢弃 page_read /
|
||||
* read_from_cache 的返回值且对外恒返回 GD5F_OK——SPI 读失败时
|
||||
* `spare[8]` 保持上一轮的**陈旧内容**,且"整片扫描全失败也返回成功",
|
||||
* 上层以为坏块表可用(rebuild 路径还会把错表持久化覆盖好副本)。
|
||||
@@ -689,7 +689,7 @@ int32_t gd5f2gq5ue_bbt_rebuild(void) {
|
||||
/* 3. 重新使能 ECC:无论扫描成败都必须恢复,而且要**验证真的生效**
|
||||
* (2026-10-23,由 bsp-err 的 consume-result 检查照出)。原先这两行返回值全丢:SPI 一抖 ECC 就保持关闭,而
|
||||
* gd5f_check_ecc() 在 ECC 关闭时恒读"无 ECC 错"——整条链再没有任何信号,设备
|
||||
* 从此在无位翻转保护下读写却"一切正常"。init 路径有 E-10 的"写完回读 B0h 校验
|
||||
* 从此在无位翻转保护下读写却"一切正常"。init 路径有"写完回读 B0h 校验
|
||||
* ECC_EN 位",这里补同一套;ECC 没恢复比扫描失败更严重,所以优先返回它。 */
|
||||
restore_ret = gd5f_set_feature(0xB0, 0x10);
|
||||
if ((gd5f_wait_busy(100) != GD5F_OK) && (restore_ret == GD5F_OK)) {
|
||||
@@ -710,7 +710,7 @@ int32_t gd5f2gq5ue_bbt_rebuild(void) {
|
||||
return restore_ret;
|
||||
}
|
||||
|
||||
/* E-42(2026-10-03,数据安全优先):扫描失败 → **放弃持久化**(保留持久区
|
||||
/* (2026-10-03,数据安全优先):扫描失败 → **放弃持久化**(保留持久区
|
||||
* 既有好副本),并从盘上回读 BBT 恢复 RAM 视图。原实现无论扫描成败都照常
|
||||
* save(),会把"读失败拼出来的错表"覆盖掉正确副本。 */
|
||||
if (ret != GD5F_OK) {
|
||||
@@ -806,7 +806,7 @@ int32_t gd5f2gq5ue_init(void) {
|
||||
DBG_INFO("Scanning bad blocks...");
|
||||
ret = gd5f_bbt_scan();
|
||||
if (ret != GD5F_OK) {
|
||||
/* E-10:扫坏块失败 → init 失败,不静默继续(上层据此上报自检并降级) */
|
||||
/* 扫坏块失败 → init 失败,不静默继续(上层据此上报自检并降级) */
|
||||
DBG_ERROR("BBT scan failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -817,7 +817,7 @@ int32_t gd5f2gq5ue_init(void) {
|
||||
DBG_ERROR("Enable ECC failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
/* E-10:回读 B0h 校验 ECC 使能位(bit4)**真正生效**——原实现只写不读,
|
||||
/* 回读 B0h 校验 ECC 使能位(bit4)**真正生效**——原实现只写不读,
|
||||
* "init 返回 OK 但 ECC 未真正打开"会使存储栈在无 ECC 保护下静默降级。 */
|
||||
{
|
||||
uint8_t feat = 0;
|
||||
|
||||
@@ -77,7 +77,7 @@ extern "C" {
|
||||
*/
|
||||
#define GD5F_DMA_THRESHOLD 1024
|
||||
|
||||
/* ==================== DMA 可达内存窗口(N-26,2026-10-12) ====================
|
||||
/* ==================== DMA 可达内存窗口(DMA 可达性三层防线,2026-10-12) ====================
|
||||
* DMA1/DMA2 物理不可达 CCM(0x10000000~0x1000FFFF):以 CCM 缓冲做 DMA 源/目的
|
||||
* 会静默不落地(2026-09-20 首轮 CCM 扩容实验实证根因;口径见
|
||||
* docs/内存管理/内存内容分配策略.md §2 与 §4 规则 R2/R3)。
|
||||
@@ -93,7 +93,7 @@ extern "C" {
|
||||
#define GD5F_CCM_RAM_BASE 0x10000000UL
|
||||
#define GD5F_CCM_RAM_END 0x10010000UL
|
||||
|
||||
/* 编译期守卫(N-26):AC5 无 _Static_assert(C11 不支持),用项目惯用的
|
||||
/* 编译期守卫(DMA 可达性三层防线一环):AC5 无 _Static_assert(C11 不支持),用项目惯用的
|
||||
* 负数组 typedef(同 App/cfg/cfg_file.c、Drivers/BSP/UI/ui_menu_tab.h)——
|
||||
* 条件不成立则数组尺寸 -1 → 编译错。
|
||||
* ① DMA 白名单窗口与 CCM 窗口**永不得重叠**(有人改上面常量时立即爆炸);
|
||||
@@ -116,7 +116,7 @@ typedef char gd5f_dma_threshold_contract_check_t[
|
||||
/* 返回值定义 */
|
||||
#define GD5F_OK 0
|
||||
#define GD5F_ERROR -1
|
||||
#define GD5F_WAIT_EXPIRED -2 /* 旧名 GD5F_BUSY_TIMEOUT;S-5 的 timeout-name 检查:**返回码不许叫 *_TIMEOUT** */
|
||||
#define GD5F_WAIT_EXPIRED -2 /* 旧名 GD5F_BUSY_TIMEOUT;timeout-name 检查:**返回码不许叫 *_TIMEOUT** */
|
||||
#define GD5F_ECC_ERROR -3
|
||||
#define GD5F_PROGRAM_FAIL -4
|
||||
#define GD5F_ERASE_FAIL -5
|
||||
@@ -160,7 +160,7 @@ int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);
|
||||
* 返回值:GD5F_OK(0) - SPI 传输完成;其它 - 错误码
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间。
|
||||
* ⚠ **raw 契约(E-56,2026-10-03 显式文档化)**:返回 0 只表示 **SPI 传输完成**,
|
||||
* ⚠ **raw 契约(2026-10-03 显式文档化)**:返回 0 只表示 **SPI 传输完成**,
|
||||
* **不代表数据无位翻转**——ECC 不可纠正页(ECCS 非零)在 SPI 层属正常完成,
|
||||
* 本函数仍返回 0。数据正确性由 **ECC 层**保证:生产主路径经 dhara glue
|
||||
* (`nand_ftl.c`)自有 ECC 检查保护;**直接使用本 raw API** 的调用方
|
||||
@@ -277,7 +277,7 @@ int32_t gd5f_bbt_reload(void);
|
||||
* 入口参数:timeout_ms - 等待上限 uint32_t 毫秒
|
||||
* 返 回 值:GD5F_OK - 已空闲;GD5F_WAIT_EXPIRED - 时限内未空闲(**可重试**,旧名 BUSY_TIMEOUT);GD5F_ERROR - SPI 读取失败
|
||||
* 限定条件:SPI 已初始化;不得在中断里调用
|
||||
* 函数说明:E-32 把"超时"与"读状态本身失败"分开返回,两者处置不同(前者再等、后者查硬件)
|
||||
* 函数说明:把"超时"与"读状态本身失败"分开返回,两者处置不同(前者再等、后者查硬件)
|
||||
*/
|
||||
int32_t gd5f_wait_busy(uint32_t timeout_ms);
|
||||
|
||||
@@ -286,7 +286,7 @@ int32_t gd5f_wait_busy(uint32_t timeout_ms);
|
||||
* 入口参数:无
|
||||
* 返 回 值:GD5F_OK - 成功;其它 - SPI 传输或等待失败错误码
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:E-53 规定本函数返回值必须被检查并上报,忽略它会让后续 program/erase 静默无效
|
||||
* 函数说明:本函数返回值必须被检查并上报(错误通道统一纪律),忽略它会让后续 program/erase 静默无效
|
||||
*/
|
||||
int32_t gd5f_write_enable(void);
|
||||
|
||||
@@ -295,7 +295,7 @@ int32_t gd5f_write_enable(void);
|
||||
* 入口参数:p_status - 状态字节输出 uint8_t* 不得为 NULL
|
||||
* 返 回 值:GD5F_OK - 成功(*p_status 有效);GD5F_ERROR - SPI 失败(*p_status 不可信,调用方不得读用)
|
||||
* 限定条件:SPI 已初始化
|
||||
* 函数说明:E-42 同族纪律——失败时出参内容不可信,故判成败看返回值而不是看 *p_status 是否像样
|
||||
* 函数说明:同族纪律——失败时出参内容不可信,故判成败看返回值而不是看 *p_status 是否像样
|
||||
*/
|
||||
int32_t gd5f_read_status(uint8_t *p_status);
|
||||
|
||||
@@ -315,7 +315,7 @@ int32_t gd5f_page_read(uint32_t page_addr);
|
||||
* size - 读取字节数 uint32_t 1 - GD5F_PAGE_SIZE-column
|
||||
* 返 回 值:GD5F_OK - 成功;GD5F_ERROR - 参数非法或 SPI 失败(**此时 p_buf 内容不可信**)
|
||||
* 限定条件:必须先成功调用 gd5f_page_read() 完成阵列→缓存装载
|
||||
* 函数说明:E-42 同族——调用方不得在失败分支继续使用 p_buf(FTL 侧据此把读失败保守判为"非空页")
|
||||
* 函数说明:同族——调用方不得在失败分支继续使用 p_buf(FTL 侧据此把读失败保守判为"非空页")
|
||||
*/
|
||||
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
|
||||
|
||||
@@ -353,7 +353,7 @@ int32_t gd5f_block_erase(uint32_t block_addr);
|
||||
* 入口参数:无
|
||||
* 返 回 值:GD5F_OK - 无不可纠错误(可能含已纠正的位翻转);GD5F_ECC_ERROR - **存在不可纠正的 ECC 错误**
|
||||
* 限定条件:SPI 已初始化且 ECC 已使能(init 期扫描在使能前调用本函数无意义)
|
||||
* 函数说明:这是数据正确性的唯一判定点——SPI 层读到"内容"不等于"内容对"(见 §8.1 与本头 E-42/E-53 注释)
|
||||
* 函数说明:这是数据正确性的唯一判定点——SPI 层读到"内容"不等于"内容对"(见 §8.1 与本头"同族纪律"注释)
|
||||
*/
|
||||
int32_t gd5f_check_ecc(void);
|
||||
|
||||
@@ -363,7 +363,7 @@ int32_t gd5f_check_ecc(void);
|
||||
* size - 传输字节数 size_t
|
||||
* 返回值:1 - 整段 DMA 可达;0 - 不可达(含 size=0 / 地址回绕 / 越窗)
|
||||
* 限定条件:无
|
||||
* 函数说明:N-26(2026-10-12)由 gd5f2gq5ue.c 私有守卫升级为公开 API——
|
||||
* 函数说明:2026-10-12 由 gd5f2gq5ue.c 私有守卫升级为公开 API(DMA 可达性三层防线一环)——
|
||||
* 驱动内部每次 >阈值 传输前用它(不可达即回落 CPU 轮询),
|
||||
* FTL 层(nand_ftl.c)在 init 期用它自检静态页缓冲的落位。
|
||||
*/
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
* 创建日期:2026-07-20
|
||||
* 修改记录:
|
||||
* 2026-07-20 王建锋 创建初始版本
|
||||
* 2026-09-21 E-31 resume 失败按错误分类:仅真空白判新卷,其余失败
|
||||
* 2026-09-21 resume 失败按错误分类:仅真空白判新卷,其余失败
|
||||
* 返回 mount 层(禁止静默清 map 触发整卷重格式化)
|
||||
* 2026-10-02 王建锋 resume 成功后增打 journal head/tail/epoch:复盘
|
||||
* "复位后所有写入丢失"时 sectors used 不足以判定
|
||||
* journal 回退(覆盖写不改 count),head/tail/epoch 是
|
||||
* 唯一能暴露 resume 锚错 checkpoint 的证据
|
||||
* 2026-10-15 王建锋 N-21(报告 N-12/N-13):① is_free 改**整页扫描**——
|
||||
* 2026-10-15 王建锋 落实 dhara 移植层两条硬约束:① is_free 改**整页扫描**——
|
||||
* 旧实现只看页首 64 B,会把"页首 0xFF、后段有数据"的
|
||||
* 已编程页误判空闲,dhara 遂往已编程页再编程 = 数据丢失
|
||||
* (页首有数据的绝大多数页走快路径,成本与旧实现同价);
|
||||
@@ -65,7 +65,7 @@
|
||||
* 而忘了同步这里的字面量,守卫会静默算错——偏大就是越界守卫失效,正是它要防的
|
||||
* 那一类错误,而且不报任何错。下面两条把"字面量 == GD5F 常量的 log2"钉死,
|
||||
* 不一致直接编译错。负数组 typedef 是 AC5 下的项目惯用法(AC5 不支持 C11
|
||||
* _Static_assert),同本目录 gd5f2gq5ue.h 的 N-26 DMA 契约守卫。 */
|
||||
* _Static_assert),同本目录 gd5f2gq5ue.h 的 DMA 契约守卫。 */
|
||||
typedef char ftl_geometry_log2_page_size_check_t[
|
||||
(FTL_PAGE_SIZE == (1 << FTL_LOG2_PAGE_SIZE)) ? 1 : -1];
|
||||
typedef char ftl_geometry_log2_ppb_check_t[
|
||||
@@ -76,7 +76,7 @@ static inline uint32_t ftl_phy_block(dhara_block_t b) { return b + FTL_START_BLO
|
||||
static inline uint32_t ftl_phy_page(dhara_page_t p) { return p + FTL_START_PAGE; }
|
||||
|
||||
/*
|
||||
* N-21(2026-10-15,报告 N-13):移植层地址守卫
|
||||
* dhara 移植层硬约束(2026-10-15):移植层地址守卫
|
||||
*
|
||||
* 旧实现把 dhara 传来的页/块号直接加偏移送进 SPI 命令,**不做任何上界校验**。
|
||||
* 越界页号(含 dhara 的"无页"哨兵 DHARA_PAGE_NONE = 0xffffffff,journal.h:46)
|
||||
@@ -89,8 +89,8 @@ static inline uint32_t ftl_phy_page(dhara_page_t p) { return p + FTL_START_PAG
|
||||
* 为什么不用 DHARA_E_BAD_BLOCK:journal.c:904 的 recover_from 只对 BAD_BLOCK
|
||||
* 走"标坏块 + 跳块重试"——对一个凭空的地址标坏块 = 毁掉一个无辜的好块。
|
||||
* 为什么不会触发静默清卷:map.c:275 的 try_recover 只对 E_RECOVER 启动回收,
|
||||
* 其余错误一律原样上抛(E-31 教训:单次瞬态毛刺不得销毁整卷数据)。
|
||||
* is_free / is_bad 没有错误通道 → 一律保守取值(判非空 / 判坏),与 E-42 同族
|
||||
* 其余错误一律原样上抛(教训:单次瞬态毛刺不得销毁整卷数据)。
|
||||
* is_free / is_bad 没有错误通道 → 一律保守取值(判非空 / 判坏),与同族
|
||||
* 口径一致:宁可少用一个块,不可把不可信的内容当有效。
|
||||
*/
|
||||
static int ftl_page_in_range(const struct dhara_nand *n, dhara_page_t p) {
|
||||
@@ -156,7 +156,7 @@ static const char *ftl_err_name(dhara_error_t err) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把 dhara 错误归类成统一失败类(C2,报告 S-5)
|
||||
* 函数功能:把 dhara 错误归类成统一失败类(C2)
|
||||
* 入口参数:err - dhara 错误枚举值
|
||||
* 返回值:BSP_ERR_CRC(位翻转不可纠 / 页太坏);BSP_ERR_NO_SPACE(journal 或映射层预算
|
||||
* 耗尽);其余(坏块 / 找回失败 / 映射损坏 / 越界)→ BSP_ERR_IO
|
||||
@@ -164,10 +164,10 @@ static const char *ftl_err_name(dhara_error_t err) {
|
||||
* 函数说明:分这三类的理由是它们在**处置上真的不同**,不是为了码好看:
|
||||
* · ECC/TOO_BAD → 该重试读或标页坏,**不是**卷的问题;
|
||||
* · JOURNAL_FULL/MAP_FULL → 介质还健康,是**块池/日志预算见底**,此时重格式化只是
|
||||
* 把问题推回去(E-31 的教训正是"别把非空白错误当新卷清 map"),该走容量恢复流程;
|
||||
* 把问题推回去(教训正是"别把非空白错误当新卷清 map"),该走容量恢复流程;
|
||||
* · BAD_BLOCK/CORRUPT_MAP/RECOVER → 映射或介质完整性故障,该保卷 + 上报,不许自愈重格式化。
|
||||
* 本函数先把这三类**打出来**(就地日志,最早且唯一同时知道 LPN 与原始码的位置);
|
||||
* 要让 defaultTask 的卷健康探针按类分支,还需要一个跨调用存活的快照(S-5 的 C4 待还)。
|
||||
* 要让 defaultTask 的卷健康探针按类分支,还需要一个跨调用存活的快照(C4 待还)。
|
||||
*/
|
||||
static bsp_err_t ftl_class_from_dhara(dhara_error_t err) {
|
||||
switch (err) {
|
||||
@@ -284,7 +284,7 @@ int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *d
|
||||
}
|
||||
|
||||
/*
|
||||
* N-21(2026-10-15,报告 N-12):整页扫描
|
||||
* dhara 移植层硬约束(2026-10-15):整页扫描
|
||||
*
|
||||
* 旧实现只看页首 64 B。dhara 的 journal 用 is_free 判断"检查点组是否未编程"
|
||||
* (journal.c:416 cp_free、journal.c:520 找下一空闲页),其正确性前提是
|
||||
@@ -317,7 +317,7 @@ int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) {
|
||||
|
||||
for (off = 0; off < FTL_PAGE_SIZE; off += sizeof(buf)) {
|
||||
if (gd5f_read_from_cache((uint16_t)off, buf, sizeof(buf)) != GD5F_OK) {
|
||||
/* E-42 同族(2026-10-03):读失败 → buf 可能为陈旧/垃圾内容,
|
||||
/* 同族(2026-10-03):读失败 → buf 可能为陈旧/垃圾内容,
|
||||
* 保守判为非空(块不可复用),避免把"没读到内容的块"判成空闲。 */
|
||||
return 0;
|
||||
}
|
||||
@@ -350,7 +350,7 @@ int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, s
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* E-42 同族(2026-10-03):read_from_cache 返回值必须检查——失败时 data 内容
|
||||
/* 同族(2026-10-03):read_from_cache 返回值必须检查——失败时 data 内容
|
||||
* 不可信,原实现仍返回 0(成功),会把未读到的数据当有效数据交给上层。 */
|
||||
if (gd5f_read_from_cache((uint16_t)offset, data, length) != GD5F_OK) {
|
||||
dhara_set_error(err, DHARA_E_ECC);
|
||||
@@ -360,7 +360,7 @@ int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, s
|
||||
}
|
||||
|
||||
/*
|
||||
* N-21(2026-10-15,报告 N-13):src / dst 双端守卫
|
||||
* dhara 移植层硬约束(2026-10-15):src / dst 双端守卫
|
||||
*
|
||||
* 报告原判断"dhara map 在回收/擦除路径会用 DHARA_NO_PAGE 试探"经逐点核对
|
||||
* **不成立**:本仓裁剪版没有 DHARA_NO_PAGE,哨兵叫 DHARA_PAGE_NONE
|
||||
@@ -401,7 +401,7 @@ DSTATUS disk_initialize(BYTE pdrv) {
|
||||
|
||||
DBG_INFO("Initializing NAND FTL...");
|
||||
|
||||
/* N-26 启动期地址自检(2026-10-12):本文件三个页缓冲是 SPI1 DMA 路径上
|
||||
/* DMA 可达性三层防线——启动期地址自检(2026-10-12):本文件三个页缓冲是 SPI1 DMA 路径上
|
||||
* 唯一的**静态**缓冲(dhara_map_read/write → dhara_nand_read/prog/copy,
|
||||
* 2048 B > GD5F_DMA_THRESHOLD;s_page_buf 页内虽只攒 132 B 元数据,
|
||||
* 但编程的是**整页**(journal.c:992),传输长度同为 2048 B、同样走 DMA,
|
||||
@@ -410,7 +410,7 @@ DSTATUS disk_initialize(BYTE pdrv) {
|
||||
* **静默不落地**(数据错而传输"成功")。缓冲地址链接期才确定,armcc
|
||||
* 无法编译期断言(见 gd5f2gq5ue.h 的窗口常量注释),故在 init 期自检:
|
||||
* 任一缓冲越出 DMA 白名单窗口 → 上报并拒绝挂载(存储降级、不挂死,
|
||||
* 与 E-31 的"保留卷、上报自检"形态一致)。每次传输的运行期守卫
|
||||
* 与"保留卷、上报自检"形态一致)。每次传输的运行期守卫
|
||||
* (gd5f_read_from_cache/gd5f_program_load 内)保留作第二层兜底。 */
|
||||
if ((gd5f_dma_buf_ok(s_cache_buf, FTL_PAGE_SIZE) == 0) ||
|
||||
(gd5f_dma_buf_ok(s_copy_buf, FTL_PAGE_SIZE) == 0) ||
|
||||
@@ -442,7 +442,7 @@ DSTATUS disk_initialize(BYTE pdrv) {
|
||||
DBG_INFO("No valid map found (dhara=%s), creating fresh", ftl_err_name(err));
|
||||
dhara_map_clear(&s_map);
|
||||
} else {
|
||||
/* E-31(2026-09-21 审计 P0 修复):非"空白"错误(ECC /
|
||||
/* (2026-09-21 审计 P0 修复):非"空白"错误(ECC /
|
||||
* BAD_BLOCK / CORRUPT_MAP / 瞬态 I/O 等)= 真实完整性故障——
|
||||
* 芯片上 journal 可能仍持有有效 checkpoint。旧行为把任何失败
|
||||
* 当新卷清 map,上层随即整卷重格式化 → 单次瞬态毛刺即静默销毁
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-09
|
||||
* 修改记录:
|
||||
* 2026-10-23 王建锋 补模块头与函数注释(S-5 的 return-doc 检查);返回值语义写明"失败类目前
|
||||
* 2026-10-23 王建锋 补模块头与函数注释(return-doc 检查);返回值语义写明"失败类目前
|
||||
* 只进日志、不外传",避免调用方以为能拿到 ECC / 预算耗尽的区别
|
||||
*/
|
||||
|
||||
@@ -27,8 +27,8 @@ extern "C" {
|
||||
* 函数说明:⚠ 这里的 -1 **抹掉了失败类**——"ECC 读不动"、"journal 满"与"映射损坏"对外
|
||||
* 都只是 -1。分类信息目前只在失败点就地进日志(见 nand_ftl.c 的 ftl_class_from_dhara
|
||||
* 与 ../架构设计/STM32F4-Base存储架构说明.md §10.4);要让调用方按类分支,需要跨调用
|
||||
* 存活的失败类快照(报告 S-5 的 C4 待还项,当前无消费者故未做)。
|
||||
* 调用方**不得**把 -1 解读成"卷坏了,可以重格式化"——E-31 的教训正在这条上。
|
||||
* 存活的失败类快照(C4 待还项,当前无消费者故未做)。
|
||||
* 调用方**不得**把 -1 解读成"卷坏了,可以重格式化"——教训正在这条上。
|
||||
*/
|
||||
int nand_ftl_init(void);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
真正兜底 = D10 预算到期 + 对账形态④ 重新登记)
|
||||
- 仅异常路径生效,正常 FIN 路径零改动
|
||||
|
||||
### 错误码(E-03 修复)
|
||||
### 错误码
|
||||
|
||||
- **按任务隔离**:`s_net_errno_slots[8]`(每槽 `{任务句柄, err}`),替代原全局 `s_net_errno`
|
||||
- `net_get_errno()` 只读本任务槽位,**对外语义不变**(成功不清零)
|
||||
|
||||
@@ -64,7 +64,7 @@ static command_t commands[] = {
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* N-19 FTP 鉴权:全局失败计数/锁定状态(跨会话持久——单连接服务器,全局
|
||||
/* FTP 鉴权:全局失败计数/锁定状态(跨会话持久——单连接服务器,全局
|
||||
* 计数即可;成功清零、锁定期满自动放行,见 lftpd_auth.c) */
|
||||
static ftp_auth_state_t s_auth_state;
|
||||
|
||||
@@ -343,7 +343,7 @@ static int cmd_cwd(lftpd_client_t *client, const char *arg)
|
||||
|
||||
static int cmd_dele(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
/* N-19:写命令门——[ftp] write=0 时拒绝(鉴权门已保证此处必为已登录态) */
|
||||
/* 写命令门——[ftp] write=0 时拒绝(鉴权门已保证此处必为已登录态) */
|
||||
const ftp_cfg_t *cfg = ftp_cfg_get();
|
||||
|
||||
if ((cfg == NULL) || (cfg->write_en == 0u)) {
|
||||
@@ -638,7 +638,7 @@ static int cmd_size(lftpd_client_t *client, const char *arg)
|
||||
|
||||
static int cmd_stor(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
/* N-19:写命令门——[ftp] write=0 时拒绝(鉴权门已保证此处必为已登录态) */
|
||||
/* 写命令门——[ftp] write=0 时拒绝(鉴权门已保证此处必为已登录态) */
|
||||
const ftp_cfg_t *cfg = ftp_cfg_get();
|
||||
|
||||
if ((cfg == NULL) || (cfg->write_en == 0u)) {
|
||||
@@ -757,7 +757,7 @@ static int handle_control_channel(lftpd_client_t *client)
|
||||
command_tmp[i] = (char)toupper((int)command_tmp[i]);
|
||||
}
|
||||
|
||||
/* N-19:未鉴权命令门——仅 QUIT/FEAT/SYST/NOOP 放行(客户端兼容需要,
|
||||
/* 未鉴权命令门——仅 QUIT/FEAT/SYST/NOOP 放行(客户端兼容需要,
|
||||
* 且无信息量);其余一律 530。USER/PASS 本身也在放行名单外,
|
||||
* 但它们在 gate 之前不可能通过 → 单独放行见下(USER/PASS 例外) */
|
||||
if (client->auth_logged_in == 0) {
|
||||
@@ -817,7 +817,7 @@ int lftpd_start(const char *directory, int port, lftpd_t *lftpd)
|
||||
lftpd->port = port;
|
||||
lftpd->server_socket = -1; /* memset 清零后显式置"无监听器"(0 是合法 fd) */
|
||||
|
||||
/* N-19:出厂默认口令在用告警(台架锚点;改了 [ftp] pwd_hash 即消失) */
|
||||
/* 出厂默认口令在用告警(台架锚点;改了 [ftp] pwd_hash 即消失) */
|
||||
if (ftp_cfg_is_default_pwd() != 0) {
|
||||
lftpd_log_info("using factory default password");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ typedef struct {
|
||||
char directory[256];
|
||||
int socket;
|
||||
int data_socket;
|
||||
/* N-19 FTP 鉴权会话态(每会话 memset 0 = 未登录;跨会话锁定计数在
|
||||
/* FTP 鉴权会话态(每会话 memset 0 = 未登录;跨会话锁定计数在
|
||||
* lftpd.c 的 s_auth_state,不在此处) */
|
||||
char auth_user[32]; /* USER 阶段暂存的用户名 */
|
||||
uint8_t auth_user_ok; /* 1 = 用户名与 [ftp] user 匹配 */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权;契约由
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权;契约由
|
||||
* test/host/ftp_auth 主机单测钉死)
|
||||
*/
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权;lftpd.c 为唯一消费者)
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权;lftpd.c 为唯一消费者)
|
||||
*/
|
||||
|
||||
#ifndef LFTPD_AUTH_H
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* E-02 头阻塞修复 + recv pending 化(M3 台架实证):
|
||||
* 头阻塞修复 + recv pending 化(M3 台架实证):
|
||||
* netTask 是全部 socket 操作的唯一执行者;lftpd 若用**阻塞** net_recv 等待对端
|
||||
* 下一行/下一块数据 → 只要客户端连上却不发完整行,netTask 就被长期占用,
|
||||
* 其余任务(含远端显示)的网络操作整体冻结(实测一次空闲 FTP 登录让 LCDMEM
|
||||
@@ -109,7 +109,7 @@ int lftpd_inet_accept_timeout(int socket, int timeout_ms)
|
||||
/* F-NET-15 M2(自愈专项实验 2026-10-12):镜像门控退役——原直读 p_sock->state 在
|
||||
* relisten 忙期(CLOSED 瞬态)误判"已关闭",扔掉 ~4.9s 等待预算;armed 门控
|
||||
* 认"监听器在服役(LISTENING)或待重建且 RX 有残留(closed-with-data 救回窗)",
|
||||
* 与 :7003 的 N-32 P1 同型修复 */
|
||||
* 与 :7003 的监听生命周期 P1 同型修复 */
|
||||
if (net_listener_armed(socket) == 0) {
|
||||
/* **故意留 error**:控制连接还活着、我方数据监听却已不在服役 →
|
||||
* 这是本侧异常,不是对端行为 */
|
||||
|
||||
@@ -123,7 +123,7 @@ void handle_connect_event(net_sock_t *p_sock) {
|
||||
p_sock->remote_port = (uint16_t)(remote[4] | (remote[5] << 8));
|
||||
/* 状态 → ESTABLISHED(监听自身转数据通道) */
|
||||
p_sock->state = NET_SOCK_STATE_ESTABLISHED;
|
||||
/* N-32(P1):新会话开始 —— 清"上一条会话已结束"电平。必须在置
|
||||
/* 监听生命周期 P1:新会话开始 —— 清"上一条会话已结束"电平。必须在置
|
||||
* ESTABLISHED 之后立刻做:否则抢先 relisten 后到新连接的窗口内
|
||||
* recv 仍会误得 EOF(把新客户端踢开) */
|
||||
p_sock->session_dead = 0U;
|
||||
@@ -164,7 +164,7 @@ void handle_connect_event(net_sock_t *p_sock) {
|
||||
p_sock->remote_port = (uint16_t)(remote[4] | (remote[5] << 8));
|
||||
/* 状态 → ESTABLISHED */
|
||||
p_sock->state = NET_SOCK_STATE_ESTABLISHED;
|
||||
/* N-32(P1):新会话开始 —— 清"上一条会话已结束"电平(同 LISTENING
|
||||
/* 监听生命周期 P1:新会话开始 —— 清"上一条会话已结束"电平(同 LISTENING
|
||||
* 分支:必须在转 ESTABLISHED 后立刻做,防抢先 relisten 窗口误 EOF) */
|
||||
p_sock->session_dead = 0U;
|
||||
/* F-NET-17 M3 前置:清可疑电平(同 LISTENING 分支) */
|
||||
@@ -201,9 +201,9 @@ void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
|
||||
/* 监听态:DISCONNECT 落在监听器上 */
|
||||
if (p_sock->state == NET_SOCK_STATE_LISTENING) {
|
||||
/* TRAP-NET-17 / N-6:监听态收到 DISCONNECT——bit3=1(0x08)下芯片**不**关该 Socket(停 0x05/0x00,芯片 TCP 侧已 CLOSED);不重开则此后所有 SYN 全被拒 = 监听永久死亡;
|
||||
/* TRAP-NET-17:监听态收到 DISCONNECT——bit3=1(0x08)下芯片**不**关该 Socket(停 0x05/0x00,芯片 TCP 侧已 CLOSED);不重开则此后所有 SYN 全被拒 = 监听永久死亡;
|
||||
* 触发 = 断连后 <~1s 快速复连时 CONNECT 中断未上报(控制块滞留 LISTENING)后该连接断开(2026-09-23 生产固件复现);
|
||||
* N-32 P2:本分支**只回读留痕、不再 relisten** —— 重建统一交
|
||||
* 监听生命周期 P2:本分支**只回读留痕、不再 relisten** —— 重建统一交
|
||||
* net_reconcile_tick 形态③(每 100 ms 读驱动缓存,监听失位即重建)。原处直接
|
||||
* relisten 是 12 个所有者之一,且位于 DISCONNECT 事件路径,可能"抢先"
|
||||
* 于消费者 recv(P1 已消除其后果,P2 消除其可能)。
|
||||
@@ -275,7 +275,7 @@ void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
/* F-NET-15 M1(自愈专项实验 2026-10-12):沉降**无条件**登记。原 recv_bytes!=0 门的
|
||||
* 受害者由事件时序定义(accept 后 ~20ms 被陈旧 DISCONNECT 杀 → recv_bytes==0 是
|
||||
* 构造性必然),门对"残渣循环自持"形态失效(n30_repro 背靠背 9/10 失败,E1 实测);
|
||||
* 门原保护目标(:21 @0ms 快复连)已被 N-32 单一重建入口延迟化(恢复窗实测 0.3~1s)架空,
|
||||
* 门原保护目标(:21 @0ms 快复连)已被监听生命周期单一重建入口延迟化(恢复窗实测 0.3~1s)架空,
|
||||
* +100ms 沉降落在窗内不可见。沉降窗口同时让残渣 DISCONNECT 在 state==CLOSED
|
||||
* (无害、仅留痕)时被消费,断开"杀下一条 → 新拆链 → 再杀"的繁殖链 */
|
||||
{
|
||||
@@ -288,12 +288,12 @@ void handle_disconnect_event(net_sock_t *p_sock) {
|
||||
}
|
||||
/* 正常释放(对端 FIN):断连时挂起项以 EOF(0) 结束 */
|
||||
p_sock->released_abnormal = 0U;
|
||||
/* N-32(P1 不变量 I):置"本会话已结束"电平。须在置 CLOSED **之前**,
|
||||
/* 监听生命周期 P1 不变量 I:置"本会话已结束"电平。须在置 CLOSED **之前**,
|
||||
* 使 session_dead 覆盖"CLOSED 等待 recv 排空"整个窗口——排空期间
|
||||
* recv 的 DONTWAIT 快路径先返数据(recv_len>0 优先),数据尽后判据
|
||||
* 落到本标志 → 仍得 EOF,不因排空慢而漏报 */
|
||||
p_sock->session_dead = 1U;
|
||||
/* N-30(TRAP-NET-20):登记本连接对端端口(幽灵态甄别基准,须在状态
|
||||
/* TRAP-NET-20:登记本连接对端端口(幽灵态甄别基准,须在状态
|
||||
* 迁移前记录——remote_port 此刻仍有效,relisten 清零) */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
/* 状态 → CLOSED */
|
||||
@@ -382,9 +382,9 @@ void handle_timeout_event(net_sock_t *p_sock) {
|
||||
* NET_ERR_CONNRESET 结束(而非 EOF),应用(lftpd STOR)回报 5xx
|
||||
* 而非误报 226(见 net_pending_tick 断连分支) */
|
||||
p_sock->released_abnormal = 1U;
|
||||
/* N-30(TRAP-NET-20):异常释放同样登记对端端口(幽灵甄别基准) */
|
||||
/* TRAP-NET-20:异常释放同样登记对端端口(幽灵甄别基准) */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
/* N-32(P1 不变量 I):异常结束同样是"本会话已结束"电平(TIMEOUT/RST
|
||||
/* 监听生命周期 P1 不变量 I:异常结束同样是"本会话已结束"电平(TIMEOUT/RST
|
||||
* 已在下方 requarantine,本标志只表达"会话结束了"这一事实,与
|
||||
* released_abnormal(结束得好不好看)正交) */
|
||||
p_sock->session_dead = 1U;
|
||||
@@ -564,7 +564,7 @@ void net_reconcile_tick(void) {
|
||||
} else if (p_sock->state == NET_SOCK_STATE_LISTENING) {
|
||||
if (c.tcp_state == CH395F_TCP_ESTABLISHED) {
|
||||
/* ② 本层 LISTENING 而芯片已完成握手(CONNECT 事件未收到)→ 补做 accept
|
||||
* N-30(TRAP-NET-20)幽灵态甄别:拆链收尾窗口内旧连接未拆净,状态如实
|
||||
* TRAP-NET-20幽灵态甄别:拆链收尾窗口内旧连接未拆净,状态如实
|
||||
* 显示 ESTABLISHED(E15:bit3=1 保留现场契约 + TCP 拆链状态机,非谎报),
|
||||
* 回读对端端口 = 上一条连接端口 → 不补 accept(补了会把数据发给
|
||||
* 已死的旧连接 = 生产 LIST#4 数据被吞的直接成因),等拆净或新 SYN。
|
||||
@@ -590,7 +590,7 @@ void net_reconcile_tick(void) {
|
||||
* 被动修正零风险:不去抖、不占冷却(原 pump 同口径) */
|
||||
DBG_INFO("sock%d reconcile: chip listening, resync CLOSED->LISTENING (U11)", i);
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* N-30(TRAP-NET-20):死连接对端端口转登幽灵甄别基准 + 探针计数清零
|
||||
/* TRAP-NET-20:死连接对端端口转登幽灵甄别基准 + 探针计数清零
|
||||
* —— 芯片监听器未被重开,芯片侧对端信息可能仍是上一条连接的,
|
||||
* 必须转登为基准,否则形态② 的幽灵拦截会失效 */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
@@ -629,7 +629,7 @@ void net_reconcile_tick(void) {
|
||||
DBG_INFO("sock%d reconcile: chip closed (0x%02X/0x%02X) -> disconnect",
|
||||
i, c.open, c.tcp_state);
|
||||
/* 复用断开状态机(含陈旧事件甄别 + 状态迁移 + 事件派发);
|
||||
* 只置事实,重建由本 tick 形态③④统一做(N-32 P2 单一所有者) */
|
||||
* 只置事实,重建由本 tick 形态③④统一做(监听生命周期 P2 单一所有者) */
|
||||
handle_disconnect_event(p_sock);
|
||||
} else if (bad == 5) {
|
||||
/* 类型⑤:静默回收 → RESYNC 语义(原 NET_MSG_RESYNC_LISTEN 分派体
|
||||
@@ -640,7 +640,7 @@ void net_reconcile_tick(void) {
|
||||
i, c.open, c.tcp_state);
|
||||
/* 仅修 MCU 侧;新连接的 CONNECT 中断会把两侧重新同步 */
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* N-30(TRAP-NET-20):死连接对端端口转登幽灵甄别基准 + 探针计数清零 */
|
||||
/* TRAP-NET-20:死连接对端端口转登幽灵甄别基准 + 探针计数清零 */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
/* 取消本 fd 在死连接上的挂起 recv(必通知,防调用方挂死) */
|
||||
|
||||
@@ -109,7 +109,7 @@ static void net_drain_chip_rx(int sockfd) {
|
||||
* 3. 复用原本地端口重新 OPEN + LISTEN,仅把 state 置回 LISTENING、不 memset
|
||||
* —— 实现 BSD 风格"监听 Socket 常驻",应用层无需手动重监听。
|
||||
* 4. fd 由参数传入(不用 `p_sock - s_net_socks` 反推),控制块经 net_get_sock()
|
||||
* 取 —— 本模块**不访问** net_socket.c 的 static 数组(N-32 §10)。
|
||||
* 取 —— 本模块**不访问** net_socket.c 的 static 数组(监听生命周期模块化)。
|
||||
*/
|
||||
static int net_relisten_locked(int sockfd) {
|
||||
uint8_t status;
|
||||
@@ -206,12 +206,12 @@ static int net_relisten_locked(int sockfd) {
|
||||
|
||||
/* 状态置回 LISTENING(fd 保留;auto_relisten 保持为 1,下次断连仍自动重监听) */
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* ⚠ 此处**刻意不清 session_dead**(N-32 P1):重建监听只是"芯片侧恢复就位",与
|
||||
/* ⚠ 此处**刻意不清 session_dead**(监听生命周期 P1):重建监听只是"芯片侧恢复就位",与
|
||||
* "应用是否已确认上一条会话结束"是两件事;清了它,抢先 relisten 之后消费者就再也
|
||||
* 得不到 EOF(B10"消费者卡死"原型)。它只由新 CONNECT 成功或 memset 清零。 */
|
||||
/* 新监听不得继承上一条连接的释放原因(TRAP-NET-18 待办 #3) */
|
||||
p_sock->released_abnormal = 0U;
|
||||
/* N-30(TRAP-NET-20):清零前把对端端口转登为幽灵甄别基准;去抖计数随之清零 */
|
||||
/* TRAP-NET-20:清零前把对端端口转登为幽灵甄别基准;去抖计数随之清零 */
|
||||
p_sock->last_peer_port = p_sock->remote_port;
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
/* 以下四项 = 清上一条的对端信息,新连接待用 */
|
||||
@@ -361,7 +361,7 @@ void net_listener_tick(void) {
|
||||
/* 已到放弃时限(0 = 尚未起算,见下) */
|
||||
if ((s_relisten_dead[i] != 0U) && ((int32_t)(now - s_relisten_dead[i]) >= 0)) {
|
||||
net_listener_clear(i);
|
||||
/* 预算耗尽 ≠ 放弃:交 reconciler 形态③④ 周期接管(N-32 单一重建入口),
|
||||
/* 预算耗尽 ≠ 放弃:交 reconciler 形态③④ 周期接管(监听生命周期单一重建入口),
|
||||
* 监听不会永久失效——旧文案 "give up" 夸大了失效语义 */
|
||||
DBG_ERROR("sock%d relisten retry budget exhausted, reconciler takes over", i);
|
||||
continue;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-12
|
||||
* 修改记录:
|
||||
* 2026-10-12 王建锋 从 net_socket.c 抽出监听生命周期(N-32 §10 模块化)
|
||||
* 2026-10-12 王建锋 从 net_socket.c 抽出监听生命周期(模块化,详述 docs/网络/网络自愈架构设计.md §9.3)
|
||||
* 2026-10-14 王建锋 单一所有者收口:net_listener_try(各调用点自带门、当场动作)
|
||||
* 改为 net_listener_request(只登记意图)+ net_listener_tick
|
||||
* (唯一执行者,门集与芯片在听判据集中一处)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* 限定条件:本文件真机专属——模拟器走 sim/net_socket_sim.c,
|
||||
* simulator/CMakeLists.txt 不编译本文件(同 net_listener.c 先例)
|
||||
*
|
||||
* 阻塞 recv / send 挂起模型(E-02 结构演进):阻塞等待 = 挂起请求 + 主循环节拍完成
|
||||
* 阻塞 recv / send 挂起模型(结构演进):阻塞等待 = 挂起请求 + 主循环节拍完成
|
||||
* "无数据 / 发送缓冲未空 + 阻塞等待"不再自旋占用 netTask:登记 pending 后立即返回
|
||||
* net_pending_tick / net_send_pending_tick(与 D10 重监听重试同节奏,
|
||||
* net_process_messages 开头每轮调用)
|
||||
@@ -73,7 +73,7 @@ void net_pending_capture_owner(void) {
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文;sockfd 合法且挂起在位(否则静默返回,防重复通知)
|
||||
* 函数说明:通知值 = 结果本身(与消息分派同一回传通道,调用方直接按通知值
|
||||
* 取结果);失败记入 netTask 自己的 errno 槽位(E-03)
|
||||
* 取结果);失败记入 netTask 自己的 errno 槽位
|
||||
*/
|
||||
static void net_pending_complete(int sockfd, int result) {
|
||||
/* 本 fd 挂起请求指针 */
|
||||
@@ -89,7 +89,7 @@ static void net_pending_complete(int sockfd, int result) {
|
||||
if (p->active == 0U) {
|
||||
return;
|
||||
}
|
||||
/* 失败结果:记入 netTask 自己的错误码槽位(E-03;不再写入跨任务共享全局) */
|
||||
/* 失败结果:记入 netTask 自己的错误码槽位(不再写入跨任务共享全局) */
|
||||
if (result < 0) {
|
||||
(void)net_set_errno(result);
|
||||
}
|
||||
@@ -233,7 +233,7 @@ void net_pending_tick(net_sock_t *p_socks) {
|
||||
}
|
||||
|
||||
/* ③ 断连:排空 → 数据或 EOF(0) + 自动重监听。
|
||||
* 判据同 net_recv_locked(N-32 P1 不变量 I):`state == CLOSED ||
|
||||
* 判据同 net_recv_locked(监听生命周期 P1 不变量 I):`state == CLOSED ||
|
||||
* session_dead`。只判 CLOSED 时,抢先 relisten 后 ①(要求 ESTABLISHED)
|
||||
* 与 ③(要求 CLOSED) 两头落空 → 挂起的 recv 只能干等 ② deadline 超时
|
||||
* (lftpd 阻塞 recv 的"消费者卡死"原型)。无限等待登记没有 ②,
|
||||
@@ -259,7 +259,7 @@ void net_pending_tick(net_sock_t *p_socks) {
|
||||
* 原内层 `state == CLOSED` 门已**上收到 tick**——它防的是"对已回
|
||||
* LISTENING 的 fd 再 open+listen → 因 USE 错误走进关闭分支、关掉
|
||||
* 正听着的 Socket"(U12 同源),属执行侧安全不变量,不由登记点复述。
|
||||
* N-32 P2 修正:**保留本处即时登记** —— 它只在有**挂起 recv**
|
||||
* 监听生命周期 P2 修正:**保留本处即时登记** —— 它只在有**挂起 recv**
|
||||
* 时到达(`p->active != 0`),属"消费者在等这条连接结局"的
|
||||
* 消费者驱动路径,不是周期性抢先(同 net_recv_locked 的理由)。 */
|
||||
if (p_sock->auto_relisten != 0U) {
|
||||
@@ -312,7 +312,7 @@ void net_pending_tick(net_sock_t *p_socks) {
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 有挂起;0 - 无(含描述符越界) int
|
||||
* 限定条件:任意上下文(纯 RAM 读,不发起 RPC)
|
||||
* 函数说明:1. N-32 §10 模块化:net_listener_tick 用它跳过"重开 Socket
|
||||
* 函数说明:1. 监听生命周期模块化:net_listener_tick 用它跳过"重开 Socket
|
||||
* 会摧毁在途连接"的 fd(挂起 recv 保险,原为直接读 s_net_pending[i].active)
|
||||
* 2. 声明在 net_pending.h(配对头,规范 S17 2.2)
|
||||
*/
|
||||
|
||||
@@ -123,7 +123,7 @@ void net_pending_settle_close(int sockfd, net_sock_t *p_sock);
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 有挂起;0 - 无(含描述符越界) int
|
||||
* 限定条件:任意上下文(纯 RAM 读,不发起 RPC)
|
||||
* 函数说明:N-32 §10 模块化:net_listener_tick 用它跳过"重开 Socket
|
||||
* 函数说明:监听生命周期模块化:net_listener_tick 用它跳过"重开 Socket
|
||||
* 会摧毁在途连接"的 fd(挂起 recv 保险)
|
||||
*/
|
||||
int net_pending_active(int sockfd);
|
||||
|
||||
@@ -255,7 +255,7 @@ int net_poll_events(net_pollfd_t *fds, int nfds, int timeout) {
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 可读(接收缓冲非空);0 - 不可读或描述符无效 int
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:1. P2-7(2026-10-04):LISTENING 态**不再**恒判可读。本网络层无
|
||||
* 函数说明:1.(2026-10-04):LISTENING 态**不再**恒判可读。本网络层无
|
||||
* "待 accept 的挂起连接队列"——新连接到达时 CONNECT 事件把该 socket
|
||||
* 自身直接转为 ESTABLISHED(handle_connect_event:"监听自身转数据通道"),
|
||||
* 故监听态恒无数据可读。原实现对 LISTENING 恒返回 1,会使
|
||||
@@ -272,7 +272,7 @@ static int check_socket_readable(int sockfd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 监听态恒不可读(见上方 P2-7 说明) */
|
||||
/* 监听态恒不可读(见上方说明) */
|
||||
if (NET_SOCK_STATE_LISTENING == p_sock->state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ int net_msg_queue_adopt(osMessageQueueId_t queue_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================= 错误码存储(E-03 修复:按任务隔离,消除跨任务覆写竞态) =================
|
||||
/* ================= 错误码存储(按任务隔离,消除跨任务覆写竞态) =================
|
||||
*
|
||||
* 旧实现是单个全局 `static int s_net_errno`:netTask 在处理消息时写入本层的
|
||||
* 「最后错误码」,调用方包装器随后在**自己的上下文**再写一次。两条写路径分属不同
|
||||
@@ -227,7 +227,7 @@ static uint32_t net_errno_slot(void) {
|
||||
* 返回值:err 原值(便于写成 `return net_set_errno(NET_ERR_XXX);`)
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 只写本任务的槽位,其他任务读不到、也改不了 → 消除跨任务覆写竞态
|
||||
* 2. N-32 §10 模块化:由 static 提升为**模块间可见**(net_listener.c
|
||||
* 2. 监听生命周期模块化:由 static 提升为**模块间可见**(net_listener.c
|
||||
* 也需要设 errno),声明与 NET_ERR_RETURN_1 宏见 net_socket.h
|
||||
*/
|
||||
int net_set_errno(int err) {
|
||||
@@ -332,7 +332,7 @@ static bool s_mac_is_zero(const uint8_t *p_mac) {
|
||||
* 无关),且违反保留位规定 → 0x09 改 0x08(2026-10-11 定案:官方手册
|
||||
* V1C/V1E/V2.2/V2.3 的 bit0 全是保留位,官方例程 bit0=0 下 Socket 0~7 照常
|
||||
* 可用、FTP 例程本身就传 0x08;证据链见 TRAP-NET-15,
|
||||
* 0x08 下的多 Socket 台架复测登记 N-31) */
|
||||
* 0x08 下的多 Socket 台架复测登记为 Phase 7 复跑项) */
|
||||
ch395f_set_fun_para(fun_para);
|
||||
|
||||
/* 设置 TCP MSS:维持 1024(512 裕量方案未采纳,当年支撑它的台架判据不可信,
|
||||
@@ -358,7 +358,7 @@ static bool s_mac_is_zero(const uint8_t *p_mac) {
|
||||
/* 设置 MAC(手册 §9.2.1 步骤①:SET_MAC 必须在 INIT_CH395 之前——SET_MAC 写芯片
|
||||
* EEPROM,而 INIT_CH395 会初始化/加载 MAC;顺序反了则本次上电芯片仍用旧 MAC,
|
||||
* UI 配置的 MAC 不生效(回落芯片内置/上次值),设备在线身份与 UI 显示不符。
|
||||
* P1-7,2026-10-04);NULL 或全零 = 保持芯片内置 MAC,不下发;
|
||||
* 2026-10-04);NULL 或全零 = 保持芯片内置 MAC,不下发;
|
||||
* 写 EEPROM,每上电重放同值幂等 */
|
||||
if (mac != NULL && s_mac_is_zero(mac) == false) {
|
||||
ch395f_set_mac_addr((uint8_t *)mac);
|
||||
@@ -572,7 +572,7 @@ int net_poll(void) {
|
||||
for (int i = 0; i < NET_MAX_SOCKETS; i++) {
|
||||
if (s_net_socks[i].in_use != 0U) {
|
||||
(void)ch395f_get_sock_state((uint8_t)i);
|
||||
/* E-02 收口:驱动 sock_reset 状态机在 net_poll 步进
|
||||
/* 驱动 sock_reset 状态机在 net_poll 步进
|
||||
* (驱动头注释契约"在 net_poll 或测试循环里周期调用")。IDLE 槽位
|
||||
* 纯 RAM 判返回 0 无 SPI;仅活跃复位机做短 SPI。作用:同步包装
|
||||
* (net_sock_reset_sync)600ms 放弃后复位机不搁浅,芯片条件满足
|
||||
@@ -663,7 +663,7 @@ void net_process_messages(void) {
|
||||
net_send_pending_tick(s_net_socks);
|
||||
/* TRAP-NET-17 状态对账 + 监听重建合一(单一 100ms tick):
|
||||
* 判据读驱动缓存(net_poll 每轮全量刷新),①②⑤ 失同步 + ③④ 重建 +
|
||||
* U11 重同步全部在此(本层唯一周期重建入口,N-32 P2 单一所有者) */
|
||||
* U11 重同步全部在此(本层唯一周期重建入口,监听生命周期 P2 单一所有者) */
|
||||
net_reconcile_tick();
|
||||
|
||||
/* 队列排空:超时 0 非阻塞取,每条处理一轮,直到本轮队列为空 */
|
||||
@@ -758,8 +758,8 @@ void net_process_messages(void) {
|
||||
break;
|
||||
}
|
||||
case NET_MSG_DROP_CONN: {
|
||||
/* E-02 收口:应用放弃会话的主动拆除(拆连接、保监听)。监听型
|
||||
* 登记即返(N-17:拆链排渣交 net_listener_tick,本消息不再占用
|
||||
/* 应用放弃会话的主动拆除(拆连接、保监听)。监听型
|
||||
* 登记即返(探针窗口心跳缺口修复:拆链排渣交 net_listener_tick,本消息不再占用
|
||||
* netTask 数百 ms) */
|
||||
net_sock_t *p_sock = net_get_sock(msg.sockfd);
|
||||
if (p_sock == NULL) {
|
||||
@@ -813,7 +813,7 @@ void net_process_messages(void) {
|
||||
uint32_t notify_val = (uint32_t)msg.result;
|
||||
|
||||
/* 失败:回传本条消息**自身**的精确错误码(NET_ERR_WOULDBLOCK 等)。
|
||||
* E-03 后取的是 netTask 自己的错误码槽位——锁定实现(*_locked)在
|
||||
* 取的是 netTask 自己的错误码槽位——锁定实现(*_locked)在
|
||||
* 失败返回前记录的码,不受其他任务影响;未置位时回退通用 NET_ERR。 */
|
||||
if (msg.result < 0) {
|
||||
int te = s_net_errno_slots[net_errno_slot()].err;
|
||||
@@ -909,7 +909,7 @@ int net_socket(int domain, int type) {
|
||||
p_sock->blocking = 1;
|
||||
/* 新 fd 不得继承上一条连接的释放原因(TRAP-NET-18 待办 #3) */
|
||||
p_sock->released_abnormal = 0;
|
||||
/* N-32(P1):新 fd 不得继承"上一条会话已结束"电平(否则新建即 EOF,
|
||||
/* 监听生命周期 P1:新 fd 不得继承"上一条会话已结束"电平(否则新建即 EOF,
|
||||
* 消费侧连数据都收不到)——与 released_abnormal 同理,显式清零 */
|
||||
p_sock->session_dead = 0;
|
||||
/* 新 fd 不得继承上一次的待重试状态(D10) */
|
||||
@@ -1165,7 +1165,7 @@ int net_listen_locked(int sockfd) {
|
||||
p_sock->state = NET_SOCK_STATE_LISTENING;
|
||||
/* 单连接模式:连接断开后由 net 层自动回到 LISTEN(模拟 BSD 监听常驻) */
|
||||
p_sock->auto_relisten = 1;
|
||||
/* N-30(TRAP-NET-20):新监听会话,事件未收到去抖计数清零 */
|
||||
/* TRAP-NET-20:新监听会话,事件未收到去抖计数清零 */
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
|
||||
/* 监听建立完成 */
|
||||
@@ -1232,7 +1232,7 @@ int net_listen(int sockfd) {
|
||||
* 限定条件:Socket 处于 LISTENING 状态
|
||||
* 函数说明:单连接:监听 Socket 自身在 CONNECT 后已转为 ESTABLISHED
|
||||
*/
|
||||
/* ===== N-30 accept 路径"CONNECT 事件未收到补 accept"(2026-10-11 台架实证;
|
||||
/* ===== TRAP-NET-20 accept 路径"CONNECT 事件未收到补 accept"(2026-10-11 台架实证;
|
||||
* E14 复核 2026-10-14:事件非芯片侧丢失,是被读清消费——机制不变)=====
|
||||
* 台架结论(剥离 OS/net/FTP 直接对芯片做受控 close→open→listen→connect 矩阵):
|
||||
* 芯片关闭连接后的拆链收尾窗口**变长且不可见**(状态寄存器显示 CLOSED 也不代表
|
||||
@@ -1274,7 +1274,7 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen
|
||||
/* 先轮询一次:把状态机刷新到芯片侧最新状态 */
|
||||
net_poll();
|
||||
|
||||
/* N-30(TRAP-NET-20):监听等待期兼做芯片状态第二信源——CONNECT 事件未收到时
|
||||
/* TRAP-NET-20:监听等待期兼做芯片状态第二信源——CONNECT 事件未收到时
|
||||
* 去抖补 accept(台架实证:拆链窗口事件未收到是数据被吞/425 的主根因,
|
||||
* E14 复核:事件非芯片丢失、被读清消费吞掉;纯事件等待无自愈可能;
|
||||
* 详见 net_lost_connect_probe 区块注释) */
|
||||
@@ -1286,13 +1286,13 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen
|
||||
* (含上一步探针补 accept 的命中);其余失同步由 net_reconcile_tick()
|
||||
* 周期对账(带去抖)兜底 */
|
||||
if (p_listen_sock->state != NET_SOCK_STATE_ESTABLISHED) {
|
||||
/* N-30(2026-10-11 手册 §5.23⑤ + 生产时间线定案):
|
||||
/* TRAP-NET-20(2026-10-11 手册 §5.23⑤ + 生产时间线定案):
|
||||
* "closed-but-readable" 接受——STOR 类传输客户端**发完数据立即关数据连接**
|
||||
* (正常 FTP 行为,Python ftplib sendall 后即 close),芯片按
|
||||
* FUN_PARA **bit3 SOCKET_CLOSE=1**(`0x08`,§5.52 ②)规格保留 Socket 与 RX 缓冲供 MCU 排空
|
||||
* (实测 DISCONNECT 后 st=0x05/0x00、recv_len=1027=完整载荷)。
|
||||
* 若 accept 轮询晚于对端 FIN,盲目 relisten(close+open 清缓冲)会把
|
||||
* 已到达的数据销毁 → 425(此前 N-30"425/被吞"的主根因,并非芯片杀连接)。
|
||||
* 已到达的数据销毁 → 425(此前 TRAP-NET-20"425/被吞"的主根因,并非芯片杀连接)。
|
||||
* 处置:芯片 RX 缓冲有数据 → 接受这条"已关但可读"连接,应用经
|
||||
* net_recv_locked 快路径(无状态门槛)排空,排空后 EOF 自然触发 relisten。 */
|
||||
int drain_pending = 0;
|
||||
@@ -1303,7 +1303,7 @@ static int net_accept_locked(int sockfd, struct net_sockaddr *addr, int *addrlen
|
||||
} else {
|
||||
/* 连接已断开(CLOSED)且无残留数据:登记重建请求,下一次 accept
|
||||
* 即可等待新连接(BSD 风格监听常驻)。
|
||||
* N-32 P2 修正:**保留本处即时登记** —— 它由消费者调 accept 触发
|
||||
* 监听生命周期 P2 修正:**保留本处即时登记** —— 它由消费者调 accept 触发
|
||||
* (消费者正在等新连接),不是周期性抢先;删掉会把 CLOSED 窗口
|
||||
* 拉长、导致调用方高频空转 accept(实测 [WD] task stall)。
|
||||
* 单一所有者收口后:动作发生在下一轮 net_listener_tick(≤1ms),非阻塞 */
|
||||
@@ -1446,7 +1446,7 @@ int net_listener_armed(int sockfd) {
|
||||
return 1;
|
||||
}
|
||||
/* 断连待重建态:**仅当芯片 RX 可能仍有残留数据**时放行,让
|
||||
* net_accept_locked 的 "closed-but-readable" 接受(N-30,对端"发完即 FIN")
|
||||
* net_accept_locked 的 "closed-but-readable" 接受(TRAP-NET-20,对端"发完即 FIN")
|
||||
* 仍能拿到该连接。
|
||||
* ⚠ 无残留时必须返回 0 —— 否则调用方会在整个 CLOSED 窗口内每轮都发起
|
||||
* net_accept(RPC:入队 + xTaskNotifyWait 回执**无上界**)→ 队列积压 →
|
||||
@@ -1694,11 +1694,11 @@ static int net_connect_locked(int sockfd, const struct net_sockaddr *addr, int a
|
||||
* 交给 CONNECT/TIM_OUT 裁定,不得当成失败(否则慢芯片会被误杀)。
|
||||
* 其余非 SUCCESS 均为明确的命令级失败(VAL/ARG/USE/OPEN/IF/MEM/BUF/RTE…):
|
||||
* 此前返回值被直接丢弃 → 本层停在 CONNECTING 干等 recv 30 s 超时才暴露
|
||||
* (2026-10-12 审计 B2)。按 §9.2.2 ⑧"再次连接需从 ⑤ 重开"回滚。 */
|
||||
* (2026-10-12 审计)。按 §9.2.2 ⑧"再次连接需从 ⑤ 重开"回滚。 */
|
||||
if ((status != CH395F_ERR_SUCCESS) && (status != CH395F_ERR_BUSY)) {
|
||||
DBG_ERROR("[CONN] sock%d TCP_CONNECT rejected by chip: 0x%02X", sockfd, status);
|
||||
(void)ch395f_close_socket((uint8_t)sockfd);
|
||||
/* N-32(P1 不变量 I):命令级失败同样是"本会话已结束"——此前这里只置
|
||||
/* 监听生命周期 P1 不变量 I:命令级失败同样是"本会话已结束"——此前这里只置
|
||||
* CLOSED,而 CONNECTING 态从未置 session_dead,挂起的 recv 只能干等
|
||||
* deadline(B2 审计发现的"停在 CONNECTING 干等 30s 超时才暴露"同源) */
|
||||
p_sock->session_dead = 1U;
|
||||
@@ -1891,7 +1891,7 @@ static int net_send_locked(net_sock_t *p_sock, const void *buf, int len,
|
||||
* 函数说明:1. 数据已在(快速路径)→ 立即读出返回
|
||||
* 2. 非阻塞(NET_MSG_DONTWAIT 单次覆盖 **或** socket blocking 属性为 0,
|
||||
* 模式属性)无数据立即返回 -1(NET_ERR_WOULDBLOCK)
|
||||
* 3. 无数据 + 阻塞等待 → 登记挂起请求(pending 化,E-02 结构演进)
|
||||
* 3. 无数据 + 阻塞等待 → 登记挂起请求(pending 化,结构演进)
|
||||
* 4. netTask 零占用立即返回;完成条件(数据/到点[仅显式预算]/断连)由 net_pending_tick 按主循环节拍检查
|
||||
* 5. 并通知 caller;关闭/网络重建/陈旧重同步由对应路径显式结束(取消必通知)
|
||||
* 6. CLOSED 状态语义(排空 + EOF + 自动重监听)逐项保留在 tick 内
|
||||
@@ -1936,7 +1936,7 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
/* 非阻塞且当前无数据:若连接已断开则视为 EOF(0) 并自动重监听,
|
||||
* 否则返回 WOULDBLOCK(-1) 表示暂时无数据 */
|
||||
/* 连接已断开(CLOSED)**或**本会话已结束(session_dead):按 EOF 处理。
|
||||
* 判据从纯 `state == CLOSED` 放宽为 `|| session_dead`(N-32 P1 不变量 I):
|
||||
* 判据从纯 `state == CLOSED` 放宽为 `|| session_dead`(监听生命周期 P1 不变量 I):
|
||||
* 抢先 relisten 后 state 已是 LISTENING 而会话确实已结束,旧判据在此
|
||||
* 恒返 WOULDBLOCK → EOF 永久失踪(新连接被旧会话静默吸收)。
|
||||
* 有数据时走上方快路径(recv_len>0 优先),本判据只在"无数据"时生效,
|
||||
@@ -1948,7 +1948,7 @@ static int net_recv_locked(net_sock_t *p_sock, void *buf, int len, int flags,
|
||||
* ch395f_close_socket → 把正听着新 SYN 的 Socket 当场关掉"(U12 同源
|
||||
* 事故)。这是**执行侧安全不变量**,不该由每个登记点各自复述——
|
||||
* 漏一处即事故;收口后 tick 另加"芯片已在听即了结",结构上不可能再犯。
|
||||
* N-32 P2 修正(2026-10-12 台架实证):**保留本处即时登记**——
|
||||
* 监听生命周期 P2 修正(2026-10-12 台架实证):**保留本处即时登记**——
|
||||
* 它是"消费者动作触发"(消费者正在等这条连接的结局),不是周期性
|
||||
* "抢先";删掉它会把 CLOSED 窗口从 ~0 拉长到 reconciler 周期+隔离期
|
||||
* (~100ms),期间调用方高频发起 net_accept(RPC 无上界)→
|
||||
@@ -2020,7 +2020,7 @@ static int net_close_locked(net_sock_t *p_sock) {
|
||||
}
|
||||
|
||||
/* 清零控制块(memset 0 = 释放本 fd 全部状态) */
|
||||
/* N-30(TRAP-NET-20):幽灵甄别基准跨 fd 释放保留(幽灵属芯片 Socket 级状态,
|
||||
/* TRAP-NET-20:幽灵甄别基准跨 fd 释放保留(幽灵属芯片 Socket 级状态,
|
||||
* 与 fd 复用无关——close 后新 listen 落在同一芯片 Socket 上仍可能撞幽灵) */
|
||||
uint16_t keep_peer = p_sock->remote_port;
|
||||
memset(p_sock, 0, sizeof(net_sock_t));
|
||||
@@ -2042,11 +2042,11 @@ static int net_close_locked(net_sock_t *p_sock) {
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 拆除完成(监听型 fd 已重监听或交 D10) int
|
||||
* 限定条件:netTask 上下文;p_sock 在用且为 TCP 流
|
||||
* 函数说明:1. E-02 收口(《网络自愈架构设计》§5.2 E13):应用放弃的会话若 TCP 仍活
|
||||
* 函数说明:1.(《网络自愈架构设计》§5.2 E13):应用放弃的会话若 TCP 仍活
|
||||
* (PC 内核自动 ACK keepalive,僵尸判不死),单连接监听被永久
|
||||
* 占住;应用又不得 net_close 监听 fd(毁监听)——本函数补上
|
||||
* "拆连接不拆监听"的应用侧形态
|
||||
* 2. 芯片侧拆除(TRAP-NET-18 异常释放语义,N-17 收口改异步):监听型 fd
|
||||
* 2. 芯片侧拆除(TRAP-NET-18 异常释放语义,探针窗口心跳缺口收口改异步):监听型 fd
|
||||
* = net_listener_settle(QUARANTINE) + REQ_DROP 登记即返,动作全部交
|
||||
* net_listener_tick(隔离窗内持续排空/关闭正是该机的既有语义);
|
||||
* 非监听 client fd = 同步 sock_reset QUARANTINE(600ms 封顶,原形态)
|
||||
@@ -2064,7 +2064,7 @@ static int net_drop_conn_locked(net_sock_t *p_sock) {
|
||||
* 在本函数体内**没有任何机器表达**,而函数体是无条件的:置 CLOSED、清
|
||||
* readable、给监听型 fd 登记重建。也就是说——将来任何新增路径绕过
|
||||
* NET_MSG_DROP_CONN 的分派门直接调它,就会把一个**健康监听器**当会话拆掉
|
||||
* (症状是端口偶发不再 accept,与 N-30 一族同源难查)。断言把这条"进来时
|
||||
* (症状是端口偶发不再 accept,与 TRAP-NET-20 一族同源难查)。断言把这条"进来时
|
||||
* 必须处于活动会话态"钉成机器可查的不变量,正是"不该发生"而不是"外部输入"。
|
||||
* ⚠ 分派处(case NET_MSG_DROP_CONN)的三条错误返回原样保留——它们负责真机
|
||||
* 上把误用变成 -1,本断言负责在测试构建里把误用**当场喊出来**。
|
||||
@@ -2091,7 +2091,7 @@ static int net_drop_conn_locked(net_sock_t *p_sock) {
|
||||
* FIN 5/5 的通过版本)。旧形态在本处理器里 net_sock_reset_sync 同步等待,
|
||||
* netTask 被占用 400ms 底/600ms 顶:串行总线整窗停摆、UI/FTP/对时的 API 回
|
||||
* 执顺延、NET 与对时位同窗丢 100ms 抽样 → 800ms 停喂声明 stall,距 IWDG
|
||||
* 最坏超时余量仅 ~0.7s(登记簿 N-17;方向评估与否决论证 → 《网络自愈架构
|
||||
* 最坏超时余量仅 ~0.7s(探针窗口心跳缺口;方向评估与否决论证 → 《网络自愈架构
|
||||
* 设计》§4.6)。返回语义随之明确:返回 = 已登记,芯片未必已拆完。
|
||||
* 非监听 client fd:无监听可保,维持同步确定性排渣(600ms 封顶;全仓
|
||||
* 零生产调用者,形态原样保留防漂移)。 */
|
||||
@@ -2105,7 +2105,7 @@ static int net_drop_conn_locked(net_sock_t *p_sock) {
|
||||
/* 会话簿记收口(保留 fd:不 memset、不动 local_port/auto_relisten) */
|
||||
p_sock->state = NET_SOCK_STATE_CLOSED;
|
||||
p_sock->readable = 0U;
|
||||
/* N-30(TRAP-NET-20):新会话起点,事件未收到去抖计数清零 */
|
||||
/* TRAP-NET-20:新会话起点,事件未收到去抖计数清零 */
|
||||
p_sock->lost_conn_seen = 0U;
|
||||
|
||||
if (p_sock->auto_relisten) {
|
||||
@@ -2293,7 +2293,7 @@ int net_recv_nb(int sockfd, void *buf, int len) {
|
||||
* 出口参数:无
|
||||
* 返回值:>0 字节数;0 = 对端关闭(EOF);-1 = 真错误(errno 可查);**-2 = 超时无数据**
|
||||
* 限定条件:非 netTask 上下文;net 层已初始化
|
||||
* 函数说明:1. E-02 修复配套接口:lftpd 等"等对端下一行/下一块数据"的调用方
|
||||
* 函数说明:1. 修复配套接口:lftpd 等"等对端下一行/下一块数据"的调用方
|
||||
* 若无限阻塞 recv,会把 netTask(全部 socket 操作的唯一执行者)长期占住
|
||||
* 2. 导致其余任务的网络操作整体冻结(M3 实测一次空闲 FTP 登录冻结远端显示 17.7s)
|
||||
* 3. 用本接口把等待切成 ≤timeout_ms 的片,调用方自行累计总预算:总超时语义不变、单次占用有界
|
||||
@@ -2335,7 +2335,7 @@ int net_recv_to(int sockfd, void *buf, int len, uint32_t timeout_ms) {
|
||||
msg.len = len;
|
||||
/* 阻塞式:netTask 侧轮询至"有数据"或"本片超时"(勿置 NET_MSG_DONTWAIT) */
|
||||
msg.flags = 0;
|
||||
/* 本片预算:netTask 最多等待 timeout_ms,保证对 netTask 的占用有界(E-02) */
|
||||
/* 本片预算:netTask 最多等待 timeout_ms,保证对 netTask 的占用有界 */
|
||||
msg.timeout_ms = timeout_ms;
|
||||
/* 记录调用任务句柄,netTask 处理后经 xTaskNotify() 精准回执 */
|
||||
msg.caller = xTaskGetCurrentTaskHandle();
|
||||
@@ -2440,9 +2440,9 @@ int net_close(int sockfd) {
|
||||
* (net 层 auto_relisten 已接管,见 AGENTS 单连接断开语义)
|
||||
* 函数说明:1. 单连接模型下挂起但 TCP 仍活的客户端会永久占住监听(keepalive
|
||||
* 被 PC 内核自动 ACK 判不死,《网络自愈架构设计》§5.2 E13);应用不得
|
||||
* net_close 监听 fd → 本 API 拆连接不拆监听(E-02 收口)
|
||||
* net_close 监听 fd → 本 API 拆连接不拆监听
|
||||
* 2. 监听型 fd:netTask 侧仅登记隔离沉降 + 重建请求即返回(动作在
|
||||
* net_listener_tick,netTask 零占用,登记簿 N-17 收口);非监听 client
|
||||
* net_listener_tick,netTask 零占用,探针窗口心跳缺口收口);非监听 client
|
||||
* fd 仍同步排渣(600ms 封顶)。见 net_drop_conn_locked
|
||||
*/
|
||||
int net_drop_conn(int sockfd) {
|
||||
@@ -2886,7 +2886,7 @@ int net_setblocking(int sockfd, int on) {
|
||||
* 出口参数:无
|
||||
* 返回值:错误码(NET_OK=0, NET_ERR_xxx=-N);本任务无失败记录时为 NET_OK
|
||||
* 限定条件:无(任何上下文可调用;中断上下文读 [0] 槽)
|
||||
* 函数说明:1. E-03 修复:错误码**按任务隔离**——只读本任务自己的槽位,
|
||||
* 函数说明:1. 错误码**按任务隔离**——只读本任务自己的槽位,
|
||||
* 其他任务的 net 调用再也覆盖不了它(旧实现为单一全局量,跨任务竞态)
|
||||
* 2. 调用成功时**不清零**(保持历史语义,向后兼容既有调用方)
|
||||
* 3. 超时场景(net_recv_to)请直接用返回码 -2 判定,不要依赖本接口
|
||||
|
||||
@@ -38,7 +38,7 @@ typedef enum {
|
||||
NET_MSG_CONNECT,
|
||||
NET_MSG_LISTEN,
|
||||
NET_MSG_ACCEPT,
|
||||
NET_MSG_DROP_CONN, /* E-02 收口:主动拆会话、保监听(见 net_drop_conn) */
|
||||
NET_MSG_DROP_CONN, /* 主动拆会话、保监听(见 net_drop_conn) */
|
||||
/* F-NET-2/3:NET_MSG_RESYNC_LISTEN / NET_MSG_RELISTEN 已删——
|
||||
* 静默回收由对账⑤自动认领(net_reconcile_tick),重建由 reconciler
|
||||
* 形态③④/D10 单一
|
||||
@@ -186,7 +186,7 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||
* 函数说明:1. 不变量 II(单一重建所有者;补齐 ESTABLISHED 放行):取代已退役的
|
||||
* `net_can_accept()`(只认 ESTABLISHED)作为监听 accept 门控——原门控使
|
||||
* :7003 永不进 `net_accept()`,
|
||||
* `net_lost_connect_probe`(CONNECT 事件未收到补 accept,N-30 实证的
|
||||
* `net_lost_connect_probe`(CONNECT 事件未收到补 accept,TRAP-NET-20 实证的
|
||||
* "数据被吞/425"主根因修复)对该端口等于从未运行
|
||||
* 2. 放行 LISTENING:探针与 "closed-but-readable 接受" 都发生在
|
||||
* `net_accept_locked` **内部**,门控挡在外面就永远到不了
|
||||
@@ -196,7 +196,7 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||
* (i104 200ms 抢不过对账② ~400ms 去抖)永不 accept、会话整场饿死
|
||||
* (实测全 18 项 FAIL)。此态 accept 必返 fd,无风暴风险
|
||||
* 3. 放行 CLOSED+auto_relisten **且 readable≠0**(有残留数据):
|
||||
* 保住 N-30 的 "closed-but-readable" 接受;**无残留时必须返回 0**,
|
||||
* 保住 TRAP-NET-20 的 "closed-but-readable" 接受;**无残留时必须返回 0**,
|
||||
* 否则调用方会在整个 CLOSED 窗口内每轮发起 `net_accept`(RPC 无上界)
|
||||
* → 队列积压 → 喂不上狗(2026-10-12 P2a 台架实测 48 条 task stall)。
|
||||
* 重建由 `net_reconcile_tick` 形态③④ 负责,调用方无需催促
|
||||
@@ -251,7 +251,7 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen);
|
||||
* 拷贝数据后触发发送——健康链路下(实测 219us 量级)行为与旧版逐字节一致
|
||||
* 2. 慢路径:自旋超预算后把请求**挂起登记**给 netTask,由
|
||||
* net_send_pending_tick 在发送缓冲区空闲时续传(分片 NET_SEND_CHUNK_MAX),
|
||||
* 调用方在任务通知上睡眠——**不再占用 netTask 执行者**(消除 E-02 队头阻塞)
|
||||
* 调用方在任务通知上睡眠——**不再占用 netTask 执行者**(消除队头阻塞)
|
||||
* 3. 短写语义:无论快路径超时还是挂起完成,只要存在未写完字节即按已发数返回,
|
||||
* 不假装全部成功;调用方按短写重试剩余部分(见 BSD_Socket_API 使用指南 §3.5)
|
||||
* 4. ⚠ 禁止在 IWDG required_mask 内任务做**无预算**的整块发送;挂起路径本身
|
||||
@@ -319,7 +319,7 @@ int net_setblocking(int sockfd, int on);
|
||||
* 函数说明:1. **显式预算**便捷入口(等价 Python settimeout,
|
||||
* 不再有隐藏默认值):net_recv 的无限阻塞 + 本接口的显式预算构成
|
||||
* 超时语义的全部来源;超时首选仍是 net_select
|
||||
* 2. E-02 修复配套:lftpd 等"等对端下一行/下一块数据"的调用方
|
||||
* 2. 修复配套:lftpd 等"等对端下一行/下一块数据"的调用方
|
||||
* 若无限阻塞 recv,会把 netTask(全部 socket 操作的唯一执行者)长期占住
|
||||
* 3. 用本接口把等待切成 ≤timeout_ms 的片,调用方自行累计总预算:总超时语义不变、单次占用有界
|
||||
* 4. 超时用独立返回码 -2(而非靠 errno 区分)→ 无跨任务 errno 竞态
|
||||
@@ -394,7 +394,7 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
int net_close(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:主动拆掉当前 TCP 会话,**保留监听**(E-02 收口)
|
||||
* 函数功能:主动拆掉当前 TCP 会话,**保留监听**
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* 返回值:0 成功(**监听型 fd 的“成功”= 拆除与重建已登记**,芯片未必已拆完,
|
||||
* 见函数说明 2;含幂等:无活动会话),-1 失败
|
||||
@@ -403,14 +403,14 @@ int net_close(int sockfd);
|
||||
* 单连接模型禁止对监听 fd net_close(连监听一起毁,AGENTS 约定),
|
||||
* 而挂起但 TCP 仍活的客户端会永久占住单连接监听(PC 内核自动
|
||||
* ACK keepalive,芯片僵尸判不死——《网络自愈架构设计》§5.2 E13 定案缺口)
|
||||
* 2. 实现(2026-10-25 N-17 起):auto_relisten fd = 登记 QUARANTINE 隔离
|
||||
* 2. 实现(2026-10-25 探针窗口心跳缺口修复起):auto_relisten fd = 登记 QUARANTINE 隔离
|
||||
* 沉降 + REQ_DROP 重建请求,**登记即返**——“FIN 关闭 + 排渣 + 排空
|
||||
* 事件 + 等 byte0=00H”(TRAP-NET-18 异常释放语义)与重建由
|
||||
* net_listener_tick 按 30ms 节奏异步执行,端口恢复窗 ~0.4-1s 与被动
|
||||
* 断连同型;**调用方不得按旧语义假设“返回即芯片已 CLOSED”**——返回
|
||||
* 后 accept/recv 得 WOULDBLOCK 直至重建完成(time_sync 既有轮询天然
|
||||
* 容忍)。旧同步形态曾使 netTask 占用 400-600ms、五任务心跳同窗
|
||||
* 缺席声明 stall(《网络自愈架构设计》§4.6 N-17 记录,勿复活)。
|
||||
* 缺席声明 stall(《网络自愈架构设计》§4.6 记录,勿复活)。
|
||||
* 非 auto_relisten(client fd)无监听可保,仍同步排渣(600ms 封顶)
|
||||
* 3. 幂等:非 ESTABLISHED/CONNECTING 直接返 0,不动监听
|
||||
*/
|
||||
@@ -441,7 +441,7 @@ int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg);
|
||||
* 出口参数:无
|
||||
* 返回值:错误码(NET_ERR_xxx);本任务尚无失败记录时为 NET_OK(0)
|
||||
* 限定条件:无(任何上下文可调用,含中断——查询为纯本任务索引读)
|
||||
* 函数说明:1. **按任务隔离**(2026-09-25 E-03 修复):错误码存在每个任务自己的
|
||||
* 函数说明:1. **按任务隔离**(2026-09-25):错误码存在每个任务自己的
|
||||
* 槽位里,任务间互不干扰,不存在跨任务覆写竞态——旧实现的单一全局
|
||||
* 变量会被 netTask/其他任务的 net 调用交错覆盖,调用方可能读到别人的码
|
||||
* 2. 语义:本任务最后一次返回 -1 的 net_*() 调用所对应的精确码;
|
||||
@@ -562,13 +562,13 @@ net_sock_t *net_sock_raw(int sockfd);
|
||||
* 返回值:err 原值(便于写成 `return net_set_errno(NET_ERR_XXX);`)
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 只写本任务的槽位,其他任务读不到、也改不了 → 消除跨任务覆写竞态
|
||||
* 2. N-32 §10 模块化:由 net_socket.c 的 static 提升为**模块间可见**,
|
||||
* 2. 监听生命周期模块化:由 net_socket.c 的 static 提升为**模块间可见**,
|
||||
* 供 net_listener.c 等同目录内部模块复用(定义仍在 net_socket.c)
|
||||
*/
|
||||
int net_set_errno(int err);
|
||||
|
||||
/* 「记本任务错误码 + 统一失败返回 -1」的紧凑写法(取代 `s_net_errno = X; return -1;`)
|
||||
* N-32 §10:原定义在 net_socket.c,随 net_set_errno 一并提升到本头文件 */
|
||||
* 监听生命周期模块化:原定义在 net_socket.c,随 net_set_errno 一并提升到本头文件 */
|
||||
#define NET_ERR_RETURN_1(err) (net_set_errno(err), -1)
|
||||
|
||||
/*
|
||||
|
||||
@@ -203,18 +203,18 @@ typedef struct {
|
||||
* 连接的释放原因。 */
|
||||
uint8_t released_abnormal;
|
||||
|
||||
/* N-30(2026-10-11 裸机芯片台架实证):本 Socket 上一条被释放连接的**对端端口**。
|
||||
/* TRAP-NET-20(2026-10-11 裸机芯片台架实证):本 Socket 上一条被释放连接的**对端端口**。
|
||||
* 芯片拆链收尾窗口内旧连接未拆净,Socket 如实为 ESTABLISHED(幽灵态,E15:
|
||||
* bit3=1 保留现场契约,非谎报),此时回读的对端端口
|
||||
* = 旧连接端口;新连接的临时端口几乎必然不同。accept 路径补 accept 与对账②
|
||||
* 均以此甄别幽灵态(见 net_socket.c TRAP-NET-20)。跨 fd 释放保留(幽灵属芯片
|
||||
* Socket 级,与 fd 复用无关),新连接断开时刷新。 */
|
||||
uint16_t last_peer_port;
|
||||
/* N-30:accept 路径"CONNECT 事件未收到补 accept"的去抖计数(连续确认次数,
|
||||
/* TRAP-NET-20:accept 路径"CONNECT 事件未收到补 accept"的去抖计数(连续确认次数,
|
||||
* 节拍 = accept 轮询 100ms;新监听/补 accept 后清零) */
|
||||
uint8_t lost_conn_seen;
|
||||
|
||||
/* N-32 / B10 根治 P1(2026-10-12,设计文档 §2.3 不变量 I):本会话"已结束"的
|
||||
/* 监听生命周期 P1(B10 根治,2026-10-12,设计文档 §2.3 不变量 I):本会话"已结束"的
|
||||
* **电平**(粘滞),取代 `state == CLOSED` 这一**边沿**。
|
||||
* 为什么必须电平化:单连接复用模型下同一个 fd 既是监听器又是连接(handle
|
||||
* aliasing),"关掉一条连接"与"毁掉一个监听器"在类型上无法区分;而"会话结束"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权)
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权)
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权)
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权)
|
||||
*/
|
||||
|
||||
#ifndef FTP_CFG_H
|
||||
@@ -24,7 +24,7 @@ extern "C" {
|
||||
|
||||
/* 工厂默认:user=admin、口令=admin(pwd_hash = SHA256("adminadmin"))、write=1。
|
||||
* write=1 保持既有维护工作流(FTP 推 config.ini);生产配置应改口令并按需 write=0。
|
||||
* 出厂默认口令在用期间,lftpd_start 每次启动打 INFO 告警锚点(N-19) */
|
||||
* 出厂默认口令在用期间,lftpd_start 每次启动打 INFO 告警锚点(FTP 鉴权) */
|
||||
#define FTP_CFG_DEF_USER "admin"
|
||||
#define FTP_CFG_DEF_HASH "d82494f05d6917ba02f7aaa29689ccb444bb73f20380876cb05d1f37537b7892"
|
||||
#define FTP_CFG_DEF_WRITE 1u
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* v1.0 2026-09-10 王建锋 创建初始版本
|
||||
* v1.1 2026-09-14 王建锋 存储迁移至 cfg_file 引擎(INI);旧 NCFG 代码移至 net_cfg_legacy
|
||||
* v1.2 2026-09-15 王建锋 保存改跨任务交接(request/state/run + 通知回调);
|
||||
* 引擎属主 = defaultTask(P1-3 竞态修复)
|
||||
* 引擎属主 = defaultTask(竞态修复)
|
||||
* v1.3 2026-10-10 王建锋 **删除旧档 NCFG 迁移**(net_cfg_legacy 整体移除):该兼容
|
||||
* 路径把整条配置写链(net_cfg_save → cfg_file_save →
|
||||
* cfg_store_write_atomic → f_rename → FTL)挂进启动期调用图,
|
||||
@@ -148,7 +148,7 @@ static bool s_mac_is_zero(const uint8_t *p_mac) {
|
||||
*/
|
||||
int net_cfg_load(void) {
|
||||
s_net_register();
|
||||
/* N-19:[ftp] 段一并注册(所有受管段必须先注册、后 load,引擎按注册表解析) */
|
||||
/* [ftp] 段一并注册(所有受管段必须先注册、后 load,引擎按注册表解析) */
|
||||
ftp_cfg_register();
|
||||
|
||||
/* 旧档 NCFG 迁移已于 2026-10-10 删除(net_cfg_legacy 移除;理由见文件头 v1.3)。
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* v1.0 2026-09-10 王建锋 创建初始版本
|
||||
* v1.1 2026-09-14 王建锋 存储迁移至 cfg_file 引擎(INI [network] 段)
|
||||
* v1.2 2026-09-15 王建锋 保存改跨任务交接(request/state/run + 通知回调);
|
||||
* 引擎属主明确为 defaultTask(P1-3 竞态修复)
|
||||
* 引擎属主明确为 defaultTask(竞态修复)
|
||||
*/
|
||||
|
||||
#ifndef NET_CFG_H
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* 2026-07-18 王建锋 创建初始版本
|
||||
* 2026-07-20 王建锋 修复 DE 时序:添加 DE 建立延时替代 for 循环
|
||||
* 2026-07-20 王建锋 清理中文注释乱码,对齐代码规范
|
||||
* 2026-10-05 王建锋 P3-4:模块描述订正为实际实现 HAL_Delay(1)
|
||||
* 2026-10-05 王建锋 模块描述订正为实际实现 HAL_Delay(1)
|
||||
* (原描述称 DWT CYCCNT 微秒延时,与代码不符)
|
||||
*/
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ uint8_t sd2506_dec_to_bcd(uint8_t dec) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把 HAL 的 I2C 状态映射成统一错误码(S-5,2026-10-23)
|
||||
* 函数功能:把 HAL 的 I2C 状态映射成统一错误码(2026-10-23)
|
||||
* 入口参数:status - HAL_I2C_* 返回的状态
|
||||
* 返回值:HAL_OK → BSP_OK;HAL_TIMEOUT → BSP_ERR_TIMEOUT(可重试);其余 → BSP_ERR_IO
|
||||
* 限定条件:仅本文件内部使用
|
||||
@@ -206,7 +206,7 @@ bsp_err_t sd2506_init(void) {
|
||||
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
|
||||
ret = sd2506_read_regs(SD2506_REG_ID_START, a_id, SD2506_ID_SIZE);
|
||||
if (ret != BSP_OK) {
|
||||
/* 启动段可观测性(2026-10-02 S10):本函数原先所有失败路径均静默返回,
|
||||
/* 启动段可观测性(2026-10-02 加):本函数原先所有失败路径均静默返回,
|
||||
* 而它是启动死机最可疑的源头(I2C1 硬件不通即卡此步且无任何输出)。
|
||||
* 日志字符串必须纯 ASCII(文件无 BOM,ARMCC 按 CP936 解码)。 */
|
||||
DBG_ERROR("sd2506_init: ID read FAIL (I2C err=%d)", ret);
|
||||
|
||||
@@ -137,7 +137,7 @@ extern "C" {
|
||||
#define SD2506_CTR1_WRITE_OFF 0x7BU
|
||||
|
||||
/* ======================== 返回值定义 ========================
|
||||
* 本模块**不再自带错误码**(2026-10-23,S-5):统一走 Drivers/BSP/bsp_err.h。
|
||||
* 本模块**不再自带错误码**(2026-10-23):统一走 Drivers/BSP/bsp_err.h。
|
||||
* 旧码映射 —— SD2506_OK → BSP_OK;SD2506_ERROR(参数类)→ BSP_ERR_PARAM;
|
||||
* SD2506_I2C_ERROR → BSP_ERR_IO,其中 I2C 超时刻意单独归成 **BSP_ERR_TIMEOUT
|
||||
* (可重试)**:旧码把"总线上没人应答"和"超时"混成一个 -2,调用方没法规避。
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
|
||||
- **周期重触发由 TIM3 更新事件(1 kHz)驱动**(2026-10-10):`app_main_init` 末尾 `HAL_TIM_Base_Start_IT(&htim3)` 启动节拍,`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback` 在 TIM3 分支内调 `tpafe5160_start_conv_irq()`(ISR 内仅两次 GPIO 写)。B1 阶段曾由 `adcTask` 每 1 ms 软触发(已改)。⚠ **节拍源必须是 TIM3 而非 TIM6**:TIM3 的同一个更新事件还经 **TRGO** 硬件触发 STM32 ADC1,两片转换器由此同源对齐;F4 上 TIM6/TIM7 的 TRGO 只接 DAC,喂不到 ADC1
|
||||
- **取数只用 `tpafe5160_snapshot(int16_t *dst, uint8_t count)`**(**仅任务上下文**,内部临界区原子拷贝;返回 1=新帧 / 0=无新帧 / -1=参数无效)。配套的 `tpafe5160_frame_seq()` 给帧序号、`tpafe5160_frstdata_err()` 给"FRSTDATA 无跳变"帧计数
|
||||
- ⚠️ **不要用缓冲区指针取数**:旧的 `get_buf()` / `data_ready()` / `clear_ready()` 已于 2026-10-10 **删除**(审计 E-45 / E-58)——`get_buf` 只给指针、任务再逐通道拷,中间被 BUSY EXTI 抢占就拷回**两帧混合**数据;`s_ready` 电平标志在 `start_conv_irq` 里被清 0,等于**用重触发去丢帧**。现由**帧序号**(只增不清)+ **临界区快照**取代
|
||||
- **`tpafe5160_init()` 做首帧有效性自检**(E-44):完整跑一帧转换+读取,若整帧读下来 FRSTDATA **没有发生任何跳变**(芯片没在驱动并行总线;板上上拉会把它钉死为高)→ 返回 `BSP_ERR_NO_DEVICE`(旧 `TPAFE5160_FRSTDATA_ERR(-3)`,2026-10-23 S-5 收编)并打 `[ERR] Init FRSTDATA no-toggle`。判据刻意不锁死"哪个 RD 边沿拉高/拉低"(手册只给"RD 下降沿 → FRSTDATA 高/低"两个跳变,本板未实测边沿次序),只要求"一帧内必须跳变" → 既抓死芯片,也不会因极性假设错误误杀好芯片
|
||||
- ⚠️ **不要用缓冲区指针取数**:旧的 `get_buf()` / `data_ready()` / `clear_ready()` 已于 2026-10-10 **删除**——`get_buf` 只给指针、任务再逐通道拷,中间被 BUSY EXTI 抢占就拷回**两帧混合**数据;`s_ready` 电平标志在 `start_conv_irq` 里被清 0,等于**用重触发去丢帧**。现由**帧序号**(只增不清)+ **临界区快照**取代
|
||||
- **`tpafe5160_init()` 做首帧有效性自检**:完整跑一帧转换+读取,若整帧读下来 FRSTDATA **没有发生任何跳变**(芯片没在驱动并行总线;板上上拉会把它钉死为高)→ 返回 `BSP_ERR_NO_DEVICE`(旧 `TPAFE5160_FRSTDATA_ERR(-3)`,2026-10-23 收编)并打 `[ERR] Init FRSTDATA no-toggle`。判据刻意不锁死"哪个 RD 边沿拉高/拉低"(手册只给"RD 下降沿 → FRSTDATA 高/低"两个跳变,本板未实测边沿次序),只要求"一帧内必须跳变" → 既抓死芯片,也不会因极性假设错误误杀好芯片
|
||||
- 采样数据的消费者 `App/task/adc_task.c` **已实现**(B1,2026-10-10:`tpafe5160_snapshot()` 原子取数 + 帧统计;`adcTask` 在 `Src/freertos.c` 的 `USER CODE` 区创建)。**v2.2(2026-10-10)**:采样节拍改由定时器更新中断硬件触发,本任务退化为纯消费者;**v2.8(2026-10-10)**:节拍源改 TIM3(同时经 TRGO 触发 ADC1 + DMA),STM32 侧改为消费 DMA 乒乓缓冲,单拍耗时 122 µs → **8 µs**
|
||||
- CONVST→BUSY→RD 三段时序对**建立/保持时间敏感**,改动需对照芯片手册
|
||||
- ⚠️ 本项目 ADC 相关功能**新增任务/缓冲前须核算 RAM**(见 `docs/内存管理/`);接消费者前的强制门禁见根 `AGENTS.md`「接 ADC / RS-485 消费者前的强制门禁」
|
||||
- ⚠️ **通道语义不是通道号**:`buf[i]` 对应哪个电气量必须查 `docs/硬件平台/ADC通道映射说明.md`(`buf[2]=IA`、`buf[5]=UN`、`buf[6]=UA`);V0.1 上 8 路电气量分属两片转换器属硬件缺陷(N-18)→ **禁止**跨转换器做相位/功率/功率因数类运算
|
||||
- ⚠️ **通道语义不是通道号**:`buf[i]` 对应哪个电气量必须查 `docs/硬件平台/ADC通道映射说明.md`(`buf[2]=IA`、`buf[5]=UN`、`buf[6]=UA`);V0.1 上 8 路电气量分属两片转换器属硬件缺陷(登记簿条目「双转换器无共同采样触发」)→ **禁止**跨转换器做相位/功率/功率因数类运算
|
||||
|
||||
## 已知问题
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* 2026-07-17 王建锋 创建初始版本,参考 AD7606 并行驱动及 TPAFE5160 手册
|
||||
* 2026-07-17 王建锋 增加 EXTI 中断读取模式
|
||||
* 2026-09-20 王建锋 V0.1 引脚适配(引脚值随 main.h 宏,驱动逻辑不变)
|
||||
* 2026-10-10 王建锋 消费者接入前置修复(E-44 / E-45 / E-58),详见 tpafe5160.h
|
||||
* 2026-10-10 王建锋 消费者接入前置修复,详见 tpafe5160.h
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "tpafe5160.h"
|
||||
#include "main.h"
|
||||
|
||||
/* taskENTER_CRITICAL:把"选缓冲 + 拷数据"收进一个不可分割动作(E-58)。
|
||||
/* taskENTER_CRITICAL:把"选缓冲 + 拷数据"收进一个不可分割动作。
|
||||
* 与 App/log/console_log.c、Drivers/BSP/NET/net_socket.c 同一写法,仅任务上下文可用 */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
@@ -73,7 +73,7 @@ static bsp_err_t tpafe5160_read_channels(uint8_t count, int16_t *buf);
|
||||
* 函数说明:1. 设置过采样为无过采样 (000)
|
||||
* 2. 确保 RD 为高、CONVST 为低
|
||||
* 3. 等待 BUSY 释放(转换空闲)
|
||||
* 4. 采一帧并校验 FRSTDATA 有跳变(E-44:旧实现只看 BUSY,芯片死亡
|
||||
* 4. 采一帧并校验 FRSTDATA 有跳变(旧实现只看 BUSY,芯片死亡
|
||||
* 时照样报 Init OK,此后所有读数都是上拉/总线残值——静默死亡)
|
||||
*/
|
||||
bsp_err_t tpafe5160_init(void) {
|
||||
@@ -91,7 +91,7 @@ bsp_err_t tpafe5160_init(void) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 首帧有效性自检(E-44):一次完整转换 + 读取,确认芯片真的在响应 RD */
|
||||
/* 首帧有效性自检:一次完整转换 + 读取,确认芯片真的在响应 RD */
|
||||
int16_t probe[TPAFE5160_CH_NUM];
|
||||
tpafe5160_start_conv();
|
||||
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||||
@@ -171,7 +171,7 @@ uint8_t tpafe5160_is_busy(void) {
|
||||
* 限定条件:GPIO 已初始化
|
||||
* 函数说明:1. 启动转换并等待 BUSY 释放
|
||||
* 2. 连续8次拉低RD读取各通道数据
|
||||
* 3. 校验 FRSTDATA 在这一帧内发生跳变(E-44)
|
||||
* 3. 校验 FRSTDATA 在这一帧内发生跳变
|
||||
*/
|
||||
bsp_err_t tpafe5160_read_all(int16_t *buf) {
|
||||
bsp_err_t ret = BSP_OK;
|
||||
@@ -266,7 +266,7 @@ uint16_t tpafe5160_read_bus(void) {
|
||||
* t14 数据建立 ≤ 21ns → 数据在 RD 下降沿后 21ns 内有效
|
||||
* 5 个 NOP (~30ns) 覆盖建立时间
|
||||
*
|
||||
* E-44(数据有效性):FRSTDATA 由芯片主动驱动,板上 R24 4.7k 上拉(V0.1 网表)。
|
||||
* 数据有效性:FRSTDATA 由芯片主动驱动,板上 R24 4.7k 上拉(V0.1 网表)。
|
||||
* 手册 Parallel Data Read:FRSTDATA 随首个 RD 下降沿变高标记通道 1 可读,t26/t27
|
||||
* 给出"RD 下降沿 → FRSTDATA 高/低"两个跳变。判据刻意**不锁死具体是哪个 RD 边沿
|
||||
* 拉高/拉低**(本板未实测确认边沿次序),只要求"一帧读下来必须发生跳变":
|
||||
@@ -304,11 +304,11 @@ static bsp_err_t tpafe5160_read_channels(uint8_t count, int16_t *buf) {
|
||||
static int16_t s_buf_a[TPAFE5160_CH_NUM];
|
||||
static int16_t s_buf_b[TPAFE5160_CH_NUM];
|
||||
static volatile uint8_t s_buf_sel = 0; /* 0 = 写 B / 读 A, 1 = 写 A / 读 B */
|
||||
/* E-45:帧序号取代"就绪电平标志"。旧 s_ready 有三个写者,其中 start_conv_irq
|
||||
/* 帧序号取代"就绪电平标志"。旧 s_ready 有三个写者,其中 start_conv_irq
|
||||
* 在触发前清 0 —— 重触发这个动作本身就会抹掉一帧还没被消费的数据(高速采样
|
||||
* 系统性丢样,且无从发现)。序号只增不清,消费者比较序号即可 */
|
||||
static volatile uint32_t s_frame_seq = 0;
|
||||
/* E-44:FRSTDATA 无跳变帧计数。中断里只加计数(不打日志),由消费者/自检侧上报 */
|
||||
/* FRSTDATA 无跳变帧计数。中断里只加计数(不打日志),由消费者/自检侧上报 */
|
||||
static volatile uint16_t s_frstdata_err = 0;
|
||||
/* 消费者游标:只在任务上下文读写(snapshot 内部),中断不碰 */
|
||||
static uint32_t s_consumer_seq = 0;
|
||||
@@ -341,7 +341,7 @@ void tpafe5160_irq_disable(void) {
|
||||
* 返回值:无
|
||||
* 限定条件:已调用 tpafe5160_irq_enable()
|
||||
* 函数说明:产生 CONVST 脉冲,转换完成后由 EXTI 中断自动读取 8 通道数据。
|
||||
* E-45:这里**不再清就绪标志**——旧实现"触发前置 0"等于用重触发去丢帧
|
||||
* 这里**不再清就绪标志**——旧实现"触发前置 0"等于用重触发去丢帧
|
||||
*/
|
||||
void tpafe5160_start_conv_irq(void) {
|
||||
TPAFE5160_CONVST_PULSE();
|
||||
@@ -352,7 +352,7 @@ void tpafe5160_start_conv_irq(void) {
|
||||
* 入口参数:无
|
||||
* 返回值:uint32_t 帧序号(EXTI 每读完一帧 +1)
|
||||
* 限定条件:中断模式已启用
|
||||
* 函数说明:见 tpafe5160.h 同名声明的契约说明(E-45)
|
||||
* 函数说明:见 tpafe5160.h 同名声明的契约说明
|
||||
*/
|
||||
uint32_t tpafe5160_frame_seq(void) {
|
||||
return s_frame_seq;
|
||||
@@ -363,7 +363,7 @@ uint32_t tpafe5160_frame_seq(void) {
|
||||
* 入口参数:无
|
||||
* 返回值:uint16_t 计数
|
||||
* 限定条件:中断模式已启用
|
||||
* 函数说明:非 0 = 至少一帧的 8 个值不可信(E-44),消费者据此上报自检
|
||||
* 函数说明:非 0 = 至少一帧的 8 个值不可信,消费者据此上报自检
|
||||
*/
|
||||
uint16_t tpafe5160_frstdata_err(void) {
|
||||
return s_frstdata_err;
|
||||
@@ -375,7 +375,7 @@ uint16_t tpafe5160_frstdata_err(void) {
|
||||
* count - 拷贝通道数 uint8_t 1 - 8
|
||||
* 返回值:1 - 取到新帧,0 - 无新帧,-1 - 参数无效
|
||||
* 限定条件:**任务上下文**(内部 taskENTER_CRITICAL,中断里不可调用)
|
||||
* 函数说明:E-58——把"选缓冲 + 拷数据 + 记游标"收进一个临界区。旧 get_buf() 只给
|
||||
* 函数说明:把"选缓冲 + 拷数据 + 记游标"收进一个临界区。旧 get_buf() 只给
|
||||
* 指针、任务再逐通道拷,两步之间被 BUSY EXTI 抢占并跨过一帧周期就拷回
|
||||
* 两帧混合的数据(8 通道失去同一时刻性)且无任何错误指示
|
||||
*/
|
||||
@@ -422,11 +422,11 @@ void tpafe5160_exti_handler(uint16_t GPIO_Pin) {
|
||||
/* 连续读取8个通道(内含 FRSTDATA 有效性校验),约 0.5µs @ 168MHz */
|
||||
int ret = tpafe5160_read_channels(TPAFE5160_CH_NUM, p_wr);
|
||||
|
||||
/* 切换缓冲区并推进帧序号(E-45:序号只增不清,重触发不会抹掉未消费帧) */
|
||||
/* 切换缓冲区并推进帧序号(序号只增不清,重触发不会抹掉未消费帧) */
|
||||
s_buf_sel ^= 1u;
|
||||
s_frame_seq++;
|
||||
|
||||
/* E-44:中断里不打日志,只累计;消费者据 tpafe5160_frstdata_err() 上报自检 */
|
||||
/* 中断里不打日志,只累计;消费者据 tpafe5160_frstdata_err() 上报自检 */
|
||||
if (ret != BSP_OK && s_frstdata_err < 0xFFFFu) {
|
||||
s_frstdata_err++;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* 2026-07-17 王建锋 创建初始版本,参考 AD7606 并行驱动及 TPAFE5160 手册
|
||||
* 2026-07-17 王建锋 增加 EXTI 中断读取模式
|
||||
* 2026-09-20 王建锋 V0.1 引脚适配(引脚值随 main.h 宏,驱动逻辑不变)
|
||||
* 2026-10-10 王建锋 消费者接入前置修复(历史审计 E-44 / E-45 / E-58,属主文档已删除):
|
||||
* 2026-10-10 王建锋 消费者接入前置修复:
|
||||
* FRSTDATA 数据有效性校验 + 帧序号取代就绪电平标志 +
|
||||
* 原子快照 API;移除会撕裂的 get_buf/data_ready/clear_ready
|
||||
*/
|
||||
@@ -69,11 +69,11 @@ typedef enum {
|
||||
} tpafe5160_range_t;
|
||||
|
||||
/* 返回值定义 */
|
||||
/* 本模块的成败码已收编到 Drivers/BSP/bsp_err.h(2026-10-23,S-5 C1):
|
||||
/* 本模块的成败码已收编到 Drivers/BSP/bsp_err.h(2026-10-23,C1):
|
||||
* 旧 TPAFE5160_OK(0) → BSP_OK;旧 TPAFE5160_ERROR(-1) → BSP_ERR_PARAM(三处全是参数校验,
|
||||
* 数值也同为 -1,**行为零变化**);旧 TPAFE5160_BUSY_TIMEOUT(-2) → BSP_ERR_TIMEOUT,
|
||||
* 于是 BUSY 不释放终于变成跨模块可判定的"可重试";
|
||||
* 旧 TPAFE5160_FRSTDATA_ERR(-3) → BSP_ERR_NO_DEVICE——它就是 E-44 的"芯片没驱动 DB 总线",
|
||||
* 旧 TPAFE5160_FRSTDATA_ERR(-3) → BSP_ERR_NO_DEVICE——它就是"芯片没驱动 DB 总线",
|
||||
* 语义是**器件不在响应**,归到 NO_DEVICE 比留在私有 -3 更有用(且避开与 BSP_ERR_TIMEOUT 撞值)。
|
||||
* ⚠ 例外:**tpafe5160_snapshot() 不是成败 API**,它是 1=有新帧 / 0=无新帧 / -1=参数错 的
|
||||
* 三态**状态**返回(0 不是错误,是"这一拍没有新数据"),因此**不套 bsp_err_t**,
|
||||
@@ -136,11 +136,11 @@ uint8_t tpafe5160_is_busy(void);
|
||||
* 函数功能:读取全部8通道转换结果
|
||||
* 入口参数:buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
|
||||
* 返回值:BSP_OK - 成功;BSP_ERR_TIMEOUT - BUSY 超时;BSP_ERR_NO_DEVICE - FRSTDATA 无跳变
|
||||
* (E-44:芯片没驱动 DB 总线,读回的是上拉残值,不可信)
|
||||
* (芯片没驱动 DB 总线,读回的是上拉残值,不可信)
|
||||
* 限定条件:GPIO 已初始化
|
||||
* 函数说明:1. 启动转换并等待 BUSY 释放
|
||||
* 2. 连续8次拉低RD读取各通道数据
|
||||
* 3. 校验 FRSTDATA 在这一帧内发生过跳变(= 芯片确实在驱动数据线,E-44)
|
||||
* 3. 校验 FRSTDATA 在这一帧内发生过跳变(= 芯片确实在驱动数据线)
|
||||
*/
|
||||
bsp_err_t tpafe5160_read_all(int16_t *buf);
|
||||
|
||||
@@ -207,7 +207,7 @@ void tpafe5160_start_conv_irq(void);
|
||||
* 入口参数:无
|
||||
* 返回值:uint32_t 帧序号,EXTI 每读完一帧 +1
|
||||
* 限定条件:中断模式已启用
|
||||
* 函数说明:取代旧的 data_ready/clear_ready **电平标志**(E-45)——电平标志有三个
|
||||
* 函数说明:取代旧的 data_ready/clear_ready **电平标志**——电平标志有三个
|
||||
* 写者,其中 start_conv_irq 在触发前清 0,于是"重触发"本身就会抹掉一帧
|
||||
* 还没被消费的数据(高速采样系统性丢样且无从发现)。序号只增不清,
|
||||
* 消费者比较 seq 即可,重触发不会丢
|
||||
@@ -220,7 +220,7 @@ uint32_t tpafe5160_frame_seq(void);
|
||||
* count - 拷贝通道数 uint8_t 1 - 8
|
||||
* 返回值:1 - 取到新帧,0 - 无新帧,BSP_ERR_PARAM(-1) - 参数无效(**三态状态返回,故意不套成败码**:0 表示"这一拍没有新数据",不是错误)
|
||||
* 限定条件:**任务上下文**调用(内部用 taskENTER_CRITICAL,不可在中断里调)
|
||||
* 函数说明:E-58——旧 get_buf() 只返回缓冲区指针,任务再逐通道拷;两步之间被 BUSY
|
||||
* 函数说明:旧 get_buf() 只返回缓冲区指针,任务再逐通道拷;两步之间被 BUSY
|
||||
* EXTI 抢占并跨过一帧周期,拷回的就是**两帧混合**的 8 通道数据(同一时刻
|
||||
* 的 8 路值互相错位),且没有任何错误指示。本函数把"选缓冲 + 拷数据 +
|
||||
* 记游标"收进一个临界区(只盖 16 字节拷贝),撕裂在设计上消除
|
||||
@@ -232,7 +232,7 @@ int tpafe5160_snapshot(int16_t *dst, uint8_t count);
|
||||
* 入口参数:无
|
||||
* 返回值:uint16_t 计数(中断里只加计数不打日志)
|
||||
* 限定条件:中断模式已启用
|
||||
* 函数说明:E-44——非 0 表示至少有一帧的 8 个值不可信;消费者据此上报自检
|
||||
* 函数说明:非 0 表示至少有一帧的 8 个值不可信;消费者据此上报自检
|
||||
*/
|
||||
uint16_t tpafe5160_frstdata_err(void);
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
* 描述符 init_cb(开页后调一次,页面模块重置运行态)
|
||||
* 2026-09-13 王建锋 cp_draw_cb 按区块拆为四个绘制子函数(cp_draw_deco/header/rows/footer),
|
||||
* 主函数仅编排(行数取一次供行窗口/页脚共用),行为零变化
|
||||
* 2026-10-15 王建锋 U1 设计令牌层:屏幕/骨架/行距/缓冲/1bpp 语义色字面量收进
|
||||
* 2026-10-15 王建锋 UI 设计令牌层:屏幕/骨架/行距/缓冲/1bpp 语义色字面量收进
|
||||
* ui_tokens.h(纯替换,像素逐位一致)
|
||||
* 2026-10-15 王建锋 U3 页面原型化:删 content_page_id_t 枚举与 s_defs[],open() 改收
|
||||
* 2026-10-15 王建锋 UI 页面原型化:删 content_page_id_t 枚举与 s_defs[],open() 改收
|
||||
* 描述符指针、s_open 变指针(NULL = 未打开);7 个页面头包含移交
|
||||
* ui_page_registry.c;日志页身份改打描述符 id(ASCII 页名,比原
|
||||
* id=%u 更可定位);绘制/按键/几何一律未动
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* 是否过密码页/所输密码决定);描述符新增 edit_capable(页面
|
||||
* 是否支持编辑模式)+ 新 API ui_content_page_is_editable
|
||||
* (页面钩子据以决定编辑行为:edit_capable 且修改权限)
|
||||
* 2026-10-15 王建锋 U3 页面原型化:删 content_page_id_t 枚举与框架侧 s_defs[],
|
||||
* 2026-10-15 王建锋 UI 页面原型化:删 content_page_id_t 枚举与框架侧 s_defs[],
|
||||
* open() 改收描述符指针;节点→页面绑定移交给生成物
|
||||
* ui_page_registry.c(菜单树 #page= 注记为唯一真源),
|
||||
* 逻辑层手写 menu_content_page_for() 随之删除;
|
||||
|
||||
@@ -53,7 +53,7 @@ uint8_t ui_menu_tab_children(uint8_t node, uint8_t out[], uint8_t max);
|
||||
* 入口参数:node - 节点下标 uint8_t 0 - 表长-1
|
||||
* 返回值:true - 有子项(显示为菜单,带 ▶);false - 无子项(叶子,可进内容页)
|
||||
* 限定条件:纯查询,无副作用;node 越界返回 false
|
||||
* 函数说明:这是**谓词**(bool 合法),不是成败码——S-5 的 err-type 检查里白名单放行的形态。
|
||||
* 函数说明:这是**谓词**(bool 合法),不是成败码——err-type 检查里白名单放行的形态。
|
||||
*/
|
||||
bool ui_menu_tab_has_children(uint8_t node);
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
* 2026-09-10 王建锋 改为双端共用:底层改用 net_* API + 平台后端;接收改为逐字节
|
||||
* 状态机(失步自动重同步);断开/网络重建后经 net_get_sock 判活
|
||||
* 并重建监听(修复 MCU 单连接模型断线后无法重连的隐患)
|
||||
* 2026-10-02 王建锋 E-04:remo_send 校验 net_send 返回值(原丢弃)——短写/失败
|
||||
* 2026-10-02 王建锋 remo_send 校验 net_send 返回值(原丢弃)——短写/失败
|
||||
* 告警,连续 3 次失败阻断推送,新连接/网络重建解除阻断
|
||||
* (MCU 单连接模型不得 net_close 数据 fd,会连监听一起毁掉)
|
||||
* 2026-10-10 王建锋 I-1 门控 + I-2 兜底(销项 N-10):accept/recv
|
||||
* 2026-10-10 王建锋 判活门控 + 兜底收帧:accept/recv
|
||||
* 前先用 net_can_accept()/net_can_read()(纯 RAM 读控制块)判
|
||||
* "有没有活",无活直接返回——台架常态(无客户端)从每 10 ms
|
||||
* 一次 RPC(100 msg/s,与 FTP 抢深度 8 的队列)降到 0 次。
|
||||
@@ -23,7 +23,7 @@
|
||||
* "RX 非空但 readable=0",故每 32 轮无条件 recv 一次(≈320 ms);
|
||||
* 门控放行时若监听控制块已消失则置 g_listen=-1 触发重建,
|
||||
* 避免"门控把端口永久不再接受新客户端"这一新卡死路径
|
||||
* 2026-10-12 王建锋 N-32(B10 根治 P1,设计文档 §2.3 不变量 II):accept 门控
|
||||
* 2026-10-12 王建锋 监听生命周期 P1(B10 根治,设计文档 §2.3 不变量 II):accept 门控
|
||||
* 由 net_can_accept()(只认 ESTABLISHED)改判
|
||||
* net_listener_armed()(LISTENING 或 CLOSED+auto_relisten)——
|
||||
* 旧门控在断连待重建窗口恒 0,:7003 永不进 net_accept,
|
||||
@@ -35,7 +35,7 @@
|
||||
* (accept / recv_nb / send / close / 重建三件套)返回点
|
||||
* 调用——风暴期单轮多往返串成 UI 心跳缺口(实测 bit4
|
||||
* 8 连击、uisn=154ms 仍在段内样本),往返本身不动
|
||||
* (语义不变,上界收口 = 登记簿 E-27/E-28 批 3),心跳
|
||||
* (语义不变,上界收口为登记簿批 3 事项),心跳
|
||||
* 在往返边界续命。钩子未注册 = 零行为变化(模拟器/黄金
|
||||
* 回归不注册)。判读与设计口径见
|
||||
* docs/UI设计/UI_设计架构.md §5.8
|
||||
@@ -51,7 +51,7 @@
|
||||
#include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */
|
||||
#include "net_latency_test.h" /* M3 探针:prod_cycle_mcu(LCDMEM 处理净成本) */
|
||||
|
||||
/* E-04(2026-10-02):发送短写/失败告警(MCU=控制台日志,模拟器=printf 桩) */
|
||||
/* (2026-10-02):发送短写/失败告警(MCU=控制台日志,模拟器=printf 桩) */
|
||||
#define DBG_TAG "[UI_NET_SINK]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
@@ -77,11 +77,11 @@ static int g_listen = -1; /* 监听 Socket fd(未启动或已停止时
|
||||
static int g_client = -1; /* 当前客户端 Socket fd(无连接时为 -1) */
|
||||
static uint16_t g_port; /* 监听端口(网络重建 / 断线后重监听用) */
|
||||
static uint32_t g_gen; /* 建立监听时的网络代次号(变化 = 网络已重建) */
|
||||
static uint8_t s_send_fail_run; /* 连续发送失败计数(E-04:0 = 正常) */
|
||||
static uint8_t s_send_fail_run; /* 连续发送失败计数(0 = 正常) */
|
||||
static uint8_t s_sink_blocked; /* 推送阻断标志(1 = 停发帧,等客户端重连解除) */
|
||||
|
||||
/*
|
||||
* 兜底收帧节拍(门控失效保险,2026-10-10 I-2):
|
||||
* 兜底收帧节拍(门控失效保险,2026-10-10):
|
||||
* net_can_read() 的 readable 只由 RECV_OK 中断置位,而 TRAP-NET-17 的
|
||||
* net_reconcile_tick() 三类对账**不含**"芯片 RX 非空但 readable=0" →
|
||||
* 该中断一旦丢失,纯门控会把已到达的数据永久藏住(改前靠每轮 recv 内的
|
||||
@@ -170,7 +170,7 @@ static void put_u32(uint8_t *p, uint32_t v) {
|
||||
* 限定条件:g_tx 空间足够容纳 4+len+1 字节;g_client >= 0(已有连接)
|
||||
* 函数说明:帧格式 [TAG_DEVICE][CMD][LEN_H][LEN_LO][DATA][CRC];CRC 为 payload 异或;
|
||||
* 无连接时直接返回
|
||||
* E-04(2026-10-02):校验 net_send 返回值(原丢弃)——短写 = 客户端收到坏帧
|
||||
* (2026-10-02):校验 net_send 返回值(原丢弃)——短写 = 客户端收到坏帧
|
||||
* (CRC 不符 → 画面卡死黑屏,而设备侧日志全正常);连续 3 次失败 →
|
||||
* 停发:客户端察觉无帧自行重连,芯片侧 DISCONNECT/TIMEOUT 自动重开
|
||||
* 监听重建连接;MCU 单连接模型严禁 net_close 数据 fd(会连监听一起毁)
|
||||
@@ -516,10 +516,10 @@ int ui_net_sink_start(uint16_t port) {
|
||||
* 3. 无客户端时**先门控**(net_listener_armed,纯 RAM 读)再 accept,成功则复位状态机。
|
||||
* 门控判据 = "监听器还在服役"(LISTENING 或 CLOSED+auto_relisten)而非
|
||||
* "本轮必然有连接"——后者会让断连待重建窗口内永不进 net_accept,
|
||||
* 把 CONNECT 事件未收到的补 accept 探测永久挡在门外(N-32)
|
||||
* 把 CONNECT 事件未收到的补 accept 探测永久挡在门外(监听生命周期模块化)
|
||||
* 4. 有客户端时**先门控**(net_can_read)再非阻塞 recv 逐字节喂入状态机;
|
||||
* 门控判为"无数据"时仍每 RM_LIVENESS_DIV 轮无条件收一次(兜底)
|
||||
* 5. 对端关闭/真错误时关闭客户端。**不再自行重建监听**(N-32 不变量 II):
|
||||
* 5. 对端关闭/真错误时关闭客户端。**不再自行重建监听**(监听生命周期不变量 II):
|
||||
* recv 的 EOF/错误分支已触发 net 层 auto_relisten,另有对账④兜底;
|
||||
* 仅 g_listen 的 fd 真消失(net_get_sock 返 NULL)才置 -1 交回 2 重启
|
||||
* ⚠ **门控只消除"无谓的往返",不消除"往返本身无上界"**:一旦真有活要干
|
||||
@@ -543,7 +543,7 @@ void ui_net_sink_poll(void) {
|
||||
sink_beat(); /* F3:重建前的一对 close 往返结束 */
|
||||
g_client = -1;
|
||||
rm_parser_reset();
|
||||
s_send_fail_run = 0U; /* 网络重建:失败累计清零、解除推送阻断(E-04) */
|
||||
s_send_fail_run = 0U; /* 网络重建:失败累计清零、解除推送阻断 */
|
||||
s_sink_blocked = 0U;
|
||||
s_liveness_ctr = 0U; /* 网络重建:兜底节拍重新起算 */
|
||||
(void)ui_net_sink_start(g_port);
|
||||
@@ -556,7 +556,7 @@ void ui_net_sink_poll(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 无客户端时尝试接受连接(N-32 P1 不变量 II:门控从 net_can_accept〔只认
|
||||
/* 无客户端时尝试接受连接(监听生命周期 P1 不变量 II:门控从 net_can_accept〔只认
|
||||
* ESTABLISHED〕换成 net_listener_armed〔LISTENING 或 CLOSED+auto_relisten〕)
|
||||
* 原因:MCU 单连接模型下断连后 fd 会停在 CLOSED 等 relisten,旧门控在该窗口
|
||||
* 内恒 0 → :7003 永不进 net_accept → net_lost_connect_probe 对该端口等于
|
||||
@@ -577,14 +577,14 @@ void ui_net_sink_poll(void) {
|
||||
if (c >= 0) {
|
||||
g_client = c;
|
||||
rm_parser_reset();
|
||||
s_send_fail_run = 0U; /* 新连接:失败累计清零、解除推送阻断(E-04) */
|
||||
s_send_fail_run = 0U; /* 新连接:失败累计清零、解除推送阻断 */
|
||||
s_sink_blocked = 0U;
|
||||
s_liveness_ctr = 0U; /* 新连接:兜底节拍重新起算 */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 有客户端时非阻塞收帧(I-1 门控 + I-2 兜底;DONTWAIT 已转正
|
||||
/* 有客户端时非阻塞收帧(判活门控 + 兜底节拍;DONTWAIT 已转正
|
||||
* 为具名 API net_recv_nb——uiTask 在 IWDG required_mask 内且有 10ms UI 推流
|
||||
* 周期职责,禁止无限阻塞 recv,非阻塞轮询是义务):
|
||||
* 无数据可读 → 多数轮次直接返回,省掉一次 recv 往返(该往返内部
|
||||
@@ -612,7 +612,7 @@ void ui_net_sink_poll(void) {
|
||||
}
|
||||
g_client = -1;
|
||||
rm_parser_reset();
|
||||
/* N-32(P1 不变量 II):重建**不再**由本处自行 bind_listen。
|
||||
/* 监听生命周期 P1 不变量 II:重建**不再**由本处自行 bind_listen。
|
||||
* ① net_recv_locked 的 EOF 分支已触发 auto_relisten(D10 非阻塞重试);
|
||||
* ② 对账④还会兜底补一次 relisten(TRAP-NET-17 实证事件面不是可靠唯一信源——
|
||||
* E14 复核:非芯片丢事件——两层不可互替)。
|
||||
@@ -625,7 +625,7 @@ void ui_net_sink_poll(void) {
|
||||
} else {
|
||||
/* n < 0:区分"暂无数据"(继续等待)与"真错误"(断开并视情况重建监听) */
|
||||
if (net_get_errno() != NET_ERR_WOULDBLOCK) {
|
||||
/* N-32(P1):加 `g_client != g_listen` 护栏,与上面 n==0 分支
|
||||
/* 监听生命周期 P1:加 `g_client != g_listen` 护栏,与上面 n==0 分支
|
||||
* 的 :552 一致——MCU 单连接模型下 g_client 可能就是 g_listen 本身,
|
||||
* 无护栏的 net_close 会直接关掉监听 fd(:563,与 :134 注释"严禁"
|
||||
* 自相矛盾),端口随 TRAP-NET-09 沉降窗短死。PC 模拟器多连接后端下
|
||||
@@ -636,7 +636,7 @@ void ui_net_sink_poll(void) {
|
||||
sink_beat(); /* F3:EOF/错误路径的 close 往返结束 */
|
||||
g_client = -1;
|
||||
rm_parser_reset();
|
||||
/* N-32(P1 不变量 II):同 EOF 分支——不再自行 bind_listen,
|
||||
/* 监听生命周期 P1 不变量 II:同 EOF 分支——不再自行 bind_listen,
|
||||
* 重建交回 net 层(recv 的错误分支同样会走 relisten)。仅 fd 真消失
|
||||
* 时置 -1 交回主循环走 start */
|
||||
if (net_get_sock(g_listen) == NULL) {
|
||||
|
||||
@@ -71,7 +71,7 @@ void ui_net_sink_poll(void);
|
||||
* 重建三件套)返回处调用钩子。动因:netTask 重建风暴期,单轮内多个
|
||||
* 往返串行排队,把宿主(uiTask,IWDG 必需位)的心跳间隔拖过 800ms
|
||||
* (2026-10-25 台架实测 bit4 单任务 8 连击窗、uisn=154ms 仍在段内);
|
||||
* 往返时长本身不改(上界收口 = 登记簿 E-27/E-28,批 3),但心跳在每个
|
||||
* 往返时长本身不改(上界收口为登记簿批 3 事项),但心跳在每个
|
||||
* 往返边界续命——单任务停摆判据(按位连击,docs/子系统设计/看门狗设计说明.md
|
||||
* §5.3)只应
|
||||
* 抓真挂死,不该抓「多个都回来了但各自偏慢的往返之和」。
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.3)
|
||||
* v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6 U2:合并两页重复实现)
|
||||
* v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6:合并两页重复实现)
|
||||
* v1.2 2026-10-25 王建锋 记录 reader 注入(道B task-6):不再 include sys_log.h,
|
||||
* 数据经组件内注入的 ui_rec_reader_t 取;日志 ID 换
|
||||
* 不透明编号 UI_W_REC_LOG_RUN(映射在两端绑定处)
|
||||
@@ -37,7 +37,7 @@
|
||||
* 返回值:总行数 uint16_t
|
||||
*/
|
||||
/*
|
||||
* 记录页薄跳板:内容全部走 ui_w_records 共享组件(W6 U2)。
|
||||
* 记录页薄跳板:内容全部走 ui_w_records 共享组件(W6)。
|
||||
* 实例 s_rec 携带日志 ID 与**本实例自己的**读盘缓存——两页共用组件代码,
|
||||
* 但缓存不得共享,否则切页会命中上一记录号而显示另一页的陈旧行。
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.4)
|
||||
* v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6 U2:合并两页重复实现)
|
||||
* v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6:合并两页重复实现)
|
||||
* v1.2 2026-10-25 王建锋 记录 reader 注入(道B task-6):不再 include sys_log.h,
|
||||
* 数据经组件内注入的 ui_rec_reader_t 取;日志 ID 换
|
||||
* 不透明编号 UI_W_REC_LOG_SELFTEST(映射在两端绑定处)
|
||||
@@ -37,7 +37,7 @@
|
||||
* 返回值:总行数 uint16_t
|
||||
*/
|
||||
/*
|
||||
* 记录页薄跳板:内容全部走 ui_w_records 共享组件(W6 U2)。
|
||||
* 记录页薄跳板:内容全部走 ui_w_records 共享组件(W6)。
|
||||
* 实例 s_rec 携带日志 ID 与**本实例自己的**读盘缓存——两页共用组件代码,
|
||||
* 但缓存不得共享,否则切页会命中上一记录号而显示另一页的陈旧行。
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(U3:原三处逐字相同的定义合并到此单一来源)
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(原三处逐字相同的定义合并到此单一来源)
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(U3:合并三处逐字重复的 flat 常量样式定义)
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(合并三处逐字重复的 flat 常量样式定义)
|
||||
*/
|
||||
#ifndef UI_STYLE_H
|
||||
#define UI_STYLE_H
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-15
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(U1:收编散落各 UI 文件的布局/颜色/缓冲魔数,
|
||||
* v1.0 2026-10-15 王建锋 创建初始版本(收编散落各 UI 文件的布局/颜色/缓冲魔数,
|
||||
* 纯替换、零行为变化;像素与替换前逐位一致)
|
||||
*/
|
||||
#ifndef UI_TOKENS_H
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-20
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-20 王建锋 创建(W6 U2)
|
||||
* v1.0 2026-10-20 王建锋 创建(W6)
|
||||
* v2.0 2026-10-25 王建锋 reader 注入(道B task-6):syslog_count/syslog_get_record
|
||||
* 直调改为 s_reader 函数表调用;未注入按空态渲染
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-20
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-20 王建锋 创建(W6 U2:合并 ui_page_runlog / ui_page_selftest 的重复实现)
|
||||
* v1.0 2026-10-20 王建锋 创建(W6:合并 ui_page_runlog / ui_page_selftest 的重复实现)
|
||||
* v2.0 2026-10-25 王建锋 记录 reader 注入(道B task-6):删 #include "sys_log.h",
|
||||
* 改 ui_rec_reader_t 函数表 + ui_w_records_bind();
|
||||
* log_id 换不透明编号(UI_W_REC_LOG_*);缓冲容量宏本地化
|
||||
|
||||
@@ -84,7 +84,7 @@ main.c
|
||||
<!-- TASK_TABLE:END -->
|
||||
|
||||
> 上表与 `Src/freertos.c` 的 `osThreadAttr_t` 一致(2026-10-14 校准,9 任务);改动栈大小前先读
|
||||
> `docs/内存管理/RAM使用分析与优化方案.md` 的定栈规则与 `docs/任务规划/基线与待确认.md` 的内存预算项。
|
||||
> `docs/内存管理/RAM使用分析与优化方案.md` 的定栈规则与 `docs/内存管理/RAM预算台账.md` 的预算项。
|
||||
|
||||
### 任务启动顺序管理
|
||||
|
||||
@@ -170,7 +170,7 @@ STM32F4-Base/
|
||||
│ └── host/ # 主机端脚本(PC 侧;@test 入口已迁至 .dsh/skills/serial-boot-test/)
|
||||
│ ├── ch395f_socket_test.py # Socket 通信测试
|
||||
│ ├── ftp_test.py / ftp_up_dl_test.py / ftp_regression.py / ftp_integrity_test.py / ftp_capture.py
|
||||
│ ├── e02_probe.py # E-02 专项探针
|
||||
│ ├── e02_probe.py # 队头阻塞专项探针
|
||||
│ ├── latency_peer.py # 链路时延 PC 对端
|
||||
│ └── time_sync_server.py # 时间同步推送服务
|
||||
├── docs/ # 项目文档(全量索引见 docs/README.md)
|
||||
|
||||
+5
-5
@@ -104,11 +104,11 @@ static int hook_ptr_ok(const void *p)
|
||||
* + 时间戳缓冲并入行缓冲(每个日志调用点原内联 192+32 B);④ 断言/故障钩子
|
||||
* 脱离 HAL 发送回退 + 缓冲静态化(configASSERT 在 FreeRTOS 内部可达 →
|
||||
* 钩子在**每个**任务的最坏链尾)。详见 docs/内存管理/RAM使用分析与优化方案.md
|
||||
* N-33(2026-10-12):B7 定值持久化把静态最坏链推到 **3016 B**(storage_bring_up →
|
||||
* 栈余量收口(2026-10-12):B7 定值持久化把静态最坏链推到 **3016 B**(storage_bring_up →
|
||||
* cfg 写 → f_rename → sync_fs → dhara GC → 控制台回显尾),1024 words 时余
|
||||
* 1080 B < 30% 线(4096×30% = 1228 B)→ 抬至 **1280 words**(5120 B),余
|
||||
* 2104 B / 41%;堆开销 +1024 B。栈值事实源仍是 `.ioc` 的 `FREERTOS.Tasks01`。
|
||||
* netTask 960 words:静态最坏链 1944 B(2026-10-12 N32 监听模块化后复测,链尾经
|
||||
* netTask 960 words:静态最坏链 1944 B(2026-10-12 监听生命周期模块化后复测,链尾经
|
||||
* net_listener_pump→net_relisten_locked→SPI 故障→日志尾;2026-10-10 整改后曾为
|
||||
* 1872 B,整改前 2664 B);3840 B 配置余量 1896 B
|
||||
* (49%),过 stack_report 规则。运行时水位仅 ~0.9 KB(错误尾未
|
||||
@@ -120,7 +120,7 @@ static int hook_ptr_ok(const void *p)
|
||||
* 靠审计 WARN 提示),建监听 → net_bind → CH395F → 日志尾。2026-10-10 整改后
|
||||
* 静态最坏链 1024 B(整改前 1736 B)、实测峰值 616 B;⚠ 余量 1024 B **正好**
|
||||
* 等于 ISR 底线 → 本任务链上再加任何调用(或新日志行)都必须先抬栈。
|
||||
* N-33(2026-10-12):链已加深到 **1264 B**(同一控制台回显尾:`dbg_console_drain`
|
||||
* 栈余量收口(2026-10-12):链已加深到 **1264 B**(同一控制台回显尾:`dbg_console_drain`
|
||||
* → `MX_USART1_UART_Init` → `Error_Handler` → 回显 Cycle),512 words 时余
|
||||
* 784 B < 1024 B ISR 底线 → 抬至 **640 words**(2.5 KB),余 1296 B / 50%;
|
||||
* 堆开销 +512 B。
|
||||
@@ -353,7 +353,7 @@ void MX_FREERTOS_Init(void) {
|
||||
uiTaskHandle = osThreadNew(StartUiTask, NULL, &uiTask_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
/* netTask = 基础线程:上方生成区的实例是全构建唯一实例(P1-6:旧
|
||||
/* netTask = 基础线程:上方生成区的实例是全构建唯一实例(旧
|
||||
* #ifdef ENABLE_NET_LAYER_TESTS 的重复创建已删除)。
|
||||
* - 产品 / NET 层测试模式:正常运行 bring_up + net_poll
|
||||
* - CH395F 硬件层测试模式:net_task_func 入口自挂起(testTask 独占 CH395F) */
|
||||
@@ -373,7 +373,7 @@ void MX_FREERTOS_Init(void) {
|
||||
iec104TaskHandle = osThreadNew(iec104_serv_task, NULL, &iec104Task_attributes);
|
||||
protectTaskHandle = osThreadNew(protect_task, NULL, &protectTask_attributes);
|
||||
|
||||
/* P2-13(2026-10-04):产品构建(未选任何 TEST_SUITE_*)不需要 testTask。
|
||||
/* 2026-10-04:产品构建(未选任何 TEST_SUITE_*)不需要 testTask。
|
||||
* 上方生成区按 .ioc FREERTOS.Tasks01 无条件创建它,其 2048 words(8KB) 栈 + TCB
|
||||
* 取自 CCM 堆;而产品构建下 start_test_task() 仅打印一行后 osDelay(1000) 永久空转,
|
||||
* 白白占用 8KB CCM。此处于内核启动前(MX_FREERTOS_Init 先于 osKernelStart 调用)
|
||||
|
||||
+5
-5
@@ -41,7 +41,7 @@
|
||||
#include "net_select.h"
|
||||
#include "app_main.h"
|
||||
#include "sys_clock.h"
|
||||
#include "task_watchdog.h" /* task_wd_tick_probe():round 8b 时间源存活性取证 */
|
||||
#include "task_watchdog.h" /* task_wd_tick_probe():第 8b 轮时间源存活性取证 */
|
||||
#include "adc_task.h" /* adc_tick_irq_notify():TIM3 采样节拍 → 唤醒 adcTask */
|
||||
/* USER CODE END Includes */
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PM */
|
||||
/* N-20(2026-10-12):本文件唯一的 DBG_* 调用点是 Error_Handler;其帧经
|
||||
/* Error_Handler 统一降级(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 注释。 */
|
||||
@@ -92,7 +92,7 @@ int main(void)
|
||||
{
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
/* round 8b 取证:**必须在 HAL_Init() 之前**把上一轮的"时间源存活性"终值
|
||||
/* 第 8b 轮取证:**必须在 HAL_Init() 之前**把上一轮的"时间源存活性"终值
|
||||
* 快照到 .noinit 的 prev 槽——TIM7 在 HAL_Init 里就 HAL_TIM_Base_Start_IT
|
||||
* 开始 1ms 中断,而开机上报要到 app_main_init(≈117ms)才跑;不先搬走,
|
||||
* 上一轮终值会被本轮开机瞬间的 tick 覆盖(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。 */
|
||||
@@ -238,7 +238,7 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
||||
HAL_IncTick();
|
||||
}
|
||||
/* USER CODE BEGIN Callback 1 */
|
||||
/* round 8b 取证:TIM7 每 1ms 把"时间源存活性"写进复位不丢的 .noinit 槽
|
||||
/* 第 8b 轮取证:TIM7 每 1ms 把"时间源存活性"写进复位不丢的 .noinit 槽
|
||||
* (uwTick + 中断计数)。复位后由 task_wd_seg_report() 打 TICKRUN 行,
|
||||
* 与"进段打点"时刻对比,判定 TIM7(HAL 时基)是否在冻结同刻停摆
|
||||
* (见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。
|
||||
@@ -278,7 +278,7 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
||||
void Error_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN Error_Handler_Debug */
|
||||
/* N-20 统一降级(2026-10-12):旧形态 __disable_irq()+while(1) = 单处外设初始化
|
||||
/* Error_Handler 统一降级(2026-10-12):旧形态 __disable_irq()+while(1) = 单处外设初始化
|
||||
* 失败即整机挂死,只剩 IWDG 兜底且无任何自检日志。现:打 1 条自检日志 +
|
||||
* NVIC_SystemReset()(与 4 个 Fault 处理同款自恢复;IWDG 是最后兜底而非唯一
|
||||
* 出路;复位归因 SFTRSTF → 开机日志 "software (NVIC_SystemReset)")。
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
| 想干什么 | 去看 |
|
||||
|---|---|
|
||||
| 先搞清 CI 需要哪三样东西就位、本机拓扑、部署前置清单 | §0 / §0.1 / §0.2 |
|
||||
| **CI 体积门禁现在怎么守**(同码凭证强制,S14 盲区已关闭;gitlint 空转仍在) | §0.0 + §7 排障表"钩子条数对不上"行 |
|
||||
| **CI 体积门禁现在怎么守**(同码凭证强制,「只声明不执行」那处盲区已关闭;gitlint 空转仍在) | §0.0 + §7 排障表"钩子条数对不上"行 |
|
||||
| 服务端开启 Actions、`DEFAULT_ACTIONS_URL` 到底管什么 | §1(含 §1.2.1,**这条最容易搞反**) |
|
||||
| 下载 / 注册 runner(`--instance` 用回环、labels 必须精确匹配 `runs-on`) | §2.2 |
|
||||
| `config.yaml` 各字段语义(capacity / timeout / labels / 日志目录…) | §2.3 |
|
||||
|
||||
+5
-5
@@ -36,9 +36,9 @@
|
||||
|
||||
---
|
||||
|
||||
## 0.0 ⚠️ CI 体积门禁:从盲区到同码凭证强制(必读,S14 → A-5 残余已销项)
|
||||
## 0.0 ⚠️ CI 体积门禁:从盲区到同码凭证强制(必读,「体积门禁 CI 强制路径」残余已销项)
|
||||
|
||||
> **曾经的盲区已关闭为强制路径(2026-10-25,A-5 残余销项,`task/A5-size-gate`→564715c)。**
|
||||
> **曾经的盲区已关闭为强制路径(2026-10-25,「体积门禁 CI 强制路径」残余销项,`task/A5-size-gate`→564715c)。**
|
||||
> `mem-numbers` 钩子的事实源是 Keil 构建产物 `MDK-ARM/**/*.map`(已 gitignore),CI 刻意不跑
|
||||
> `@build` → 该钩子在远端只会被跳过(现以 `SKIP` 名单诚实显示 Skipped,不再伪装 Passed)。
|
||||
> 补上的强制路径 = **同码凭证**:本地 `@build` 0/0 后 `ram_baseline.py --update` 顺手盖
|
||||
@@ -797,7 +797,7 @@ Get-ChildItem 'C:\ProgramData\anaconda3\Lib\site-packages' -Filter 'cfgv*'
|
||||
|
||||
#### 4.1.2 ⚠️ 关键坑:`doc-links` 钩子「本地过、CI 挂」= 引用了 gitignore 的运行时文件
|
||||
|
||||
**现象**:`文档链接存在性 (D-3/D-7)` 钩子在**本地** `pre-commit run --all-files` 通过,
|
||||
**现象**:`文档链接存在性` 钩子在**本地** `pre-commit run --all-files` 通过,
|
||||
一上 CI 就 `Failed`(exit 1),报的行/路径你在本地明明存在。
|
||||
|
||||
**根因**:CI 的 `actions/checkout@v4` 拉的是**干净克隆**,而钩子校验的是
|
||||
@@ -955,7 +955,7 @@ github.com 会在 checkout 步骤卡住/失败。三种处置:
|
||||
|
||||
**建议**:**先按原样试一次**。若 checkout 步骤报网络错误,再换成上面的零依赖写法(方案 3)。
|
||||
|
||||
### 5.4 产物存档与结果汇总(S3,2026-10-02)
|
||||
### 5.4 产物存档与结果汇总(2026-10-02 起)
|
||||
|
||||
**目标**:CI 失败时**不查 MySQL / 不翻 runner 磁盘**,在 Gitea 页面即可看到完整输出与摘要。
|
||||
|
||||
@@ -1210,7 +1210,7 @@ runner 的日志走 **stderr**,于是上面两个日志文件都栽在这两
|
||||
`commit-msg` stage 的钩子(gitlint)不在其中,靠 `ci_gitlint_range.ps1` 单独守(见 §7);
|
||||
- **`@build` / L1 / L2**:永远留在本地,不要往 CI 加(需 Keil 授权与真机);
|
||||
- **runner 长时间不可用**时:CI 只是"多一道网",**提交前的本地 `ci_local.py` 仍是 L0 的唯一保障**,
|
||||
不能因"已经配了 CI"放宽(见 [docs/任务规划/登记簿.md](../任务规划/登记簿.md) 的 T-7「CI 远端闭环未验证(runner 本机已就绪)」条)。
|
||||
不能因"已经配了 CI"放宽(见 [docs/任务规划/登记簿.md](../任务规划/登记簿.md) 的条目「CI 远端闭环未验证」)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
+15
-15
@@ -11,7 +11,7 @@
|
||||
```
|
||||
docs/
|
||||
├── README.md 本索引(阅读顺序 + 全量清单)
|
||||
├── 任务规划/ 统一待办登记簿(README 排序权威 + 8 个拆分文件)
|
||||
├── 任务规划/ 统一待办(README 单一入口 + 5 个职责文件)
|
||||
├── 产品需求/ 产品需求设计文档(面向产品的需求基线)
|
||||
├── 架构设计/ 全局架构与设计方法论
|
||||
├── 子系统设计/ 各功能子系统的设计说明
|
||||
@@ -31,7 +31,7 @@ docs/
|
||||
```
|
||||
|
||||
> ⚠️ `docs/芯片手册/`(约 120 MB / 1754 张图)为**只读外部输入资料**,与代码演进无关;
|
||||
> 后续按原报告 D-1 建议拆分独立仓库或降采样(登记簿 **N-29**,已决策暂不处理)。
|
||||
> 后续拆分独立仓库或降采样(登记簿条目「仓库体积膨胀观察」,已决策暂不处理)。
|
||||
|
||||
---
|
||||
|
||||
@@ -89,7 +89,7 @@ docs/
|
||||
| [`系统墙钟时间维护说明.md`](子系统设计/系统墙钟时间维护说明.md) | `sys_clock`:RTC 基准、tick 回绕防护、30 天惰性再基准化 |
|
||||
| [`控制台单写者日志设计说明.md`](子系统设计/控制台单写者日志设计说明.md) | 控制台日志单写者模型(避免多任务交错输出) |
|
||||
| [`运行与自检记录设计说明.md`](子系统设计/运行与自检记录设计说明.md) | `/log/run.log`·`selftest.log` 定长记录与待机显示 |
|
||||
| [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5)。**§11 = 任务级饥饿的诊断方法与 `[PROF]` 实测基线**(Task #32 沉淀:判读口径、量化归因、三条脆弱点、明确不做的四条) |
|
||||
| [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5)。**§11 = 任务级饥饿的诊断方法与 `[PROF]` 实测基线**(诊断沉淀:判读口径、量化归因、三条脆弱点、明确不做的四条) |
|
||||
| [`ADC采样节拍设计说明.md`](子系统设计/ADC采样节拍设计说明.md) | **ADC 采集链路采样节拍(现状说明)**:TIM3 1 kHz 更新事件同时触发 TPAFE CONVST 与(经 TRGO)STM32 ADC1 + DMA 乒乓缓冲;节拍源为什么是 TIM3、DMA 硬约束、事件驱动消费与就绪门、实现落点、观测计数、性能特征与设计边界 |
|
||||
| [`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 公开语义、示例、常见问题 |
|
||||
@@ -105,7 +105,7 @@ docs/
|
||||
|---|---|
|
||||
| [`傅氏算法详解.md`](算法/傅氏算法详解.md) | 全周波傅氏(单频 DFT)原理与工程实现:正交性、梳状带通、滑动窗、系数表折算有效值;保护/遥测双轨有效值的算法基础 |
|
||||
| [`采集与算法层设计说明.md`](算法/采集与算法层设计说明.md) | **`App/algo/` 的现状设计说明**:L1 语义取数与有效性判定、L2 采样环/相量/测频、L3 定值模型与元件框架的契约、关键机制(双区原子切换、乒乓半区、自适应测频窗、定值封存-自检-闭锁)、精度判据 J1~J6 与资源足迹 |
|
||||
| [`继电保护功能复现规划.md`](算法/继电保护功能复现规划.md) | **保护功能复现规划**:把参考系统(CLS100)保护链路映射到本项目(STM32F4)的分层功能/性能/参数清单、平台适配(N-18 等)与分阶段路线图 |
|
||||
| [`继电保护功能复现规划.md`](算法/继电保护功能复现规划.md) | **保护功能复现规划**:把参考系统(CLS100)保护链路映射到本项目(STM32F4)的分层功能/性能/参数清单、平台适配(「双转换器无共同采样触发」等)与分阶段路线图 |
|
||||
| [`采样有效性校验设计说明.md`](算法/采样有效性校验设计说明.md) | **`App/algo/adc_health.c` 单文件深设计**:四类判据(静默 / 丢帧 / 序号倒退 / 错误计数增量)的判据推导与 100 ms 门限依据、电平-锁存-事件三种位语义与位号对齐、闭锁与看门狗兜底的责任划分、观测盲区、主机侧验证分工与现场判读手册(含三张配图生成器) |
|
||||
|
||||
---
|
||||
@@ -133,7 +133,7 @@ docs/
|
||||
| [`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 断言点 |
|
||||
| [`时间同步单元测试说明.md`](测试说明/时间同步单元测试说明.md) | time_sync 分片喂狗延时 / 监听退避 / 时间包重组 / 任务主循环(探针窗口心跳缺口的全部逻辑贡献者),80 断言点 |
|
||||
| [`主机单测覆盖率口径.md`](测试说明/主机单测覆盖率口径.md) | **跨全部 16 套 L0.5 的覆盖率**:`scripts/check_test_coverage.py` 用法 / 统计面与生成物例外 / 基线增量门禁(只拦回退)/ **为什么本机拿不到分支覆盖** / 九条限制与盲区 / 未挂 pre-commit 的三条理由与待粘 YAML |
|
||||
|
||||
**模块测试指南**(动手写测试前先读)
|
||||
@@ -183,7 +183,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) |
|
||||
| [`RAM预算台账.md`](内存管理/RAM预算台账.md) | **预算台账**:子系统配额、CCM 分配、趋势基线与 92% 门禁(登记簿条目「缩堆迁 CCM 后半(暂缓)」,2026-10-12) |
|
||||
| `data/` | `ram_report_*.md` / `stack_report_*.md` 快照数据(由 `scripts/` 生成) |
|
||||
|
||||
### 7.3 问题记录(**改驱动前必读**)
|
||||
@@ -225,7 +225,7 @@ docs/
|
||||
> 未闭环项 → [`任务规划/登记簿.md`](任务规划/登记簿.md);内存与 Flash 结论(含整簇暂缓的**重开条件**)
|
||||
> → [`内存管理/RAM使用分析与优化方案.md`](内存管理/RAM使用分析与优化方案.md) §2.5 / §2.6.4 / §4;
|
||||
> UI 侧硬约束 → [`UI设计/UI_设计架构.md`](UI设计/UI_设计架构.md) §1.4;
|
||||
> 六个自有编号族 → [`编号规范.md`](编号规范.md) §4.3(属主文档已删,存量冻结)。
|
||||
> 六个自有编号族 → [`编号规范.md`](编号规范.md) §4.5(属主文档已删,只能删号留事实,别再引用那些号)。
|
||||
### 9.2 环境与部署
|
||||
|
||||
| 文档 | 内容 |
|
||||
@@ -238,7 +238,7 @@ docs/
|
||||
|
||||
| 位置 | 内容 |
|
||||
|---|---|
|
||||
| [`任务规划/`](任务规划/README.md) | **统一待办登记簿(文件夹)**——`README.md` = 排序权威(推进顺序 + 波次语义名)+ 编号空间 + 维护约定;`UI补齐.md` = **当前波次**(21 叶补齐,7 张卡);`数据可信.md` = 收口态(软件侧已 100%,剩 3 张台架/外部卡);`采集保护收尾.md` = 「采集保护收尾」剩余(只剩 M3 送检);`登记簿.md` = N/S/T-x 长期观察;`排期详述.md` / `暂缓-RS485.md` / `测试与架构专项.md` = 波内详表 / 冻结簇 / 专项;`基线与待确认.md` = 待确认(开放问题,OQ 族属主);**`并行分道方案.md` = 待办横向归道(写域 域1~域8 + 道E~道H + 批次 + 合并次序)**;只保留未闭环项,完成即删 |
|
||||
| [`任务规划/`](任务规划/README.md) | **统一待办入口(6 个文件)**:`README.md` = 现在做什么与全项目顺序;`UI补齐.md` = 当前唯一执行清单;`登记簿.md` = 后续任务、外部窗口与长期观察;`数据可信.md` = 三项阻塞任务及代码引用的设计约束;`暂缓-RS485.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/` 生成) |
|
||||
@@ -254,26 +254,26 @@ docs/
|
||||
5. **配图**:走 [`架构设计/图形绘制流程.md`](架构设计/图形绘制流程.md)(`draw-diagram` skill),产物统一放 `images/`,脚本与 `.svg` 一并提交。
|
||||
6. **门禁**:文档提交同样过 pre-commit(乱码 / 行尾空白 / gitlint + **文档链接存在性 `doc-links` / 任务表一致性 `task-table`**,见 [`文档规范.md`](文档规范.md) §4 与 [`代码规范/pre-commit使用说明.md`](代码规范/pre-commit使用说明.md))。
|
||||
|
||||
> **目录/文件名一律不含空格**(D-6,2026-09-25 起强制):空格会迫使脚本、链接、CI 处处加引号,
|
||||
> **目录/文件名一律不含空格**(2026-09-25 起强制):空格会迫使脚本、链接、CI 处处加引号,
|
||||
> 且 URL 编码形态(`UI%20设计`)易被漏改。现全库已无含空格目录。
|
||||
|
||||
---
|
||||
|
||||
## 11. 已知文档问题(待清理)
|
||||
|
||||
> 来源:原报告 D-3/D-7(两条已销项,正文已移入 [`文档规范.md`](文档规范.md) 与 pre-commit 门禁)。
|
||||
> **原报告 D-3 / D-6 / D-7 已处理完毕**(2026-09-25):全库链接存在性检查通过(66 文档 0 失效)、任务表与
|
||||
> 来源:原审查报告两条已销项(正文已移入 [`文档规范.md`](文档规范.md) 与 pre-commit 门禁)。
|
||||
> **原报告三条已处理完毕**(2026-09-25):全库链接存在性检查通过(66 文档 0 失效)、任务表与
|
||||
> `Src/freertos.c` 一致、文档规范已建立。下表保留**仍需跟踪**的项。
|
||||
|
||||
| 问题 | 位置 | 说明 |
|
||||
|---|---|---|
|
||||
| ~~指向未实现工具~~ | ~~`UI_界面制作流程.md`、`UI_自动化测试流程.md` → `tools/ui_mock/`~~ | ✅ 已改指(2026-10-14 核实):两份 UI 文档正文已无该路径(仅存变更记录里的历史提及);golden 回归落在 `simulator/tests/`(四脚本) |
|
||||
| ~~产物缺失~~ | ~~`内存管理/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` 钩子 |
|
||||
| ~~产物缺失~~ | ~~`内存管理/RAM使用分析与优化方案.md` → `scripts/ram_baseline.tsv`~~ | ✅ 已建(2026-10-12,「缩堆迁 CCM 后半(暂缓)」的前半):`ram_baseline.py` + `ram_baseline.tsv` + [`RAM预算台账.md`](内存管理/RAM预算台账.md) + `mem-budget` 钩子 |
|
||||
| ~~目录名含空格~~ | ~~`UI 设计/`~~ | ✅ 已修复(2026-09-25):改名为 `UI设计/`,全仓库 67 处引用同步;命名约定见 [`文档规范.md`](文档规范.md) §1 |
|
||||
| ~~缺一致性门禁~~ | — | ✅ 已落地(2026-09-25/26,文档门禁三档齐):`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) |
|
||||
|
||||
> 未闭环的文档类问题**只剩原报告 D-1 仓库膨胀**(登记簿 **N-29**,已决策暂不处理)。
|
||||
> 未闭环的文档类问题**只剩仓库膨胀**(登记簿条目「仓库体积膨胀观察」,已决策暂不处理)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警
|
||||
| 多页大面积像素差异 | 字体重生成(`ui_font_cn_12.c` 变化)→ 确认字形正确后 `--update` 刷参考 |
|
||||
| 单行/单字差异 | 几何或文案变更 → 先判断是否有意修改;无意 = bug |
|
||||
| 池水位门禁失败但像素一致 | 样式/结构变更(如动态样式转常量、增删框体)→ 确认新值合理后更新常量并登记 |
|
||||
| **只有 `netcfg_save` 一页失败**(保存行值列是 `ERR` 而非 `OK`),其余 10 页全过 | **不是代码回归,是工作目录被污染**:模拟器按**当前工作目录**读写 `sim_config.ini`,而 `--cfgtest` 的 E-01 场景 2 会伪造一份 4000 B 纯注释"雷"文件、并断言"拒写后原文件不动"——**连通过路径也会把它留在原地**。之后每次运行都加载它 → 外科手术保存要在 4000 B 注释后补写受管段 → 输出必超 `CFG_FILE_MAX_LEN` → 引擎**按设计拒写** → 保存行显示 ERR,与"工厂默认态"的黄金参考对不上。处置:两套回归已改为**在空临时目录里跑**(传 `cwd=tmp`),`--cfgtest` 已加外壳无论成败一律删除伪造档;手工跑 `lvgl_sim` 时删掉当前目录的 `sim_config.ini` 即恢复。**禁止为此刷黄金参考**(参考是对的) |
|
||||
| **只有 `netcfg_save` 一页失败**(保存行值列是 `ERR` 而非 `OK`),其余 10 页全过 | **不是代码回归,是工作目录被污染**:模拟器按**当前工作目录**读写 `sim_config.ini`,而 `--cfgtest` 超长守卫的场景 2 会伪造一份 4000 B 纯注释"雷"文件、并断言"拒写后原文件不动"——**连通过路径也会把它留在原地**。之后每次运行都加载它 → 外科手术保存要在 4000 B 注释后补写受管段 → 输出必超 `CFG_FILE_MAX_LEN` → 引擎**按设计拒写** → 保存行显示 ERR,与"工厂默认态"的黄金参考对不上。处置:两套回归已改为**在空临时目录里跑**(传 `cwd=tmp`),`--cfgtest` 已加外壳无论成败一律删除伪造档;手工跑 `lvgl_sim` 时删掉当前目录的 `sim_config.ini` 即恢复。**禁止为此刷黄金参考**(参考是对的) |
|
||||
| `找不到 lvgl_sim.exe` | 模拟器未构建或 build/ 被删 → 先按 §一 重建 |
|
||||
| 终端输出中文乱码 | PowerShell 代码页渲染问题,不影响判定(看 PASS/FAIL 与退出码) |
|
||||
|
||||
|
||||
@@ -64,13 +64,13 @@ 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%)**。现两端对齐为同一子集:
|
||||
10. **两份 `lv_conf.h` 必须是同一特性子集(2026-10-11 定;裁剪 LVGL 死代码时暴露的漂移)**:`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。
|
||||
⚠ 将来引入控件型页面(原型库)时**两端同步**逐项打开并复跑 `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。
|
||||
|
||||
@@ -277,7 +277,7 @@ ENTER/→ 进子框:
|
||||
accept / recv_nb / send / close / 重建三件套的**返回处**调用钩子,uiTask 注册
|
||||
`task_wd_beat(TASK_WD_UI)`。效果 = 心跳间隔从「整轮往返之和」收敛到「单个往返」;
|
||||
只要每次往返都会回来,UI 就不该被记停摆。往返本身**不改**(有界化/请求序号 =
|
||||
`docs/任务规划/README.md` 的 E-27/E-28 跨上下文收口,批 3;§11.6 否决过的
|
||||
`docs/任务规划/README.md` 的跨上下文收口条目,批 3;§11.6 否决过的
|
||||
「给 net_* 裸加超时」不重演)。
|
||||
- **分层**:Drivers/BSP 不得 include App 头(layer-deps),故心跳经回调注入而非直调;
|
||||
钩子内只做置位类动作、不阻塞(契约写在 `Drivers/BSP/UI/ui_net_sink.h`)。
|
||||
@@ -287,7 +287,7 @@ ENTER/→ 进子框:
|
||||
护栏不变)。
|
||||
- **残留暴露**(刻意不归本票):单个往返自身仍无上界(osMessageQueuePut(osWaitForever)
|
||||
+ xTaskNotifyWait(portMAX_DELAY)),netTask 若被单消息拖 >800ms,UI 位连击照成立——
|
||||
那是「判据抓对了人」,处置在 net 侧(E-27/E-28),不在心跳侧。
|
||||
那是「判据抓对了人」,处置在 net 侧,不在心跳侧。
|
||||
|
||||
## 六、变更记录(简表)
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
| 日志 | `syslog_count/get_record` | ✅ 真 | 仅运行、自检 2 类 |
|
||||
| SOE | **无读取 API**(只有 `soe_store_post/drain`) | ❌ 需新增 | `/log/soe.log` 二进制 64 B |
|
||||
| 元件状态 | `prot_elm_state/active_mask/name` | ✅ 真 | 当前态,无历史 |
|
||||
| 功率/电度 | — | ❌ 禁止 | N-18:跨转换器不同步,V0.1 不得算功率 |
|
||||
| 功率/电度 | — | ❌ 禁止 | 双转换器无共同采样触发:跨转换器不同步,V0.1 不得算功率 |
|
||||
| 直流通道 | — | ❌ 无硬件 | `ADC_SEM` 只有 8 电气 + 温度 |
|
||||
| 开入/开出 | — | ❌ 无代码 | 固件完全没有开入开出实现 |
|
||||
|
||||
@@ -379,7 +379,7 @@ int run_par_set_deadband(uint8_t yc_idx, float v); /* 范围校验 */
|
||||
| 1 | `104端口` | `IEC104_PORT`(App 侧常量) |
|
||||
| 2 | `公共地址` | `iec104_serv` 的 CA(当前固定 1) |
|
||||
| 3 | `显示端口` | RemoDispBus 端口常量 |
|
||||
| 4 | `FTP用户` | FTP 配置(`admin`,N-19 鉴权后) |
|
||||
| 4 | `FTP用户` | FTP 配置(`admin`,鉴权启用后) |
|
||||
|
||||
- 需要在 `ui_data_rt_t` **尾部追加**一个 `comm` 子结构(`port`/`ca`/`disp_port` + 8 B 用户名),按 `docs/UI设计/UI_数据供给设计说明.md` §4.3 的扩展规则升 `UI_DATA_VER`。
|
||||
- 规模:**S**(快照 +4 字段,页面 60 行)。
|
||||
|
||||
+1
-1
@@ -266,6 +266,6 @@ ui_page_ac / ui_page_yx / ui_page_setlist ...
|
||||
|
||||
## 9. 与既有规划的关系
|
||||
|
||||
- 本文替代"剩余页面尚未规划"的状态(原 `docs/任务规划/README.md` §2.0 中「UI 扩展」的 U5 条目按"暂缓、未规划"记载)。
|
||||
- 本文替代"剩余页面尚未规划"的状态(原 `docs/任务规划/README.md` §2.0 中「UI 扩展」的剩余 21 叶页面落地项按"暂缓、未规划"记载)。
|
||||
- 页面工作**不应独立于数据层推进**:批 1 的第 1 项(快照层)与采集/遥测链共享同一批数据,建议与"测量子系统"同波次排期。
|
||||
- 与 `docs/问题记录/采集与数据处理短板评测.md` 的对应:该文的标定(`docs/问题记录/采集与数据处理短板评测.md` §4.1)与遥测链(同文 §4.2)落地后,交流量/直流量/遥测系数三页才有真实数据可显示。
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
<rect x="26" y="718.0" width="690" height="120.0" rx="10" fill="#fff1f2" stroke="#e11d48" stroke-opacity="0.45"/>
|
||||
<text x="42" y="740.0" font-size="15" font-weight="700" fill="#e11d48">⑤ 传达员被按在椅子上</text>
|
||||
<text x="211" y="740.0" font-size="11.5" fill="#64748b">队头阻塞</text>
|
||||
<rect x="42" y="750.0" width="477" height="34" rx="7" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="280" y="771.5" font-size="13" fill="#1e293b" text-anchor="middle">症状:FTP banner 迟到 25~30 s(E-02 实测),netTask 频繁冻结半秒</text>
|
||||
<rect x="42" y="750.0" width="465" height="34" rx="7" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="274" y="771.5" font-size="13" fill="#1e293b" text-anchor="middle">症状:FTP banner 迟到 25~30 s(台架实测),netTask 频繁冻结半秒</text>
|
||||
<rect x="42" y="792.0" width="468" height="34" rx="7" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="276" y="813.5" font-size="13" fill="#1e293b" text-anchor="middle">机制:30 s 阻塞收 + 500 ms 盲等忙等,而 netTask 是全楼唯一传达员</text>
|
||||
<rect x="844" y="718.0" width="690" height="120.0" rx="10" fill="#f0fdf4" stroke="#16a34a" stroke-opacity="0.45"/>
|
||||
@@ -101,8 +101,8 @@
|
||||
<rect x="26" y="1038.0" width="690" height="120.0" rx="10" fill="#fff1f2" stroke="#e11d48" stroke-opacity="0.45"/>
|
||||
<text x="42" y="1060.0" font-size="15" font-weight="700" fill="#e11d48">⑦ 只会礼貌送客不会轰人</text>
|
||||
<text x="226" y="1060.0" font-size="11.5" fill="#64748b">close 无 RST(手册定案)</text>
|
||||
<rect x="42" y="1070.0" width="440" height="34" rx="7" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="262" y="1091.5" font-size="13" fill="#1e293b" text-anchor="middle">症状:FIN_WAIT_2 长尾,监听恢复受限于对端 close(E-02 定案)</text>
|
||||
<rect x="42" y="1070.0" width="429" height="34" rx="7" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="256" y="1091.5" font-size="13" fill="#1e293b" text-anchor="middle">症状:FIN_WAIT_2 长尾,监听恢复受限于对端 close(台架定案)</text>
|
||||
<rect x="42" y="1112.0" width="483" height="34" rx="7" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="283" y="1133.5" font-size="13" fill="#1e293b" text-anchor="middle">机制:芯片 close 只发 FIN;对端内核存活但应用挂死 = 回 ACK 却不起身</text>
|
||||
<rect x="844" y="1038.0" width="690" height="120.0" rx="10" fill="#f0fdf4" stroke="#16a34a" stroke-opacity="0.45"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user