diff --git a/docs/pPowerManger_代码审查与架构优化报告.html b/docs/pPowerManger_代码审查与架构优化报告.html new file mode 100644 index 0000000..d9e3bd9 --- /dev/null +++ b/docs/pPowerManger_代码审查与架构优化报告.html @@ -0,0 +1,651 @@ + + + + + +pPowerManger 代码审查与架构优化报告 + + + +
+ +
+ +
+
CODE REVIEW & ARCHITECTURE OPTIMIZATION
+

pPowerManger 电源管理软件
代码审查与架构优化报告

+
项目:H100PowerManger(UUV 复合能源功率管理) | 范围:src/pPowerManger、src/pPowerMangerHost、test、ci | 日期:2026-09-03 | 版本:V1.0
+
+ + +

1 审查概述

+

pPowerManger 是 H100 水下无人航行器复合能源系统的电源管理进程,运行于 MOOS 中间件之上,承担上位机指令接收、下位机(CCU/配电)设备管理、14 工况状态机调度、故障检测与上报、数据持久化及本地 Web 监视等职责。本次审查覆盖下位机核心代码约 2.4 万行(不含第三方 mongoose 库约 2.8 万行),重点评估代码架构、控制逻辑、通信逻辑、故障管理与测试体系,并给出以保持外部特性严格一致为前提的优化方案。

+ +
+
14
工况状态(tinyfsm)
+
4
并发线程(无统一模型)
+
3
套互不统一的故障编码
+
0
状态机 / 上位机测试用例
+
+ +
+

总体结论

+

现有代码功能可用,但工程化程度不足,核心问题集中在五条主线:

+
    +
  1. 存在多个确定性缺陷:Lifting(起吊准备)状态永远无法到达;浮力调节状态反馈因赋值误写为比较而永不更新;MOOS 邮件偏斜检查导致整批邮件被丢弃等(见 3.1)。
  2. +
  3. 状态机退化:tinyfsm 仅被用作事件路由外壳,真正的转移逻辑集中在基类两个巨型 switch 中;无 guard 条件;故障检测与 FaultState 完全脱钩。
  4. +
  5. 通信层职责混杂:协议知识(JSON 键名、设备 ID、帧格式)散落在 Driver、UpperCommManager、udpComm 三处;4 个线程对共享数据加锁纪律不一致,存在数据竞争与忙等阻塞。
  6. +
  7. 故障码体系三分天下:电池故障表、配电故障表、0xLSSS 系统故障魔数三套编码并存,双故障存储导致单一事实源被破坏,上报与显示映射脱节。
  8. +
  9. 测试存在三大空白:状态机迁移、故障注入、上位机链路均无测试,重构缺乏安全网。
  10. +
+

优化应以"先建测试基线、再做特性等价重构"为总原则,分四个阶段实施(见第 7、8 章)。

+
+ + +

2 现状架构分析

+ +

2.1 进程与通信拓扑

+
+ Web 页面(浏览器)⇄ WebSocket + pPowerMangerHost(上位机桥)⇄ MOOSDB (TCP:9000, JSON 字符串变量) + pPowerManger(本进程)⇄ UDP 二进制帧 (5001↔7000) + pCCU / pCanBridge⇄ CAN + 电池 / 燃料电池 / 配电设备 +
+

另有两条旁路: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.cpp261 行MOOS 邮件 JSON 解析、周期状态广播解析失败误报 Unknown key;无连接管理
下位机通信LowerCommManager.cpp890 行UDP 监听线程、帧校验入队、状态注入、断路器闭环操作回调直改全局数据;忙等最长约 7 s;35 个操作函数重复
协议编解码udpcomm/udpComm.cpp, ccuUdpMsg.h~600 行UDP 二进制帧打包/解析、校验硬编码帧长;死分支;每帧堆分配
设备策略表driver.cpp1094 行68 字段 DriverTable、9 张工况设备表、JSON 编解码9 张表约 600 行逐字段重复赋值;错误码形同虚设
系统数据systemData.h, pmSysvariable.h~2250 行协议镜像结构体 + 全局数据池单例头文件内实现;百字段全局池;锁纪律不一致
故障码faultCode.h125 行电池 54 项 + 配电 45 项故障表与第三套 0xLSSS 魔数并存;上报/显示映射脱节
本地 Webhttpserver/(mongoose + 内嵌页面)~7000 行dashboard/日志/测试注入/电池/燃料电池页面与上位机 Web 重复建设;HTTP 回调越权直改状态
+ +

