Files
DCU_APP/.trae/documents/cleanup-residual-low-risk-optimizations.md
T
cbb 76da7a3dd6 Refactor and clean up codebase
- Removed unused constants and type aliases in app_spi_command_defs.h and temp_sensor.h.
- Simplified ISpiCommandHandler destructor to default.
- Eliminated redundant methods and comments in temp_sensor and usart0 files.
- Consolidated writeU32Le function to app_spi_command_defs.h for reuse.
- Updated comments for clarity and accuracy in task_manager.h and task_runner.cpp.
- Removed hardfault.h as it contained no declarations.
- Cleaned up CANFD and watchdog implementations by removing dead code and unnecessary comments.
- Updated version numbers in config.h to reflect recent changes.
- Enhanced error handling and diagnostics in hardfault handling.
2026-07-15 02:49:37 +08:00

15 KiB
Raw Blame History

清理剩余低风险可优化项 Plan

Summary

在上一轮 purge-legacy-dead-code spec(已清除 23 项 A 类死代码 + 11 项 B/C/D 类改进)基础上,对 GD32E508_DCU_APP/ 进行二次扫描,识别并处理 13 项剩余低风险可优化项(不改变运行期行为,构建可验证)。

覆盖三类:

  • A 类残留死代码(6 项):死别名、写而不读成员/静态、空头文件、冗余类型别名
  • 过期/缺失注释(3 项):PRIMASK 过期说明、taskHeartbeat 过期注释、复位前 PRIMASK 缺解释
  • 重复代码提取(4 项):spinDelay 延时循环、armRingPending 赋值模式、writeU32Le 跨文件去重、adc.cpp namespace 合并

Current State Analysis

已完成(上一轮 spec)

  • A1-A14:23 项死代码清除(CAN 兼容轮询链/SPI v1 状态机/EtherCAT 死 API/ADC/USART0/Watchdog/TempSensor 死接口等)
  • B1-B6:6 项前瞻性桩加 TODO 标记
  • C1-C4:4 项条件编译优化
  • D1:1 项历史注释清理
  • 构建验证通过(43/43,0 错误,Code=47462)

本轮新发现(经 Grep 交叉验证)

二次扫描发现 13 项上一轮未覆盖的低风险优化项,均不改变运行期行为。

Proposed Changes

第一组:A 类残留死代码清除(6 项)

1.1 删除死类型别名 canfd_diag_t

  • 文件:GD32E508_DCU_APP/device/canfd.h:211
  • 现状:using canfd_diag_t = CanFd::Diag; — Grep 全项目仅命中声明自身,无任何使用方
  • 改法:删除第 211 行及相邻空行
  • 风险:低(纯类型别名删除)

1.2 删除写而永不读成员 m_ringTxLen

  • 文件:
    • GD32E508_DCU_APP/device/spi/app_spi_command.h:119-120(声明 + 注释)
    • GD32E508_DCU_APP/device/spi/app_spi_command.cpp:407, 712, 745, 859, 867(5 处写入)
  • 现状:volatile uint16_t m_ringTxLen = 0U; 头注释称"供 onCsAssert 装入 DMA",但 onCsAssert 实际使用的是 m_txLen。在 requestEsp32Read(745-746 行)中 m_ringTxLen 与 m_txLen 被赋同值,m_ringTxLen 是冗余写者。Grep 确认 5 处写入、0 处读取。
  • 改法:
    1. 删除 app_spi_command.h:119-120(注释 + 成员声明)
    2. 删除 app_spi_command.cpp 中 5 处 m_ringTxLen = ...; 赋值语句
  • 风险:低(仅删除写操作,m_txLen 与 m_ringTxLen 始终同值,读取路径不变)
  • 注意:745 行 m_ringTxLen = (uint16_t)built_len; 的下一行 m_txLen = (uint16_t)built_len; 必须保留

1.3 删除写而永不读静态 s_pushCount

  • 文件:GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp:257, 282
  • 现状:static uint32_t s_pushCount = 0U; 仅被自增(282 行 ++s_pushCount;),从未被读取、打印或暴露给诊断接口
  • 改法:删除 257 行声明与 282 行自增语句(含其注释 /* 仅保留计数,不打印心跳日志 */)
  • 风险:低(纯死状态)

1.4 删除 4 个未使用本地别名

  • 文件:GD32E508_DCU_APP/device/spi/app_spi_command.cpp:49, 51, 54, 55
  • 现状:本地别名块(48-56 行)共 9 个别名,其中 kTypeCanSend/kTypeAppPing/kRspAck/kRspWait 4 个在文件内从未被引用(Grep 仅命中声明行)。其余 5 个(kTypeBootReq/kTypeOtaReq/kTypeOtaData/kTypeOtaCancel/kRspError)仍被使用。
  • 改法:删除第 49、51、54、55 行
  • 风险:低(删除 static constexpr 未使用符号)

