串口采集/测试去重:删 tools/serial_tool 与 test/host run_boot_test/verify_log 副本,引用改指 .dsh/skills 串口 skill
This commit is contained in:
@@ -89,6 +89,7 @@ docs/
|
||||
| [`控制台单写者日志设计说明.md`](子系统设计/控制台单写者日志设计说明.md) | 控制台日志单写者模型(避免多任务交错输出) |
|
||||
| [`运行与自检记录设计说明.md`](子系统设计/运行与自检记录设计说明.md) | `/log/run.log`·`selftest.log` 定长记录与待机显示 |
|
||||
| [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5) |
|
||||
| [`ADC采样节拍设计说明.md`](子系统设计/ADC采样节拍设计说明.md) | **ADC 采集链路采样节拍**:现状(任务周期软触发)→ 目标(TIM6 更新中断触发 CONVST)的方案选型、CubeMX 配置规格、代码改造清单、时序/优先级分析与验收判据;**规划中(未实现)** |
|
||||
| [`网络/BSD_Socket_API_使用指南.md`](网络/BSD_Socket_API_使用指南.md) | **上层视图**:BSD Socket API 公开语义、示例、常见问题 |
|
||||
| [`网络/BSD_Socket_API_函数调用关系.md`](网络/BSD_Socket_API_函数调用关系.md) | **内部视图**:netTask / 消息队列 / L3 挂起 / 重试对账的调用关系与设计意图 |
|
||||
| [`网络/Net层阻塞模型架构设计.md`](网络/Net层阻塞模型架构设计.md) | **等待模型**:pending 请求架构、五路完成语义、设计边界与性能特征 |
|
||||
@@ -261,6 +262,7 @@ docs/
|
||||
|
||||
| 日期 | 变更 | 说明 |
|
||||
|---|---|---|
|
||||
| 2026-10-10 | 新增子系统设计 | 新建 `docs/子系统设计/ADC采样节拍设计说明.md`(ADC 采集链路 1 kHz 硬件定时器触发的 CubeMX 配置规格与代码改造清单,B1 遗留项规划);本索引 §5.1 补登记 |
|
||||
| 2026-10-10 | 新增算法专题 | 新建 `docs/算法/继电保护功能复现规划.md`(保护链路复现总规划);本索引 §1 目录树补 `算法/`、§5 拆为 5.1 功能子系统 / 5.2 算法专题并登记 `算法/` 两篇文档 |
|
||||
| 2026-10-05 | 新增产品需求文档 | 新建 `docs/产品需求/产品需求设计文档.md`(面向产品的需求基线);本索引新增 §3 产品需求、§2 推荐阅读顺序置顶、§1 目录树补 `产品需求/`;后续章节号顺延(架构设计 §4 … 变更记录 §12) |
|
||||
| 2026-09-25 | 目录重组 + 索引建立 | 顶层 16 篇散落 md 归入 5 个新目录(`架构设计/` `子系统设计/` `网络/` `审查记录/` `环境与部署/`);全仓库 90 处引用同步;新建本索引(D-5) |
|
||||
|
||||
@@ -249,7 +249,7 @@ flowchart TD
|
||||
## 六、P3:MCU 验证
|
||||
|
||||
1. `@build` → **0 错误 0 警告**(AGENTS.md 强制;有警告看 `MDK-ARM/build_log.txt`)
|
||||
2. `@test`(一键 `test/host/run_boot_test.py`):
|
||||
2. `@test`(一键 `.dsh/skills/serial-boot-test/scripts/run_boot_test.py`):
|
||||
- 采集窗口**先于烧录武装**(脚本 `--flash-cmd` 已保证顺序)
|
||||
- Required:`UI port init: OK;UI menu init: OK` + 本界面定制标记(按屏增补,如页头标题字样)
|
||||
- Forbidden:`FAIL;ERROR;HardFault`
|
||||
|
||||
@@ -550,7 +550,7 @@ mac=00:00:00:00:00:00
|
||||
| 端口 | USART1(PA9 TX / PA10 RX),115200 8N1 |
|
||||
| 用途 | printf 日志 + `@test` 采集通道 |
|
||||
| 配置源 | `.serialtest/config.json`(单一配置源) |
|
||||
| 工具 | 仓库内置 Python 串口助手(`tools/serial_tool/`,常驻 daemon + CLI) |
|
||||
| 工具 | 仓库内置 Python 串口助手(`.dsh/skills/serial-assistant/`,常驻 daemon + CLI) |
|
||||
|
||||
**启动标记(`@test` 判据)**:
|
||||
- Required:`App init OK` / `Network Init` / `net init: OK` / `PHY: 100M FULL`
|
||||
@@ -647,7 +647,7 @@ mac=00:00:00:00:00:00
|
||||
# 构建(@build):0 错 0 警
|
||||
& "MDK-ARM\build.bat" # 退出码 0=PASS / 1=构建失败 / 2=环境错误
|
||||
# 自动化测试(@test):串口采集 → 烧录 → 日志校验
|
||||
& '.venv\Scripts\python.exe' test/host/run_boot_test.py # 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法错误
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py # 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法错误
|
||||
# 全库门禁
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
@@ -287,14 +287,14 @@ void vApplicationMallocFailedHook(void)
|
||||
& '.venv\Scripts\python.exe' scripts/stack_report.py
|
||||
|
||||
# 推荐用法(静态 + 实测一起判,判定取两者更坏者;报告归档到 data/)
|
||||
& '.venv\Scripts\python.exe' scripts/stack_report.py -s tools\serial_tool\state\serial.log `
|
||||
& '.venv\Scripts\python.exe' scripts/stack_report.py -s .dsh\skills\serial-assistant\scripts\state\serial.log `
|
||||
-o 'docs\内存管理\data\stack_report_<日期>.md'
|
||||
```
|
||||
|
||||
| 参数 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `-i/--html` | `MDK-ARM/STM32F407-Demo/STM32F407-Demo.htm` | 静态调用图(Keil 构建产物) |
|
||||
| `-s/--serial` | 无 | 串口日志,解析其中 `[STACK]` 行(daemon 全量历史:`tools/serial_tool/state/serial.log`) |
|
||||
| `-s/--serial` | 无 | 串口日志,解析其中 `[STACK]` 行(daemon 全量历史:`.dsh/skills/serial-assistant/scripts/state/serial.log`) |
|
||||
| `-o/--output` | 无(打印到 stdout) | 报告输出文件(Markdown) |
|
||||
| `--isr-margin` | `1024` | ISR 嵌套预留(字节) |
|
||||
| `--min-free-pct` | `30` | 要求的最小余量百分比 |
|
||||
@@ -616,7 +616,7 @@ V4 逐项相加须与 V1 的 `Image component sizes` 逐 .o 吻合;不等则
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' scripts/ram_report.py -o "docs\内存管理\data\ram_report_<日期>.md" # 静态台账
|
||||
& '.venv\Scripts\python.exe' scripts/stack_report.py -s tools\serial_tool\state\serial.log `
|
||||
& '.venv\Scripts\python.exe' scripts/stack_report.py -s .dsh\skills\serial-assistant\scripts\state\serial.log `
|
||||
-o "docs\内存管理\data\stack_report_<日期>.md" # 栈预算(静态 × 实测)
|
||||
& '.venv\Scripts\python.exe' tools/arch_diagram/gen_memory_svg.py # 内存布局/任务栈预算图
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- Source: `MDK-ARM\STM32F407-Demo\STM32F407-Demo.htm` (Keil static call graph)
|
||||
- Linker run: Mon Sep 21 04:28:54 2026
|
||||
- Global maximum stack usage: **2776 B** (any call tree)
|
||||
- Runtime watermark: 6 task(s) from `tools\serial_tool\state\serial.log`
|
||||
- Runtime watermark: 6 task(s) from `.dsh\skills\serial-assistant\scripts\state\serial.log`
|
||||
|
||||
Rule: `margin = cfg - max(static_max_depth, runtime_peak)` must be >= max(1024 B ISR, 30% of cfg).
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- Source: `MDK-ARM\STM32F407-Demo\STM32F407-Demo.htm` (Keil static call graph)
|
||||
- Linker run: Sat Oct 10 10:13:26 2026
|
||||
- Global maximum stack usage: **2776 B** (any call tree)
|
||||
- Runtime watermark: 5 task(s) from `tools\serial_tool\state\serial.log`
|
||||
- Runtime watermark: 5 task(s) from `.dsh\skills\serial-assistant\scripts\state\serial.log`
|
||||
|
||||
Rule: `margin = cfg - max(static_max_depth, runtime_peak)` must be >= max(1024 B ISR, 30% of cfg).
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ LVGL 集合明细(均在 SRAM):LVGL 池 40,960B(`lv_mem_core_builtin.o`
|
||||
任何 scatter / CCM / 堆大小变更后,依次通过以下全部关卡方可认定生效:
|
||||
|
||||
1. `@build`(0 错误 0 警告)
|
||||
2. 产品构建 `@test`(`test/host/run_boot_test.py`):标记校验 PASS,且**零 `[ERR]`**(默认 Forbidden 已含 `[ERR]`)
|
||||
2. 产品构建 `@test`(`.dsh/skills/serial-boot-test/scripts/run_boot_test.py`):标记校验 PASS,且**零 `[ERR]`**(默认 Forbidden 已含 `[ERR]`)
|
||||
3. 存储套件(`test_config.h` → `TEST_SUITE_STORAGE`):只读模式实测 **3/3 PASSED**(破坏性关,卷内容不变);开启 `ENABLE_DESTRUCTIVE_TESTS` 后为全量集成用例(2026-09-20 实测 **20/20 PASSED**,含 TC-STO-01/02/03/05/06)
|
||||
4. GD5F 套件(`TEST_SUITE_GD5F`):只读模式实测 **16/16 PASSED**(含只读的守卫负路径 TC-GD5F-704);开启 `ENABLE_DESTRUCTIVE_TESTS` 后 **68/68 PASSED**,并确认日志出现 2 条守卫负路径 `[ERR]`(TC-GD5F-704/705)——破坏性模式运行须放宽 Forbidden(`--forbidden "FAIL;HardFault"`)
|
||||
5. 恢复跑(`ENABLE_STORAGE_RECOVERY_TESTS`):期望 **3/3 PASSED**
|
||||
@@ -140,7 +140,7 @@ LVGL 集合明细(均在 SRAM):LVGL 池 40,960B(`lv_mem_core_builtin.o`
|
||||
| DMA 阈值 1024B | `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h`(`GD5F_DMA_THRESHOLD`,公开供测试对齐上下界) |
|
||||
| DMA 可达性运行时守卫 | `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c`(`gd5f_dma_buf_ok`,`gd5f_read_from_cache`/`gd5f_program_load` 两处接入) |
|
||||
| 守卫负路径用例(读/写) | `test/board/gd5f_test_task.c`(TC-GD5F-704/705) |
|
||||
| 错误级日志纳入自动闭环 | `test/host/verify_log.py` + `test/host/run_boot_test.py`(默认 Forbidden 含 `[ERR]`,`[FAIL]` 仍拦截断言失败) |
|
||||
| 错误级日志纳入自动闭环 | `.dsh/skills/serial-boot-test/scripts/verify_log.py` + `.dsh/skills/serial-boot-test/scripts/run_boot_test.py`(默认 Forbidden 含 `[ERR]`,`[FAIL]` 仍拦截断言失败) |
|
||||
| LVGL 越界保护带 32B | `Drivers/BSP/UI/ui_port.c`(`UI_DRAW_BUF_GUARD`) |
|
||||
| 栈溢出/分配失败钩子 | `Inc/FreeRTOSConfig.h` + `Src/freertos.c`(`vApplicationStackOverflowHook` / `vApplicationMallocFailedHook`) |
|
||||
| HardFault 现场上报 | `Src/stm32f4xx_it.c`(`HardFault_Handler` → `hard_fault_report`) |
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
# ADC 采样节拍设计说明(1 kHz 硬件定时器触发)
|
||||
|
||||
> **状态**:**规划中(未实现)**——B1 遗留项。本文件给出 **CubeMX 配置规格**与**代码改造清单**,
|
||||
> 供实施时按序执行;实现并台架验收后按 §7 判定,并把本行状态改为"已实现"。
|
||||
> **上位需求**:`docs/算法/继电保护功能复现规划.md` §3.2 L1-1(周期 CONVST 重触发 1000 Hz,
|
||||
> 与保护节拍同源)。**驱动契约**:`Drivers/BSP/TPAFE5160/README.md`。
|
||||
> **背景**:B1(2026-10-10)已把 `adc_task` 由空桩改为真实消费者,但采样节拍是
|
||||
> **任务周期软触发**(`osDelayUntil`)——CONVST 边沿 = 任务被调度到的时刻,抖动大、
|
||||
> 且与 FreeRTOS tick 同源。本项把节拍源换成**硬件定时器**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与现状
|
||||
|
||||
| 项 | 现状(B1 软触发) | 目标(硬件定时器) |
|
||||
|---|---|---|
|
||||
| CONVST 边沿时刻 | 任务被调度时刻(抖动 = 调度延迟,可达数十 µs~ms) | TIM 更新事件 + ISR 入口延迟(亚 µs~数 µs) |
|
||||
| 周期精度 | 依赖 FreeRTOS tick(TIM7) | 定时器硬件分频,**晶振级** |
|
||||
| 丢拍 | 任务被长时抢占会漏拍 | 硬件自动重载,不漏拍 |
|
||||
| 等周波调频 | 需改 tick 周期(不可行) | 运行期写 ARR 即可(§8) |
|
||||
|
||||
**判据(L1-1)**:周期 CONVST 重触发 1000 Hz;示波器见 U3 `BUSY` 连续脉冲;
|
||||
`tpafe5160_frame_seq()` 持续递增(每秒 +1000)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 方案选型
|
||||
|
||||
### 2.1 方案 A(**推荐**):TIM6 更新中断 → ISR 内脉冲 CONVST
|
||||
|
||||
- TIM6 配成 1 kHz 更新中断;ISR 内调 `tpafe5160_start_conv_irq()`(现成的 GPIO 脉冲)。
|
||||
- **零引脚改动**:PC9 保持 `GPIO_Output`,驱动与 `tpafe5160_init()` 首帧自检**完全不动**。
|
||||
- BUSY(PC7)EXTI 读帧路径**完全不动**。
|
||||
- 周期精度 = 定时器硬件(晶振级);CONVST 边沿抖动 = TIM6 ISR 入口延迟(见 §5)。
|
||||
- 运行期改 ARR 即改变采样率 → 天然支持等周波调频。
|
||||
|
||||
### 2.2 方案 B(备选,暂不采用):TIM3_CH4/TIM8_CH4 硬件 PWM 直驱 PC9
|
||||
|
||||
- PC9 复用为定时器通道,硬件直接产生 CONVST 方波,**零软件抖动**。
|
||||
- **代价**:① 需把 PC9 由 `GPIO_Output` 改为 AF(`.ioc` 引脚改动);
|
||||
② 驱动的 `TPAFE5160_CONVST_*` 宏(GPIO 写)全部失效 → `tpafe5160_init()` 的
|
||||
"确保 CONVST 低"与首帧自检(E-44)都要重写;③ 上电初值/空闲电平语义变化。
|
||||
- **结论**:收益(把抖动从"数 µs"降到"0")不足以抵消改动面与回归风险。
|
||||
**仅当 §5 实测抖动超过算法容限时再考虑**。
|
||||
|
||||
> 下文只展开**方案 A**。
|
||||
|
||||
---
|
||||
|
||||
## 3. CubeMX 配置规格(照此配置)
|
||||
|
||||
**前提**:SYSCLK = 168 MHz / HCLK = 168 MHz / APB1 = 42 MHz(÷4)→ **APB1 定时器时钟 = 84 MHz**;
|
||||
HAL 时基 = TIM7(已占用);**TIM1~TIM6、TIM8~TIM14 当前全部空闲**。
|
||||
|
||||
| 步骤 | 位置 | 值 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 1 | Timers → **TIM6** → *Activated* | ✅ 勾选 | 基础定时器,**无输出引脚**(不占用任何 IO) |
|
||||
| 2 | Prescaler (PSC) | **83** | 84 MHz ÷ (83+1) = **1 MHz** 计数时钟(1 µs 分辨率) |
|
||||
| 3 | Counter Period (ARR) | **999** | 1 MHz ÷ (999+1) = **1.000 kHz**(= 1000 µs 周期) |
|
||||
| 4 | Counter Mode | Up | 向上计数 |
|
||||
| 5 | **auto-reload preload** | **Enable** | 运行期写 ARR 在下一个更新事件生效,不产生毛刺(§8 等周波调频要用) |
|
||||
| 6 | Clock Division | No Division | 与采样无关 |
|
||||
| 7 | Trigger Event Selection | Reset(默认) | 不用 TRGO |
|
||||
| 8 | NVIC Settings → **TIM6 global interrupt** | ✅ 勾选;**Preemption Priority = 6** | 与 BUSY EXTI(`EXTI9_5_IRQn` = 6)**同级**:两者互不抢占,避免"读 RD 时序被 CONVST 脉冲打断"(§5);且 6 ≥ `configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`(=5),将来 ISR 里发任务通知也安全 |
|
||||
|
||||
**生成后**:CubeMX 会新建 `Src/tim.c` + `Inc/tim.h`(含 `htim6` 与 `MX_TIM6_Init()`)、
|
||||
在 `Src/stm32f4xx_it.c` 生成 `TIM6_IRQHandler()`、在 `Src/main.c` 的 `main()` 里插入 `MX_TIM6_Init()`。
|
||||
⚠ **`MX_TIM6_Init()` 只做配置,不启动计数**——启动由用户代码负责(§4 第 4 条)。
|
||||
|
||||
> **为何不用 TIM7 那种"1 MHz 计数 + ARR=999"以外的心跳**:本配置与 HAL 时基 TIM7
|
||||
> 的构造完全一致(同为 84 MHz / 1 MHz / 1000 计数),便于对照排查;PSC 取 83 使 ARR 与
|
||||
> "微秒"一一对应,等周波调频时可直接 `ARR = 周期µs - 1`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 代码改造清单
|
||||
|
||||
> 约定:**生成区不改**(CubeMX 重生成会覆盖);所有手工代码落 `USER CODE` 保护区(L2)。
|
||||
|
||||
### 4.1 `Src/main.c`(CubeMX 生成;改 `USER CODE` 区)
|
||||
|
||||
- `USER CODE BEGIN Includes` 增加 `#include "tpafe5160.h"`。
|
||||
- `HAL_TIM_PeriodElapsedCallback()`(**注意:该回调被 TIM7 时基共用**)增加 TIM6 分支:
|
||||
|
||||
```c
|
||||
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
||||
{
|
||||
if (htim->Instance == TIM7) { HAL_IncTick(); } /* 既有:HAL 时基 */
|
||||
/* USER CODE BEGIN Callback 0 / 1 —— 加在既有 TIM7 分支之后 */
|
||||
if (htim->Instance == TIM6)
|
||||
{
|
||||
tpafe5160_start_conv_irq(); /* 1 kHz 采样节拍:ISR 内脉冲 CONVST(仅 GPIO 写,亚 µs) */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
⚠ **不得**破坏既有 `HAL_IncTick()` / `task_wd_tick_probe()` 两条 TIM7 分支。
|
||||
|
||||
### 4.2 `Src/tim.c` / `Inc/tim.h` / `Src/stm32f4xx_it.c`
|
||||
|
||||
- **CubeMX 自动生成,勿手改**(`TIM6_IRQHandler` → `HAL_TIM_IRQHandler(&htim6)`)。
|
||||
|
||||
### 4.3 `App/app/app_main.c`
|
||||
|
||||
- 增加 `#include "tim.h"`。
|
||||
- 第 213 行的**单次** `tpafe5160_start_conv_irq();` 替换为**启动周期触发**:
|
||||
|
||||
```c
|
||||
/* 6) 启动 1 kHz 采样节拍(TIM6 更新中断 → ISR 内触发 CONVST) */
|
||||
(void)HAL_TIM_Base_Start_IT(&htim6);
|
||||
```
|
||||
|
||||
- ⚠ **必须在 `tpafe5160_init()` 成功之后**再启动 TIM6,否则 ISR 会在驱动自检
|
||||
(首帧有效性探针,E-44)期间乱发 CONVST。
|
||||
|
||||
### 4.4 `App/task/adc_task.c`
|
||||
|
||||
- **删除**主循环里的 `tpafe5160_start_conv_irq();`(触发已交给 TIM6)。
|
||||
- 本任务退化为**纯消费者**:`tpafe5160_snapshot()` + 帧统计 + 10 s 心跳 + 看门狗心跳。
|
||||
1 ms 节拍(`osDelayUntil`)保留,仅用于"消费节奏"。
|
||||
- 更新文件头与函数注释(说明节拍由 TIM6 提供,本任务不再触发)。
|
||||
- **看门狗不变**:`TASK_WD_ADC` 心跳仍在循环内每拍调用。
|
||||
|
||||
### 4.5 文档同步
|
||||
|
||||
- `docs/算法/继电保护功能复现规划.md` §3.2 L1-1:把"🔶 软触发;硬件定时器为后续"改为 ✅。
|
||||
- `docs/待处理内容.md` §2.1:本项(B4)销项。
|
||||
- `AGENTS.md` 架构要点 `adcTask` 条:删"本阶段为任务周期软触发"的注。
|
||||
- `Drivers/BSP/TPAFE5160/README.md`:补一句"运行期周期触发由 TIM6 更新中断提供"。
|
||||
|
||||
---
|
||||
|
||||
## 5. 时序与优先级分析
|
||||
|
||||
**单周期时序(1 kHz,周期 1000 µs)**:
|
||||
|
||||
```
|
||||
t=0 TIM6 更新事件 → ISR → CONVST 脉冲(LOW→HIGH 上升沿)
|
||||
t≈0+ε TPAFE5160 开始转换(BUSY 拉高)
|
||||
t≈23 µs BUSY 拉低 → PC7 EXTI(优先级 6) → 读 8 通道(≈0.5 µs)→ seq++
|
||||
t=1000 µs 下一个 TIM6 更新事件
|
||||
```
|
||||
|
||||
- **读窗口与下一次 CONVST 相隔 ~977 µs**(转换仅 ~23 µs),互不重叠。
|
||||
- **TIM6 与 BUSY EXTI 同级(6)→ 互不抢占**:读 RD 时序(8 次 RD 翻转)不会被打断,
|
||||
避免"读到一半被新转换刷新数据总线"。这是选 6 而非 5 的原因。
|
||||
- **触发抖动**:TIM6 是最高优先级之一(5 为 USART,7 为 SPI/DMA);被更高优先级 ISR
|
||||
(USART,~数 µs)挡住的概率与时长都有限 → 最坏抖动约数 µs,占 1000 µs 的 <0.5%。
|
||||
- **ISR 时长**:`tpafe5160_start_conv_irq()` 只做两次 `HAL_GPIO_WritePin`(寄存器写)
|
||||
→ 亚 µs,不拖累其它中断。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与注意事项
|
||||
|
||||
1. **共用回调**:`HAL_TIM_PeriodElapsedCallback` 同时服务 TIM7(HAL 时基)与 TIM6 →
|
||||
分支必须并列、不得互相干扰;改动后必须确认开机日志时间戳正常(时基未坏)。
|
||||
2. **启动顺序**:TIM6 必须在 `tpafe5160_init()` 之后启动(§4.3)。
|
||||
3. **不新增任务/栈**:本项**零新任务、零新栈、零动态分配** → 不动 `stack_report.py` 预算。
|
||||
4. **CubeMX 生存**:新增生成文件 `Src/tim.c` / `Inc/tim.h`;改后跑一次
|
||||
`python scripts/check_cubemx_survival.py`,若有新增生成文件被登记,按提示重建基线。
|
||||
5. **Keil 工程**:从 CubeMX 重生成工程(含 MDK-ARM)以把 `tim.c` 纳入编译;随后
|
||||
`@build` 必须 0 错 0 警。
|
||||
6. **`mem-numbers`**:本项改 Flash/RAM 极小但非零 → 若 `check_mem_numbers.py` FAIL,
|
||||
跑 `python scripts/check_mem_numbers.py --write` 刷新快照。
|
||||
7. **消费端与生产端同频**:TIM6(84 MHz 派生)与 FreeRTOS tick TIM7(同源)都恰好 1 kHz,
|
||||
相位固定 → 消费端每拍恰好取到 1 帧。**一旦进入等周波调频(采样率≠1 kHz),
|
||||
1 ms 轮询会与生产端失配 → 消费端须改为"帧就绪事件驱动"**(见 §8)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收判据
|
||||
|
||||
| # | 判据 | 手段 |
|
||||
|---|---|---|
|
||||
| 1 | `@build` 0 错 0 警,且镜像无源文件新于 hex | `MDK-ARM/build.bat` + `find -newermt` |
|
||||
| 2 | PC9 `CONVST` 周期 = 1.000 kHz(±0.1%) | 示波器 / 逻辑分析仪 |
|
||||
| 3 | PC7 `BUSY` 每周期一个脉冲,与 CONVST 相位固定 | 示波器双通道 |
|
||||
| 4 | `frame_seq()` 每秒 +1000(每 10 s +10000),严格递增 | `@test` 串口 `[ADC_TASK] frames=... seq=...` |
|
||||
| 5 | `frst_err=0`、`idle` 基本为 0 | 同上 |
|
||||
| 6 | 开机日志时间戳正常(TIM7 时基未坏) | 串口日志 |
|
||||
| 7 | 无 HardFault / `[ERR]` / 看门狗复位 | `@test` Forbidden 判定 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 后续:等周波调频(不在本项范围)
|
||||
|
||||
参考系统按 **20 点/周波**采样:电网频率漂移时需让采样率跟随(50.0 Hz → 1000 Hz;
|
||||
49.5 Hz → 990 Hz),以保持傅氏全波窗对齐一个工频周期。
|
||||
|
||||
- 本配置已为此预留:**1 µs 分辨率** + **ARR 预装载使能** → 运行期
|
||||
`__HAL_TIM_SET_AUTORELOAD(&htim6, 周期µs - 1)` 即可无毛刺改频。
|
||||
- 需要配套:① 测频源(过零/DFT 相位);② 把当前采样率同步给保护算法(相量/定值);
|
||||
③ **消费端改事件驱动**(§6.7)——否则 1 ms 轮询在非 1 kHz 采样率下会丢帧。
|
||||
|
||||
---
|
||||
|
||||
## 9. 变更记录
|
||||
|
||||
| 日期 | 变更 | 说明 |
|
||||
|---|---|---|
|
||||
| 2026-10-10 | 建立本文档 | B1(ADC 采集链路)遗留项"精确 1 kHz 硬件定时器触发"的规划:方案选型(A/B)、CubeMX 配置规格、代码改造清单、时序与优先级分析、风险与验收判据、等周波调频预留 |
|
||||
@@ -572,4 +572,4 @@ python scripts/check_cubemx_survival.py --write # 确认改动无误后重建
|
||||
| 2026-10-09 | **4 个 Fault Handler 由 L3 升级为 L2:重生成完全零 diff** | 用与 iwdg 同一招。`Src/stm32f4xx_it.c` 的 4 个 `__asm void X_Handler` 从**生成区**搬进 `USER CODE BEGIN 0`,且必须写在文件头那 4 个 `#define X X_Gen` **之前**(写在之后会被自己的宏改名);配套的 `#undef X` 随之删除(不再需要)。生成区恢复 CubeMX 原样(`void HardFault_Handler(void)` + `HardFault_IRQn 0` / `W1_HardFault_IRQn 0` 标记区),4 个锚点注释(`HARD-FAULT-ANCHOR` / `MEMFAULT-ANCHOR` / `BUSFAULT-ANCHOR` / `USAGEFAULT-ANCHOR`)全部退役。`.map` 复核:`startup_stm32f407xx.o(RESET) refers to stm32f4xx_it.o(.emb_text) for HardFault_Handler`(向量表仍指向我们的 `__asm` 版)、`.emb_text → i.fault_entry_* → i.hard_fault_report`、4 个 `*_Gen` 各 `Removing ... (2 bytes)`。审计项 `hardfault-handler` / `fault-handlers-3` 由 `wholefile`+锚点改为 `protected`(区 `0`)+ 代码符号断言。**L3 清单清空**,重生成预期零 diff。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 |
|
||||
| 2026-10-10 | **正文按当前代码重写(清理立项前的描述)** | 正文原样保留了大量**立项前(2026-09-29)**的状态取证与已废弃的"路径 C 考古",与现状矛盾,故重写为现状设计说明(1144 → 571 行;**历史与逐次决策全部保留在下方变更记录,未删**)。主要更正:① §1.1"现状:无任何硬件看门狗"改为**历史背景**(IWDG 已在 `.ioc`/`hal_conf`/工程三处就位);② **ftpTask 早已纳入监视**——经 `lftpd_task_wd_beat()` 弱钩子(事件循环 / RETR·STOR 分片 / 连接与卷锁等待),旧正文"首期不埋点、由掩码剔除"及 R3/O1 已作废;③ 补齐正文缺失的三块现状实现:**FTL 心跳路由**(`task_wd_bind()` + `nand_ftl_wd_beat()`)、**FTL 直接喂狗**(`nand_ftl_wd_feed()`,含"设备存活但功能冻结"边界)、**round 7/8/8b 取证机制**(`/log/wdev.log`、`.noinit` 槽 + `UNINIT_CCM`、`PREVRUN`/`TICKRUN`/`MISS start`/`STALL`);④ 掩码改为按代码实况列表(产品 `0x37` / `ENABLE_HW_LAYER_TESTS` `0x35` / **testTask 任何构建都不入掩码**)并写明"精确相等"语义;⑤ 复位原因优先级按 `reset_cause_str()` 实际顺序改写(旧表把 POR 排第一,代码是 **IWDG 最优先**);⑥ §4.6 更正为**已集成**:看门狗复位写 `selftest.log`(旧正文"未改");⑦ §8 预算改用 `.map` 实测(`task_watchdog.o` 1098 B 代码 / 44+104 B RAM / `.noinit` 48 B;`iwdg.o` 仅 28 B,`HAL_IWDG_Init` 94 B 被移除);⑧ 附录 B 自相矛盾修正(旧表把"借助 CubeMX"列为否决,实际已选它);⑨ §7 由"验证计划"改为**验证记录**(V2/V4/V5 ✅、U1 39 断言 ✅,V3/V6/V7 标为未单独注入、V8 未做)。连带同步:`Src/iwdg.c`、`Src/freertos.c`、`App/task/default_task.c` 三处注释的章节引用(旧 §4.5/§4.6 → 新 §4.1/§4.4),`scripts/check_cubemx_survival.py` 提示语(§10.1 → §10.2/§10.3),`实机测试实施指南.md`(§10 → §7 ×3)、`看门狗单元测试说明.md`(§10 → §7)、`模拟器差距清单.md` H-1(§4.9 → §4.7,并更正"模拟器不编译本模块、无 IWDG 桩")、`docs/README.md`("实施中/待验证" → 已闭环)。⚠️ **下方历史行里的 §x.y 引用指当时编号**,不再回溯改写 |
|
||||
| 2026-10-10 | **adcTask 纳入监视(B1):必需掩码 `0x37`→`0x77`** | B1(ADC 采集链路)落地时新增 `adcTask`(`App/task/adc_task.c`:1 ms 采样节拍触发 CONVST + 消费 `tpafe5160_snapshot()`)并纳入 IWDG 监视:`task_wd_id_t` 新增 `TASK_WD_ADC = 6`;`wd_required_mask()` 产品 `0x37`→`0x77`、`ENABLE_HW_LAYER_TESTS` `0x35`→`0x75`(adcTask **不依赖任何就绪屏障** → 任何构建都置位)。adcTask 主循环每 1 ms `task_wd_beat(TASK_WD_ADC)`;**不做 NAND I/O → 不登记 FTL 心跳路由**(无需 `task_wd_bind()`)。主机侧单测 `beat_all()` 与掩码断言(`tc_003`/`tc_005`/`tc_011` 的 `0x37`→`0x77`)同步,39 断言全绿。任务由 `Src/freertos.c` 的 `USER CODE` 区手工创建(同 timeSyncTask,不在 `.ioc` 里),栈 512 words、优先级 Normal,`stack_report.py` 实测最坏链 504 B(余 1544 B / 75%)。见 `docs/算法/继电保护功能复现规划.md` §7 |
|
||||
| 2026-10-10 | **B1 `@test` 实机闭环:adcTask 掩码真机验证** | 台架闭环(`test/host/run_boot_test.py`)确认 `adcTask` 纳入掩码后**无 adc 相关误复位**:`[STACK] adc peak_used=496/2048 free_min=1552 (75%)`、`frames/seq` 每 10 s 严格 +10000(=1000 Hz)、`idle=0`/`frst_err=0`、全片回读 `No difference found`。启动早期两次 `MISS start snap=0x67 req=0x77` 仅缺 bit4(`timeSync` 未启动,属正常就绪窗;adc 的 bit6 自始置位),掩码随后收敛、全程无复位。同轮另修 `dbg_console_writer_ready()` 门控(adcTask 周期日志在写者注册前抑制,见 `控制台单写者日志设计说明.md`) |
|
||||
| 2026-10-10 | **B1 `@test` 实机闭环:adcTask 掩码真机验证** | 台架闭环(`.dsh/skills/serial-boot-test/scripts/run_boot_test.py`)确认 `adcTask` 纳入掩码后**无 adc 相关误复位**:`[STACK] adc peak_used=496/2048 free_min=1552 (75%)`、`frames/seq` 每 10 s 严格 +10000(=1000 Hz)、`idle=0`/`frst_err=0`、全片回读 `No difference found`。启动早期两次 `MISS start snap=0x67 req=0x77` 仅缺 bit4(`timeSync` 未启动,属正常就绪窗;adc 的 bit6 自始置位),掩码随后收敛、全程无复位。同轮另修 `dbg_console_writer_ready()` 门控(adcTask 周期日志在写者注册前抑制,见 `控制台单写者日志设计说明.md`) |
|
||||
|
||||
@@ -166,19 +166,19 @@ ST-LINK_CLI -c SWD HOTPLUG -Dump 20000000 1000000 dump.bin :: 转储 SRAM
|
||||
**A. 只重启不烧录**(固件没变,重新验证启动/串口行为;配合 serial_tool)
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' tools/serial_tool/serial_tool.py open
|
||||
& '.venv\Scripts\python.exe' tools/serial_tool/serial_tool.py clear
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-assistant/scripts/serial_tool.py open
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-assistant/scripts/serial_tool.py clear
|
||||
& "MDK-ARM\reset.bat" # 等价于 ST-LINK_CLI -c SWD -Rst -Run -Q
|
||||
# 等待开机完成后直接读日志文件校验标记:
|
||||
Get-Content tools\serial_tool\state\serial.log -Tail 100
|
||||
Get-Content .dsh\skills\serial-assistant\scripts\state\serial.log -Tail 100
|
||||
```
|
||||
|
||||
顺序关键:**先开串口清日志,再复位**,否则错过开机头几行(与 `run_boot_test.py` 的"采集先于烧录"原则一致)。
|
||||
ⓞ 实测补充三条:
|
||||
|
||||
1. **改过 `serial_config.json` 端口后必须重启 daemon**:daemon 在启动时缓存配置,改端口后 `close` + 杀旧进程再 `open`,否则一直按旧端口报 `port not found`;
|
||||
1. **改过 `.serialtest/config.json` 端口后必须重启 daemon**:daemon 在启动时缓存配置,改端口后 `close` + 杀旧进程再 `open`,否则一直按旧端口报 `port not found`;
|
||||
2. **(已修,2026-10-10)旧版 `wait-idle` 在复位突发期会报 `daemon unresponsive after auto-start: timed out`**:CLI 把**超时**误判成"daemon 不可达",顺手拉起一个绑不上 HTTP 口的实例再把命令撞一次超时(数据其实已落盘)。现 CLI 超时按 `--max-ms` 自动推导、超时只快速失败不再误拉起实例;
|
||||
3. **日志校验**:`tools/serial_tool/state/serial.log` 用 Read/Grep 直接查最直观;`serial_tool.py log --grep <标记>` 现已可作判定依据(2026-10-10 修:旧实现先 `--tail`(默认 50)后 grep,开机标记必然查不到——这就是当年"实测 0 命中、语义存疑"的真根因)。
|
||||
3. **日志校验**:`.dsh/skills/serial-assistant/scripts/state/serial.log` 用 Read/Grep 直接查最直观;`serial_tool.py log --grep <标记>` 现已可作判定依据(2026-10-10 修:旧实现先 `--tail`(默认 50)后 grep,开机标记必然查不到——这就是当年"实测 0 命中、语义存疑"的真根因)。
|
||||
|
||||
**B. 完整烧写 + 运行**:`MDK-ARM\reset.bat flash [xxx.hex]`(= `-c SWD -P <hex> -Rst -Run`;09/19 实测 1MB ≈ 6.5s,09/20 复测 6.47s + 全片 `-CmpFile` 逐字节一致)。正式流程仍以 `download.bat`(`UV4 -f`)为准,CLI 路径作为脚本化/快速路径。
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# 启动日志(boot_log)存档
|
||||
|
||||
本目录保存 `@test`(`test/host/run_boot_test.py`)产出的**开机串口日志快照**,
|
||||
本目录保存 `@test`(`.dsh/skills/serial-boot-test/scripts/run_boot_test.py`)产出的**开机串口日志快照**,
|
||||
用于**跨构建的栈 / 堆水位对照**(解析其中的 `[STACK]` 行)。
|
||||
|
||||
## 1. 为什么需要存档
|
||||
|
||||
- `@test` 默认把 boot_log 写到临时目录 `%TEMP%\opencode\boot_log_<时间戳>.txt`——**会被系统清理**;
|
||||
- `@test` 默认把 boot_log 写到临时目录 `%TEMP%\serial-boot-test\boot_log_<时间戳>.txt`——**会被系统清理**;
|
||||
- 台架长跑的 20MB 串口日志**截头会丢掉早期 `[STACK]` 水位行**(开机首次约 5s 后才报告);
|
||||
- 两者叠加导致**无法事后回溯**"改代码前后"的栈水位变化。
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
| 项 | 规则 |
|
||||
|---|---|
|
||||
| 来源 | `& '.venv\Scripts\python.exe' test/host/run_boot_test.py`(默认 `--out-dir %TEMP%\opencode`,产出 `boot_log_<stamp>.txt` + `selftest_log_<stamp>.log`) |
|
||||
| 来源 | `& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py`(默认 `--out-dir %TEMP%\serial-boot-test`,产出 `boot_log_<stamp>.txt` + `selftest_log_<stamp>.log`) |
|
||||
| 命名 | `<日期>_<构建标识>_boot_log.txt`,例:`20261005_prod_none_boot_log.txt` |
|
||||
| 构建标识 | 建议含:产品 / 测试套件(`prod_none` / `suite_ch395f` …)+ 负载(`idle` / `v5_30min` / `ftp_stress`) |
|
||||
| 随文件登记 | 在下方 §3 清单登记日期、构建、负载、关键 `[STACK]` 水位(如 defaultTask 峰值) |
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
|
||||
```powershell
|
||||
& "MDK-ARM\build.bat"
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py clear
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py clear
|
||||
& '.venv\Scripts\python.exe' test\latency_peer.py --host 192.168.1.100 --port 8080 `
|
||||
--json "docs\测试说明\m2_data\m2_quiet_runNN.json" --wait 170 # 后台作业
|
||||
& "MDK-ARM\download.bat"
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py wait-idle `
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py wait-idle `
|
||||
--timeout 120 --settle-ms 10000 --max-ms 110000
|
||||
# 判定:Grep tools\serial_tool\state\serial.log
|
||||
# 判定:Grep .dsh\skills\serial-assistant\scripts\state\serial.log
|
||||
```
|
||||
|
||||
判定标记:Required `App init OK` / `Network Init` / `net init: OK` / `[LAT] report complete` /
|
||||
|
||||
+16
-16
@@ -2,7 +2,7 @@
|
||||
|
||||
> **定位**:面向使用者的操作手册——命令怎么敲、输出怎么看、常见场景怎么组合、出问题怎么排查。
|
||||
> **关联文档**:设计决策与实现细节见 `串口助手设计说明.md`;@test 自动化闭环见 `自动化测试流程.md`;`AGENTS.md`「串口调试」节有一页速查。
|
||||
> **版本**:v1.1(与 `tools/serial_tool/serial_tool.py` 当前实现一致,2026-09-15)
|
||||
> **版本**:v1.1(与 `.dsh/skills/serial-assistant/scripts/serial_tool.py` 当前实现一致,2026-09-15)
|
||||
|
||||
## 1. 它是什么(60 秒版)
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
|---|---|---|
|
||||
| **常驻 daemon** | 后台 Python 进程,**串口唯一持有者**,把 RX/TX 持续写进日志文件 | 不用管它——任何命令发现它没在跑会**自动拉起** |
|
||||
| **薄 CLI** | `serial_tool.py <命令>`,每条命令都是带 5s 硬超时的短操作 | 你唯一直接打交道的界面 |
|
||||
| **日志文件** | `tools/serial_tool/state/serial.log`,**全量历史就在这里** | 想回看任何时刻的数据 → 直接读这个文件 |
|
||||
| **日志文件** | `.dsh/skills/serial-assistant/scripts/state/serial.log`,**全量历史就在这里** | 想回看任何时刻的数据 → 直接读这个文件 |
|
||||
|
||||
**统一入口**(仓库根目录执行,任意 cwd 均可):
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' <命令> [参数]
|
||||
& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' <命令> [参数]
|
||||
```
|
||||
|
||||
两条心法:
|
||||
@@ -28,19 +28,19 @@
|
||||
|
||||
```powershell
|
||||
# ① 看本机有哪些 COM 口(不依赖 daemon;configured = 配置目标口)
|
||||
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' ports
|
||||
& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' ports
|
||||
|
||||
# ② 开串口(自动拉起 daemon;端口取 .serialtest/config.json,也可显式指定)
|
||||
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' open
|
||||
& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' open
|
||||
|
||||
# ③ 发一条数据(文本;--hex 发字节;--nl 末尾加 CRLF)
|
||||
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' send "hello" --nl
|
||||
& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' send "hello" --nl
|
||||
|
||||
# ④ 看最近 20 行日志(含刚发的 TX 和收到的 RX)
|
||||
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' log --tail 20
|
||||
& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' log --tail 20
|
||||
|
||||
# ⑤ 不用了:关串口(daemon 继续常驻;要彻底退出用 stop)
|
||||
& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' close
|
||||
& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' close
|
||||
```
|
||||
|
||||
## 3. 命令速查
|
||||
@@ -163,8 +163,8 @@
|
||||
- 等价的手动读法(Agent 用 Read/Grep 工具、人用 PowerShell 均可):
|
||||
|
||||
```powershell
|
||||
Get-Content 'tools\serial_tool\state\serial.log' -Tail 20
|
||||
Select-String -Path 'tools\serial_tool\state\serial.log' -Pattern 'App init OK'
|
||||
Get-Content '.dsh\skills\serial-assistant\scripts\state\serial.log' -Tail 20
|
||||
Select-String -Path '.dsh\skills\serial-assistant\scripts\state\serial.log' -Pattern 'App init OK'
|
||||
```
|
||||
|
||||
### 3.7 `clear` — 清空日志(代际 +1)
|
||||
@@ -229,9 +229,9 @@ Select-String -Path 'tools\serial_tool\state\serial.log' -Pattern 'App init OK'
|
||||
|
||||
| 文件 | 内容 | 谁读 |
|
||||
|---|---|---|
|
||||
| `tools/serial_tool/state/serial.log` | RX/TX/SYS 全量日志 | 你(Read/Grep/`log` 命令) |
|
||||
| `tools/serial_tool/state/meta.json`(运行时未创建) | daemon pid / 版本 / 当前 gen / 口配置快照 | 工具内部 |
|
||||
| `tools/serial_tool/state/daemon.log` | daemon 自身 stderr(启动行、异常) | 排障时看 |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/serial.log` | RX/TX/SYS 全量日志 | 你(Read/Grep/`log` 命令) |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/meta.json`(运行时未创建) | daemon pid / 版本 / 当前 gen / 口配置快照 | 工具内部 |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/daemon.log` | daemon 自身 stderr(启动行、异常) | 排障时看 |
|
||||
|
||||
整个 `state/` 目录已 gitignore——纯本机运行时产物。
|
||||
|
||||
@@ -283,7 +283,7 @@ Select-String -Path 'tools\serial_tool\state\serial.log' -Pattern 'App init OK'
|
||||
... serial_tool.py log --tail 0 # 取本轮全量 → 人眼/脚本检查标记
|
||||
```
|
||||
|
||||
自动化版本 = `test/host/run_boot_test.py`(同一序列 + 烧录 + 标记校验,见 `自动化测试流程.md`)。
|
||||
自动化版本 = `.dsh/skills/serial-boot-test/scripts/run_boot_test.py`(同一序列 + 烧录 + 标记校验,见 `自动化测试流程.md`)。
|
||||
|
||||
### 5.3 长时运行监控
|
||||
|
||||
@@ -298,7 +298,7 @@ daemon 常驻,日志持续累积(20MB 自动截头),几天不关也没
|
||||
|
||||
```powershell
|
||||
... serial_tool.py status # ① state/error/counters 一眼看全
|
||||
Get-Content 'tools\serial_tool\state\daemon.log' -Tail 30 # ② daemon 自身输出
|
||||
Get-Content '.dsh\skills\serial-assistant\scripts\state\daemon.log' -Tail 30 # ② daemon 自身输出
|
||||
... serial_tool.py restart # ③ 重建 daemon(顺带加载新代码)
|
||||
```
|
||||
|
||||
@@ -337,7 +337,7 @@ Get-Content 'tools\serial_tool\state\daemon.log' -Tail 30 # ② daemon 自身
|
||||
| `state: error` + `error: read: …` | 读串口出错/拔线 | daemon 仍活着;重插后 `open` 即恢复(自动 1.5s 退避) |
|
||||
| 改了 `serial_tool.py` 行为没变 | 运行中的 daemon 还是旧代码 | `… serial_tool.py restart` |
|
||||
| 开机日志"缺尾巴"(如缺 `Network Init`) | ① `--settle-ms` 太小,在 boot 自然静默间隙就收尾;② 首字节晚于 `--settle-ms` 到达,静默计时提前触发 | 用默认 8000(`run_boot_test.py` 已内置);2026-10-10 起首字节到达前不启动静默计时,必要时调 `--first-timeout-ms` |
|
||||
| `open` 报 `no port specified … [config: …]` | `serial_config.json` 缺失 / JSON 语法错 / `baudRate`·`httpPort` 非整数(现按 `utf-8-sig` 读并**显式回显**错误,不再静默回退成"没配端口") | 按 `config_error` 文本修文件;`status` 与 `ports` 也回显 `config_error` |
|
||||
| `open` 报 `no port specified … [config: …]` | `.serialtest/config.json` 缺失 / JSON 语法错 / `baudRate`·`httpPort` 非整数(现按 `utf-8-sig` 读并**显式回显**错误,不再静默回退成"没配端口") | 按 `config_error` 文本修文件;`status` 与 `ports` 也回显 `config_error` |
|
||||
| 旧日志"丢了" | 20MB 截头丢的是**文件头** | 查旧内容要看尾部;重要轮次开始前先 `clear` 分代 |
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
@@ -63,8 +63,8 @@ flowchart LR
|
||||
D --> L[("state/daemon.log<br/>daemon 自身 stderr")]
|
||||
```
|
||||
|
||||
- **单文件实现**:`tools/serial_tool/serial_tool.py`(stdlib + pyserial,约 1000 行);解释器统一 `.venv\Scripts\python.exe`
|
||||
- **Agent 调用式**:`& '.venv\Scripts\python.exe' 'tools/serial_tool/serial_tool.py' <命令> [参数]`(任意 cwd;state 目录按脚本位置解析,与 cwd 无关)
|
||||
- **单文件实现**:`.dsh/skills/serial-assistant/scripts/serial_tool.py`(stdlib + pyserial,约 1000 行);解释器统一 `.venv\Scripts\python.exe`
|
||||
- **Agent 调用式**:`& '.venv\Scripts\python.exe' '.dsh/skills/serial-assistant/scripts/serial_tool.py' <命令> [参数]`(任意 cwd;state 目录按脚本位置解析,与 cwd 无关)
|
||||
- **历史 = 日志文件**:Agent 用 Read/Grep 直接看历史,无需专门 API(`log` 命令只是便捷封装)
|
||||
- **配置沿用** `.serialtest/config.json`(`portName`/`baudRate` 为默认值;新增可选字段 `httpPort`,默认 18765)
|
||||
|
||||
@@ -128,9 +128,9 @@ flowchart LR
|
||||
|
||||
| 文件 | 用途 |
|
||||
|---|---|
|
||||
| `tools/serial_tool/state/serial.log` | RX/TX/SYS 全量日志(Agent 直接读) |
|
||||
| `tools/serial_tool/state/meta.json`(运行时未创建) | daemon 元数据(pid / 版本 / 当前 gen) |
|
||||
| `tools/serial_tool/state/daemon.log` | daemon 自身 stderr(排障用) |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/serial.log` | RX/TX/SYS 全量日志(Agent 直接读) |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/meta.json`(运行时未创建) | daemon 元数据(pid / 版本 / 当前 gen) |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/daemon.log` | daemon 自身 stderr(排障用) |
|
||||
|
||||
### 5.2 行格式
|
||||
|
||||
@@ -167,7 +167,7 @@ flowchart LR
|
||||
|
||||
| 项 | 规定 |
|
||||
|---|---|
|
||||
| 单实例 | HTTP 端口(默认 18765,`serial_config.json` 的 `httpPort` 可改)绑定即锁;端口被占 = 已有实例。Windows 上真独占靠 `SO_EXCLUSIVEADDRUSE`(**真名,值 -5**;2026-10-10 修:旧代码写成 `SO_EXCLUSIVEADDRPLACE` + 数值 `0x0040`,`setsockopt` 抛 `WinError 10042` 被静默吞掉,"独占绑定"从未生效),失败现打 WARN,`status.daemon.exclusive_bind` 可验。⚠️ 进程列表里出现**两个** `--serve` 是正常的:`.venv\Scripts\python.exe` 是 venv stub,会派生基解释器(本机 anaconda)子进程跑真 daemon(`status.daemon.pid` = 子进程),不是多实例 |
|
||||
| 单实例 | HTTP 端口(默认 18765,`.serialtest/config.json` 的 `httpPort` 可改)绑定即锁;端口被占 = 已有实例。Windows 上真独占靠 `SO_EXCLUSIVEADDRUSE`(**真名,值 -5**;2026-10-10 修:旧代码写成 `SO_EXCLUSIVEADDRPLACE` + 数值 `0x0040`,`setsockopt` 抛 `WinError 10042` 被静默吞掉,"独占绑定"从未生效),失败现打 WARN,`status.daemon.exclusive_bind` 可验。⚠️ 进程列表里出现**两个** `--serve` 是正常的:`.venv\Scripts\python.exe` 是 venv stub,会派生基解释器(本机 anaconda)子进程跑真 daemon(`status.daemon.pid` = 子进程),不是多实例 |
|
||||
| 拉起方式 | CLI detached 子进程(`CREATE_NO_WINDOW`),解释器 = `sys.executable`(保证同一 .venv) |
|
||||
| `open` 语义 | 幂等(同端口已开 = 成功返回,**不重开**);端口不存在 → ≤1s 报错并附可用端口列表;被占用 → ≤1s 报错(Windows 共享冲突即时返回)并提示持有者 |
|
||||
| DTR/RTS | 默认**不翻转**(`dtr=False, rts=False`,防 STM32 自动复位电路误复位目标板);`--dtr/--rts` 可显式置位 |
|
||||
@@ -201,7 +201,7 @@ flowchart LR
|
||||
| 对象 | 变更 |
|
||||
|---|---|
|
||||
| `requirements.txt` | 增 `pyserial`(**保持纯 ASCII** 注释) |
|
||||
| `.gitignore` | 增 `tools/serial_tool/state/` |
|
||||
| `.gitignore` | 增 `.dsh/skills/serial-assistant/scripts/state/` |
|
||||
| `opencode.json`(仓库根) | **删除 `mcp.serial` 段**(指向缺失二进制,已失效) |
|
||||
| `测试说明/serial-mcp-server_串口调试.md` | **已删除**(内容精华并入本文;工具历史见 §12) |
|
||||
| `AGENTS.md` | "串口调试"节改写为 serial_tool(配置源仍 `serial_config.json`);"自动化测试"节同步新流程 |
|
||||
@@ -238,7 +238,7 @@ flowchart LR
|
||||
| # | 步骤 | 产物 | 验证 |
|
||||
|---|---|---|---|
|
||||
| S1 | `requirements.txt` 增 pyserial + `.venv` 安装 | requirements.txt | `pip show pyserial` 有版本 ✅ |
|
||||
| S2 | daemon 核心:HTTP 服务 + 串口读线程 + 行日志写(含 clear/截头/DTR 策略) | `tools/serial_tool/serial_tool.py`(daemon 部分) | `start→status→send→stop` 手动走通 ✅ |
|
||||
| S2 | daemon 核心:HTTP 服务 + 串口读线程 + 行日志写(含 clear/截头/DTR 策略) | `.dsh/skills/serial-assistant/scripts/serial_tool.py`(daemon 部分) | `start→status→send→stop` 手动走通 ✅ |
|
||||
| S3 | CLI 全子命令 + 自动拉起 + 超时契约 + `selftest` | serial_tool.py(CLI 部分) | `selftest` 0=PASS + 命令走查 ✅ |
|
||||
| S4 | 验收 A1–A6、A8 | 测试通过记录 | A1–A5/A8 PASS ✅(本机无 CH340,A6 待硬件) |
|
||||
| S5 | @test 集成:`run_boot_test.py` 采集步改写 + 参数收敛 | run_boot_test.py | 负路径(端口缺失→exit 2)+ 假烧录全链路 + verify 正/负判定 ✅;**A7 正路径待 CH340 接入** |
|
||||
@@ -253,7 +253,7 @@ flowchart LR
|
||||
| O1 | 换机器缺 CH340 驱动 → `ports` 无目标口 | `ports` 返回带提示("未找到 CH340,请装驱动");文档列驱动要求 |
|
||||
| O2 | 截头瞬间 Agent 恰在读文件 → 视图错位 | 已知可接受:Agent 读**尾部**;`log` 命令经 daemon 读更稳 |
|
||||
| O3 | daemon 强杀瞬间丢 ≤1 行(内存残行) | 可接受(debug 日志场景);`close` 路径保证冲刷 |
|
||||
| O4 | HTTP 端口 18765 与其他服务冲突 | `serial_config.json` `httpPort` 可改 |
|
||||
| O4 | HTTP 端口 18765 与其他服务冲突 | `.serialtest/config.json` `httpPort` 可改 |
|
||||
| O5 | TX 用途(本工程 USART1 单向输出) | 保留能力,面向未来交互/RS485 调试;v1 仅保证进日志 |
|
||||
| O6 | daemon 常驻内存(Python 进程 ~20MB) | 可接受;`stop` 即释放 |
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
| A5 | 全量静态门禁 | `pre-commit run --all-files` | **17/17 Passed**(2026-10-03 实测;含新增 S5 `test-cppcheck`) |
|
||||
| A6 | ST-LINK 探针可见 | `& "MDK-ARM\reset.bat" list`(CLI 经 `DTU_STLINK_CLI` / PATH 解析) | 列出探针 SN `0B250D12A218303030303032` |
|
||||
| A7 | 串口端口正确 | 检查 `.serialtest/config.json` 的 `portName` | 端口单一配置源 = 此文件(当前 COM6,2026-09-21 台架重插后由 COM7 变更);变更后先杀旧 daemon 再 `open` |
|
||||
| A8 | 串口助手 daemon 可用 | `& '.venv\Scripts\python.exe' tools/serial_tool/serial_tool.py status` | 返回 JSON,`open: true/false` 可读 |
|
||||
| A8 | 串口助手 daemon 可用 | `& '.venv\Scripts\python.exe' .dsh/skills/serial-assistant/scripts/serial_tool.py status` | 返回 JSON,`open: true/false` 可读 |
|
||||
|
||||
> ⚠️ **A4 的陷阱**(2026-10-02 实测):`@build` 是**增量**构建,`run_all_suites.py` 跑完
|
||||
> 套件后 `.map` 会停在**测试套件体积**;仅还原 `test_config.h` 不够,**必须再编一次产品固件**
|
||||
@@ -71,10 +71,10 @@
|
||||
|
||||
```bash
|
||||
# 一键闭环(selftest.log 自动 FTP 抓取;无故障记录 = PASS)
|
||||
& '.venv\Scripts\python.exe' test/host/run_boot_test.py --require-selftest-pass --device-ip 192.168.1.100
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py --require-selftest-pass --device-ip 192.168.1.100
|
||||
|
||||
# 或手动联合判定(本轮串口日志 + selftest.log 本地副本)
|
||||
& '.venv\Scripts\python.exe' test/host/verify_log.py \
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/verify_log.py \
|
||||
--log-path <本轮串口日志> \
|
||||
--require-selftest-pass \
|
||||
--selftest-log <下载的 selftest.log 本地副本>
|
||||
@@ -122,7 +122,7 @@
|
||||
```bash
|
||||
# --- V2/V4(故障注入,需临时改码)---
|
||||
# 1) 基线:跑一轮产品 @test 确认无复位
|
||||
& '.venv\Scripts\python.exe' test/host/run_boot_test.py
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py
|
||||
|
||||
# 2) 注入:按设计说明 §10 在 netTask 循环插 while(1);(或 taskDISABLE_INTERRUPTS(); while(1);)
|
||||
# —— 改完 @build,然后烧录
|
||||
@@ -382,7 +382,7 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创
|
||||
`PASV→STOR` 间隔达 **11 s**(数据口 LISTEN 半开)。这是**与看门狗误复位不同的另一类
|
||||
问题**(CH395F 数据通道);若修复后仍复现 425,转 §7.1 专项。
|
||||
4. **串口采集铁律**:`open` → `clear` → 烧录/复位 → `wait-idle --timeout 90 --settle-ms 8000
|
||||
--max-ms 45000` → **直接 Read/Grep `tools/serial_tool/state/serial.log`**
|
||||
--max-ms 45000` → **直接 Read/Grep `.dsh/skills/serial-assistant/scripts/state/serial.log`**
|
||||
(`log --grep` 子命令不可信);daemon 残留 → `taskkill /F /PID <旧>` 后重新 `open`。
|
||||
5. **只用 `.venv\Scripts\python.exe`**(PATH 里的 `python` 是 msys64);每次烧录后**必须**
|
||||
ST-LINK 全片回读 `No difference found`。
|
||||
|
||||
@@ -266,10 +266,10 @@ static void phaseN_run(void) {
|
||||
|
||||
| 工具 | 位置 | 作用 |
|
||||
|---|---|---|
|
||||
| Python 串口助手(serial_tool) | `tools/serial_tool/serial_tool.py`(.venv + pyserial) | 常驻 daemon + 薄 CLI:列端口 / 开关 / 收发 / 全量日志 / wait-idle |
|
||||
| `test/host/run_boot_test.py` | `test/host/` | 一键闭环:采集 → 烧录 → 提取日志 → 校验 |
|
||||
| Python 串口助手(serial_tool) | `.dsh/skills/serial-assistant/scripts/serial_tool.py`(.venv + pyserial) | 常驻 daemon + 薄 CLI:列端口 / 开关 / 收发 / 全量日志 / wait-idle |
|
||||
| `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` | `test/host/` | 一键闭环:采集 → 烧录 → 提取日志 → 校验 |
|
||||
| **`test/run_all_suites.py`** | `test/` | **多套件回归编排**(T-5)+ **`--check-build` 只编译不烧录**(S6,纯离机,验证 4 套件可编译性);见 §3.6 |
|
||||
| `test/host/verify_log.py` | `test/host/` | 日志标记校验(Required / Forbidden → PASS / FAIL)+ 可选套件汇总行校验(T-2) |
|
||||
| `.dsh/skills/serial-boot-test/scripts/verify_log.py` | `test/host/` | 日志标记校验(Required / Forbidden → PASS / FAIL)+ 可选套件汇总行校验(T-2) |
|
||||
| **`test/host/common/unit_test_harness.py`** | `test/host/common/` | **L0.5 通用单测 harness**:工具链探测 / `INCLUDE`-`LIB` 拼接 / 编译运行 / 退出码契约。各模块运行器只做声明式配置(T-3) |
|
||||
| **`test/host/watchdog/run_unit_test.py`** | `test/host/watchdog/` | **L0.5 主机侧单测**:声明 `TestBuild` 后调 harness,穷举看门狗判定状态机(零硬件、秒级)。**需本机装有 VS(`cl.exe`)**,见 §4.2 环境要求 |
|
||||
| **`test/host/sys_clock/run_unit_test.py`** | `test/host/sys_clock/` | **L0.5 主机侧单测**:穷举 `sys_clock` 时间换算 / 星期 / 惰性再基准化 / RTC 故障降级(100 断言,S1-a) |
|
||||
@@ -420,7 +420,7 @@ flowchart TD
|
||||
|
||||
```powershell
|
||||
# 测试套件构建回归(要求套件跑到汇总行,且断言数 > 0)
|
||||
& '.venv\Scripts\python.exe' test/host/run_boot_test.py `
|
||||
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py `
|
||||
--required "=== Storage Tests:" --forbidden "ERROR;HardFault;[ERR]" `
|
||||
--require-suite-summary "Storage Tests"
|
||||
```
|
||||
@@ -476,7 +476,7 @@ flowchart TD
|
||||
| 代码变更影响判定标准 | 立即同步更新模块 Test_Guide,并登记该文档变更记录 |
|
||||
| 有意改视觉 | 刷黄金参考 + 重生成目检 PNG + `UI_自动化测试流程.md` 变更记录登记 |
|
||||
| 新增测试工具 / 新标记 | `自动化测试流程.md` 命令清单登记(§4.2) |
|
||||
| 栈 / 堆预算相关改动或基线重采 | 把 `@test` 的 boot_log 快照(`%TEMP%\opencode\boot_log_*.txt`)存入 `docs/测试说明/boot_log_data/` 并在其 §3 清单登记(防临时目录被清理 / 长跑日志截头丢 `[STACK]` 行) |
|
||||
| 栈 / 堆预算相关改动或基线重采 | 把 `@test` 的 boot_log 快照(`%TEMP%\serial-boot-test\boot_log_*.txt`)存入 `docs/测试说明/boot_log_data/` 并在其 §3 清单登记(防临时目录被清理 / 长跑日志截头丢 `[STACK]` 行) |
|
||||
|
||||
> 文档与代码一体两面:**代码改了文档没同步 = 未完成**。
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
├─ Step 0 预检 .venv + serial_tool(selftest/ports 确认 COM)
|
||||
├─ Step 1 构建验证 MDK-ARM/build.bat (0 错误 0 警告才继续)
|
||||
├─ Step 2 烧录 + 采集(一条命令编排:开串口 + 清日志 → 烧录 → 固件身份戳 → wait-idle → 提取 → 回读校验)
|
||||
│ test/host/run_boot_test.py (内置 serial_tool + download.bat + reset.bat cmp + verify_log.py)
|
||||
│ .dsh/skills/serial-boot-test/scripts/run_boot_test.py (内置 serial_tool + download.bat + reset.bat cmp + verify_log.py)
|
||||
├─ Step 3 判定:
|
||||
│ PASS → 报告,结束
|
||||
│ FAIL → 依据 [MISS]/[HIT] + 日志尾部,修改代码后回到 Step 1
|
||||
@@ -23,10 +23,10 @@
|
||||
| `@build` | `keil-build` skill(`.dsh/skills/keil-build/`,自带 `keil_build.py`) | 仅编译验证(0 错误 0 警告) |
|
||||
| `@download` | `stlink-flash` skill(`.dsh/skills/stlink-flash/`,自带 `stlink_flash.py`) | 编译并烧录(ST-Link) |
|
||||
| `@test` | `serial-boot-test` skill(`.dsh/skills/serial-boot-test/`) | 完整闭环:build → capture+download → verify → iterate |
|
||||
| `test/host/run_boot_test.py` | `test/host/run_boot_test.py` | **一键闭环**:open + clear → 烧录 → 固件身份戳(从 hex 抠 `fw build:`)→ wait-idle(静默 8s/超时 30s/首字节 12s)→ 提取 `[RX]`(每行加 `[+Nms]` 时基)→ 回读校验(`reset.bat cmp`)→ verify;`--keep-out` 清旧产物;退出码 0=PASS/1=FAIL/2=采集·烧录·回读错误/3=用法错误 |
|
||||
| `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` | `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` | **一键闭环**:open + clear → 烧录 → 固件身份戳(从 hex 抠 `fw build:`)→ wait-idle(静默 8s/超时 30s/首字节 12s)→ 提取 `[RX]`(每行加 `[+Nms]` 时基)→ 回读校验(`reset.bat cmp`)→ verify;`--keep-out` 清旧产物;退出码 0=PASS/1=FAIL/2=采集·烧录·回读错误/3=用法错误 |
|
||||
| `MDK-ARM/reset.bat` | `MDK-ARM/reset.bat` | ST-LINK CLI 封装:`flash [hex]`(烧 + `-Rst -Run`)/ **`cmp [hex]`(回读比对,2026-10-10 加)**/ 无参复位 / `halt`/`run`/`list`/`hard` |
|
||||
| `tools/serial_tool/serial_tool.py` | `tools/serial_tool/` | Python 串口助手(常驻 daemon + 薄 CLI):ports/open/send/status/log/clear/wait-idle/selftest;全量日志 `state/serial.log`(20MB 自动截头) |
|
||||
| `test/host/verify_log.py` | `test/host/verify_log.py` | 对日志做标记校验:Required/Forbidden(**命中逐行打印行号+内容**)、启动锚点计数 `--max-boot-count`、原因白名单 `--expect-reset-cause`、标记顺序 `--require-order`、开机耗时 `--done-marker`/`--max-boot-ms`、`--json` 结构化输出;退出码 0/1/2 |
|
||||
| `.dsh/skills/serial-assistant/scripts/serial_tool.py` | `.dsh/skills/serial-assistant/` | Python 串口助手(常驻 daemon + 薄 CLI):ports/open/send/status/log/clear/wait-idle/selftest;全量日志 `state/serial.log`(20MB 自动截头) |
|
||||
| `.dsh/skills/serial-boot-test/scripts/verify_log.py` | `.dsh/skills/serial-boot-test/scripts/verify_log.py` | 对日志做标记校验:Required/Forbidden(**命中逐行打印行号+内容**)、启动锚点计数 `--max-boot-count`、原因白名单 `--expect-reset-cause`、标记顺序 `--require-order`、开机耗时 `--done-marker`/`--max-boot-ms`、`--json` 结构化输出;退出码 0/1/2 |
|
||||
|
||||
## 判定标准
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
| 现象 | 处理 |
|
||||
|---|---|
|
||||
| `selftest` FAIL / `ModuleNotFoundError: pyserial` | venv 未建或依赖缺失:按 AGENTS.md「Python 环境」重建 `.venv` + `pip install -r requirements.txt` |
|
||||
| daemon 无响应 / 自动拉起失败 | 查 `tools/serial_tool/state/daemon.log`;`serial_tool.py restart` 重建 daemon;检查 COM 是否被其他程序占用 |
|
||||
| daemon 无响应 / 自动拉起失败 | 查 `.dsh/skills/serial-assistant/scripts/state/daemon.log`;`serial_tool.py restart` 重建 daemon;检查 COM 是否被其他程序占用 |
|
||||
| 抓到日志缺 `Network Init` | `-SettleMs` 太小(boot 任务间有 ~3-4s 自然静默间隙),用默认 8000ms;2026-10-10 起首字节到达前不启动静默计时(`--first-wait-ms` 默认 12000),首行晚到不再被提前收尾 |
|
||||
| 烧录后无数据(`no_data`) | 2026-10-10 起 `no_data` = **本次等待期间零字节**(旧实现比 daemon 累计字节,常驻 daemon 下永不触发,只能靠"日志里没有 [RX] 行"兜底);开串口 + clear 必须在烧录**前**完成(`run_boot_test.py` 已保证);核对 COM 号、板子供电,看 `state/serial.log` 有无 SYS 异常行 |
|
||||
| `[MISS] fw build: <戳>`(其余标记全中) | 芯片里跑的不是这份构建:烧录没落地 / 烧错文件 / hex 未重编译。处置:`reset.bat flash <hex>` 重烧并看 `READBACK: OK`;确认 `@build` 已重跑(戳取自编译时刻) |
|
||||
|
||||
@@ -241,11 +241,11 @@ DTU-RemoteLCD 客户端;输出每帧 `req2first / total / recv 批次到达时
|
||||
# 2) 一键闭环:开串口+清日志 → 烧录(reset.bat flash,Reset&Run)→ wait-idle → 标记校验
|
||||
# 硬件层模式无 "Network Init" 标记(netTask 自挂起),且 TEST_REPORT 行含 "failed=0"
|
||||
# → Required/Forbidden 均用下方定制集(见 §4.4)
|
||||
& '.venv\Scripts\python.exe' test\run_boot_test.py `
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-boot-test\scripts\run_boot_test.py `
|
||||
--required "App init OK;net init: OK;PHY: 100M FULL;[LAT] report complete" `
|
||||
--forbidden "HardFault;assert;[ERR];[FAIL];verdict=FAIL"
|
||||
# 3) 提取 [LAT] 报告行(直接读日志文件最可靠)
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py log --grep "\[LAT\]"
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py log --grep "\[LAT\]"
|
||||
```
|
||||
|
||||
台架已跑通:7 段全部 OK、零尖峰、p95≈min(零抖动),§6.1 基线已按实测重锁。
|
||||
@@ -257,13 +257,13 @@ DTU-RemoteLCD 客户端;输出每帧 `req2first / total / recv 批次到达时
|
||||
# ENABLE_PHASE13_TESTS(Phase 13 必须独占 8080 socket,勿与其他 NET 层 Phase 同启)
|
||||
& "MDK-ARM\build.bat" # 必须 0 错 0 警
|
||||
# 1) 先 clear 再起 peer(顺序不可反:wait-idle 需要看到会话活跃期)
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py clear
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py clear
|
||||
& '.venv\Scripts\python.exe' test\latency_peer.py --host 192.168.1.100 --port 8080 `
|
||||
--json "docs\测试说明\m2_data\m2_quiet.json" --wait 170 # 后台作业
|
||||
# 2) 烧录(Reset&Run 不可省)
|
||||
& "MDK-ARM\download.bat"
|
||||
# 3) 等空闲:会话本身约 2s,但 peer 首连常因"芯片空闲超时 RST"失败一次并 2s 后重试
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py wait-idle `
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py wait-idle `
|
||||
--timeout 120 --settle-ms 10000 --max-ms 110000
|
||||
# 4) 判定:**直接 Grep serial.log 文件**(勿用 log --grep,勿只看 wait-idle)
|
||||
# Required: "App init OK" / "net init: OK" / "[LAT] report complete" / "=== Phase 13: 1/1 PASSED"
|
||||
@@ -282,7 +282,7 @@ DTU-RemoteLCD 客户端;输出每帧 `req2first / total / recv 批次到达时
|
||||
```powershell
|
||||
# M3(生产路径,需要设备已烧 Phase 14 固件)
|
||||
& "MDK-ARM\build.bat" # 0 错 0 警(Phase 14 构建)
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py clear
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py clear
|
||||
& "MDK-ARM\download.bat" # 先烧录,再起 peer(顺序不可反!)
|
||||
& '.venv\Scripts\python.exe' test\latency_peer.py --mode remo --host 192.168.1.100 --port 7003 `
|
||||
--samples 45 --stress-from 15 --ftp-stress --json "docs\测试说明\m3_data\m3_dev_stress.json"
|
||||
|
||||
@@ -518,14 +518,14 @@ Get-ChildItem 'C:\ProgramData\anaconda3\Lib\site-packages' -Filter 'cfgv*'
|
||||
**根因**:CI 的 `actions/checkout@v4` 拉的是**干净克隆**,而钩子校验的是
|
||||
**仓库根相对路径是否存在**(`check_backtick_paths`,见 `scripts/check_doc_links.py`)。
|
||||
若文档引用了 **gitignore 的运行时产物**——
|
||||
如 `tools/serial_tool/state/meta.json`(运行时未创建,`.gitignore:132` 排除整个 `state/`)——
|
||||
如 `.dsh/skills/serial-assistant/scripts/state/meta.json`(运行时未创建,`.gitignore:132` 排除整个 `state/`)——
|
||||
你本机因为跑过工具所以文件在,**克隆里则不存在** → 判为失效引用。
|
||||
|
||||
**修复**:按 `docs/文档规范.md` §3.3 给该行加**状态词**(脚本的 `STATUS_HINTS`:
|
||||
`计划 / 未创建 / 尚未创建 / 已删除 / 删除 / 不存在 / 缺失 / 断链 / 待建 / 待创建`)。
|
||||
|
||||
```markdown
|
||||
| `tools/serial_tool/state/meta.json`(运行时未创建) | daemon 元数据(pid / 版本 / gen) |
|
||||
| `.dsh/skills/serial-assistant/scripts/state/meta.json`(运行时未创建) | daemon 元数据(pid / 版本 / gen) |
|
||||
```
|
||||
|
||||
> ⚠️ **状态词必须与引用在【同一行】且 ≤40 字符**(`STATUS_PROXIMITY`)。
|
||||
@@ -536,9 +536,9 @@ Get-ChildItem 'C:\ProgramData\anaconda3\Lib\site-packages' -Filter 'cfgv*'
|
||||
|
||||
```bash
|
||||
# 把运行时目录临时移开,模拟干净克隆,跑完再移回
|
||||
mv tools/serial_tool/state tools/serial_tool/state.bak_ci
|
||||
mv .dsh/skills/serial-assistant/scripts/state .dsh/skills/serial-assistant/scripts/state.bak_ci
|
||||
& '.venv\Scripts\python.exe' scripts/check_doc_links.py --all # 或全局 python -m pre_commit run doc-links --all-files
|
||||
mv tools/serial_tool/state.bak_ci tools/serial_tool/state
|
||||
mv .dsh/skills/serial-assistant/scripts/state.bak_ci .dsh/skills/serial-assistant/scripts/state
|
||||
```
|
||||
|
||||
> 更一般的教训:**任何校验"文件是否存在/数字是否一致"的钩子,都要在
|
||||
@@ -662,7 +662,7 @@ date +%s # 对比 last_online
|
||||
| `l0-gates` 报 `TerminatorExpectedAtEndOfString` / 缺少右引号 | `run:` 块含**中文**,被 WinPS 5.1 按 GBK 解码撞坏引号 | `run:` 块改**纯 ASCII**(中文只留 `name:`/注释) |
|
||||
| `l0-gates` 报 `Cannot find: pwsh in PATH` | `shell: pwsh` = PS7,本机只有 WinPS 5.1 | 改 `shell: powershell` |
|
||||
| `l0-gates` 报 `runner missing: cppcheck` | cppcheck 仅在用户级 PATH(SYSTEM 不可见) | 设机器级 `DTU_CPPCHECK`(§5.1.3,**workflow 不再回落固定目录**),或把 cppcheck 目录加入系统 PATH 后重启任务 |
|
||||
| 🔴 `doc-links` 钩子本地过、CI 挂 | 引用了 **gitignore 的运行时文件**(如 `tools/serial_tool/state/*.json`),本地存在、干净克隆里没有 | 该行加状态词「未创建/不存在」等(§5.1.2) |
|
||||
| 🔴 `doc-links` 钩子本地过、CI 挂 | 引用了 **gitignore 的运行时文件**(如 `.dsh/skills/serial-assistant/scripts/state/*.json`),本地存在、干净克隆里没有 | 该行加状态词「未创建/不存在」等(§5.1.2) |
|
||||
| `l05` 日志 `::warning::runner has no MSVC` 但仍 **PASS** | 缺 MSVC 时 L0.5 **降级跳过**(不硬失败) | 装 VS BuildTools(§5.2),否则该层无覆盖 |
|
||||
| 任务跑满 72 小时被杀 | 计划任务默认 `ExecutionTimeLimit` | 设为 `0`(不限时长),见 §4.3 警示 |
|
||||
| `config set log.file ...` 报 `unknown config key` | **v4 的 log 段无 `file` 键** | 用 `log.job.dir` 或输出重定向(§3) |
|
||||
@@ -910,7 +910,7 @@ powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\fix_cfgv_global.p
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-02 | **workflow 接入 `DTU_*` 机器级变量**(`本地工具路径环境变量方案.md` 实施):`l0-gates` 的 Python 探测改为 `DTU_PRECOMMIT_PYTHON`(显式,无效即硬失败)→ PATH → `.venv`(删除 `C:\ProgramData\anaconda3` 固定候选);cppcheck 改为 `DTU_CPPCHECK` 优先 → PATH(**移除 `C:\Program Files\Cppcheck` 固定回退**);`l05` 的 MSVC 探测改为 `DTU_MSVC_CL` 优先 → `%ProgramFiles*` 推导的标准根(与 `unit_test_harness.py` 同步,不再写死盘符);新增 §5.1.3 机器级变量设置方法(`Machine` scope + 重启任务)、§5.2 与 check_env 样例、排障表 cppcheck 行、TL;DR 5b 步骤。§5.1.1 的报错原文标注为历史记录 |
|
||||
| 2026-10-02 | ✅ **首次 CI 全绿(run#8:L0 + L0.5 双 job Success)**。定位并修复最后两个拦路虎:① **§5.1.1 用户态 site-packages 陷阱**——`pre-commit` 只装在 `%APPDATA%\Roaming\Python\...`(SYSTEM 不可见);且提权 `pip install --no-user` 会因 pip 扫**整个 `sys.path`**、见 user-site 已满足而**静默跳过**全局安装(9 依赖中唯 `cfgv` 因版本相同被跳过→`-s` 下 `ModuleNotFoundError`)。修法:提权 + `--force-reinstall --no-deps` 强制落全局,并以 `-s` 复验。② **§5.1.2 doc-links 假阳性**——文档引用了 gitignore 的运行时文件 `tools/serial_tool/state/meta.json`,本地存在、干净克隆无 → 按《文档规范》§3.3 加同状态词「运行时未创建」。排障表补 6 条(pre-commit 不可见 / GBK 中文 / pwsh / cppcheck / doc-links / MSVC 降级)。新增配套脚本 `install_precommit_global.ps1`、`install_precommit_global2.ps1`、`fix_cfgv_global.ps1` |
|
||||
| 2026-10-02 | ✅ **首次 CI 全绿(run#8:L0 + L0.5 双 job Success)**。定位并修复最后两个拦路虎:① **§5.1.1 用户态 site-packages 陷阱**——`pre-commit` 只装在 `%APPDATA%\Roaming\Python\...`(SYSTEM 不可见);且提权 `pip install --no-user` 会因 pip 扫**整个 `sys.path`**、见 user-site 已满足而**静默跳过**全局安装(9 依赖中唯 `cfgv` 因版本相同被跳过→`-s` 下 `ModuleNotFoundError`)。修法:提权 + `--force-reinstall --no-deps` 强制落全局,并以 `-s` 复验。② **§5.1.2 doc-links 假阳性**——文档引用了 gitignore 的运行时文件 `.dsh/skills/serial-assistant/scripts/state/meta.json`,本地存在、干净克隆无 → 按《文档规范》§3.3 加同状态词「运行时未创建」。排障表补 6 条(pre-commit 不可见 / GBK 中文 / pwsh / cppcheck / doc-links / MSVC 降级)。新增配套脚本 `install_precommit_global.ps1`、`install_precommit_global2.ps1`、`fix_cfgv_global.ps1` |
|
||||
| 2026-10-02 | 🔴 **常驻方式重大修正(实测推翻)**:`Start-Service gitea-runner` 必然失败。经二进制逆向确认 `gitea-runner v4.0.1` **无任何服务管理代码**(命令集仅 `bug-report/cache-server/config/daemon/exec/register`,无 `service.go`;`OpenSCManagerW` 等符号来自 Docker SDK,非服务安装代码),`daemon` 是纯控制台程序、不向 SCM 回报 `SERVICE_RUNNING`。**§4 全节重写为「任务计划程序」方案**(新增 §4.0 根因分析、§4.2 `deploy_runner_task.ps1`、§4.3 手动命令 + 72h 超时警示、§4.5 清理遗留服务、§4.6 提权);删除失效的 `deploy_runner.ps1`/`install_service.ps1`;排障表补「错误 1053」「LastTaskResult 非 0」「72 小时被杀」3 条;拓扑图/§5.1/§7 日志/§10 TL;DR 同步更新。另修 `config.yaml` 的 `fetch_timeout` 5m→1m(超出 RPC 1m 会被截断并告警) |
|
||||
| 2026-10-02 | **修 `.ps1` 编码坑(实测)**:无 BOM 的 UTF-8 脚本被 WinPS 5.1 按 GBK 解读 → 中文乱码(`鐒跺悗鎵ц`)并撞坏引号配对,报 `源文本中包含无法识别的标记`/`字符串缺少终止符`。**新增 §7.1「给含中文的 `.ps1` 加 UTF-8 BOM」**(两种修法 + 解析器校验命令);排障表补该条目;配套脚本已统一加 BOM |
|
||||
| 2026-10-02 | **按实测修正为 v4.0.1**:act_runner 已更名 **`gitea-runner`**,发布页迁至 `gitea.com/gitea/runner`。**v4 移除 `daemon install/start/stop/status`**;**v4 `log` 段无 `file` 键**(§3 改用 `log.job.dir`);补 §1.3 命令集对照、§6.1 MySQL 直查核验、排障表(`accepts at most 0 arg(s)` / `unknown config key log.file` 等) |
|
||||
|
||||
@@ -183,11 +183,11 @@ python tools/font_gen/extract_cn.py # 任意 python3:
|
||||
|
||||
| 项 | 要求 | 说明 |
|
||||
|---|---|---|
|
||||
| Python 串口助手 | `tools/serial_tool/serial_tool.py`(仓库内置,.venv + pyserial) | 常驻 daemon + 薄 CLI + 全量日志文件;无需单独安装,`pip install -r requirements.txt` 即就绪 |
|
||||
| Python 串口助手 | `.dsh/skills/serial-assistant/scripts/serial_tool.py`(仓库内置,.venv + pyserial) | 常驻 daemon + 薄 CLI + 全量日志文件;无需单独安装,`pip install -r requirements.txt` 即就绪 |
|
||||
| 调试串口 | USART1(PA9 TX / PA10 RX),115200 8N1 | **端口单一配置源 `.serialtest/config.json`**(本机 COM6);换端口只改这个文件 |
|
||||
| 一键测试 | `test\run_boot_test.py`(即 `@test`,`& '.venv\Scripts\python.exe' test/host/run_boot_test.py`) | 开串口 + 清日志 → 烧录 → wait-idle(静默 8s/超时 30s)→ 提取 → 标记校验;默认端口取 `serial_config.json` / 115200,退出码 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法错误 |
|
||||
| 一键测试 | `.dsh/skills/serial-boot-test/scripts/run_boot_test.py`(即 `@test`,`& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py`) | 开串口 + 清日志 → 烧录 → wait-idle(静默 8s/超时 30s)→ 提取 → 标记校验;默认端口取 `.serialtest/config.json` / 115200,退出码 0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法错误 |
|
||||
|
||||
> 采集 = 端口常开 + daemon 全量日志(`tools/serial_tool/state/serial.log`),无采集窗口/first-byte/EOF 重试仪式。
|
||||
> 采集 = 端口常开 + daemon 全量日志(`.dsh/skills/serial-assistant/scripts/state/serial.log`),无采集窗口/first-byte/EOF 重试仪式。
|
||||
> 命令契约与陷阱见 `docs/测试说明/串口助手设计说明.md`、`docs/测试说明/自动化测试流程.md`。
|
||||
|
||||
### 2.7 代码审查钩子(pre-commit)
|
||||
@@ -328,11 +328,11 @@ pre-commit run --all-files # 首次全库基线扫描
|
||||
|
||||
**第 7 步:Python 串口助手**(串口调试 / 自动测试,仓库内置)
|
||||
|
||||
无需单独安装:`tools/serial_tool/` 随仓库提供,依赖 `pyserial`(已入 `requirements.txt`,第 4 步建 `.venv` 时一并安装)。建成后验证:
|
||||
无需单独安装:`.dsh/skills/serial-assistant/` 随仓库提供,依赖 `pyserial`(已入 `requirements.txt`,第 4 步建 `.venv` 时一并安装)。建成后验证:
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py selftest # → SELFTEST PASS
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py ports # 枚举 COM 口
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py selftest # → SELFTEST PASS
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py ports # 枚举 COM 口
|
||||
```
|
||||
|
||||
**第 8 步:下载器驱动**(ST-Link)
|
||||
@@ -344,7 +344,7 @@ pre-commit run --all-files # 首次全库基线扫描
|
||||
**第 9 步:串口配置**
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py ports # 查 COM 号
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py ports # 查 COM 号
|
||||
```
|
||||
|
||||
把查到的端口写入 `.serialtest\config.json` 的 `portName`(唯一配置源,换口只改这里)。
|
||||
@@ -358,7 +358,7 @@ pre-commit run --all-files # 首次全库基线扫描
|
||||
& '.venv\Scripts\python.exe' simulator\tests\check_menu_rows.py # PASS
|
||||
& '.venv\Scripts\python.exe' simulator\tests\check_content_pages.py --update # 首次生成参考图
|
||||
& '.venv\Scripts\python.exe' simulator\tests\check_content_pages.py # 再跑 → PASS
|
||||
& '.venv\Scripts\python.exe' tools\serial_tool\serial_tool.py selftest # → SELFTEST PASS
|
||||
& '.venv\Scripts\python.exe' .dsh\skills\serial-assistant\scripts\serial_tool.py selftest # → SELFTEST PASS
|
||||
& '.venv\Scripts\python.exe' test\host\watchdog\run_unit_test.py # 主机侧单测 → 39/39 PASSED(退出码 0)
|
||||
pre-commit run --all-files
|
||||
```
|
||||
@@ -526,7 +526,7 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级
|
||||
|---|---|
|
||||
| 下载方式 | `& "MDK-ARM\download.bat"`(Keil 命令行:烧写 + Reset & Run) |
|
||||
| 下载器 | 工程已配 `UL2CM3` 调试驱动 + `STM32F4xx_1024` Flash 算法;**物理下载器按现场选择**(本机为 ST-Link——不占 COM 口,可与串口调试并存);新机器在 Keil `Options for Target → Debug → Settings` 选择与硬件一致的驱动 |
|
||||
| 调试日志 | USART1 115200;抓开机日志必须**先武装采集再复位**(`test/host/run_boot_test.py --flash-cmd` 已保证顺序) |
|
||||
| 调试日志 | USART1 115200;抓开机日志必须**先武装采集再复位**(`.dsh/skills/serial-boot-test/scripts/run_boot_test.py --flash-cmd` 已保证顺序) |
|
||||
|
||||
### 3.5 功能配置开关
|
||||
|
||||
|
||||
@@ -367,7 +367,7 @@ D-2/D-3 的共同根因:**没有任何机械检查**保证"文档说的 = 代
|
||||
|
||||
> **实施记录(2026-09-18,按方案 A 落地,两处必要调整)**:① 分配接口**不写在 `cfg_file.c` 里,而是加到 `cfg_file_store` 后端**(`cfg_store_alloc` / `cfg_store_free` / `cfg_store_heap_free`)——`cfg_file.c` 是双端共享源码,直接写 `pvPortMalloc` 会让 PC 模拟器断链;MCU 后端 = `pvPortMalloc/vPortFree`,模拟器后端 = `malloc/free`。② 编译期峰值约束改用 **typedef 负数组**(`CFG_HEAP_BUDGET = 10,240 B`)——ARMCC 5 只到 C99,**没有 `_Static_assert`**(方案 A 原文此处需修正)。③ 另补 4 条失败路径的 `syslog_post(SYSLOG_SELFTEST, …)` 与 `load: OK …, heap free NB` 实测行。台账:`docs/待处理内容.md` **P0-1**;设计文档:`docs/子系统设计/配置文件设计说明.md` §6.2/§8 + v1.11。
|
||||
|
||||
> **诚实标注**:本结论由静态分析得出(映射表 + 启动文件 + 调用图三重证据),**尚未上机确认**。`test/host/verify_log.py`(PowerShell 版 `test/verify_log.ps1:34-35` 已删除,2026-09-20 起为纯 Python)的 Forbidden 标记含 `FAIL`,而失败路径会打印 `read: malloc 4097B FAIL` / `template: malloc 5120B FAIL`,因此**一次 `@test` 即可判定**(若近期 `@test` 曾 PASS,需先确认当时是否真的进入了文件读取分支,例如 `/config.ini` 不存在或大小为 0)。**(2026-09-18 已按上述实施;修复后的真机 `@test` 正向确认——`load: OK` 出现且 `FAIL` 零命中——已于 2026-10-03 执行 PASS,2026-10-04 独立复跑再次 PASS:`[CFG] load: OK (523B, heap free 8840B)`、Forbidden `FAIL`/`ERROR`/`HardFault`/`[ERR]` 零命中、`selftest: no fault record`、ST-LINK 全片回读 `No difference found`。)**
|
||||
> **诚实标注**:本结论由静态分析得出(映射表 + 启动文件 + 调用图三重证据),**尚未上机确认**。`.dsh/skills/serial-boot-test/scripts/verify_log.py`(PowerShell 版 `test/verify_log.ps1:34-35` 已删除,2026-09-20 起为纯 Python)的 Forbidden 标记含 `FAIL`,而失败路径会打印 `read: malloc 4097B FAIL` / `template: malloc 5120B FAIL`,因此**一次 `@test` 即可判定**(若近期 `@test` 曾 PASS,需先确认当时是否真的进入了文件读取分支,例如 `/config.ini` 不存在或大小为 0)。**(2026-09-18 已按上述实施;修复后的真机 `@test` 正向确认——`load: OK` 出现且 `FAIL` 零命中——已于 2026-10-03 执行 PASS,2026-10-04 独立复跑再次 PASS:`[CFG] load: OK (523B, heap free 8840B)`、Forbidden `FAIL`/`ERROR`/`HardFault`/`[ERR]` 零命中、`selftest: no fault record`、ST-LINK 全片回读 `No difference found`。)**
|
||||
|
||||
### 4.5 日志子系统(`App/log/sys_log_mcu.c`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user