2.3 线程模型

+ + + + + + +
线程周期主要工作风险
MOOS 主线程4 HzOnNewMail → 指令解析 → 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 级,建议立即修复)

+

以下问题经逐行核对确认,均有明确代码证据,与架构无关,应在重构前先以最小改动修复并补充回归用例。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#级别问题位置影响与修复建议
B1P0Lifting(起吊准备)状态永远无法到达:handleWorkCmd 的 case 12 只有日志,缺少 transit<Lifting>() 与 break,直接坠入 defaultfsm/PowerManagerFsm.cpp:2934-2936上位机"起吊准备"指令被静默吞掉,Lifting 为死状态。补 transit<Lifting>(); break;。
B2P0赋值误写为比较:s.buoyage == 1;(共 4 处,值 1/3/2/0),语句无副作用fsm/PowerManagerFsm.cpp:1471,1475,1482,1486浮力调节(buoyage)状态反馈永远不更新,上报恒为初值。改为 =。
B3P0MOOS 邮件整批丢弃:OnNewMail 中任一邮件偏斜超差即 return true,同批其余邮件全部丢失PowerManger.cpp:121-125网络抖动时会成片丢失上位机指令。改为跳过该条、继续处理后续邮件。
B4P1JSON 键名不匹配导致指令静默丢失:发送端键 "mastLiftingServo_uint8",接收端解析键 "mastLiftingServo_uint8_uint8"driver.cpp:844 vs 924桅杆升降舵指令永远解析失败。统一键名,并增加"未知键/未命中"告警日志。
B5P1故障检测函数重复执行:basicNavigationFault(); extendedNavigationFault(); 连续调用两遍fsm/PowerManagerFsm.cpp:52-554 Hz 下双倍开销,且对带副作用的故障逻辑是隐患。删除重复行。
B6P1三个断路器故障永不上报:fbReserved/tyzReserved/reserved 调用的是无 faultCode 参数的重载,disfaultMap 中 code 10-12 成死码systemData.h:1026-1028补齐故障码入参,或删除死码并同步前端映射。
B7P1数组越界风险:故障注入 batfaultMap[e.eventId].code 对来自外部的 eventId 无边界检查;systemData.h:524 循环 i<2 越界访问 reserved2[1](cppcheck error 级)fsm/PowerManagerFsm.cpp:3053;systemData.h:524注入接口可被越界触发,属安全隐患。加边界校验;修正循环上界。
B8P1TestEvent 故障注入 switch 缺 break:case 15 坠入 default;另有 react(TestEvent) 直接构造 FaultEvent 调故障函数,绕过事件机制fsm/PowerManagerFsm.cpp:2988-2989, 3084-3092注入 15 号故障时行为未定义。补 break,统一走事件派发。
B9P2未初始化返回(cppcheck error 级):uPlan_taskStart.h:29 返回未初始化 mission;uExternComm_setDeviceSwitch.h:39 可能返回未初始化 cmd.cmd;uPower_pmState.h:14 对含 std::vector 的结构体 memsetupmsg/ 多个头文件协议层偶发脏数据。统一在结构体定义处给默认值,禁止对非 POD memset。
B10P2注释与代码不符:FaultState.cpp:46 注释"5 秒超时"实为 20 秒(:53),且与 Lifting.cpp:40、PowerManagerFsm.cpp 内 5 处同逻辑代码的 5 秒超时不一致;同一段超时代码共复制 7 份FaultState.cpp:46/53 等误导维护且各副本超时阈值漂移。随重构统一为命名常量。
B11P1模板残留致每条正常指令误报 "Unhandled Mail":OnNewMail 的 for 循环内保留了 MOOS 模板代码 if(key=="FOO")...else if(key!="APPCAST_REQ") reportRunWarning(...),除 APPCAST_REQ 外的所有邮件(含已正常处理的 uPower_*_cmd/fb)都被计入 run warningPowerManger.cpp:142-146AppCast 告警计数被污染、日志刷屏。删除该模板残留,仅在 processUpperMsg 返回 false 时告警。
B12P1畸形 JSON 可致崩溃:loadDriverIdFromJson 对 root[field].asUInt() 无类型检查(JSON_USE_EXCEPTION=1),调用处无 try/catch;SafeReadUInt 用 std::cerr 报错而非日志框架driver.cpp:935-936;UpperCommManager.cpp:48恶意/畸形指令使 jsoncpp 抛异常向上传播。统一走 SafeReadUInt 并捕获异常、改走日志。
B13P1HTTP 服务在配置就绪前启动:m_httpServer->start() 在构造函数调用,此时 mission 配置未加载、m_db 为 nullptr、UDP 未 bind,控制接口(/fcs/control 等)已对外可访问PowerManger.cpp:83-87存在"配置生效前已暴露控制面"窗口。将 start() 移到 OnStartUp 末尾(DB/端口就绪后)。
B14P2死代码与孤儿成员: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 现状转移关系(经代码还原)