1.5 删除空头文件 sys/hardfault.h

  • 文件:GD32E508_DCU_APP/sys/hardfault.h(整文件,仅 3 行 include guard)
  • 现状:A13 清理后该头文件已无任何声明,仅剩 #ifndef HARDFAULT_H / #define HARDFAULT_H / #endif。sys/hardfault.cpp 不包含此头,全项目无任何 #include "sys/hardfault.h" 或 #include "hardfault.h"(Grep 确认 0 匹配)。
  • 改法:删除整个文件
  • 风险:低(无引用方)
  • 注意:需确认构建脚本(CMakeLists.txt)中是否显式列出该头文件作为安装目标;若仅有 .cpp 参与编译则无影响

1.6 删除 TempSensor 冗余类型别名(方案 A)

  • 文件:
    • GD32E508_DCU_APP/device/temp_sensor.h:12(using Data = Adc::Sample;)
    • GD32E508_DCU_APP/device/temp_sensor.h:24(using temp_sensor_data_t = TempSensor::Data;)
    • GD32E508_DCU_APP/sys/task_runner.cpp:62(消费点 temp_sensor_data_t sample{};)
  • 现状:两层 using 别名链(temp_sensor_data_t → TempSensor::Data → Adc::Sample),仅 1 处消费(task_runner.cpp:62)。TempSensor 类本身保留(init() 仍被 initial.cpp:45 调用,作为语义门面有价值)。
  • 改法:
    1. 删除 temp_sensor.h:12 的 using Data = Adc::Sample;
    2. 删除 temp_sensor.h:24 的 using temp_sensor_data_t = TempSensor::Data;
    3. 修改 task_runner.cpp:62 将 temp_sensor_data_t sample{}; 改为 Adc::Sample sample{};
    4. 确认 task_runner.cpp 已 #include "device/adc.h"(若未包含则需添加,但 task_runner.cpp 大概率已包含)
  • 风险:低(仅类型别名替换,不改语义)
  • 注意:TempSensor 类的 init() 声明与 doxygen 注释保留不动

第二组:过期/缺失注释修正(3 项)

2.1 修正 task_manager.h PRIMASK 过期注释

  • 文件:GD32E508_DCU_APP/sys/task_manager.h:45
  • 现状:注释 内部通过保持当前 PRIMASK 状态的临界区保证可由主循环与 ISR 共享调用,但实现已迁移至 BASEPRI(RT_CRITICAL_SAVE/RESTORE)
  • 改法:改为 内部通过 BASEPRI 临界区(RT_CRITICAL_SAVE/RESTORE)保证可由主循环与 ISR 共享调用
  • 风险:低(纯注释)

2.2 修正 task_runner.cpp taskHeartbeat 过期注释

  • 文件:GD32E508_DCU_APP/sys/task_runner.cpp:225
  • 现状:taskHeartbeat 的 doxygen 注释 @note 当前仅保留任务入口。 已过期失真——函数体实际已有实现(227 行:OtaBootRequest::process(); CanFd::refreshFeedbackSnapshot(); canFeedbackStartRound();)。相邻的 taskStatusLed(216 行)确实空体,其注释准确。
  • 改法:将 225 行注释改为 @note 当前任务:推进 OTA Boot 请求、刷新 CAN 反馈快照、启动反馈推送轮次。
  • 风险:低(纯注释)
  • 注意:taskStatusLed 的 216 行注释保持不动(空体桩是 B 类前瞻性桩)

2.3 补充 ota_boot_request.cpp PRIMASK 解释注释

  • 文件:GD32E508_DCU_APP/sys/ota_boot_request.cpp:220
  • 现状:__disable_irq(); NVIC_SystemReset(); 是全项目唯一使用 PRIMASK 而非 BASEPRI 的位置。这是合法用法(系统复位前最终屏蔽全部中断,防止复位过程中断嵌套),但缺乏解释,后续维护者可能误判为违反项目硬约束。
  • 改法:在 220 行前加注释:/* 复位前最终关中断:必须用 PRIMASK 全屏蔽(非 BASEPRI),确保 NVIC_SystemReset 过程中无任何中断嵌套 */
  • 风险:低(纯注释)

第三组:重复代码提取(4 项)

