pPowerManger 电源管理软件
代码审查与架构优化报告
+
+1 审查概述
+pPowerManger 是 H100 水下无人航行器复合能源系统的电源管理进程,运行于 MOOS 中间件之上,承担上位机指令接收、下位机(CCU/配电)设备管理、14 工况状态机调度、故障检测与上报、数据持久化及本地 Web 监视等职责。本次审查覆盖下位机核心代码约 2.4 万行(不含第三方 mongoose 库约 2.8 万行),重点评估代码架构、控制逻辑、通信逻辑、故障管理与测试体系,并给出以保持外部特性严格一致为前提的优化方案。
+ +总体结论
+现有代码功能可用,但工程化程度不足,核心问题集中在五条主线:
+-
+
- 存在多个确定性缺陷:Lifting(起吊准备)状态永远无法到达;浮力调节状态反馈因赋值误写为比较而永不更新;MOOS 邮件偏斜检查导致整批邮件被丢弃等(见 3.1)。 +
- 状态机退化:tinyfsm 仅被用作事件路由外壳,真正的转移逻辑集中在基类两个巨型 switch 中;无 guard 条件;故障检测与 FaultState 完全脱钩。 +
- 通信层职责混杂:协议知识(JSON 键名、设备 ID、帧格式)散落在 Driver、UpperCommManager、udpComm 三处;4 个线程对共享数据加锁纪律不一致,存在数据竞争与忙等阻塞。 +
- 故障码体系三分天下:电池故障表、配电故障表、0xLSSS 系统故障魔数三套编码并存,双故障存储导致单一事实源被破坏,上报与显示映射脱节。 +
- 测试存在三大空白:状态机迁移、故障注入、上位机链路均无测试,重构缺乏安全网。 +
优化应以"先建测试基线、再做特性等价重构"为总原则,分四个阶段实施(见第 7、8 章)。
+2 现状架构分析
+ +2.1 进程与通信拓扑
+另有两条旁路:pPowerManger 内嵌 HTTP 服务(端口 8000/8090)直读内部对象提供本地监视页面;上位机 HostSim 自带 Web 服务(端口 18080)+ SQLite 历史库。上位机与下位机之间不存在直接 socket 连接,全部交互以 MOOSDB 上的 JSON 字符串变量为协议载体(uPower_*_cmd 下行、uPower_*_fb 上行)。
2.2 下位机内部模块
+| 模块 | 文件 | 规模 | 实际职责 | 主要问题 |
|---|---|---|---|---|
| 主应用 | PowerManger.h/.cpp | ~680 行 | MOOSApp 框架、线程拉起、全局对象持有 | 上帝对象,持有全部管理器指针并被 FSM 反向穿透 |
| 状态机 | fsm/PowerManagerFsm.cpp + 14 状态 | ~3700 行 | 事件路由 + 设备指令处理 + 故障编码 + 状态拷贝 | 上帝类;转移逻辑退化到基类 switch;无 guard |
| 上位机通信 | UpperCommManager.cpp | 261 行 | MOOS 邮件 JSON 解析、周期状态广播 | 解析失败误报 Unknown key;无连接管理 |
| 下位机通信 | LowerCommManager.cpp | 890 行 | UDP 监听线程、帧校验入队、状态注入、断路器闭环操作 | 回调直改全局数据;忙等最长约 7 s;35 个操作函数重复 |
| 协议编解码 | udpcomm/udpComm.cpp, ccuUdpMsg.h | ~600 行 | UDP 二进制帧打包/解析、校验 | 硬编码帧长;死分支;每帧堆分配 |
| 设备策略表 | driver.cpp | 1094 行 | 68 字段 DriverTable、9 张工况设备表、JSON 编解码 | 9 张表约 600 行逐字段重复赋值;错误码形同虚设 |
| 系统数据 | systemData.h, pmSysvariable.h | ~2250 行 | 协议镜像结构体 + 全局数据池单例 | 头文件内实现;百字段全局池;锁纪律不一致 |
| 故障码 | faultCode.h | 125 行 | 电池 54 项 + 配电 45 项故障表 | 与第三套 0xLSSS 魔数并存;上报/显示映射脱节 |
| 本地 Web | httpserver/(mongoose + 内嵌页面) | ~7000 行 | dashboard/日志/测试注入/电池/燃料电池页面 | 与上位机 Web 重复建设;HTTP 回调越权直改状态 |
2.3 线程模型
+| 线程 | 周期 | 主要工作 | 风险 |
|---|---|---|---|
| MOOS 主线程 | 4 Hz | OnNewMail → 指令解析 → FSM dispatch;Iterate → CycleTriggeredEvent → 故障检测 | 断路器闭环忙等约 7 s 会卡住整个邮件循环 |
| commLoop 线程 | 1 Hz | 心跳/周期指令下发、队列清理 | 内含 usleep/MOOSPause;与监听线程并发清队列 |
| UDP 监听线程 | 阻塞读 | 收帧 → 校验入队 → 出队写 systemData / SQLite | 直接写业务数据;退出时可能挂在 recv 上 |
| HTTP 线程 | 事件驱动 | Web 页面与 REST | /fcs/control 回调里加锁直改 PowerManger 状态 |
共享数据 m_msCmd、m_depth、m_insData、m_thrustRpm 等由 MOOS 线程无锁写、其余线程读;m_stateMutex 仅保护部分字段;updateSystemData 持 m_dataMutex 再取 faultCodeMutex,锁顺序仅靠注释约定(PowerManger.h:92-93)。整体属"能跑但不具备可论证的线程安全性"。
3 代码审查意见
+ +3.1 确定性缺陷(Bug 级,建议立即修复)
+以下问题经逐行核对确认,均有明确代码证据,与架构无关,应在重构前先以最小改动修复并补充回归用例。
+| # | 级别 | 问题 | 位置 | 影响与修复建议 |
|---|---|---|---|---|
| B1 | P0 | +Lifting(起吊准备)状态永远无法到达:handleWorkCmd 的 case 12 只有日志,缺少 transit<Lifting>() 与 break,直接坠入 default |
+ fsm/PowerManagerFsm.cpp:2934-2936 | +上位机"起吊准备"指令被静默吞掉,Lifting 为死状态。补 transit<Lifting>(); break;。 |
+
| B2 | P0 | +赋值误写为比较:s.buoyage == 1;(共 4 处,值 1/3/2/0),语句无副作用 |
+ fsm/PowerManagerFsm.cpp:1471,1475,1482,1486 | +浮力调节(buoyage)状态反馈永远不更新,上报恒为初值。改为 =。 |
+
| B3 | P0 | +MOOS 邮件整批丢弃:OnNewMail 中任一邮件偏斜超差即 return true,同批其余邮件全部丢失 |
+ PowerManger.cpp:121-125 | +网络抖动时会成片丢失上位机指令。改为跳过该条、继续处理后续邮件。 | +
| B4 | P1 | +JSON 键名不匹配导致指令静默丢失:发送端键 "mastLiftingServo_uint8",接收端解析键 "mastLiftingServo_uint8_uint8" |
+ driver.cpp:844 vs 924 | +桅杆升降舵指令永远解析失败。统一键名,并增加"未知键/未命中"告警日志。 | +
| B5 | P1 | +故障检测函数重复执行:basicNavigationFault(); extendedNavigationFault(); 连续调用两遍 |
+ fsm/PowerManagerFsm.cpp:52-55 | +4 Hz 下双倍开销,且对带副作用的故障逻辑是隐患。删除重复行。 | +
| B6 | P1 | +三个断路器故障永不上报:fbReserved/tyzReserved/reserved 调用的是无 faultCode 参数的重载,disfaultMap 中 code 10-12 成死码 | +systemData.h:1026-1028 | +补齐故障码入参,或删除死码并同步前端映射。 | +
| B7 | P1 | +数组越界风险:故障注入 batfaultMap[e.eventId].code 对来自外部的 eventId 无边界检查;systemData.h:524 循环 i<2 越界访问 reserved2[1](cppcheck error 级) |
+ fsm/PowerManagerFsm.cpp:3053;systemData.h:524 | +注入接口可被越界触发,属安全隐患。加边界校验;修正循环上界。 | +
| B8 | P1 | +TestEvent 故障注入 switch 缺 break:case 15 坠入 default;另有 react(TestEvent) 直接构造 FaultEvent 调故障函数,绕过事件机制 |
+ fsm/PowerManagerFsm.cpp:2988-2989, 3084-3092 | +注入 15 号故障时行为未定义。补 break,统一走事件派发。 | +
| B9 | P2 | +未初始化返回(cppcheck error 级):uPlan_taskStart.h:29 返回未初始化 mission;uExternComm_setDeviceSwitch.h:39 可能返回未初始化 cmd.cmd;uPower_pmState.h:14 对含 std::vector 的结构体 memset |
+ upmsg/ 多个头文件 | +协议层偶发脏数据。统一在结构体定义处给默认值,禁止对非 POD memset。 | +
| B10 | P2 | +注释与代码不符:FaultState.cpp:46 注释"5 秒超时"实为 20 秒(:53),且与 Lifting.cpp:40、PowerManagerFsm.cpp 内 5 处同逻辑代码的 5 秒超时不一致;同一段超时代码共复制 7 份 | +FaultState.cpp:46/53 等 | +误导维护且各副本超时阈值漂移。随重构统一为命名常量。 | +
| B11 | P1 | +模板残留致每条正常指令误报 "Unhandled Mail":OnNewMail 的 for 循环内保留了 MOOS 模板代码 if(key=="FOO")...else if(key!="APPCAST_REQ") reportRunWarning(...),除 APPCAST_REQ 外的所有邮件(含已正常处理的 uPower_*_cmd/fb)都被计入 run warning |
+ PowerManger.cpp:142-146 | +AppCast 告警计数被污染、日志刷屏。删除该模板残留,仅在 processUpperMsg 返回 false 时告警。 | +
| B12 | P1 | +畸形 JSON 可致崩溃:loadDriverIdFromJson 对 root[field].asUInt() 无类型检查(JSON_USE_EXCEPTION=1),调用处无 try/catch;SafeReadUInt 用 std::cerr 报错而非日志框架 |
+ driver.cpp:935-936;UpperCommManager.cpp:48 | +恶意/畸形指令使 jsoncpp 抛异常向上传播。统一走 SafeReadUInt 并捕获异常、改走日志。 | +
| B13 | P1 | +HTTP 服务在配置就绪前启动:m_httpServer->start() 在构造函数调用,此时 mission 配置未加载、m_db 为 nullptr、UDP 未 bind,控制接口(/fcs/control 等)已对外可访问 |
+ PowerManger.cpp:83-87 | +存在"配置生效前已暴露控制面"窗口。将 start() 移到 OnStartUp 末尾(DB/端口就绪后)。 | +
| B14 | P2 | +死代码与孤儿成员:FsmLoop()/_FsmCB 无任何线程启动;m_deviceCmdQuenue 及 push/pop/isEmpty 三接口无消费者;buildReport() 为 MOOS 模板占位;SKEW_TOLERANCE 5 宏未用而 m_skew=10 硬编码 |
+ PowerManger.cpp:18/339;PowerManger.h:48/121 | +误导维护。随阶段 3 一并清理,SKEW 阈值统一为命名常量。 | +
3.2 状态机与控制逻辑
+3.2.1 现状转移关系(经代码还原)
+| 源状态 | 事件 | 目标状态 | 说明 |
|---|---|---|---|
| InitState | entry() 内直接切换 | StandbyState | 初始化完成即转 |
| 任意状态 | MasterCommandEvent(workCMD 1~11) | Standby / ShoreBasedReady / WaterBasedReady / RemoteControl / Cruise / HighSpeed / FloatDown / FloatAdjust / UnderwaterSurvey / SurfaceSurvey / DJMode | PowerManagerFsm.cpp:2887-2941;workCMD=12 无效(B1) |
| 任意状态 | TaskStartEvent | THROW_LOAD→FaultState;FLOAT_UP/SAT_COMM 等→FloatDown;RECYCLE/SAIL→CruiseMode;HOVER→FloatAdjust;TOUR→UnderwaterSurvey | PowerManagerFsm.cpp:948-985 |
| 任意状态 | TaskStopEvent | RemoteControl | 基类 react,cpp:13 |
| StandbyState | StandbyEvent | StandbyState(自切换) | 语义存疑 |
| FaultState | —(无显式出口) | — | 仅能借基类 handleWorkCmd 间接跳出 |
3.2.2 主要架构问题
+handleWorkCmd 与 react(TaskStartEvent) 两个巨型 switch;14 个状态类的 react(MasterCommandEvent) 是完全相同的复制粘贴。tinyfsm 提供的 transit<S>(action, condition) guard 重载(tinyfsm.hpp:151)全项目零使用——任何状态下都可经 workCMD 直接跳进任何工况(如巡航中直接切起吊),无任何合法性检查。
+xxxFault() 检测函数只写故障码集合,从不触发状态迁移;FaultState 唯一入口是"抛载任务"(TaskStartEvent::THROW_LOAD),即 3 级故障(如深度失效 0x1302)发生时系统仍停留在原工况。FaultState 无显式出口、无故障恢复确认、无故障码清除联动。FaultEvent 无任何 dispatch 点,基类 react(FaultEvent) 为死代码。
+MOOSPause(10000);ShoreBasedReady.cpp:24-35 while 轮询等待约 10 s;断路器操作闭环在事件处理路径上忙等约 7 s。状态迁移期间 FSM 无法响应任何新事件,上位机指令被积压在邮件队列。
+static PowerManger* pm 裸指针直接读写宿主几十个公有成员、直接操作断路器、直接 sendNotification,单向依赖原则被彻底打破。
+-
+
- A5 语义错位:FloatDown 实际承载"上浮/下潜/卫星通信/卫星标定"四种业务;
handelPowerCmd、underWterDevices、mergencyLithiumBattery等拼写错误已进入接口。
+ - A6 死代码:
PowerSystemState枚举(hpp:21)、DeviceCmd事件(hpp:41)、handleDeviceCmd空壳(cpp:2966)、m_currentDriver(hpp:87)、checkError()被注释掏空(PowerManger.cpp:344-356)。
+ - A7 日志混乱:基类兜底 react 用
std::cout << "No Idear"(hpp:94);entry/exit 大量使用 RTTI demangle + ANSI 转义打印,嵌入式目标上属无效开销;printState用desp.find("故障")字符串匹配决定日志级别(PowerManger.cpp:359),极其脆弱。
+
3.3 通信逻辑
+LowerCommManager::m_connectState(h:131)与 PowerManger::connectState(h:136)声明后从未使用;上位机方向无心跳、无在线检测、无断线告警,仅靠 MOOS 库自动重连。下位机方向有 30 s 超时检测(systemData.h:752-761),但超时后仅置标志位,未与故障系统联动。
+Driver 实例在 PowerManger 与 UpperCommManager 各持一份,表状态易不一致。
+-
+
- C3 校验与错误处理形同虚设:
loadDriverTableFromJson的 error_code 每次被覆盖、从不检查,恒返回 0(driver.cpp:962-1093);sendCcuSetParmCmd无论发送成败都 return true(udpComm.cpp:609-619);帧长硬编码 18/6/8 且旁边就是 TODO(udpComm.cpp:467/491/515/539)。
+ - C4 死分支与静默吞帧:
CCU_UDPMSG_START1 == DIS_MSG_HEAD1 == 0x40,udpComm.cpp:47-56 两个 else-if 完全等价;未知 id 走 default 后仍 return true(udpComm.cpp:344-347)。
+ - C5 高频路径低效:每帧堆分配(udpComm.cpp:462 等 5 处)、每个 UDP 包 cout 打印一次(LowerCommManager.cpp:381)、发送函数失败后仍 return true(LowerCommManager.cpp:142-144)。 +
- C6 废弃函数藏错误数据:
sendDisSysCommand对 bus3 无视入参硬编码0x55/0xAA(LowerCommManager.cpp:183-189),若被误用将下发错误指令。
+ - C7 双 Web 重复建设:下位机 httpserver(8000)与上位机 HostSim Web(18080)都用 mongoose + /ws + 内嵌页面提供状态监视;下位机 /fcs/control 在 HTTP 回调中直改状态(httpserver.cpp:150),绕过 MOOS 消息机制,属越权控制通道。 +
3.4 故障码体系
+| 编码方案 | 定义位置 | 格式 | 使用方 | 问题 |
|---|---|---|---|---|
| 电池故障表 | faultCode.h:17-76,batfaultMap[54] | 单字节 0x01-0xC9,带 1-4 级 | 仅故障注入使用 | level 字段与描述矛盾(:35);注入越界风险(B7) |
| 配电故障表 | faultCode.h:78-124,disfaultMap[45] | 十进制 1-45 | 断路器故障上报 | code 10-12 死码(B6) |
| 系统故障码 | 无定义文件,散落在 PowerManagerFsm.cpp:1676 起 | 16 位魔数 0xLSSS(高半字节=等级) | 8 个检测函数 + getFaultLevel | 约 200 个裸字面量无注释无枚举;等级提取依赖位运算巧合 |
-
+
- F1 双重故障存储:真实故障进
SystemData::sysFaultCodes(set,自恢复即自动清除,无锁存/确认);注入故障进PowerManger::faultCode(vector,不去重、只能整体 clear)。getFaultLevel()(PowerManger.cpp:448)与上报用的data.getFaultCodeLevel()(uPower_pmState.h:50)读的是不同集合,同一件事故障等级可能不一致。
+ - F2 死赋值:UpperCommManager.cpp:240 把
getFaultCodes()赋给 state,但uPower_pmState.h:41-73 buildMsg()忽略该赋值,改用data.getFaultCode()。
+ - F3 映射脱节:故障码→中文描述的映射 C++ 侧仅用于注入,上报只发数字码;显示文本由前端 JS 重写一份(httpserver/fuelcell.h:834-840);0xLSSS 系统故障码没有任何文本映射。 +
- F4 条件编译陷阱(经核实比初版更严重):
DEBUG宏在全部构建中均未被定义(CMake 无-DDEBUG),而 systemData.h:776 的#ifdef DEBUG依赖它——因此燃料电池 4 级故障字fc_fault_level_1..4在所有构建下都永不更新(恒为 0),并非仅"发布版";systemData.h:7 的#define DEBUFG是拼写错误且定义了无用宏。同理#ifdef _DEBUG(UpperCommManager.cpp 全篇)在 Linux 下也永不生效,其调试追踪成员均为死代码。
+ - F5 无故障事件持久化:SQLite 仅随帧存原始故障字段,无独立故障事件表,故障发生/恢复时间不可追溯,无法支撑事后分析。 +
3.5 数据管理与并发
+-
+
- D1 全局数据池:SystemData(systemData.h,1219 行头文件内实现)是百字段单例,字段基本是 pmSysvariable 协议镜像的 float 转换副本,
updateSystemData()逐字段拷贝数百行——协议每改一处需同步三处(pmSysvariable / systemData / updateSystemData)。构造函数 60+ 项手工初始化(:660-690)。
+ - D2 锁纪律不一致:提供了部分加锁访问器(:694-733),但 FSM 大量裸读绕过锁(PowerManagerFsm.cpp:1813/1920/1930);TestEvent 直接无锁写
fc_fault_level_1(:3060);msg_*_Update_time无锁写、另一把锁下读(LowerCommManager.cpp:229-233)。
+ - D3 整数截断风险:对 double 使用未限定的
abs()(systemData.h:757),可能解析为 C 的int abs截断时间差,应使用std::fabs。
+ - D4 双重时间基准:commLoop 每 1 s
clearMsgQueue()(LowerCommManager.cpp:124)与监听线程并发清队列,语义冗余且可能丢未消费帧。
+
3.6 上位机与 Web 服务
+上位机 HostSim 的设计相对干净:网页指令经 WebSocket 入 m_cmdQueue、Iterate 中 drainCommands() 转发 MOOSDB(HostSim.cpp:213-228);上行订阅 12 个反馈变量,dirty 后 broadcast(buildSnapshot()) 推送,并以 SQLite 表 fb_log 保留 24 h 历史(FeedbackStore.cpp:62-69),提供 /api/history、/api/rawlog。可作为重构时"松耦合桥"的保留样板。
-
+
- H1 功能重叠:与下位机 httpserver 在状态监视上重复;建议下位机 Web 退化为纯调试用途(仅本机回环监听),状态监视统一归上位机。 +
- H2 越权通道:下位机 /fcs/control 直改内部状态,破坏"一切控制经 MOOS 指令"的单入口原则,应迁移为向 MOOSDB 发指令或加鉴权。 +
- H3 页面硬编码:1912 行 data.h 等 7 个页面以
R"(...)"字符串嵌在头文件,无构建期资源管线(上位机已有 webassets_gen.h 方案可复用)。
+
3.7 构建工程与配置管理
+-
+
- E1 编译告警默认关闭、无 sanitizer 门禁:CMakeLists 仅加
-fPIC -g -Wdeprecated-declarations,-Wall需手动开 WALL_ON 且写法含语法错误("-Wall" -C++11,CMakeLists.txt:86);Release 依赖 CMake 默认-O3未显式声明;无 ASan/UBSan/TSan 构建类型。建议:开启-Wall -Wextra并清零告警,新增 sanitizer 构建目标,测试二进制输出到 build 目录。
+ - E2 依赖不可复现、GLOB 反模式:
FILE(GLOB ...)收集头/库目录(CMakeLists.txt:49/53),新增文件不触发重配;mongoose/sqlite3/loguru/jsoncpp 以源码 vendor 进 src 且无版本号与许可证记录;CMAKE_MINIMUM_REQUIRED(VERSION 3.5)过老。建议 GLOB 改显式列表,第三方库固定版本并登记 LICENSE。
+ - E3 配置解析无校验:OnStartUp 用
atoi解析 ccuport/iport(PowerManger.cpp:229/233),非法值静默为 0,端口无 1-65535 范围检查;缺配置块仅 warning。建议显式校验非法值并告警。
+ - E4 SQLite 高频路径重复 prepare:
insertGeneric每次插入重拼 SQL 并 prepare(SQLite.cpp:610-625),onFrame每帧同样 prepare;建议缓存 prepared statement,故障事件表纳入 SCHEMA_VERSION 迁移机制。
+ - E5 日志/错误输出三套并存:loguru、
std::cerr(driver.cpp:887/904/957)、std::cout(LowerCommManager.cpp:373/381)混用,无法按级别关停。建议统一 loguru,cout/cerr 清零。
+
4 架构优化方案
+优化总原则:外部特性严格不变(MOOS 变量名与 JSON 契约、UDP 帧格式、Web 接口、故障码取值、状态迁移外部可观测序列均保持),内部结构彻底重整。所有重构以第 6 章的一致性保障与第 7 章的测试基线为前提。
+ +4.1 目标分层架构
+┌─────────────────────────────────────────────────────────────┐
+│ 接口层 (Interface) UpperCommManager │ HttpServer(调试) │ ← 只做协议适配,不含业务判断
+├─────────────────────────────────────────────────────────────┤
+│ 应用层 (Application) PowerManagerApp │ ← 协调者:指令→校验→派发到领域服务
+│ ├─ CommandService(指令接收、校验、应答) │
+│ ├─ FaultManager (故障检测、锁存、上报、事件持久化) │
+│ └─ StateMachineService(工况状态机,表驱动 + guard) │
+├─────────────────────────────────────────────────────────────┤
+│ 领域层 (Domain) DeviceRegistry │ PowerPolicy(Driver表) │ ← 纯 C++,无 MOOS/网络依赖,可单测
+│ SystemState(线程安全的状态仓储) │
+├─────────────────────────────────────────────────────────────┤
+│ 基础设施层 (Infra) UdpLink(收发) │ ProtocolCodec(编解码) │ ← 唯一知道帧格式/JSON键名的地方
+│ SQLiteStore │ MoosGateway │
+└─────────────────────────────────────────────────────────────┘
+-
+
- 依赖方向单向化:接口层 → 应用层 → 领域层 ← 基础设施层(依赖倒置)。状态机不再持有
PowerManger*裸指针,改为构造时注入ISystemState、IDeviceCommand、IFaultSink三个窄接口。
+ - 线程模型统一:确立"单线程事件循环 + 生产者队列"模型——UDP 监听线程、HTTP 线程只做收包→解析→投递事件队列,所有业务处理(FSM dispatch、故障检测、状态写入)在 MOOS Iterate 所在线程串行执行,从根上消除数据竞争。断路器闭环操作改为异步状态(发送→等待反馈事件→超时事件),消灭 7 s 忙等。 +
- Driver 拆分:模式设备策略表(领域层 PowerPolicy)与 JSON 键名/设备 ID 映射(基础设施层 ProtocolCodec)分离;Driver 单例化并归属 CommandService,消除双实例不一致。 +
4.2 状态机重构方案
+4.2.1 转移表显式化
+将隐藏在两个 switch 中的转移逻辑提取为静态转移表 + guard 函数,作为唯一事实源,并据此生成文档与测试用例:
+// fsm/TransitionTable.h —— 唯一事实源(示例,迁移时须以现状代码逐条核对生成)
+struct TransitionRule {
+ StateId from; // ANY 表示任意源状态
+ EventId event;
+ StateId to;
+ GuardFn guard; // nullptr 表示无条件(保持现状语义)
+ const char* comment; // 对应原代码位置,便于审计
+};
+static const TransitionRule kRules[] = {
+ { ANY, EV_WORK_CMD, STANDBY, nullptr, "workCMD=1 Fsm.cpp:2891" },
+ { ANY, EV_WORK_CMD, LIFTING, nullptr, "workCMD=12 Fsm.cpp:2934(修复B1后生效)" },
+ { ANY, EV_TASK_START, FAULT, GuardThrowLoad, "THROW_LOAD Fsm.cpp:953" },
+ { FAULT, EV_FAULT_CLEARED, REMOTE_CTRL, GuardFaultAcked, "新增:故障确认后恢复" },
+ ...
+};
+guard 第一阶段全部保持现状(无条件),仅把"可迁移性"显式化;第二阶段再与总体组确认后补充工况互斥规则(如 HighSpeed 与 DJMode 互斥需先回 Standby)。
+ +4.2.2 FaultState 与故障系统联动
+-
+
- 入口:FaultManager 检测到 3 级及以上故障(或抛载任务)→ 投递
FaultEvent→ FSM 迁移 FaultState,保持现有"抛载进 FaultState"路径不变。
+ - entry 动作异步化:断路器断开序列改为 FaultState 内部的子步骤计时状态(step + deadline),每拍 Iterate 推进,取代 MOOSPause(10000) 与 20 s 忙等;超时阈值统一为命名常量并修正注释。 +
- 出口:显式定义恢复路径——故障码集合清空且收到上位机确认指令(workCMD=4 RemoteControl,保持现有指令集不变)→ 退出 FaultState。该路径当前已可经基类 handleWorkCmd 间接实现,重构只是将其显式化并纳入转移表,不改变外部行为。 +
4.2.3 代码组织
+-
+
- PowerManagerFsm.cpp 拆分:
DeviceCommandHandlers.cpp(10 个设备指令函数,提取公共模板消除重复)、StatusSnapshot.cpp(10 个 getXxxStatus)、FaultEncoding.cpp(故障编码,配合 4.4 迁移到 FaultManager)。
+ - 14 个状态类删除重复的
react(MasterCommandEvent),统一由基类按转移表处理;状态类只保留 entry/exit 与状态特有事件。
+ - 命名修正(handelPowerCmd→handlePowerCmd 等)仅在内部接口进行,MOOS 变量名、JSON 键名等外部标识一律不改。 +
4.3 通信层重构方案
+4.3.1 协议单一事实源
+建立 protocol/ 目录,以一份定义文件同时生成/校验编解码两侧,杜绝键名失配(B4)类问题:
-
+
protocol/MoosVariables.def:全部 MOOS 变量名 + JSON schema(键名、类型、取值范围),UpperCommManager 与 Driver 的 JSON 编解码均引用之;增加"未知键/解析失败"统一告警(修复 C3、B4 的静默丢失)。
+ protocol/DeviceId.h:设备 ID 唯一枚举,driver.cpp:908-930 的 0-61 硬编码与 DeviceId 枚举合并;UDP 帧长由结构体 sizeof 计算,删除 18/6/8 魔法数。
+ - 修正 udpComm.cpp:47-56 等价死分支与 default 静默吞帧:未知帧计入统计并日志告警。 +
4.3.2 上下行链路管理
+| 链路 | 现状 | 优化后 | 外部行为变化 |
|---|---|---|---|
| 上位机(MOOS) | 无连接管理,1 s 周期广播状态 | 启用已声明未使用的 connectState:以"周期广播正常发出 + 收到上位机任何邮件"维护在线标志;掉线仅记录与上报,不触发控制动作 | 无(只新增可观测性) |
| 下位机(UDP) | 30 s 超时置 isTimeout,超时母线发清零指令 | 超时事件接入 FaultManager(通信类故障码,新增码段,不占用现有码值);清零指令逻辑保持不变 | 故障上报新增通信超时码(经总体确认后启用) |
| 断路器闭环 | 事件路径忙等约 7 s + 600 ms 复位 | 异步化:发送→注册期望反馈→超时定时器,状态机以子状态跟踪;指令时序与重发次数严格保持现状 | 无(时序一致,仅不再阻塞线程) |
| Web 通道 | 下位机 8000 全功能 + /fcs/control 越权直改 | 下位机 Web 保留页面但控制类接口改为转发 MOOS 指令(等价于上位机下发);上位机 Web 方案不变 | 接口路径与参数不变,仅内部实现改道 |
4.3.3 工程细节修复
+-
+
- 发送路径消除每帧堆分配(栈对象 + sendto);删除每包 cout(LowerCommManager.cpp:381),统一走 loguru 并按级别可关。 +
- UDP 监听线程退出改为 socket 超时 + quit 标志,保证进程可干净退出。 +
- 移除
sendDisSysCommand废弃函数(含 0x55/0xAA 硬编码),或修正后纳入 ProtocolCodec。
+
4.4 故障码统一方案
+目标:一套编码、一个存储、一处映射
+1. 统一故障码定义文件 fault/FaultCodeDef.h(生成式,唯一事实源):
// 每条故障一行:枚举名, 码值(保持现有取值不变), 等级, 来源域, 中文描述
+FAULT_DEF(BAT_CELL_OVERVOLT_L1, 0x01, 1, DOMAIN_BATTERY, "单体过压1级")
+FAULT_DEF(DIS_BREAKER_TRIP_MAIN, 1, 2, DOMAIN_DIS, "主断路器脱扣")
+FAULT_DEF(SYS_DEPTH_INVALID_L3, 0x1302, 3, DOMAIN_SYSTEM, "深度数据失效")
+// 0xLSSS 系统码约 200 个字面量全部收编为枚举,码值、等级语义与现状逐条核对一致
+由此单文件自动生成:C++ 枚举、码值→描述映射表(供上报与前端共用,替代 fuelcell.h:834 的 JS 重写)、测试用的完整性校验数据。电池 0x01-0xC9、配电 1-45、系统 0xLSSS 三段码值全部原样保留,上位机与前端所见故障码不变。
+2. 合并故障存储为 FaultManager 单一集合:
+-
+
- 统一存放真实故障与注入故障(注入故障带 source 标记区分),上报接口(uPower_pmState)与等级查询(getFaultLevel)改为读同一集合,消除 F1/F2 的双源不一致;对外 JSON 字段名与格式不变。 +
- 锁存语义保持现状(自恢复即自动清除),但 FaultManager 内部记录"发生/恢复"事件并写入 SQLite 新增
fault_event(ts, code, level, event, source)表——新增表不影响现有表结构。
+ - 故障码访问统一加
faultCodeMutex,消除 TestEvent 无锁写。
+
3. 修复编码层缺陷:删除 systemData.h:776 的 #ifdef DEBUG(发布版也更新燃料电池 4 级故障字)、修正 DEBUFG 拼写、补齐 B6 三个断路器的故障码入参、faultCode.h:35 的 level 字段与描述对齐。
4.5 上位机 / 下位机管理方案
+| 主题 | 职责划分(优化后) |
|---|---|
| 控制通道 | 唯一入口:一切控制指令(网页、调试页、测试注入)最终都变为 MOOSDB 上的 uPower_*_cmd JSON,由 CommandService 统一校验、应答、记录。下位机 HTTP 控制接口仅做转发。 |
| 状态监视 | 上位机 HostSim 为主(跨机、带历史库 /api/history);下位机 Web 退化为本机调试页(日志查看、测试注入保留),dashboard/data 等监视页标注"调试用"。 |
| 指令校验 | CommandService 对 workCMD/powerCMD/modCMD 做范围校验 + 当前状态下合法性校验(按转移表),非法指令回执拒绝原因(新增 JSON 字段,缺省不发送,兼容旧上位机)。 |
| 指令清空哨兵 | workCMD=99 等魔法哨兵改为命名常量 CMD_NONE=99,取值不变,仅消除魔法数。 |
| 页面资源 | 下位机 7 个硬编码页面迁移到 CMake 资源管线(复用上位机 webassets_gen.h 方案),HTML 独立成文件,便于前端维护;URL 与页面功能不变。 |
5 设备上电管理与状态机转移重构详细设计
+ +5.1 总体设计判断
+设备上电/管理与状态机转移是同一个问题的两面——"声明"与"执行"混在一起。9 张工况设备表(driver.cpp 约 600 行逐字段赋值)本质是"数据",却写成了代码;状态 entry() 里的上电时序本质是"流程",却硬编码成一串串 setDeviceState + MOOSPause + operate_*_Breaker;状态转移本质是"一张表",却隐式埋在 handleWorkCmd/react(TaskStartEvent) 两个 switch 里。重构的共同方向:把数据从代码里剥离出来,把流程交给统一执行器,把转移显式化。
5.2 设备上电与管理重构
+5.2.1 现状代码证据
+-
+
- 工况表即代码:
Driver::getSTANDBYTable()(driver.cpp:151-228)等 9 个函数,每个约 70 行driverTable->depth1 = 1;式逐字段赋值,getCRUISE/getHIGH_SPEED/getASCEND_DESCEND内容几乎一致,且混用裸 0/1 与DRIVER_*宏。
+ - 上电时序硬编码且阻塞:
CruiseMode::entry()的典型模式是:查当前设备状态 →operate_HVA_actuatorCircuit_Breaker(0)(内部忙等最长 6 s)→MOOSPause(3)→ 连续 6 次setDeviceState→ 上报,每个状态 entry 都是这段逻辑的变体手写。
+ - 断路器闭环时序散落:
operateBreakerGeneric(LowerCommManager.cpp:843-890)的"首次发包 → 100 ms 等待 → 轮询反馈 → 200 ms 重发 → 6 s 超时 → 3×200 ms 复位"被 35 个operate_*函数复用,但在事件处理路径上同步执行,阻塞 MOOS 线程。
+ - 校验逻辑十份复制:
WaterBasedReady::react(DevCmdExt)中的校验链(Debug 模式豁免 → 深度上电保护 → 前视声纳先关机再断电并 MOOSPause(10000) → 写设备状态)在 10 个handleXxxCmd中各有变体,细微偏差正是 B4(桅杆键名失配)类缺陷的温床。
+
5.2.2 目标结构
+现状(分散) 目标(收敛)
+┌──────────────────────────────┐ ┌────────────────────────────────┐
+│ 9 张工况表 = 600 行赋值函数 │ │ ① 设备描述表 DeviceDef │
+│ entry() 硬编码时序+忙等6~20s │ 收敛 │ 68 设备 × 属性(域/断路器通道/ │
+│ 10 个 handleXxxCmd 校验复制 │ ───────────► │ 保护规则/9 工况默认值) │
+│ 35 个 operate_*_Breaker │ │ ② 功率时序执行器 PowerSequencer │
+└──────────────────────────────┘ │ 声明式步骤,异步分步执行 │
+ │ ③ 统一指令管线:模式校验→深度 │
+ │ 保护→前置动作→执行→回执 │
+ │ ④ DeviceManager 唯一写口 │
+ │ m_subDisSysCmd 只经它修改 │
+└────────────────────────────────┘
+5.2.3 重构四件事
+-
+
- 工况表从函数变成数据。9 个
getXxxTable()函数替换为静态二维表kModeDeviceTable[9][68](或按设备分组的声明式定义),查表替代函数调用。迁移时用程序把现有 9 个函数的输出快照为 golden data,重构后逐字节比对——这是"特性严格一致"最直接的保证。
+ - 上电时序从阻塞硬编码变成声明式步骤 + 异步执行器。每个工况声明一个步骤序列,例如 CruiseMode:
[条件:任一bowCover≠0] → 断启闭机构断路器(超时6s) → 延时3s → 批量下电bowCover1~6 → 上报。PowerSequencer 每拍 Iterate 推进一步,operateBreakerGeneric的时序参数(100 ms 首等、200 ms 重发、6 s 超时、3×200 ms 复位)原样保留为步骤属性,但不再阻塞线程;entry() 只提交序列即返回,状态机始终可响应事件。
+ - 设备指令校验收敛为一条管线。所有设备指令走同一链路:模式校验(Normal 禁操作)→ 域保护(水下设备深度检查,Debug 豁免)→ 设备专属前置动作(forwardSonar 先关机再断电等,注册在设备描述表)→ 经 Sequencer 执行 → 回执。各状态只声明"本状态允许操作哪些设备域",不再各写一份校验。 +
- 设备状态唯一写口。
m_subDisSysCmd现在被 FSM、HTTP 回调、测试注入直接修改,重构后只能经 DeviceManager 写入;外部可观测的写序列(日志、上报、SQLite 落库)保持一致。
+
5.3 状态机转移重构
+5.3.1 核心问题:转移关系隐式化
+转移关系不存在于任何一张表中,而是从两个 switch 的行为里"涌现"出来的——这正是 case 12 漏写 transit 导致 Lifting 不可达(B1)这类缺陷的结构性根源。重构目标是把转移机制从"隐式 switch"变为"显式表驱动":
现状:隐式转移 目标:显式转移
+MasterCommandEvent 到达 事件统一规范化
+ │ │
+handleWorkCmd 巨型 switch 查转移表 TransitionRule[]
+(14 状态 × 14 份复制 react) (源状态×事件→目标,唯一事实源)
+ │ │
+无 guard 直接 transit guard 校验
+(case 12 漏写 = Lifting 死状态) (一期空 guard 记日志,二期补互斥)
+ │ │
+entry() 阻塞上电 CHANING 过渡子状态
+(期间 FSM 不响应任何事件) (上电序列异步执行,完成即落定)
+ │ │
+FaultState 孤岛 FaultEvent 显式迁移
+(故障检测不触发迁移,无出口) (3 级以上故障进入,确认后退出)
+5.3.2 重构五步走
+-
+
- 把转移关系从代码里"挖"出来变成表。逐行核对
handleWorkCmd(workCMD 1~12)、react(TaskStartEvent)(THROW_LOAD/FLOAT_UP/RECYCLE/SAIL/HOVER/TOUR 等)、react(TaskStopEvent)与 InitState 自迁移,生成TransitionRule[]静态表,每行标注原代码位置(如workCMD=5 → CruiseMode, Fsm.cpp:2903)。该表同时是实现、文档与测试基准。handleWorkCmd的 switch 删除,14 个状态中复制粘贴的react(MasterCommandEvent)全部删除,统一由基类查表处理;workCMD=99 清空哨兵语义保留,仅改为命名常量。
+ - guard 分两期落地。一期所有 guard 为 nullptr(无条件),严格保持现状转移语义——这是特性一致的红线;基类在每次迁移时记录"源状态→事件→目标"日志。二期依据日志与总体组确认应禁止的迁移(如巡航中直接切 DJMode),再逐步补 guard。重构本身不引入行为变化,行为收紧是另一个独立、可评审的决策。 +
- entry() 阻塞上电改为 CHANING 过渡子状态。现状代码已有雏形——每个 entry 开头
workCondition = CHANING、干完活才置为目标工况,只是"干活"方式是阻塞的。重构将其正式化:迁移发生时进入目标状态的过渡形态,上电序列交 PowerSequencer 异步执行,序列完成事件使状态落定;期间 FSM 照常响应事件(如 FaultEvent 可打断上电)。上位机看到的 workCondition 变化序列与现状完全一致。
+ - FaultState 接入主转移图。故障检测发现 3 级以上故障 → FaultManager 派发 FaultEvent → 转移表新增
ANY × FaultEvent → FaultState规则(保留现有 THROW_LOAD 入口不变);FaultState 增加显式出口:故障码集合清空 + 上位机确认(复用现有 workCMD=4 指令,不新增协议)→ RemoteControl;FaultState 内的断路器断开序列同样改为 Sequencer 步骤,消灭 20 s 忙等。
+ - 状态类只做"差异"。状态类只保留本状态特有内容:entry 提交的上电序列、特有事件处理(如 DJMode 专属指令)、exit 清理;公共行为上收基类。CruiseMode/HighSpeedMode 等任务态的共性(均响应 TaskStopEvent→RemoteControl)通过继承 MissionStateBase 表达,利用 tinyfsm 原生支持的状态继承。 +
5.4 两块重构的衔接与线程模型
+5.5 迁移顺序与一致性验证
+-
+
- 快照基线:采集 9 张工况表输出、标准指令脚本下的状态上报时序,作为 golden data; +
- 设备描述表与指令管线(风险最低,先行); +
- PowerSequencer 替换阻塞上电; +
- 转移表替换 switch。 +
每一步均以 golden data diff 为空为通过条件,diff 非空则不进入下一步。B1(Lifting 不可达)等已确认缺陷在挖掘转移表时即会暴露,按第 6 章约定单独登记为行为变更点修复。
+ + +6 特性一致性保障策略
+"优化后与原代码特性严格一致"需要通过可执行的基线来保证,而不是靠评审印象。建议按以下四层契约冻结外部行为:
+| 契约层 | 冻结内容 | 验证手段 |
|---|---|---|
| MOOS 接口契约 | 全部订阅/发布变量名、JSON 键名与类型、周期广播频率(1 s)、应答时序 | 契约测试:录制现有系统真实 MOOS 流量为"黄金报文集",重构后回放比对(键集合、类型、取值范围) |
| UDP 帧契约 | 6 类反馈帧 ID、帧头 0x40 0x40、#pragma pack(1) 布局、校验算法、帧长 | 字节级编解码往返测试(现有 udp_feeder/full_feeder 扩展为参数化用例);结构体 static_assert 尺寸断言 |
| 行为契约 | 状态迁移外部可观测序列(指令→状态上报序列)、断路器操作时序(重发间隔、6 s 超时、600 ms 复位)、故障码取值与上报内容 | 黄金主测试:对现状系统跑标准指令脚本,录制 uPower_currentState_st 等上报时序为基线;重构后同脚本回放,时序逐拍比对 |
| Web/存储契约 | URL 路由、REST 参数、SQLite 现有表结构 | HTTP 接口冒烟用例;数据库 schema 比对脚本 |
7 测试方案
+7.1 测试金字塔与补齐重点
+| 层级 | 现状 | 补齐内容 | 框架/工具 |
|---|---|---|---|
| 单元测试 | GTest 56 例:Driver 表、SystemData、故障码表、upmsg 往返、UDP 校验和、pack 布局 | ① 状态机迁移测试:转移表全覆盖(每个规则至少 1 例)+ guard 边界;② FaultManager:检测→锁存→上报→清除全链路,含 B6/B7 回归;③ ProtocolCodec:JSON 键名完整性(防 B4 复发)、帧编解码参数化 | GTest;领域层零 MOOS 依赖后可脱离 MOOSDB 运行 |
| 集成测试 | udp_feeder/full_feeder 字节级仿真 CCU+4 路配电 + check_db.py 验证落库 | ① 故障注入 feeder:丢包、坏校验和、越界值、故障位置位、30 s 静默(超时路径);② MOOS 端到端:上位机指令→状态迁移→上报序列黄金比对(第 6 章基线);③ 断路器闭环时序仿真 | Python 脚本 + 现有 feeder 扩展;MOOSDB 冒烟沿用 |
| 系统/回归 | CI 有构建+冒烟,但集成失败不阻塞(build-test.sh:181),已知 std::bad_alloc 被容忍 | ① 集成失败纳入 ALL_OK 阻塞;② cppcheck/clang-tidy 接入 CI(error 级清零);③ 测试二进制输出改到 build 目录,清理 test/ 下 in-source 构建残留 | Gitea Actions 现有流水线扩展 |
| 上位机测试 | 零 | HostSim 指令队列 drain、FeedbackStore 落盘/24 h 清理、/api/history 时序提取、WS 快照广播 | GTest(队列/存储)+ Python(HTTP/WS 接口) |
7.2 关键专项测试
+-
+
- 状态机迁移矩阵:以 4.2.1 转移表为基准自动生成用例骨架——14 状态 × 11 事件全组合标注"允许/拒绝/保持",重构前后两轮运行,输出 diff 必须为空(除已登记的行为变更点)。 +
- 故障注入矩阵:覆盖三级来源——UDP 层(坏帧/丢帧/超时)、设备层(断路器脱扣、绝缘低、BMS 各级故障位)、系统层(深度失效、功率不足等 0xLSSS 码);断言故障码上报内容、FaultState 迁移、SQLite fault_event 落库三处一致。 +
- 并发压力:UDP 监听线程 100 Hz 灌包 + MOOS 指令并发注入,ThreadSanitizer 构建跑 10 min,数据竞争报告清零;队列上限 100 的溢出行为固定为"丢最旧 + 计数告警"并测试。 +
- 长稳:feeder 连续 24 h 正常流量 + 周期故障注入,监控内存(消除每帧 new/delete 后应无增长)、句柄、落库行数。 +
7.3 验收标准
+-
+
- 黄金报文比对:MOOS/UDP 契约测试 100% 通过,diff 为空; +
- 状态迁移矩阵:除已登记变更点(B1 修复后 Lifting 可达等)外零差异; +
- 新增单测覆盖率:fsm/、FaultManager、ProtocolCodec 行覆盖 ≥ 80%; +
- cppcheck error 级清零,TSan 报告清零; +
- CI 全链路(构建→单测→集成→黄金比对)绿灯且失败阻塞。 +
8 实施路线图
+ +阶段 0:缺陷修复与测试基线(先行,约 2 周)
+最小改动修复 B1-B14 并逐项登记行为变更;开启 -Wall -Wextra 清零告警、接入 sanitizer 构建目标与 cppcheck(error 级阻塞);配置项端口校验;搭建黄金报文基线与状态迁移矩阵录制工具。产出:行为基线库 + 变更清单 + 可运行的安全网。
阶段 1:故障码与数据层统一(约 2-3 周)
+落地 FaultCodeDef.h 生成式定义;合并双故障存储为 FaultManager;新增 fault_event 持久化表;修复条件编译与死码。该阶段相对独立、风险最低,先行可立即改善可维护性。产出:统一故障子系统 + 故障注入测试矩阵。
+阶段 2:通信层与线程模型(约 3-4 周)
+建立 protocol/ 单一事实源;UDP 监听线程改为纯生产者;断路器闭环异步化;连接状态管理启用;下位机 HTTP 控制接口改道 MOOS。产出:单线程事件循环模型 + 契约测试全绿。
+阶段 3:状态机重构(约 3-4 周)
+转移表显式化;PowerManagerFsm 上帝类拆分;状态类去重;FaultState 出入路径显式化;entry 阻塞消除。每步以迁移矩阵 diff 为空为准入。产出:表驱动状态机 + 迁移矩阵零差异报告。
+阶段 4:上位机与收尾(约 2 周)
+上位机测试补齐;下位机页面资源管线迁移;长稳与并发压力测试;文档更新(转移表自动生成状态图)。产出:完整测试体系 + 验收报告。
+9 附录:问题清单汇总
+| 编号 | 级别 | 类别 | 问题摘要 | 位置 | 处置 |
|---|---|---|---|---|---|
| B1 | P0 | Bug | Lifting 状态不可达(case 12 无 transit/break) | PowerManagerFsm.cpp:2934 | 阶段0修复(行为变更登记) |
| B2 | P0 | Bug | s.buoyage == 赋值误写为比较 ×4 | PowerManagerFsm.cpp:1471-1486 | 阶段0修复(行为变更登记) |
| B3 | P0 | Bug | 邮件偏斜 return 导致整批丢弃 | PowerManger.cpp:121-125 | 阶段0修复 |
| B4 | P1 | Bug | 桅杆升降舵 JSON 键名失配,指令静默丢失 | driver.cpp:844/924 | 阶段0修复(行为变更登记) |
| B5 | P1 | Bug | 故障检测函数重复调用两遍 | PowerManagerFsm.cpp:52-55 | 阶段0修复 |
| B6 | P1 | Bug | 3 个断路器故障码死码 | systemData.h:1026-1028 | 阶段0修复 |
| B7 | P1 | Bug | 故障注入数组越界 + systemData.h:524 越界 | PowerManagerFsm.cpp:3053 | 阶段0修复 |
| B8 | P1 | Bug | TestEvent switch 缺 break;绕过事件机制 | PowerManagerFsm.cpp:3084 | 阶段0修复 |
| B9 | P2 | Bug | 协议结构体未初始化返回 / memset 非 POD | upmsg/ 多处 | 阶段0修复 |
| B10 | P2 | 规范 | 超时常量与注释不符(FaultState 20s vs 其余 5s),复制 7 份 | FaultState.cpp:46/53 等 | 阶段3统一 |
| B11 | P1 | Bug | 模板残留致每条正常指令误报 "Unhandled Mail" run warning | PowerManger.cpp:142-146 | 阶段0修复 |
| B12 | P1 | Bug | 畸形 JSON 类型致 jsoncpp 抛异常(asUInt 无类型检查、无 catch) | driver.cpp:935 / UpperCommManager.cpp:48 | 阶段0修复 |
| B13 | P1 | Bug | HTTP 服务在配置/DB 就绪前于构造函数启动,提前暴露控制面 | PowerManger.cpp:83-87 | 阶段0修复 |
| B14 | P2 | 规范 | FsmLoop/_FsmCB、m_deviceCmdQuenue 无消费者;buildReport 占位;SKEW_TOLERANCE 宏未用 | PowerManger.h:48/121 等 | 阶段3清理 |
| A1 | P0 | 架构 | 状态机退化为路由壳,转移逻辑集中在基类 switch | fsm/ 全局 | 阶段3重构 |
| A2 | P0 | 架构 | 故障检测与 FaultState 脱钩;FaultState 无出口 | PowerManagerFsm.cpp:948-953 | 阶段1+3 |
| A3 | P1 | 架构 | entry() 内 MOOSPause/忙等最长 10-20 s | WaterBasedReady.cpp:50 等 | 阶段3异步化 |
| A4 | P1 | 架构 | 3096 行上帝类;static pm 裸指针全局穿透 | PowerManagerFsm.hpp:86 | 阶段3拆分 |
| A5-A7 | P2 | 规范 | 语义错位/拼写错误/死代码/日志混乱 | 多处 | 阶段3清理 |
| C1 | P1 | 通信 | 上位机无连接管理,connectState 死字段 | LowerCommManager.h:131 | 阶段2启用 |
| C2 | P1 | 通信 | 协议知识三处散养;Driver 双实例 | driver.cpp / UpperCommManager.h:93 | 阶段2收敛 |
| C3-C6 | P1 | 通信 | 错误码虚设/死分支/硬编码帧长/废弃函数错误数据 | udpComm.cpp / driver.cpp | 阶段2修复 |
| C5 | P2 | 性能 | 每帧堆分配、每包 cout | udpComm.cpp:462 等 | 阶段2优化 |
| C7 | P1 | 架构 | 双 Web 重复建设;/fcs/control 越权直改状态 | httpserver.cpp:150 | 阶段2+4 |
| F1 | P0 | 故障 | 双重故障存储,等级查询与上报读不同集合 | PowerManger.cpp:448 / uPower_pmState.h:50 | 阶段1合并 |
| F2 | P1 | 故障 | 上报死赋值 | UpperCommManager.cpp:240 | 阶段1清理 |
| F3 | P1 | 故障 | 三套编码并存;系统码无文本映射;前端 JS 重写映射 | faultCode.h / fuelcell.h:834 | 阶段1统一 |
| F4 | P1 | 故障 | DEBUG 宏全构建未定义,4 级故障字恒为 0;DEBUFG 拼写;_DEBUG 死代码 | systemData.h:7,776 等 | 阶段0修复 |
| F5 | P2 | 故障 | 无故障事件持久化表 | SQLite 层 | 阶段1新增 |
| D1-D4 | P1 | 并发 | 全局数据池/锁纪律不一致/abs 截断/并发清队列 | systemData.h 等 | 阶段2统一线程模型 |
| H1-H3 | P2 | 上位机 | 功能重叠/越权通道/页面硬编码 | HostSim / httpserver | 阶段4 |
| E1 | P1 | 构建 | -Wall 未启用且写法错误;无 sanitizer;测试输出残留 | CMakeLists.txt:80-86 | 阶段0接入 |
| E2 | P2 | 构建 | GLOB 反模式;第三方库无版本/许可证记录;CMAKE 版本过老 | CMakeLists.txt:9,49,53 | 阶段0接入 |
| E3 | P1 | 配置 | ccuport/iport atoi 无校验、无范围检查 | PowerManger.cpp:229/233 | 阶段0修复 |
| E4 | P2 | 性能 | SQLite insertGeneric/onFrame 高频重复 prepare | SQLite.cpp:610-625 | 阶段1优化 |
| E5 | P2 | 规范 | 日志/错误输出三套并存(loguru/cerr/cout) | driver.cpp / LowerCommManager.cpp | 阶段3统一 |
| T1 | P0 | 测试 | 状态机/故障注入/上位机三大测试空白 | test/ | 阶段0起持续 |
| T2 | P1 | CI | 集成失败不阻塞;cppcheck 未接入;in-source 构建残留 | ci/build-test.sh:181 | 阶段0 |
+本报告基于 2026-09-03 工作区代码静态审查生成,所有问题均标注文件与行号,关键缺陷(B1、B2、B3、B5)已经人工逐行复核。报告未对任何源代码做修改。 +
+ +