+ + + + + + + + +
源状态事件目标状态说明
InitStateentry() 内直接切换StandbyState初始化完成即转
任意状态MasterCommandEvent(workCMD 1~11)Standby / ShoreBasedReady / WaterBasedReady / RemoteControl / Cruise / HighSpeed / FloatDown / FloatAdjust / UnderwaterSurvey / SurfaceSurvey / DJModePowerManagerFsm.cpp:2887-2941;workCMD=12 无效(B1)
任意状态TaskStartEventTHROW_LOAD→FaultState;FLOAT_UP/SAT_COMM 等→FloatDown;RECYCLE/SAIL→CruiseMode;HOVER→FloatAdjust;TOUR→UnderwaterSurveyPowerManagerFsm.cpp:948-985
任意状态TaskStopEventRemoteControl基类 react,cpp:13
StandbyStateStandbyEventStandbyState(自切换)语义存疑
FaultState—(无显式出口)—仅能借基类 handleWorkCmd 间接跳出
+ +

3.2.2 主要架构问题

+
+A1 状态机退化为"事件路由壳"。真正的状态转移表不在各状态类中,而集中在基类 handleWorkCmd 与 react(TaskStartEvent) 两个巨型 switch;14 个状态类的 react(MasterCommandEvent) 是完全相同的复制粘贴。tinyfsm 提供的 transit<S>(action, condition) guard 重载(tinyfsm.hpp:151)全项目零使用——任何状态下都可经 workCMD 直接跳进任何工况(如巡航中直接切起吊),无任何合法性检查。 +
+
+A2 故障检测与 FaultState 完全脱钩。8 个 xxxFault() 检测函数只写故障码集合,从不触发状态迁移;FaultState 唯一入口是"抛载任务"(TaskStartEvent::THROW_LOAD),即 3 级故障(如深度失效 0x1302)发生时系统仍停留在原工况。FaultState 无显式出口、无故障恢复确认、无故障码清除联动。FaultEvent 无任何 dispatch 点,基类 react(FaultEvent) 为死代码。 +
+
+A3 entry() 内长阻塞。WaterBasedReady.cpp:50 与 Lifting.cpp:10 在 entry() 中 MOOSPause(10000);ShoreBasedReady.cpp:24-35 while 轮询等待约 10 s;断路器操作闭环在事件处理路径上忙等约 7 s。状态迁移期间 FSM 无法响应任何新事件,上位机指令被积压在邮件队列。 +
+
+A4 上帝类与全局穿透。PowerManagerFsm.cpp(3096 行)集中了 10 个高度重复的设备指令处理函数、10 个 getXxxStatus 状态拷贝函数、8 组故障编码函数(约 200 个 0xLSSS 魔法故障码)。状态代码经 static PowerManger* pm 裸指针直接读写宿主几十个公有成员、直接操作断路器、直接 sendNotification,单向依赖原则被彻底打破。 +
+ + +