3.1 提取 spinDelay 延时循环(CANFD.cpp)

  • 文件:GD32E508_DCU_APP/device/CANFD.cpp
  • 现状:9 处 for (volatile uint32_t d = 0; d < NNNNN; ++d) {} 纯延时循环散落在 hardResetCan、runSelfTestFrame、init 退出 init mode 兜底等多处。魔数 {40000, 50000, 100000, 200000} 无命名常量。
  • 改法:
    1. 在文件顶部匿名 namespace 内新增:
      /* 纯软件延时循环(不依赖 SysTick,可在任何中断状态下使用) */
      static inline void spinDelay(uint32_t loops) {
          for (volatile uint32_t d = 0; d < loops; ++d) { }
      }
      
    2. 把以下纯延时循环(无 break/条件)替换为 spinDelay(N);:
      • 94 行:for (volatile uint32_t dly = 0; dly < 40000U; ++dly) { } → spinDelay(40000U);
      • 120 行:for (volatile uint32_t d = 0; d < 100000U; ++d) {} → spinDelay(100000U);
      • 125 行:for (volatile uint32_t d = 0; d < 50000U; ++d) {} → spinDelay(50000U);
      • 130 行:for (volatile uint32_t d = 0; d < 200000U; ++d) {} → spinDelay(200000U);
      • 142 行:需确认是否纯延时(读取后判断)
      • 382 行:需确认是否纯延时
      • 384 行:需确认是否纯延时
    3. 不替换带有条件 break 的轮询等待循环(如 355 行 for (...) { if ((CAN_STAT(...) & CAN_STAT_IWS) == 0U) break; })——这些不是纯延时,保持原样
  • 风险:低(纯结构提取,延时循环行为不变)
  • 注意:执行时需逐一 Read 确认 134/142/382/384 行的实际内容,仅替换纯延时循环

3.2 提取 armRingPending 赋值模式(app_spi_command.cpp)

  • 文件:GD32E508_DCU_APP/device/spi/app_spi_command.cpp
  • 现状:7 处重复的"按铃挂起"赋值模式:
    m_ringPending = true;
    m_ringDeadline = SysTickTimer::getMs() + kRingTimeoutMs;
    
    出现在 516-517, 556-557, 580-581, 604-605, 630-631, 699-700, 748-749 行。
  • 改法:
    1. 在 app_spi_command.h 的 AppSpiCommand private 段新增声明:void armRingPending();
    2. 在 app_spi_command.cpp 新增实现:
      void AppSpiCommand::armRingPending() {
          m_ringPending = true;
          m_ringDeadline = SysTickTimer::getMs() + kRingTimeoutMs;
      }
      
    3. 把 7 处两行赋值替换为 armRingPending();
  • 风险:低(语义等价提取,两个独立赋值无依赖关系,顺序无关)
  • 注意:第 7 处(748-749)原顺序为先 m_ringDeadline 后 m_ringPending,提取后顺序统一为方法内顺序,不影响语义

3.3 writeU32Le 跨文件去重

  • 文件:
    • GD32E508_DCU_APP/device/spi/app_spi_cmd_can_feedback.cpp:53-59(static void writeU32Le)
    • GD32E508_DCU_APP/device/spi/app_spi_cmd_id_snapshot.cpp:33-39(inline void writeU32Le)
  • 现状:两个 SPI 命令处理器的匿名 namespace 各自定义了字节序相同的 writeU32Le(uint8_t *dst, uint32_t val),实现完全一致(仅参数名 value/val 不同)。
  • 改法:
    1. 在 GD32E508_DCU_APP/device/spi/app_spi_command_defs.h 末尾(SpiDefs namespace 外或内,视现有结构而定)新增:
      inline void writeU32Le(uint8_t *dst, uint32_t val) {
          dst[0] = static_cast<uint8_t>(val & 0xFFU);
          dst[1] = static_cast<uint8_t>((val >> 8) & 0xFFU);
          dst[2] = static_cast<uint8_t>((val >> 16) & 0xFFU);
          dst[3] = static_cast<uint8_t>((val >> 24) & 0xFFU);
      }
      
    2. 删除 app_spi_cmd_can_feedback.cpp:53-59 的本地 writeU32Le 定义
    3. 删除 app_spi_cmd_id_snapshot.cpp:33-39 的本地 writeU32Le 定义
    4. 确认两个 .cpp 已 #include "device/spi/app_spi_command_defs.h"(大概率已包含,因它们使用 SpiDefs::kType* 常量)
  • 风险:低(纯函数提取,实现完全一致)

3.4 合并 adc.cpp 双重匿名 namespace

  • 文件:GD32E508_DCU_APP/device/adc.cpp:12-27, 29-35
  • 现状:同一文件有两个独立 namespace { ... } 块:
    • 第一个(12-27):含 kMinValidVdda 等校验常量与 isValidAdcRaw/isFiniteFloat
    • 第二个(29-35):含 kDmaPeriph/kDmaChannel/s_dmaBuffer 等 DMA 配置
    • 语义上都属于文件内静态,无必要分两块
  • 改法:合并为单一 namespace { ... } 块,用空行 + 注释分隔逻辑分组:
    namespace {
    /* ---- 校验常量与辅助函数 ---- */
    constexpr float kMinValidVdda = 2.7f;
    ... (原第一块内容)
    
    /* ---- DMA 配置 ---- */
    constexpr uint32_t kDmaPeriph = DMA0;
    ... (原第二块内容)
    } // namespace
    
  • 风险:低(纯结构调整)