3.3 通信逻辑

+
+C1 上位机连接无管理。LowerCommManager::m_connectState(h:131)与 PowerManger::connectState(h:136)声明后从未使用;上位机方向无心跳、无在线检测、无断线告警,仅靠 MOOS 库自动重连。下位机方向有 30 s 超时检测(systemData.h:752-761),但超时后仅置标志位,未与故障系统联动。 +
+
+C2 协议知识三处散养。JSON 键名与设备 ID 映射(协议层知识)写在 Driver(业务策略层)里,与发送端 driver.cpp 各维护一份,已造成 B4 键名失配;UDP 帧格式在 udpComm,设备 ID 0-61 在 driver.cpp:908-930 与 DeviceId 枚举重复硬编码。Driver 实例在 PowerManger 与 UpperCommManager 各持一份,表状态易不一致。 +
+ + +

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 个裸字面量无注释无枚举;等级提取依赖位运算巧合
+ + +

3.5 数据管理与并发

+ + +

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。可作为重构时"松耦合桥"的保留样板。

+ + +

3.7 构建工程与配置管理

+ + + +

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            │
+└─────────────────────────────────────────────────────────────┘
+
+ + +

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 与故障系统联动

+
    +
  1. 入口:FaultManager 检测到 3 级及以上故障(或抛载任务)→ 投递 FaultEvent → FSM 迁移 FaultState,保持现有"抛载进 FaultState"路径不变。
  2. +
  3. entry 动作异步化:断路器断开序列改为 FaultState 内部的子步骤计时状态(step + deadline),每拍 Iterate 推进,取代 MOOSPause(10000) 与 20 s 忙等;超时阈值统一为命名常量并修正注释。
  4. +
  5. 出口:显式定义恢复路径——故障码集合清空且收到上位机确认指令(workCMD=4 RemoteControl,保持现有指令集不变)→ 退出 FaultState。该路径当前已可经基类 handleWorkCmd 间接实现,重构只是将其显式化并纳入转移表,不改变外部行为。
  6. +
+ +

4.2.3 代码组织

+ + +

4.3 通信层重构方案

+

4.3.1 协议单一事实源

+

建立 protocol/ 目录,以一份定义文件同时生成/校验编解码两侧,杜绝键名失配(B4)类问题:

+ +

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 工程细节修复

+ + +

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 现状代码证据

+ + +

5.2.2 目标结构

+
+
现状(分散)                                    目标(收敛)
+┌──────────────────────────────┐               ┌────────────────────────────────┐
+│ 9 张工况表 = 600 行赋值函数   │               │ ① 设备描述表 DeviceDef          │
+│ entry() 硬编码时序+忙等6~20s  │    收敛       │   68 设备 × 属性(域/断路器通道/ │
+│ 10 个 handleXxxCmd 校验复制   │ ───────────►  │   保护规则/9 工况默认值)        │
+│ 35 个 operate_*_Breaker       │               │ ② 功率时序执行器 PowerSequencer │
+└──────────────────────────────┘               │   声明式步骤,异步分步执行       │
+                                               │ ③ 统一指令管线:模式校验→深度   │
+                                               │   保护→前置动作→执行→回执       │
+                                               │ ④ DeviceManager 唯一写口        │
+                                               │   m_subDisSysCmd 只经它修改      │
+└────────────────────────────────┘
+
+ +

5.2.3 重构四件事

+
    +
  1. 工况表从函数变成数据。9 个 getXxxTable() 函数替换为静态二维表 kModeDeviceTable[9][68](或按设备分组的声明式定义),查表替代函数调用。迁移时用程序把现有 9 个函数的输出快照为 golden data,重构后逐字节比对——这是"特性严格一致"最直接的保证。
  2. +
  3. 上电时序从阻塞硬编码变成声明式步骤 + 异步执行器。每个工况声明一个步骤序列,例如 CruiseMode:[条件:任一bowCover≠0] → 断启闭机构断路器(超时6s) → 延时3s → 批量下电bowCover1~6 → 上报。PowerSequencer 每拍 Iterate 推进一步,operateBreakerGeneric 的时序参数(100 ms 首等、200 ms 重发、6 s 超时、3×200 ms 复位)原样保留为步骤属性,但不再阻塞线程;entry() 只提交序列即返回,状态机始终可响应事件。
  4. +
  5. 设备指令校验收敛为一条管线。所有设备指令走同一链路:模式校验(Normal 禁操作)→ 域保护(水下设备深度检查,Debug 豁免)→ 设备专属前置动作(forwardSonar 先关机再断电等,注册在设备描述表)→ 经 Sequencer 执行 → 回执。各状态只声明"本状态允许操作哪些设备域",不再各写一份校验。
  6. +
  7. 设备状态唯一写口。m_subDisSysCmd 现在被 FSM、HTTP 回调、测试注入直接修改,重构后只能经 DeviceManager 写入;外部可观测的写序列(日志、上报、SQLite 落库)保持一致。
  8. +
+ +

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 重构五步走

+
    +
  1. 把转移关系从代码里"挖"出来变成表。逐行核对 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 清空哨兵语义保留,仅改为命名常量。
  2. +
  3. guard 分两期落地。一期所有 guard 为 nullptr(无条件),严格保持现状转移语义——这是特性一致的红线;基类在每次迁移时记录"源状态→事件→目标"日志。二期依据日志与总体组确认应禁止的迁移(如巡航中直接切 DJMode),再逐步补 guard。重构本身不引入行为变化,行为收紧是另一个独立、可评审的决策。
  4. +
  5. entry() 阻塞上电改为 CHANING 过渡子状态。现状代码已有雏形——每个 entry 开头 workCondition = CHANING、干完活才置为目标工况,只是"干活"方式是阻塞的。重构将其正式化:迁移发生时进入目标状态的过渡形态,上电序列交 PowerSequencer 异步执行,序列完成事件使状态落定;期间 FSM 照常响应事件(如 FaultEvent 可打断上电)。上位机看到的 workCondition 变化序列与现状完全一致。
  6. +
  7. FaultState 接入主转移图。故障检测发现 3 级以上故障 → FaultManager 派发 FaultEvent → 转移表新增 ANY × FaultEvent → FaultState 规则(保留现有 THROW_LOAD 入口不变);FaultState 增加显式出口:故障码集合清空 + 上位机确认(复用现有 workCMD=4 指令,不新增协议)→ RemoteControl;FaultState 内的断路器断开序列同样改为 Sequencer 步骤,消灭 20 s 忙等。
  8. +
  9. 状态类只做"差异"。状态类只保留本状态特有内容:entry 提交的上电序列、特有事件处理(如 DJMode 专属指令)、exit 清理;公共行为上收基类。CruiseMode/HighSpeedMode 等任务态的共性(均响应 TaskStopEvent→RemoteControl)通过继承 MissionStateBase 表达,利用 tinyfsm 原生支持的状态继承。
  10. +
+ +

5.4 两块重构的衔接与线程模型

+
+衔接点在 CHANING 子状态:状态机管"去哪里",PowerSequencer 管"到了之后按什么时序上电",DeviceManager 管"每一笔设备状态怎么写"。三者通过事件队列串行协作,全部运行在 MOOS 主线程上,从根上消除现有四个线程交叉读写全局数据的问题。UDP 监听线程与 HTTP 线程退化为纯生产者:收包→解析→投递事件队列,不再直接触碰业务状态。 +
+ +