Assumptions & Decisions

假设

  1. 上一轮 purge-legacy-dead-code spec 的所有修改已合并且构建通过(已确认:Code=47462)
  2. task_runner.cpp 已 #include "device/adc.h"(因 Adc::collectSample 在 319 行被调用)
  3. 两个 SPI 命令处理器 .cpp 已 #include "device/spi/app_spi_command_defs.h"(因使用 SpiDefs::kType* 常量)
  4. CMake 构建脚本未显式安装 sys/hardfault.h(头文件通常不被列为编译目标)

决策

  1. 3.1 spinDelay 仅提取纯延时循环:带条件 break 的轮询等待循环(如 355 行)保持原样,避免改变语义
  2. 1.6 TempSensor 保留类本身:仅删冗余别名,TempSensor::init() 作为语义门面保留(initial.cpp:45 仍调用)
  3. 不处理 3 项中风险项:CANFD.cpp 文件名大小写、init() 重构、volatile 审计——需独立 spec
  4. 不主动清理 #include:即使某头文件因移除类型而变冗余,也不删除 #include(避免过度改动,构建不会因此报错)

Verification Steps

1. Grep 验证(清除项零残留)

  • canfd_diag_t → 0 匹配
  • m_ringTxLen → 0 匹配
  • s_pushCount → 0 匹配
  • kTypeCanSend|kTypeAppPing|kRspAck|kRspWait(在 app_spi_command.cpp 内)→ 0 匹配
  • #include.*hardfault.h → 0 匹配
  • temp_sensor_data_t → 0 匹配
  • writeU32Le(在 app_spi_cmd_can_feedback.cpp 和 app_spi_cmd_id_snapshot.cpp 内)→ 0 匹配(仅 app_spi_command_defs.h 中有定义)

2. Grep 验证(新增项存在)

  • spinDelay 在 CANFD.cpp 中出现 ≥1 次(定义)+ N 次(调用)
  • armRingPending 在 app_spi_command.h 中出现 1 次(声明)+ app_spi_command.cpp 中出现 1 次(定义)+ 7 次(调用)
  • writeU32Le 在 app_spi_command_defs.h 中出现 1 次(定义)
  • BASEPRI 临界区 在 task_manager.h:45 出现 1 次
  • 推进 OTA Boot 请求 在 task_runner.cpp 出现 1 次
  • 复位前最终关中断 在 ota_boot_request.cpp 出现 1 次

3. 构建验证

  • cmake --build g:\Robot_v2\DCU\DCU_APP\tmp\1 --clean-first
  • 0 编译错误
  • 0 新增警告(#warning 仅来自上一轮 C1 的 EXMC,不计入)
  • Program Size: Code 应略小于 47462(因删除了死代码)

4. 活跃代码完整性核查

  • Adc::collectSample 仍被 task_runner.cpp 引用
  • TempSensor::init 仍被 initial.cpp:45 引用
  • m_txLen/m_ringPending/m_ringDeadline 成员完整保留
  • SpiDefs::kTypeCanSend 等常量在 app_spi_command_defs.h 中完整保留(仅本地别名删除)
  • HardFault_Handler/HardFault_Handler_C 在 hardfault.cpp 中完整保留

任务分组与执行顺序

可并行执行的三组(文件集不交叠)

组 A:device/spi/ 目录(1.2 + 1.3 + 1.4 + 3.2 + 3.3)

  • 文件:app_spi_command.h/.cpp、app_spi_cmd_can_feedback.cpp、app_spi_cmd_id_snapshot.cpp、app_spi_command_defs.h
  • 注意:3.2(armRingPending)和 1.2(m_ringTxLen)都改 app_spi_command.h/.cpp,需顺序执行

组 B:device/ 目录(1.1 + 1.6 + 3.1 + 3.4)

  • 文件:canfd.h、CANFD.cpp、temp_sensor.h、task_runner.cpp、adc.cpp
  • 注意:1.6 改 temp_sensor.h + task_runner.cpp;3.1 改 CANFD.cpp

组 C:sys/ 目录(1.5 + 2.1 + 2.2 + 2.3)

  • 文件:hardfault.h(删除)、task_manager.h、task_runner.cpp、ota_boot_request.cpp
  • 注意:2.2 和 1.6 都改 task_runner.cpp,需顺序执行或合并到同一子代理

建议执行顺序

  1. 先并行执行组 A、组 B、组 C(三组文件集基本不交叠,仅 task_runner.cpp 跨组)
  2. task_runner.cpp 的改动(1.6 改类型 + 2.2 改注释)合并到同一子代理
  3. 最后执行构建验证