5.5 迁移顺序与一致性验证

+
    +
  1. 快照基线:采集 9 张工况表输出、标准指令脚本下的状态上报时序,作为 golden data;
  2. +
  3. 设备描述表与指令管线(风险最低,先行);
  4. +
  5. PowerSequencer 替换阻塞上电;
  6. +
  7. 转移表替换 switch。
  8. +
+

每一步均以 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 比对脚本
+
+已知缺陷的处理约定:3.1 节 B1-B10 属"现状特性"的一部分还是"应修复的缺陷",须逐项与总体确认。建议原则:外部可观测且显然错误的行为(如 buoyage 恒不更新、Lifting 不可达、桅杆指令丢失)按缺陷修复并在报告中显式登记"行为变更点";纯内部问题(重复调用、死代码)直接修复,行为不变。所有行为变更点单独列入变更清单,重构完成时逐项回归确认。 +
+ + +

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 关键专项测试

+ +

7.3 验收标准

+
    +
  1. 黄金报文比对:MOOS/UDP 契约测试 100% 通过,diff 为空;
  2. +
  3. 状态迁移矩阵:除已登记变更点(B1 修复后 Lifting 可达等)外零差异;
  4. +
  5. 新增单测覆盖率:fsm/、FaultManager、ProtocolCodec 行覆盖 ≥ 80%;
  6. +
  7. cppcheck error 级清零,TSan 报告清零;
  8. +
  9. CI 全链路(构建→单测→集成→黄金比对)绿灯且失败阻塞。
  10. +
+ + +

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 周)

+

上位机测试补齐;下位机页面资源管线迁移;长稳与并发压力测试;文档更新(转移表自动生成状态图)。产出:完整测试体系 + 验收报告。

+
+
+风险控制:每阶段结束必须 CI 全绿(含黄金比对)方可合并;协议/故障码/状态序列的任何外部可见变更必须已在变更清单登记并经确认;保持 pCCU、pMotor、pELoad 等周边进程接口不动,重构范围严格限定在 pPowerManger 与 pPowerMangerHost。 +
+ + +

9 附录:问题清单汇总

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
编号级别类别问题摘要位置处置
B1P0BugLifting 状态不可达(case 12 无 transit/break)PowerManagerFsm.cpp:2934阶段0修复(行为变更登记)
B2P0Bugs.buoyage == 赋值误写为比较 ×4PowerManagerFsm.cpp:1471-1486阶段0修复(行为变更登记)
B3P0Bug邮件偏斜 return 导致整批丢弃PowerManger.cpp:121-125阶段0修复
B4P1Bug桅杆升降舵 JSON 键名失配,指令静默丢失driver.cpp:844/924阶段0修复(行为变更登记)
B5P1Bug故障检测函数重复调用两遍PowerManagerFsm.cpp:52-55阶段0修复
B6P1Bug3 个断路器故障码死码systemData.h:1026-1028阶段0修复
B7P1Bug故障注入数组越界 + systemData.h:524 越界PowerManagerFsm.cpp:3053阶段0修复
B8P1BugTestEvent switch 缺 break;绕过事件机制PowerManagerFsm.cpp:3084阶段0修复
B9P2Bug协议结构体未初始化返回 / memset 非 PODupmsg/ 多处阶段0修复
B10P2规范超时常量与注释不符(FaultState 20s vs 其余 5s),复制 7 份FaultState.cpp:46/53 等阶段3统一
B11P1Bug模板残留致每条正常指令误报 "Unhandled Mail" run warningPowerManger.cpp:142-146阶段0修复
B12P1Bug畸形 JSON 类型致 jsoncpp 抛异常(asUInt 无类型检查、无 catch)driver.cpp:935 / UpperCommManager.cpp:48阶段0修复
B13P1BugHTTP 服务在配置/DB 就绪前于构造函数启动,提前暴露控制面PowerManger.cpp:83-87阶段0修复
B14P2规范FsmLoop/_FsmCB、m_deviceCmdQuenue 无消费者;buildReport 占位;SKEW_TOLERANCE 宏未用PowerManger.h:48/121 等阶段3清理
A1P0架构状态机退化为路由壳,转移逻辑集中在基类 switchfsm/ 全局阶段3重构
A2P0架构故障检测与 FaultState 脱钩;FaultState 无出口PowerManagerFsm.cpp:948-953阶段1+3
A3P1架构entry() 内 MOOSPause/忙等最长 10-20 sWaterBasedReady.cpp:50 等阶段3异步化
A4P1架构3096 行上帝类;static pm 裸指针全局穿透PowerManagerFsm.hpp:86阶段3拆分
A5-A7P2规范语义错位/拼写错误/死代码/日志混乱多处阶段3清理
C1P1通信上位机无连接管理,connectState 死字段LowerCommManager.h:131阶段2启用
C2P1通信协议知识三处散养;Driver 双实例driver.cpp / UpperCommManager.h:93阶段2收敛
C3-C6P1通信错误码虚设/死分支/硬编码帧长/废弃函数错误数据udpComm.cpp / driver.cpp阶段2修复
C5P2性能每帧堆分配、每包 coutudpComm.cpp:462 等阶段2优化
C7P1架构双 Web 重复建设;/fcs/control 越权直改状态httpserver.cpp:150阶段2+4
F1P0故障双重故障存储,等级查询与上报读不同集合PowerManger.cpp:448 / uPower_pmState.h:50阶段1合并
F2P1故障上报死赋值UpperCommManager.cpp:240阶段1清理
F3P1故障三套编码并存;系统码无文本映射;前端 JS 重写映射faultCode.h / fuelcell.h:834阶段1统一
F4P1故障DEBUG 宏全构建未定义,4 级故障字恒为 0;DEBUFG 拼写;_DEBUG 死代码systemData.h:7,776 等阶段0修复
F5P2故障无故障事件持久化表SQLite 层阶段1新增
D1-D4P1并发全局数据池/锁纪律不一致/abs 截断/并发清队列systemData.h 等阶段2统一线程模型
H1-H3P2上位机功能重叠/越权通道/页面硬编码HostSim / httpserver阶段4
E1P1构建-Wall 未启用且写法错误;无 sanitizer;测试输出残留CMakeLists.txt:80-86阶段0接入
E2P2构建GLOB 反模式;第三方库无版本/许可证记录;CMAKE 版本过老CMakeLists.txt:9,49,53阶段0接入
E3P1配置ccuport/iport atoi 无校验、无范围检查PowerManger.cpp:229/233阶段0修复
E4P2性能SQLite insertGeneric/onFrame 高频重复 prepareSQLite.cpp:610-625阶段1优化
E5P2规范日志/错误输出三套并存(loguru/cerr/cout)driver.cpp / LowerCommManager.cpp阶段3统一
T1P0测试状态机/故障注入/上位机三大测试空白test/阶段0起持续
T2P1CI集成失败不阻塞;cppcheck 未接入;in-source 构建残留ci/build-test.sh:181阶段0
+ +

+本报告基于 2026-09-03 工作区代码静态审查生成,所有问题均标注文件与行号,关键缺陷(B1、B2、B3、B5)已经人工逐行复核。报告未对任何源代码做修改。 +

+ +
+
+ + diff --git a/docs/电子负载说明手册/1_3_1_IT6000C User Manual-CN.pdf b/docs/电子负载说明手册/1_3_1_IT6000C User Manual-CN.pdf new file mode 100644 index 0000000..8c902d7 Binary files /dev/null and b/docs/电子负载说明手册/1_3_1_IT6000C User Manual-CN.pdf differ diff --git a/docs/电子负载说明手册/IT6000C-Programming-Guide.pdf b/docs/电子负载说明手册/IT6000C-Programming-Guide.pdf new file mode 100644 index 0000000..f612dec Binary files /dev/null and b/docs/电子负载说明手册/IT6000C-Programming-Guide.pdf differ