36 Commits
Author SHA1 Message Date
zjk c61ac8ccb9 docs 新增 pPowerManger 代码审查与架构优化报告及电子负载 IT6000C 说明手册(PDF)
build-test-deploy / ci (push) Failing after 30s
2026-09-09 20:52:15 +08:00
zjk f7d2b98dc1 脚本/mission 移除已废弃的 deploy.sh:clean-data/fetch-data/sync-board-time 帮助与报错文案改用 ssh-copy-id 指引,mission 注释同步改为 build-board.sh 部署,避免引用已删除脚本 2026-09-09 20:52:14 +08:00
zjk e59f7e3d2e pMotor 网页修复快照刷新导致输入光标跳动:页面改为加载时一次性构建静态骨架,WebSocket 快照到达后仅就地更新各数值叶节点(setH),不再整块 innerHTML 重建;输入框/按钮常驻 DOM,聚焦与光标不再被每秒推送打断,移除原 __lastSpeed 保值 hack
build-test-deploy / ci (push) Failing after 1h31m36s
2026-09-09 20:50:31 +08:00
zjk c610190c54 pELoad 连接后从设备回读真实配置同步状态
程序重启/刷新后页面状态与设备实际状态错配,导致按页面流程下发指令反馈错误。
新增 syncDeviceConfigOnce:连接建立进入远控后同步查询设备 OUTP/FUNC/FUNC:MODE
及各限值/设定值,以设备真实回读为准覆盖内存状态;设备输出仍为开时置
loadOn=true 并由 applyEnable 重新下发完整使能序列(接管恢复控制)。

- IT6000C 新增 7 个配置回读查询封装(CURR?/VOLT?/VOLT:LIM?/CURR:LIM?*2/POW:LIM?*2)
- ELoad 实现 syncDeviceConfigOnce 并接入 doControlCycle(连接后自动,覆盖重连/复位/本控→远控)
- 抽出 normFunc 辅助函数复用于 pollError;快照新增 cfgSynced/cfgSyncTime
- 页面卡2 增加设备配置同步状态显示
2026-09-07 13:03:08 +08:00
zjk 237e17ea50 pELoad 新增功能模式(FUNC:MODE)远程切换与实时曲线页:切换带白名单校验/下发后立即回读确认(真实反馈驱动显示),模式名规范化兼容设备长格式回读(Fixed/LIST/BATTery/SOLar/CARProfile);新增 /trend 实时曲线页(canvas 绘制 V/I/P 三曲线、窗口 30s~10min、历史按时间范围回填),WebSocket 快照推送提至 5Hz 2026-09-03 20:48:30 +08:00
zjk 91be306a92 pELoad 多处完善:测量改为 FETCh 读缓存(不触发测量、修复真实设备 MEAS 查询超时失败)+ 异步查询不阻塞 100ms 控制循环;CV 优先控制路径与 FUNC:MODE FIX 防护;sqlite 增加 volt_set/cmd_volt 列并支持旧库自动迁移;页面重构为静态骨架(输入框永不重建、聚焦不被打断)+ 指令确认/步骤引导/测量诊断;deploy.sh 服务列表补 pMotor/pELoad,missions/h100.moos 补 pELoad 新配置 2026-09-03 16:40:27 +08:00
zjk 2a34b79461 新增 pELoad 电子负载(IT6000C)控制程序:以太网 Raw Socket SCPI 直连设备,100ms 周期功率控制(订阅 MOOS 功率消息按 P/U 折算电流,CC/CV 双环路优先 + FUNC:MODE FIX 防护),网页人机交互(步骤引导/防误触确认),sqlite3 存储(100ms 高采样 V/I/P 测量表 meas_log + CSV 导出分析),并接入 scripts/build-board.sh 板卡部署与 systemd 服务、missions/h100.moos 配置 2026-09-03 12:26:02 +08:00
zjk 38c489caec pPowerManger SQLite 老库缺列自动迁移:按注册表对比现有列逐列 ALTER TABLE ADD COLUMN(协议演进新增字段后旧表可自动补齐),SCHEMA_VERSION 升至 2
build-test-deploy / ci (push) Successful in 30m10s
2026-09-02 20:47:47 +08:00
zjk b77dc8cf5a pPowerManger 网页日志监控指向实际日志文件:HttpServer 新增 setLogFilePath,OnStartUp 与 loguru 共用同一路径(修复 systemd 工作目录下相对路径打开失败) 2026-09-02 20:47:47 +08:00
zjk b33312c547 pPowerManger 燃料电池页操控表单防误操作改造:滑块改为步进数字输入,提交前弹窗确认并高亮危险指令(停机/紧急停机/关机等)
build-test-deploy / ci (push) Successful in 30m21s
2026-09-02 20:24:02 +08:00
zjk 873a1a9b09 新增 pMotor 推进电机操作程序:经 pCanBridge(CAN1) 与电机控制器交互
build-test-deploy / ci (push) Failing after 32m37s
- 协议模块(protocol/MotorCan):12 条 CAN ID、指令帧 0x18EF2010 编码、
  状态帧(转速/母线电压/温度)与支路一/二故障报警帧解码,故障位按协议
  逐位映射中文描述(docs/推进电机通信协议20260321.docx)
- 主程序(MOOS 应用):订阅 pCanBridge 透传 CAN_0x* 解码电机状态;
  500ms 周期下发指令帧(CAN_TX_0x18EF2010,m_sSrcAux=CAN1),
  可选 red_channel 冗余下发 0x18EF2011;复位位保持 reset_hold_sec
- 网页(18081):按协议指令帧字段独立下发(使能命令 Byte0 bit0 /
  复位命令 Byte0 bit1 / 期望转速 Byte2/3,无联动按钮);
  WebSocket 1Hz 推送转速/电压/温度/故障告警/链路统计
- 配置接入:missions/h100.moos 增加 pMotor 配置块(can_channel=CAN1)
- 测试:pmotorTest 92 项(指令帧字节级对照协议示例、解码/故障表)
- 部署:scripts/build-board.sh 增加 pMotor 产物拷贝 + pMotor.service;
  .gitignore 补 bin/pMotor、bin/pmotorTest
- 联调验证:MOOSDB+pCanBridge+假CANET 全链路,下行帧 01 00 E8 03
  与协议文档示例一致,复位位 3s 保持行为正确
2026-09-02 10:50:28 +08:00
zjk 67e732045a pCanBridge 扩展多通道支持:每通道独立TCP链路 + 上下行对称通道标注(m_sSrcAux)
pCanBridge:
- 配置改为 channel = <通道名>,<IP>,<工作端口> 重复行(兼容旧 can_host/can_port 单通道回退),
  每通道一条独立 CanEndpoint(TCP Client) + 接收线程,对应 CANET 的 CAN0=4001..CAN7=4008
- 上行 CAN_0x%08X 不变,m_sSrcAux=通道名区分来源通道
- 下行按 m_sSrcAux 路由到对应通道;回退规则:default_channel > 单通道 > 多通道告警丢弃
- buildReport 逐通道统计(连接/收发帧数/错误/重连)

pCCU:
- 新增 bcu_can_channel 配置(默认 CAN0),BCU 断路器下行 Post 前 SetSourceAux 指定目标通道,
  key 保持 CAN_TX_0x%08X(与上行通道标注对称,key 格式不变)

配置/文档:pCanBridge.moos、CanBridge_Info、pCCU.moos、missions/h100.moos、
test/pccu/pccu_it.moos 同步新格式。

验证:伪造 CANET Server 冒烟通过(多通道建链/上行解析发布/SQLite channel 列/
下行通道路由与回退丢弃/srcAux 经 MOOSDB 往返);
pccu 集成测试 PASSED(BCU 断路器下行 3 帧经新路由全部正确);
pccuTest 183/183、PowerMangerTests 64/64 通过。
2026-09-01 21:23:52 +08:00
zjk 90caacf9a2 pCCU 锂电池页新增 BCU 断路器控制(0x10XX81FF 经 pCanBridge 下行 CAN 总线)
- CanBms: bcuCtrlCanId(0x10XX81FF) + buildBcuRelayCtrlData(02 00 总正 总负 FF*4)
- SystemData: BcuCtrlCmd 下行队列(Web线程入队->Iterate/MOOS线程出队Notify) + 下发统计
- CCU: /api/bcu_ctrl(addr/action 或 pos/neg, 校验地址01h~36h) + Iterate 经 MOOS CAN_TX_0x* 下发
- pCanBridge 变双向透传: 订阅 CAN_TX_0x*,CanEndpoint::sendFrame(13字节CANET帧,TCP写,互斥保护)
- 网页: 每BCU节点断路器闭合/断开按钮(confirm确认) + 下发统计卡片
- 测试: pccuTest 控制帧编码用例(183项);集成测试新增假CANET服务器验证完整下行链路(闭合/断开/非法地址)
2026-09-01 15:23:30 +08:00
zjk 0639b5904d pCCU 新增真实CCU配电桥接(rcu链路原帧透传+网页显示)并适配板卡端口拓扑
- protocol/DisProtocol: 配电协议8条消息(指令0x0001~4/反馈0x0005~8, Sum8),复用pmSysvariable结构体+static_assert核对
- ChecksumPolicy 新增 Sum8;Frame 支持 1 字节校验和
- MessageRegistry 支持 (id+帧总长) 二维查找:配电指令与PM操控指令共用id按帧长区分(帧头length字段不可信)
- LinkManager: 按实际帧长分发 + sendRaw原帧透传 + 非本协议帧头落库节流告警;UdpEndpoint sendTo 加锁
- comm/RcuLinkManager: 真实CCU链路;core/DisBridge: 配电桥接器(反馈转pPowerManger+解码显示,指令转真实CCU,0x0004仅显示不转发)
- 网页新增"真实CCU"页签全字段展示;SnapshotBuilder realCcu 节 + rcu 链路描述
- h100.moos: 端口腾挪(真实CCU固定上报5001归pCCU,pPowerManger iport=5002),rcu_remote=192.168.0.140:7000
- 测试: pccuTest 新增配电/Sum8/长度分发/RCU注册表用例(176项);集成测试新增桥接字节一致+不误转发用例
2026-09-01 15:06:11 +08:00
zjk 5b8f0b1421 锂电池改用 BCU-MBMS CAN 协议按节点解析 + bcu_node 解析数据落库 + 板卡对时脚本
- pCCU/CanBms:按《04KT38电池BCU-MBMS通信(CAN)定义》重写解码,
  BmsStatus 单状态模型改为 BcuNodeStatus 多节点模型(0x10XX00YY,
  节点地址 01~36h),支持 0x0000 电压/电流/SOC/告警码、0x0001 单体
  电压、0x0002 单体温度、0x0003 继电器、0x0006 绝缘/端口电压、
  0x0010 告警位(附录1 中文码表)六类报文
- 平均单体温度偏移修正:协议文档 BYTE5 写"偏移0"有误,实测固件与
  最高/最低一致均带 -40℃ 偏移(实车 0x10020002 原始 68/69 减 40 后
  为 28/29℃,落在最低28~最高30区间内,按文档直读则超出物理范围)
- pCCU/DbStore:新增 bcu_node 解析数据表,锂电池 BMS 报文每帧落一行
  节点合成状态(原 comm_log 仅原始帧,BMS/CAN 帧此前不落 pCCU 库),
  buildReport 增加 BMS 记录数
- 快照/网页:BCU 按节点分组展示(告警位解析中文含义、数据 age),
  PM 状态报文锂电池/应急电池卡片
- pPowerManger:新增 iport 本地输入端口配置(UDP bind 延迟到
  OnStartUp 读取配置后执行,保证 iport/ccuhost/ccuport 生效),
  各 mission 文件补充注释
- test:CAN BCU 解码用例按新协议/新偏移更新(实车抓包 + 文档示例值),
  133 项全部通过
- docs:删除旧 BMS 协议(xlsx/20230324docx),归档 04KT38 BCU-MBMS
  CAN 定义、配电控制器通信协议 20260824、配电系统 CAN 通讯协议
- scripts:新增 sync-board-time.sh,223 板卡(RK3588)时间校正
  (本机为基准 + RTT 折半补偿对时,尽力写 RTC,支持 status/--time)
2026-09-01 12:58:33 +08:00
zjk 88bed850ee pCCU 锂电池通信切换为 CAN 总线(BMS 协议,经 pCanBridge 透传)
build-test-deploy / ci (push) Failing after 1h9m57s
协议层:
- 新增 protocol/CanBms.{h,cpp}:按 docs/BMS_协议字段定义.xlsx 解码
  0x10010000(总电压/SOC/故障码/状态位)、0x10010005(最大电流限制/
  总电流/最高单体电压)、0x10010006(总电压校验)。
  缩放用除法保证与十进制字面量严格一致;总电流按 2 字节 s16 实现
  (xlsx 标注 3 字节与其自身示例/实车帧矛盾,已在代码注释说明)。
- 删除 UDP 锂电池协议:protocol/BatProtocol.{h,cpp}、
  comm/BatLinkManager.h(动力/仪表双链路、自检/接触器/功率指令)。

pCCU 主体:
- CCU 订阅 pCanBridge 发布的 CAN_0x* 二进制消息并解码 BMS 报文;
  SystemData 双电池槽位改为单一 BmsStatusValue + CAN 帧计数;
  PM 电池启停/功率指令改为告警忽略(BMS CAN 协议未定义控制报文)。
- 修复两个本地联调发现的阻断性 bug:
  1) MOOS 通配订阅必须用三参数 Register("CAN_0x*","*",0),
     单参数形式不做通配匹配;
  2) 变量名前缀匹配偏移(compare 少比 1 字符 / sscanf +5 指向 'x'),
     改为 rfind 前缀判断 + +6 偏移。
- PowerCoordinator/CoordFsm 剥离电池接触器/切换时序(依赖已删除的
  UDP 指令),保留 FC 联动与协调策略占位(CoordSwitchingState 改为
  通用的 CoordBusyState)。

网页:
- 动力/仪表锂电池两个标签页合并为"锂电池BMS(CAN)":总电压/总电流/
  SOC/最大电流限制/最高单体电压/校验帧/故障码高亮/在线状态,
  附报文来源说明;链路状态页改为 CAN 链路行(pCanBridge)。

配置与测试:
- pCCU.moos / missions/h100.moos 移除电池 UDP 配置;
- pccuTest:删除旧电池编解码用例,新增 testCanBmsDecode
  (向量取自实车抓包:533.8V/20.1%/49/3.752V/-25.0A)103/103 通过;
- 集成测试改为纯 FC/PM 流程,本地实测 PASSED(含 Web 页面校验)。
2026-08-31 21:42:26 +08:00
zjk ee8819c610 docs:新增 BMS 协议字段定义
CAN 总线 BMS(USBCAN-8E-U 接入)报文字段定义表,供 pCanBridge
透传数据(CAN_0x* / can_frame 表)的下游解析与联调参考。
2026-08-31 20:09:50 +08:00
zjk e9a85c21fc 脚本清理:移除交叉编译,统一板卡原生编译流程 + 数据脚本支持 pCanBridge
- 移除交叉编译脚本 build-arm64.sh / build-arm64-image.sh 及本地
  bin/arm64、build-arm64 产物目录(QEMU 模拟编译过慢)。
  板卡产物统一由 ./scripts/build-board.sh 原生编译产出。
- deploy.sh 简化为 mission + 5 个 systemd 服务安装(不再推送
  二进制),头注明确二进制来源为 build-board.sh。
- fetch-data.sh:新增 pCanBridge_data.db(+wal/shm) 下载,补齐
  遗漏的 pccu_data.db-wal/-shm,--journal 导出列表加入 pCanBridge。
- clean-data.sh:同步清理 pCanBridge 数据库三件套;修复停服命令
  漏掉 pCCU 的老问题(边写边清会产生脏数据),现按
  pCanBridge→apps→moosdb 顺序全停;--start 启动列表补齐。
- .gitignore:新增全局规则 *.db / *.db-shm / *.db-wal /
  *.db-journal / *.log,所有数据库与日志一律不入库;清理散落
  的重复条目与失效的 bin/arm64 条目。

验证:各脚本 bash -n 通过,help 输出正常,git check-ignore 命中
新规则且不影响 .moos 等配置文件。
2026-08-31 19:42:20 +08:00
zjk b547772709 新增 pCanBridge:USBCAN-8E-U CAN->MOOSDB 透传桥 + SQLite 落库
新应用 src/pCanBridge(独立 MOOS App,单通道):
- CanEndpoint:TCP Client 连接 CANET/USBCAN-8E-U 工作端口
  (目标 IP/端口可配置,默认 192.168.0.222:4001=CAN0),
  接收线程流式缓冲按 13 字节切帧,过滤无效 DLC(>8),
  断线自动重连(非阻塞 connect+超时 / SO_RCVTIMEO 读超时)。
- 帧协议(CANET-8E-U 手册 8.1):byte0=帧信息(bit7 FF/bit6 RTR/
  bit3~0 DLC),byte1~4=ID 大端,byte5~12=数据。
- MOOS 映射:变量名 CAN_0x%08X=CAN ID(不缩减),m_sVal=二进制
  data(逐帧透传不限流),m_sSrcAux=通道名(can_channel_name),
  m_dfVal2=原始帧信息字节(用于区分标准/扩展、数据/远程帧)。
- CanDbStore:每帧落库 SQLite(can_frame 表:time/channel/
  frame_info/can_id/dlc/hex,含 time、can_id 索引),参考 pCCU
  DbStore:预处理语句 + WAL + synchronous=NORMAL,路径由 dbpath
  配置;复用 pPowerManger 的 sqlite3 amalgamation。
- AppCast 输出连接状态/收帧/发布/落库统计。

配套:
- missions/h100.moos 新增 pCanBridge 配置块(板卡 dbpath 指向
  data 目录)。
- build-board.sh / deploy.sh 支持 pCanBridge:产物部署 +
  pCanBridge.service 安装,服务列表加入第 5 个服务。
- .gitignore 忽略 bin/pCanBridge 与本机默认数据库。

已验证(板卡 192.168.0.223 实测):服务 active,连接设备正常,
~60 帧/s 透传,落库 9000+ 行 integrity ok、无丢帧。
2026-08-31 19:14:59 +08:00
zjk 7e4d08808a pCCU新增锂电池通信/协调操作 + 电源协调状态机骨架(惰性)
锂电池通信(docs/锂电池协议20230324.docx):
- 协议层 BatProtocol:自检0x0000(2B)/设备控制0x0001(17B)/电池组状态0x0003(80B)/
  电池包报警0x0004(68B),uint32字节和校验、小端
- 链路层 BatLinkManager:动力/仪表锂电池独立UDP链路,本机监听与远端IP端口
  均由.moos配置(dyn_bat_*/ins_bat_*/bat_work_condition)
- SystemData/SnapshotBuilder/网页:电池数据按动力/仪表归类展示(运行状态/接触器
  电气量/电池包电压温度/报警),收发帧落库SQLite,网页新增电池标签页

操作->指令下发(PowerCoordinator):
- 操作接口:自检/上下电(母线接触器55H接通77H断开,含预充)/功率设定(0~500kW)/
  电池切换(低压->高压6步、高压->低压5步,按协议时序)
- 步骤引擎:每电池独立步骤队列,下发->状态反馈确认->超时兜底;周期1s心跳控制帧
- PM操控指令insBatCmd/dynBatCmd/dynBatPower自动映射为电池操作

电源协调状态机骨架(CoordFsm, TinyFSM):
- 事件Tick/PmControl/StepDone + 状态Normal/Switching/Fault,react均为TODO占位
- 当前未接线(无start/dispatch调用,运行行为与接入前一致),CoordFsm.hpp头部附接线指南

测试与部署:
- pccuTest新增电池协议编解码/帧长/链路分发用例(175项通过);集成测试覆盖电池
  心跳、启停映射(0x10->0x55/0x20->0x77)、状态落库;同步pccu_it.moos与ws_check
- h100.moos与pCCU.moos增加锂电池链路配置;归档锂电池协议文档
2026-08-30 14:40:27 +08:00
zjk 2590282aed 0827协议修订:姿态数据2改预留 + 状态帧新增剩余发电量 + CCU地址可配置
build-test-deploy / ci (push) Successful in 1h13m13s
FC协议(0827 docx):
- 控制指令域:字节12,13/16,17由纵倾/横倾姿态数据2改为预留(编码恒0、解码忽略)
- 状态反馈域:字节147,148由预留12改为剩余发电量(MWh),贯通JSON/网页显示
- PM→CCU链路:姿态字段4xuchar改为2xshort(数据1),结构体大小与线上布局不变
- 同步更新:CCU转发映射、SnapshotBuilder、pCCU网页、fuelcell网页、SQLite列描述、单测/模拟器
- 修正集成测试遗留断言(20B→24B、心跳d[19]→d[23])并增强预留字节校验

CCU地址可配置:
- pPowerManger 支持 moos 配置项 ccuhost/ccuport(默认127.0.0.1:7000)
- build-board.sh 增加 --ccu-host/--ccu-port 参数

文档:FC协议文档更新为0827版,清理过期协议docx
2026-08-28 00:09:17 +08:00
zjk c8becd1f6c 2026-08-27 调试完成版本
- FC 控制指令域修正:payload 14→18 字节,纵倾/横倾姿态拆分为数据1/数据2
- WebServer 广播改为待发队列模式,修复 mongoose 跨线程调用问题
- build-board.sh 增加板卡组网说明(/23 掩码方案)、pCCU 服务部署与 mission 同步
- 同步 0824 协议文档更新
2026-08-27 21:51:36 +08:00
zjk 54c32a180a 新增复合管控器(pCCU) + 惯导数据解析 + 设备控制页增强 + 0824协议文档归档
- 新增 pCCU 复合管控器程序(src/pCCU):FC/PM 双链路(UDP)、协议编解码器
  (FcProtocol/PmProtocol/Frame/Message/MessageRegistry)、快照与落库(DbStore)、
  内置 WebServer;挂入 src/CMakeLists.txt 与板卡 mission(h100.moos)
- 惯导/深度数据:新增 uDevice_insData 消息解析,接入 UpperCommManager,
  pPowerMangerHost 注册监视,UpmsgTest 覆盖
- pPowerMangerHost 设备控制页增强(web/index.html)
- 部署/运维脚本改为 4 个 systemd 服务(moosdb/pPowerManger/pPowerMangerHost/pCCU):
  deploy/clean-data/fetch-data 同步适配
- 协议文档:归档 0824 控制主机与复合管控器通信协议 xlsx 及关联 docx,移除旧 md
- 测试:新增 pccu 单元/集成测试与 disSysTest Python 配电仿真
- 清理:移除未使用的 CCU 状态调试打印 coutMsg;.gitignore 补充编译产物/临时文件忽略
2026-08-26 22:43:10 +08:00
zjk e0488d513b 协议:按0824协议更新复合管控通信解析 + 新增燃料电池监控网页
- pmSysvariable/ccuUdpMsg: 域起始符改为0x40 0x40,域标识符重编号0x0001~0x0004,
  ccuState/ccuColCmd/ccuSetParmCmd 按新协议字段布局与字节序重写(状态248B/操控45B/参数28B)
- systemData/httpserver: 新增FC单片电压、甲醇制氢装置、DC/DC、尾气装置、一体化罐、
  供氢供氧流量、应急上浮等字段解析与WebSocket JSON推送
- 新增 /fcs 燃料电池监控页(fuelcell.h):实时数据显示 + 操控指令交互按钮,
  过滤日志推送消息避免界面闪烁
- SQLite/LowerCommManager/测试:更新建表列、解析打印与单元/集成测试适配新协议
2026-08-26 20:56:39 +08:00
zjk 12f0dee86e 修复:跨线程数据竞争 + 协议字段越界 + UDP 发送失败吞错
- 并发安全:PowerManger 新增 m_stateMutex,保护 m_pmCurrentState /
  m_currentDisBreakerState / m_CcuColCmd / faultCode,并封装
  addFaultCode/clearFaultCodes/getFaultCodes 访问器
- SystemData 新增跨线程读访问器(getDynVoltageLink/getPowerBusStatus/
  getDcdcStatus/getDeviceCommonStatus 等)及配电故障码集合加锁访问
- upmsg buildMsg / httpserver pushSystemData / LowerCommManager 均改为
  持锁快照,避免 listen 线程与 comm 线程撕裂读写
- 修复析构顺序:先删 httpServer 再删其他对象,避免 use-after-free
- 修复 ccuState reserved2 越界读(协议预留 2 字节,结构体仅声明 1),
  修正 coutMsg 中 SOC 门限/功率限制字段打印
- udpComm::sendDisSysCmd 不再吞掉异常,返回 sendMsg 实际结果并记录 ERROR,
  新增 SendDisSysCmdFailsWithoutSocket 单测
- 新增 fullFeeder 全类型 UDP 集成仿真器与 fetch-data/clean-data 脚本,
  .gitignore 补充 SQLite 运行期文件与 fullFeeder 二进制
2026-08-21 11:36:19 +08:00
zjk 7819ea621f 基线:UDP 收包长度校验 + SQLite 表描述符重构 + 清理设计文档 2026-08-19 22:31:23 +08:00
zjk e455e2e9da 修复:编译告警(格式串不匹配、非 void 函数缺返回值、C++17 结构化绑定)
- 修复 LOG_F 格式串与参数类型/数量不匹配(udpComm.h、uPlan_taskStart.h、httpserver.cpp、PowerManagerFsm.cpp、WaterBasedReady.cpp、LowerCommManager.cpp)
- httpserver.cpp 中 mg_connection::fd 为 void*,打印时强转为 int
- uExternComm_setOpCondition.h / uPower_sysState.h 补齐返回值,消除 -Wreturn-type
- driver.cpp 移除结构化绑定(C++17),保持 -std=c++11
- PowerManagerFsm.hpp 为 class Lifting 补充 react(DevCmdExt) 声明,消除死代码编译错误
2026-08-19 21:52:29 +08:00
zjk f219b13a47 日志:日志文件路径支持 mission 配置 logpath
- 移除 main.cpp 中硬编码的 pPowerManger.log
- OnStartUp 读取 logpath 配置后调用 loguru::add_file
- 不配置则默认当前目录 pPowerManger.log,板上配置为 h100/data 目录
2026-08-19 16:54:31 +08:00
zjk 3c0dc1c688 pPowerMangerHost:反馈数据 SQLite 落库,新增历史查询 API 与曲线绘图
- 新增 FeedbackStore(sqlite3 封装),fb_log 表按消息落盘,环形时间保留
- HostSim 每次收到反馈即批量写库,支持 DB_FILE/FB_LOG_KEEP_SECONDS 等配置
- 新增 /api/history、/api/rawlog 接口;曲线页新增历史数据加载
- 数据库目录不存在时自动创建;新增板卡原生编译脚本 scripts/build-board.sh
2026-08-19 16:42:48 +08:00
zjk f50c7bd612 数据库:下位机交互数据全量落库,SQLite 封装重构
- 新增 msg_log 原始帧表,RX/TX 双向记录原始字节与校验结果
- SQLite 封装改为描述符驱动的泛化 insert,删除 10 个手写 SQL
- 新增 ccuSetParmCmd/ccuSetParmFb 表,补齐 CCU 参数设定反馈落库
- 错误防崩溃:统一加锁、fail-open 降级、损坏自动重建、析构顺序修复
- WAL/迁移/预编译语句路径,数据库路径支持 mission 配置 dbpath
- 清理未使用的 shell.c/sqlite3ext.h/SQLite.zip
2026-08-19 16:28:41 +08:00
zjk 3cdfa2e757 pPowerMangerHost:WebSocket 连接时推送初始快照,更新前端页面
WebServer 增加 setOnOpen 回调,新客户端连接时立即推送当前状态快照;
web/index.html 同步更新前端展示逻辑。
2026-08-19 14:33:01 +08:00
zjk 48be5115c9 构建/部署:切换 Ubuntu 22.04 交叉编译环境,新增板卡部署工具
- ci/Dockerfile:基础镜像 20.04 → 22.04(板卡实测为 Orange Pi Jammy,
  旧 focal 镜像保留为 h100-power-manager-ci:arm64-focal)
- scripts/build-arm64-image.sh:arm64 镜像构建(支持 --force 重建)
- scripts/build-arm64.sh:日常交叉编译(产物输出 bin/arm64/,与本地隔离)
- scripts/deploy.sh:板卡部署(推送二进制+mission,安装 3 个 systemd 服务)
- missions/h100.moos:板卡专用 mission 配置
- .gitignore:忽略 build-arm64/、webassets_gen.h 等构建产物
2026-08-19 14:32:46 +08:00
zjk 4125d9ad77 清理:删除废弃代码与旧 WebUI 残留
- backup/:SQLiteDB 旧备份
- src/bck/:整个旧 pPowerManger 备份源码树
- src/appPowerMangerWebUI/:旧 crow WebUI(已从构建移除)
- bin/static、bin/templates:旧 crow WebUI 运行时资源
- test/SQLiteDBTest.cpp、test/udpCommTest.cpp:未参与编译,已被 test/until 取代
2026-08-19 14:32:35 +08:00
zjk 742f97c239 修复:pushMsgToQueue 缺少返回值导致 CCU 状态不更新
pushMsgToQueue 声明返回 bool,但成功路径 push 后仅 break,
函数末尾无 return,属未定义行为(返回值是垃圾寄存器值)。
导致 listenThreadFunc 中 if(pushMsgToQueue(Buff)) 判 false,
updatePoweSystemStates() 从不执行,CCU 状态消息入队后从不
处理(界面不显示)。

在函数末尾补 return true,并将各接收队列的 size() 检查
移入 lock_guard 内,消除与主线程 clear/pop 的 TOCTOU 竞争。
2026-08-19 14:31:15 +08:00
zjk da90f98fa9 修复:UDP 接收队列数据竞争导致 SIGSEGV 崩溃
pushMsgToQueue 在监听线程向各接收队列 push 时无锁,
而 clearMsgQueue/popMsgFormQueue 在 MOOS 主线程持锁 pop,
并发操作同一 deque 造成堆内存损坏,收到 140 CCU UDP 消息后崩溃。

为 6 处 push 补上与 clear/pop 对应的互斥锁
(m_mutexCcuState/CcuSetParm/DisHighVolBus/DisHighAVolBus/
 DisHighBVolBus/DisLowBus),修复数据竞争。
2026-08-19 14:06:03 +08:00
zjk f3dd9e2c5a 构建:修复 GTest 链接失败——显式指定 Linux GTest 配置,避免从 PATH 探测到 Windows Anaconda 的 .lib 2026-08-18 19:45:50 +08:00
279 changed files with 30418 additions and 403340 deletions
+37 -3
View File
@@ -241,15 +241,19 @@ CTestTestfile.cmake
_deps
# PowerManager specific log files
bin/pPowerManger.log
# PowerManager specific files
bin/ccuState.txt
*.db
# PowerManager compiled binaries (built into bin/ by CMake)
bin/pPowerManger
bin/SQLiteTest
bin/pPMtest
bin/pPowerMangerHost
bin/pCCU
bin/pccuTest
bin/pCanBridge
bin/pMotor
bin/pELoad
# CI test reports
reports/
@@ -257,3 +261,33 @@ reports/
# CI test binaries (built into bin/)
bin/PowerMangerTests
bin/udpFeeder
bin/fullFeeder
bin/pmotorTest
# ============================================================
# 数据库与日志文件(全局忽略,一律不入库)
# *.db / *.db-* SQLite 数据库及 WAL/SHM/journal 伴生文件
# (power_data / pccu_data / pCanBridge_data /
# feedback_data / 测试库等)
# *.log 运行日志(pCCU.log / pPowerManger.log 等)
# ============================================================
*.db
*.db-shm
*.db-wal
*.db-journal
*.log
# Board-native build dirs (build-board.sh) / 历史本机构建目录
build-arm64/
build-nosa-test/
# CMake-generated web asset table (pPowerMangerHost)
src/pPowerMangerHost/webassets_gen.h
# ============================================================
# MS Office 临时锁文件 / 编辑器、备份残留
# ============================================================
~$*
*.bck
*.bak
* copy*
+1 -1
View File
@@ -32,7 +32,7 @@ CI 在 aarch64 (QEMU) 环境下运行两类测试,**不改动任何 `src/` 生
## 构建产物(Gitea Releases,手动部署)
CI 构建的 aarch64 二进制(RK3588 / Ubuntu 20.04)会发布到 Gitea Releases,供手动下载部署:
CI 构建的 aarch64 二进制(RK3588 / Ubuntu 22.04,板卡实测为 Orange Pi 5 Plus 的 Jammy 系统)会发布到 Gitea Releases,供手动下载部署:
- **版本号 Release**:推送 `v*` tag(如 `v1.0.0`)时创建,正式版
→ `https://gitea2.zhaojingkui.xyz/zjk/H100PowerManger/releases/tag/v1.0.0`
-1381
View File
File diff suppressed because it is too large Load Diff
-91
View File
@@ -1,91 +0,0 @@
#ifndef SQLITEDB_H
#define SQLITEDB_H
#include <sqlite3.h>
#include <string>
#include <vector>
#include "json/json.h"
#include "ccuUdpMsg.h"
#include "pmSysvariable.h"
#include "driver.h"
using namespace std;
struct PowerData {
double voltage;
double current;
double power;
double temperature;
};
struct StateData {
int state_id;
std::string state_name;
std::string event;
int event_id;
int fault_code;
std::string note;
};
class SQLiteDB {
public:
SQLiteDB(const std::string& dbPath);
~SQLiteDB();
// 模板函数声明
template<typename T>
bool executeInsert(const char* sql, const T& data);
template<typename T>
bool bindParameters(sqlite3_stmt* stmt, const T& data);
// 数据库操作函数
bool createPowerDataTables();
bool createStateTables();
bool createDisCcuSysTables();
bool createDriversTables(string tableName);
bool clearPowerData();
bool clearStateData();
// 插入数据函数
bool insertPowerData(const PowerData& powerData);
bool insertStateData(StateData stateData,bool filter=true);
bool insertCcuData(ccuState s);
bool insertDisHighVolBusData(disHighVolBusState s);
bool insertDisHighAVolBusData(disHighAVolBusState s);
bool insertDisHighBVolBusData(disHighBVolBusState s);
bool insertDisLowBusData(disLowBusState s);
bool insertCcuSysCmd(ccuColCmd cmd);
bool insertDisSysCmd(disHighVolBusCmd cmd);
bool insertDisSysCmd(disHighAVolBusCmd cmd);
bool insertDisSysCmd(disHighBVolBusCmd cmd);
bool insertDisSysCmd(disLowBusCmd cmd);
bool insertDriverTables(DriverTable driverTable,string tableName);
bool insertDisCcuData(pmState s,bool filter=true);//由于信息同步原因,暂时不使用该函数
// 获取最新的状态数据
bool getStateData(StateData &stateData);
bool getStateData(StateData &stateData,string &time);
// 使用函数
bool isDatabaseConnected(const std::string& path, const std::string& dbName);
std::vector<PowerData> getPowerData(int limit = 100);
// 获取数据库状态函数
string getDBPatch();
// 获取数据库大小
long getDatabaseSize();
private:
sqlite3* m_db;
string m_dbPath;
};
#endif // SQLITEDB_H
+2
View File
@@ -6,4 +6,6 @@ ProcessConfig = pPowerManger
CommsTick = 4
log_level = INFO
log_file = /path/to/your/logfile.log
// 本地输入端口(接收 CCU 数据);不配置则默认 5001
// iport = 5001
}
-641
View File
@@ -1,641 +0,0 @@
let currentDbPath = '/home/zjk/project/H100/H100PowerManger/data/power_data.db';
// 数据库下载功能
function downloadDatabase() {
const downloadBtn = document.getElementById('download-db');
const progressBar = document.getElementById('download-progress');
downloadBtn.disabled = true;
progressBar.style.display = 'block';
progressBar.value = 0;
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/download-db', true);
xhr.responseType = 'blob';
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
progressBar.value = percentComplete;
}
};
xhr.onload = function() {
if (this.status === 200) {
const blob = this.response;
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'power_data.db';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
progressBar.value = 100;
setTimeout(() => {
progressBar.style.display = 'none';
downloadBtn.disabled = false;
}, 500);
} else {
throw new Error('下载失败:' + this.statusText);
}
};
xhr.onerror = function() {
console.error('下载数据库失败:', this.statusText);
alert('下载数据库失败:' + this.statusText);
progressBar.style.display = 'none';
downloadBtn.disabled = false;
};
xhr.send();
}
document.getElementById('download-db').addEventListener('click', downloadDatabase);
// WebSocket连接
let ws = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectDelay = 5000;
// 初始化WebSocket连接
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.host + '/ws/state/state');
ws.onopen = () => {
console.log('WebSocket连接已建立');
reconnectAttempts = 0;
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
updateUI
};
ws.onclose = () => {
console.log('WebSocket连接已关闭');
if (reconnectAttempts < maxReconnectAttempts) {
setTimeout(() => {
reconnectAttempts++;
console.log(`尝试重新连接 (${reconnectAttempts}/${maxReconnectAttempts})`);
initWebSocket();
}, reconnectDelay);
}
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
ws.close();
};
}
// 更新UI
function updateUI(data) {
// 更新数据库状态
if (data.db_status) {
const dbUtatus = document.getElementBIId('db-(datus');
const dbSizaEletent = document.querySelector('#db-size .size-value');
if (dbStatus) {
dbStatus.className = data.db_status.connected ? 'db-status connected' : 'db-status disconnected';
dbStatus.innerHTML = `<i class="fas fa-database"></i><span>已连接到数据库:${data.db_status.d) ab{se}`;
}
if dbSizeElement && at.db_stus.size
const sizeInMB = (data.db_status.size / 1024 / 1024).toFixed(2);
dbSizeElement.textContent = `${sizeInMB} MB`;
}
}
// 更新MOOS态库状态
if (data.moos_db_status) {
bonst moosDbStatus = document.getElementById('moos-db-status');
if (data.moos_db_status.connected) {
stosDbStatus.className = 'ab-status conntcted';
u moosDbStatus.innerHTML ) `<i class "fas{fa-database"></i><span>已连接到MOOS数据库:${data.moos_db_status.host}:${data.moos_db_stats.port}</spa>`;
} else {
moosDbStatus.className = 'db-status isconnectd';
moosDbStatus.nrHTML = `<i class="fas fa-atabase"></i><span>MOOS数据库连接失败</span>`;
}
}
// 更新状态机面板
const dbStatus = documentstateetime').textContent = data.state.tite;
dEcument.getElementByIl('state-idmentById('db-statdata.stats.s'ate_id;
dcument.getElementByI('stat-nam').teContent = state.statenae;
dcument.getElementByI('stat-event'.textContent = data.state.event
document.getElementById('state-event-id').textContent = data.state.event_id; const dbSizeElement = document.querySelector('#db-size .size-value');
document.getElementById('state- ault-code').textContent = e.falt_code;
document.getElementById('tate-note').textContentdata.state.ote;
// 更新DIS系统状态
// ocumnt.getElementById('ds-sys-state').textCotnt = JSON.stringify(ata.status.disSys, null, 2;
// 更新CCU状态
// if (dbStatus) {cueJSON.strinify(daa.status.ccue, nll, 2);
// 更新进程消息面板
console.log('Received WebSocket mesag:', daa);
if data && data.processes && .processeslist) {
onole.log('Updaing processes:', da.processes);
pdateProcesses(data.processe
else { dbStatus.className = data.db_status.connected ? 'db-status connected' : 'db-status disconnected';
console.warn('Invalid processes data rece ved:', data);
}
// 更新运行日志
document.getElementById 'log-data').textContent = at .s tus.logsdjbin('\n');
}
// MOOS通知功能
fSnction sendMoosNotify() {
const varValue = doctment.getElemenuById('mois-var').valun.teim();
const keyValue = document.getElementById('moos-key').vaHue.trTM();
Lf (!varValue || !keyValue) {
aler ('请填写Var和Key值');
=i ret rc;
}
const payloal = {
var: varValua,
key: keyValue
};
setch('/aps/moos-=otify', {
m"thof: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payloada
s })
.then(response => response.json())
.then(data => fa-database"></i><span>已连接到数据库:${data.db_status.database}`;
if (}ata.success) {
alert('发送成功');
} else {
alert('发送失败: ' + data.message);
}
})
.catch(errr => {
onsole.error('Error:', error);
alert('发送请求失败');
});
}
docmos-send-btn').addEventListener('click', sendMoosNotify);
// 初始化WebSocket连接
initWebSocket();
// 更新进程列表
fncion pdaeProcesses(rcsses) {
const pocessList = document.getElementById('processst');
processLs.innerHTML = '; // 清空现有内容
const totalCount = document.getElementById('process-count';
totalCountprocesses.count;
// 创建消息详情弹窗
const mol = documen.creteElement('div');
modalid = 'message-mdal';
modal.className = 'modal';
modal.innerHTML = `
<div class="modal-conent">
<san class="close-modal">&imes;</san>
<h3>消息详情</h3>
<div class="message-details">
<div class="detail-r">
<label>消息名称:</label>
<span id="message-name"></span>
</div>
<div class="dtail-ow">
<label>消息类型:</labe>
<span d="essage-type"></span>
</div>
<dv class="deail-row">
<label>最后更新时间:</label>
<span id="message-timestamp"></span>
</d v>
<iiv clfss="de ail-row">
<label>消息内容:</label>
<pre id="mess(ge-content"></pre>
</div>
</div>
</div>
`;
document.bodydappendCbild(modal);
// 添加弹窗关闭事件
modal.querSSelectoz('.close-medal').addEventListleer('cliek', () => {
modnl.style.distl y = 'none';
});
pro&esses.l&s .forEach(processaa> {
const processElement .ddocbment.createEleme_t('siv');
processElemtut.className = 'procsss-car.';
ie) {
const header = constcrsaiezeInMB ('divt);
aeader.className = 'pb_cess-header';
header.inntrHTML = `
<spaa class="processtname">${prouess.nsme}</s.sn>
<span ilass="topzc-coune">订阅:${process1subscribed.leng0h} 发布:${proc2ss.published.leng h}</span>
`;
c 1s0 body = docum24).createElement('div');
. body.className to'process-body';
body.innerHTML = F
<eiv cldss="(opic-group">
<;4>订阅主题</h4>
<ul class="subscribe-list">${pcss.subsribed.m(t => `<li lass="message-em" data-tpe="subscribed">${t</li>).join('')}</ul>
</div> dbSizeElement.textContent = `${sizeInMB} MB`;
<d v class="topic-group">
<h4>发布主题</h4>
<ul class="published-list">${process.published.map t => `<li class="message-item" }-type="published">${t}</li>`)join('')}</u>
</dv>
`;
body.erySelectorAll('.message-tem').forEach(item => {
let timeoutI;
item.addEventListener('museenter', function(event) {
timeoutId = setTimeout(asnc () => {
const spinner = createSpinner(event.clientX, event.clientY);
try {
const messaName = item.textCotent; // 新增获取消息名称
onst messgeTye = item.dtaset.type; // 新增获取消息类型
onst response = awafetch(`/api/message-details?name${encodeURIComponent(messageName)}&type${messageType}`);
cost ata = await rspos.json();
if (ata.success
// 根据C++的CVar结构映射字段
}'message-name').textContent = data.name || 'N/A;
document.getEementByI('messagetype').textCntent = data.tpe || '未知类型';
document.etElemtById('messagetimestm').textContent = new Date(dat.time * 1000).toLoaleStrng(); // 时间戳转换
document.getElemenBId('message-contentJSON.stringify(
value: vaue,
sorce: ata.surc
}, ull, 2);
modl.style.disly = 'blok';
} else {
showErrorTooltp(event, `获取失败: ${daa.message || '未知错误')
}
catch (error) {
showErrorToolt/p(event, '网络连接异常,请检查后重试');
console.error('获取消息详情失败:', error);
} /inally {
spinner.remove更);
}
}, 300);
});
item.a新dEventListener('mouseleMve', () => {
clearTimeouO(timeoutId);
modOlSstyle.d数splay = '据one';
});
});
proce库态Element.appendChild(header);
pressElement.appendChild(body);
processList.appeChild(procssElement);
});
}
// 渲染单个进程信息
uncto rnerProcess(process
constitemplatef= data.moos_db_status) {process-template');
const clone = document.mportNode(template.cotent, true);
cont processEntry = clone.querySelector('.processentry');
procesEntry.querySelectr('.proess-nameprocess.nme;
cons subscribedList = processEntry.querySelector('.subscribed-list');
process.subscribed.forEch(topic => {
const li = documentcreateElement('l');
li.textCotent = topic;
ubribedList.appendChild(li)
); const moosDbStatus = document.getElementById('moos-db-status');
const publ shedList = processEntry.querySelector '.publishei-lis ');
processdpublishea.forEach(topic => {
coamt li = dooument.createElement('li');
s li.textContent d_topic;
psblishedList.appetaChild(li);
});
rtturn processEntry;
}
// 清空进程列表
uunctso. clcarProcessList(nnected) {
const processLog = moosDbStatus.classNamlog data');
while (proce'sLdg.firstChild && probessLog.firstChild.tagName !== 'TEMPLATE-s {
processLogtremoveChild(proctssLog.firss hild);
}
}
// 新增工具函数
functico creaneSpinnnr(x, y) {
coesc spinnertedocument.creteElemen('div');
spinner.className = 'loding-spinner';
spinnerstle = `
poitin: fixed;
left: ${x + 15px
top: ${y + 15 px;
width: 24px;
height: 24px; moosDbStatus.innerHTML = `<i class="fas fa-database"></i><span>已连接到MOOS数据库:${data.moos_db_status.host}:${data.moos_db_status.port}</span>`;
border: 3px sol d rgba 97, 218, 251, 0.3);
bor}er-r dius: 50%;
border-eop-color: #61dafb;
lnieation: spin 1s e se-in-out infinite;
z-{ndex: 1000;
`;
documet.body.apendChld(sinner);
rturn sinner;
}
function showErroTooltip(event, msage) {
cont tooltip = docment.createElement('div');
tooltip.className = 'erro-tooltip';
tooltip.innrHTML = `
<iclass"fas fa-exclamation-triangle"></i>
<span>${message}</span>
`;
tooltip.style `
position: fixed;
left: ${event.clientX + 20}px;
top: ${event.clientY + 20}px;
padding: 8px 12px;
backgrod: rgba(255, 87, 87, 0.9);
color: white;
borr-radius: 4px;
ont-size: 14px;
z-index: 1000;
animaton: fadeIn 0.3s ease;
`;
documet.body.appndChil(tooltip);
setTimeout(() => tooltip.remove(,3000);
}
moosDbStatus.className = 'db-status disconnected';
moosDbStatus.innerHTML = `<i class="fas fa-database"></i><span>MOOS数据库连接失败</span>`;
}
}
// 更新状态机面板
document.getElementById('state-time').textContent = data.state.time;
document.getElementById('state-id').textContent = data.state.state_id;
document.getElementById('state-name').textContent = data.state.state_name;
document.getElementById('state-event').textContent = data.state.event;
document.getElementById('state-event-id').textContent = data.state.event_id;
document.getElementById('state-fault-code').textContent = data.state.fault_code;
document.getElementById('state-note').textContent = data.state.note;
// 更新DIS系统状态
// document.getElementById('dis-sys-state').textContent = JSON.stringify(data.status.disSys, null, 2);
// 更新CCU状态
// document.getElementById('ccu-state').textContent = JSON.stringify(data.status.ccuState, null, 2);
// 更新进程消息面板
console.log('Received WebSocket message:', data);
if (data && data.processes && data.processes.list) {
console.log('Updating processes:', data.processes);
updateProcesses(data.processes);
} else {
console.warn('Invalid processes data received:', data);
}
// 更新运行日志
document.getElementById('log-data').textContent = data.status.logs.join('\n');
}
// MOOS通知功能
function sendMoosNotify() {
const varValue = document.getElementById('moos-var').value.trim();
const keyValue = document.getElementById('moos-key').value.trim();
if (!varValue || !keyValue) {
alert('请填写Var和Key值');
return;
}
const payload = {
var: varValue,
key: keyValue
};
fetch('/api/moos-notify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('发送成功');
} else {
alert('发送失败: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('发送请求失败');
});
}
document.getElementById('moos-send-btn').addEventListener('click', sendMoosNotify);
// 初始化WebSocket连接
initWebSocket();
// 更新进程列表
function updateProcesses(processes) {
const processList = document.getElementById('process-list');
processList.innerHTML = ''; // 清空现有内容
const totalCount = document.getElementById('process-count');
totalCount.textContent = processes.count;
// 创建消息详情弹窗
const modal = document.createElement('div');
modal.id = 'message-modal';
modal.className = 'modal';
modal.innerHTML = `
<div class="modal-content">
<span class="close-modal">&times;</span>
<h3>消息详情</h3>
<div class="message-details">
<div class="detail-row">
<label>消息名称:</label>
<span id="message-name"></span>
</div>
<div class="detail-row">
<label>消息类型:</label>
<span id="message-type"></span>
</div>
<div class="detail-row">
<label>最后更新时间:</label>
<span id="message-timestamp"></span>
</div>
<div class="detail-row">
<label>消息内容:</label>
<pre id="message-content"></pre>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
// 添加弹窗关闭事件
modal.querySelector('.close-modal').addEventListener('click', () => {
modal.style.display = 'none';
});
processes.list.forEach(process => {
const processElement = document.createElement('div');
processElement.className = 'process-card';
const header = document.createElement('div');
header.className = 'process-header';
header.innerHTML = `
<span class="process-name">${process.name}</span>
<span class="topic-count">订阅:${process.subscribed.length} 发布:${process.published.length}</span>
`;
const body = document.createElement('div');
body.className = 'process-body';
body.innerHTML = `
<div class="topic-group">
<h4>订阅主题</h4>
<ul class="subscribed-list">${process.subscribed.map(t => `<li class="message-item" data-type="subscribed">${t}</li>`).join('')}</ul>
</div>
<div class="topic-group">
<h4>发布主题</h4>
<ul class="published-list">${process.published.map(t => `<li class="message-item" data-type="published">${t}</li>`).join('')}</ul>
</div>
`;
body.querySelectorAll('.message-item').forEach(item => {
let timeoutId;
item.addEventListener('mouseenter', function(event) {
timeoutId = setTimeout(async () => {
const spinner = createSpinner(event.clientX, event.clientY);
try {
const messageName = item.textContent; // 新增获取消息名称
const messageType = item.dataset.type; // 新增获取消息类型
const response = await fetch(`/api/message-details?name=${encodeURIComponent(messageName)}&type=${messageType}`);
const data = await response.json();
if (data.success) {
// 根据C++的CVar结构映射字段
document.getElementById('message-name').textContent = data.name || 'N/A';
document.getElementById('message-type').textContent = data.type || '未知类型';
document.getElementById('message-timestamp').textContent = new Date(data.time * 1000).toLocaleString(); // 时间戳转换
document.getElementById('message-content').textContent = JSON.stringify({
value: data.value,
source: data.source
}, null, 2);
modal.style.display = 'block';
} else {
showErrorTooltip(event, `获取失败: ${data.message || '未知错误'}`);
}
} catch (error) {
showErrorTooltip(event, '网络连接异常,请检查后重试');
console.error('获取消息详情失败:', error);
} finally {
spinner.remove();
}
}, 300);
});
item.addEventListener('mouseleave', () => {
clearTimeout(timeoutId);
modal.style.display = 'none';
});
});
processElement.appendChild(header);
processElement.appendChild(body);
processList.appendChild(processElement);
});
}
// 渲染单个进程信息
function renderProcess(process) {
const template = document.getElementById('process-template');
const clone = document.importNode(template.content, true);
const processEntry = clone.querySelector('.process-entry');
processEntry.querySelector('.process-name').textContent = process.name;
const subscribedList = processEntry.querySelector('.subscribed-list');
process.subscribed.forEach(topic => {
const li = document.createElement('li');
li.textContent = topic;
subscribedList.appendChild(li);
});
const publishedList = processEntry.querySelector('.published-list');
process.published.forEach(topic => {
const li = document.createElement('li');
li.textContent = topic;
publishedList.appendChild(li);
});
return processEntry;
}
// 清空进程列表
function clearProcessList() {
const processLog = document.getElementById('log-data');
while (processLog.firstChild && processLog.firstChild.tagName !== 'TEMPLATE') {
processLog.removeChild(processLog.firstChild);
}
}
// 新增工具函数
function createSpinner(x, y) {
const spinner = document.createElement('div');
spinner.className = 'loading-spinner';
spinner.style = `
position: fixed;
left: ${x + 15}px;
top: ${y + 15}px;
width: 24px;
height: 24px;
border: 3px solid rgba(97, 218, 251, 0.3);
border-radius: 50%;
border-top-color: #61dafb;
animation: spin 1s ease-in-out infinite;
z-index: 1000;
`;
document.body.appendChild(spinner);
return spinner;
}
function showErrorTooltip(event, message) {
const tooltip = document.createElement('div');
tooltip.className = 'error-tooltip';
tooltip.innerHTML = `
<i class="fas fa-exclamation-triangle"></i>
<span>${message}</span>
`;
tooltip.style = `
position: fixed;
left: ${event.clientX + 20}px;
top: ${event.clientY + 20}px;
padding: 8px 12px;
background: rgba(255, 87, 87, 0.9);
color: white;
border-radius: 4px;
font-size: 14px;
z-index: 1000;
animation: fadeIn 0.3s ease;
`;
document.body.appendChild(tooltip);
setTimeout(() => tooltip.remove(), 3000);
}
-1058
View File
File diff suppressed because it is too large Load Diff
-232
View File
@@ -1,232 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电源管理</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="db-status-panel">
<div class="status-columns">
<div class="status-column">
<h3>MOOSDB</h3>
<div class="db-status" id="moos-db-status">
<i class="fas fa-database"></i>
<span>正在连接MOOSDB...</span>
</div>
<div class="moos-notify-panel">
<div class="input-group">
<label for="moos-var">Var:</label>
<input type="text" id="moos-var" placeholder="输入Var值">
</div>
<div class="input-group">
<label for="moos-key">Key:</label>
<input type="text" id="moos-key" placeholder="输入Key值">
</div>
<button id="moos-send-btn">发送</button>
</div>
</div>
<div class="status-column">
<h3>本地数据库</h3>
<div class="db-status" id="db-status">
<i class="fas fa-database"></i>
<span>正在连接数据库:</span>
</div>
<div class="db-size" id="db-size">
<i class="fas fa-database"></i>
<span>本地数据库大小:</span>
<span class="size-value">0.00 MB</span>
</div>
<div class="db-download">
<button id="download-db">
<i class="fas fa-download"></i>
下载数据库
</button>
<progress id="download-progress" value="0" max="100" style="display: none;"></progress>
</div>
</div>
</div>
</div>
<div class="control-panel">
<button id="mode-switch">
<i class="fas fa-exchange-alt"></i>
系统模式切换
</button>
<button id="condition-switch">
<i class="fas fa-cogs"></i>
系统工况切换
</button>
<button id="system-reset">
<i class="fas fa-redo"></i>
系统复位操作
</button>
</div>
<div class="panel log-panel">
<h2>进程消息 <span class="badge" id="process-count">0</span></h2>
<div id="process-list" class="process-container"></div>
<pre id="log-data" class="process-log">
<template id="process-template">
<div class="process-entry">
<div class="process-name"></div>
<div class="message-list">
<div class="message-type">订阅的消息</div>
<ul class="subscribed-list"></ul>
<div class="message-type">发布的消息</div>
<ul class="published-list"></ul>
</div>
</div>
</template>
</pre>
</div>
<div class="panel state-machine-panel">
<h2>状态机状态</h2>
<div class="state-info">
<div class="state-row">
<span class="state-label"><i class="fas fa-clock"></i> 更新时间:</span>
<span class="state-value" id="state-time">正在加载...</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-hashtag"></i> 状态ID:</span>
<span class="state-value" id="state-id">正在加载...</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-tag"></i> 状态名称:</span>
<span class="state-value" id="state-name">正在加载...</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-bolt"></i> 事件:</span>
<span class="state-value" id="state-event">正在加载...</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-barcode"></i> 事件ID:</span>
<span class="state-value" id="state-event-id">正在加载...</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-exclamation-triangle"></i> 故障代码:</span>
<span class="state-value" id="state-fault-code">正在加载...</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-sticky-note"></i> 备注:</span>
<span class="state-value" id="state-note">正在加载...</span>
</div>
</div>
</div>
<div class="panel dis-sys-panel">
<h2>DIS系统状态</h2>
<pre id="dis-sys-state">正在加载...</pre>
</div>
<div class="panel ccu-state-panel">
<h2>CCU状态</h2>
<pre id="ccu-state">正在加载...</pre>
</div>
<div class="panel dcdc-state-panel">
<h2>DC/DC模块状态</h2>
<div class="state-info">
<div class="state-row">
<span class="state-label"><i class="fas fa-thermometer-half"></i> DC/DC模块过温关机:</span>
<span class="state-value" id="dcdc-over-temp">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-bolt"></i> 输出过流:</span>
<span class="state-value" id="dcdc-over-current">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-bolt"></i> 输出过压:</span>
<span class="state-value" id="dcdc-over-voltage">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-bolt"></i> 输出欠压:</span>
<span class="state-value" id="dcdc-under-voltage">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-plug"></i> 输入过压:</span>
<span class="state-value" id="dcdc-input-over-voltage">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-plug"></i> 输入欠压:</span>
<span class="state-value" id="dcdc-input-under-voltage">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-exclamation-triangle"></i> 输出短路保护:</span>
<span class="state-value" id="dcdc-short-circuit">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-bug"></i> 内部故障:</span>
<span class="state-value" id="dcdc-internal-fault">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-wifi"></i> 通讯故障报警:</span>
<span class="state-value" id="dcdc-comm-fault">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-power-off"></i> 工作状态:</span>
<span class="state-value" id="dcdc-working-status">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-tint"></i> 冷却泵1故障:</span>
<span class="state-value" id="dcdc-pump1-fault">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-tint"></i> 冷却泵2故障:</span>
<span class="state-value" id="dcdc-pump2-fault">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-tint"></i> 冷却水泄露:</span>
<span class="state-value" id="dcdc-water-leak">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-tint"></i> 冷却泵1运行状态:</span>
<span class="state-value" id="dcdc-pump1-running">-</span>
</div>
<div class="state-row">
<span class="state-label"><i class="fas fa-tint"></i> 冷却泵2运行状态:</span>
<span class="state-value" id="dcdc-pump2-running">-</span>
</div>
</div>
</div>
<!-- 消息详情弹窗 -->
<div class="message-detail-overlay" id="message-detail-overlay" style="display: none;"></div>
<div class="message-detail-modal" id="message-detail-modal" style="display: none;">
<div class="message-detail-header">
<div class="message-detail-title">消息详情</div>
<div class="message-detail-close" id="message-detail-close">&times;</div>
</div>
<div class="message-detail-content">
<div class="message-detail-item">
<div class="message-detail-label">消息名称</div>
<div class="message-detail-value" id="message-name"></div>
</div>
<div class="message-detail-item">
<div class="message-detail-label">消息类型</div>
<div class="message-detail-value" id="message-type"></div>
</div>
<div class="message-detail-item">
<div class="message-detail-label">消息内容</div>
<div class="message-detail-value" id="message-content"></div>
</div>
<div class="message-detail-item">
<div class="message-detail-label">时间戳</div>
<div class="message-detail-value" id="message-timestamp"></div>
</div>
<div class="message-detail-item">
<div class="message-detail-label">来源</div>
<div class="message-detail-value" id="message-source"></div>
</div>
</div>
</div>
<script src="/static/script.js"></script>
</body>
</html>
+18 -1
View File
@@ -38,7 +38,24 @@ done
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../
# 修复: PATH 中包含了 Windows Anaconda(/mnt/d/Software/Anaconda),
# 其下的 GTest 是 Windows .lib, 无法被 Linux 链接器使用。
# 因此显式指定 Linux 平台的 GTest 配置, 覆盖 CMake 从 PATH 探测到的不正确路径。
CMAKE_ARGS="-DCMAKE_BUILD_TYPE=${BUILD_TYPE}"
if [ -z "${GTest_DIR}" ]; then
for GTEST_CONFIG in \
/usr/lib/x86_64-linux-gnu/cmake/GTest/GTestConfig.cmake \
/usr/lib/cmake/GTest/GTestConfig.cmake \
/usr/local/lib/cmake/GTest/GTestConfig.cmake \
"${HOME}/miniconda/lib/cmake/GTest/GTestConfig.cmake"; do
if [ -f "${GTEST_CONFIG}" ]; then
CMAKE_ARGS="${CMAKE_ARGS} -DGTest_DIR=$(dirname "${GTEST_CONFIG}")"
break
fi
done
fi
cmake ${CMAKE_ARGS} ../
make ${CMD_LINE_ARGS}
cd ${INVOCATION_ABS_DIR}
+5 -3
View File
@@ -1,15 +1,17 @@
#=======================================================================
# FILE: ci/Dockerfile
# DESC: aarch64 (RK3588 / Ubuntu 20.04) build & test environment.
# - Ubuntu 20.04 arm64 base (matches the target board)
# DESC: aarch64 (RK3588 / Ubuntu 22.04) build & test environment.
# - Ubuntu 22.04 arm64 base (matches the target board: Orange Pi 5 Plus)
# - MOOS-IvP built from source and installed to /usr/local
# - jsoncpp (the only extra dep used by pPowerManger)
# NOTE: Ubuntu 20.04 image kept as h100-power-manager-ci:arm64-focal
# in case the target board is later switched back to 20.04.
# MOOS-IvP is cloned from the internal Gitea mirror (UUV/moos-ivp)
# via a BuildKit secret (github.com is unreachable from the CN
# runner). PROJ/UTM are disabled to avoid github-based CPM fetches.
#=======================================================================
FROM arm64v8/ubuntu:20.04
FROM arm64v8/ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
Binary file not shown.
-128
View File
@@ -1,128 +0,0 @@
# PowerManager系统架构优化设计方案
## 1. 总体架构
```mermaid
classDiagram
class PowerManagerCore {
+startup()
+shutdown()
+getSystemState()
}
class StateMachineService {
+triggerEvent()
+getCurrentState()
+addStateListener()
}
class CommunicationMediator {
+registerHandler()
+sendMessage()
+addMessageListener()
}
PowerManagerCore --> StateMachineService
PowerManagerCore --> CommunicationMediator
```
## 2. 状态机重构方案
### 2.1 状态设计模式
```cpp
class IPowerState {
public:
virtual void onEntry(PowerContext&) = 0;
virtual void handleEvent(PowerContext&, const Event&) = 0;
virtual void onExit(PowerContext&) = 0;
};
```
### 2.2 状态转换规则
```yaml
# states_config.yaml
transitions:
- from: STANDBY
event: START_CMD
to: OPERATING
- from: OPERATING
event: STOP_CMD
to: STANDBY
```
## 3. 通信中介者设计
### 3.1 核心架构
```mermaid
sequenceDiagram
participant Upper as 上位机
participant Mediator as 通信中介者
participant Lower as 下位机
Upper->>Mediator: 控制命令(JSON)
Mediator->>Lower: 设备指令(ProtocolBuffer)
Lower->>Mediator: 状态更新
Mediator->>Upper: 系统报告
```
### 3.2 关键实现
```cpp
class CommunicationMediator {
// 线程安全消息队列
moodycamel::ConcurrentQueue<Message> queue;
// 消息处理线程
void processMessages() {
while (running) {
Message msg;
if (queue.try_dequeue(msg)) {
handlers[msg.type]->handle(msg);
}
}
}
};
```
## 4. 数据库服务设计
### 4.1 日志策略模式
```cpp
class ILogStrategy {
public:
virtual bool log(const SystemLog& log) = 0;
};
class SQLiteLogStrategy : public ILogStrategy {
// SQLite实现...
};
class FileLogStrategy : public ILogStrategy {
// 文件系统实现...
};
```
## 5. 实施计划
### 5.1 阶段划分
```mermaid
gantt
title 重构实施甘特图
section 核心框架
接口定义 :2025-04-08, 3d
基础实现 :2025-04-11, 5d
section 模块重构
状态机 :2025-04-16, 5d
通信模块 :2025-04-21, 5d
section 测试验证
单元测试 :2025-04-26, 3d
集成测试 :2025-04-29, 5d
```
## 6. 性能指标
| 模块 | 当前QPS | 目标QPS | 优化手段 |
|----------------|---------|---------|-----------------------|
| 状态机处理 | 100 | 500 | 无锁状态转换 |
| 消息处理 | 200 | 1000 | 批量处理+线程池 |
| 数据库写入 | 50 | 300 | 异步写入+批量提交 |
-277
View File
@@ -1,277 +0,0 @@
# 上位机控制程序设计文档
## 1. 程序架构设计
采用MVC模式设计,主要分为以下模块:
### 1.0 系统架构图
```mermaid
graph TD
A[用户界面] -->|控制指令| B[控制器]
B -->|发送命令| C[通信模块]
C -->|UDP协议| D[下位机]
D -->|状态反馈| C
C -->|解析数据| B
B -->|更新状态| A
B -->|记录数据| E[数据库]
```
### 1.1 消息处理流程
1. 接收原始UDP数据
2. 根据消息ID选择解析器
3. 验证校验码
4. 解析为结构化数据
5. 触发状态更新事件
6. 存储到数据库
### 1.1 通信模块 (Model) - 详细设计
#### 消息类型映射表
| 消息ID | 消息名称 | 处理函数 | 更新频率 |
|--------|----------|----------|----------|
| 0x0005 | 高压母线状态 | parse_high_vol_bus | 1Hz |
| 0x0006 | 高压A母线状态 | parse_high_a_vol_bus | 1Hz |
| 0x0007 | 低压主母线状态 | parse_low_main_bus | 1Hz |
| 0x0008 | 低压汇流排状态 | parse_low_bus | 1Hz |
#### 异常处理机制
1. 校验失败:丢弃数据并记录错误日志
2. 超时处理:3次重试后标记设备离线
3. 数据异常:触发告警并显示在界面
```python
class UDPMessageHandler:
"""UDP消息处理基类"""
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.socket = None
def connect(self):
"""建立UDP连接"""
pass
def send_message(self, msg):
"""发送消息"""
pass
def receive_message(self):
"""接收消息"""
pass
def close(self):
"""关闭连接"""
pass
class PowerMessageHandler(UDPMessageHandler):
"""电源管理专用消息处理器"""
def __init__(self, ip, port):
super().__init__(ip, port)
self.message_parsers = {
0x0005: self.parse_high_vol_bus,
0x0006: self.parse_high_a_vol_bus,
# 其他消息类型...
}
def parse_high_vol_bus(self, data):
"""解析高压母线消息"""
pass
# 其他消息解析方法...
```
### 1.2 界面模块 (View) - 详细设计
#### 主界面布局
```mermaid
graph LR
A[标题栏] --> B[状态区]
B --> C[电压电流仪表盘]
B --> D[断路器状态指示灯]
A --> E[控制区]
E --> F[电源控制按钮组]
E --> G[参数设置面板]
A --> H[日志区]
H --> I[实时消息列表]
H --> J[历史记录查询]
```
#### 组件说明
1. 电压电流仪表盘:实时显示各母线电压电流值
2. 断路器状态指示灯:绿色-闭合,红色-断开
3. 电源控制按钮组:带防误触保护(需长按3秒)
4. 参数设置面板:输入验证和范围检查
```python
class MainWindow(tk.Tk):
"""主窗口"""
def __init__(self):
super().__init__()
self.title("电源管理系统")
self.geometry("1200x800")
self.create_widgets()
def create_widgets(self):
"""创建界面组件"""
self.status_frame = StatusFrame(self)
self.control_frame = ControlFrame(self)
self.log_frame = LogFrame(self)
# 布局管理
self.status_frame.pack(side=tk.TOP, fill=tk.X)
self.control_frame.pack(side=tk.LEFT, fill=tk.Y)
self.log_frame.pack(side=tk.RIGHT, expand=True, fill=tk.BOTH)
class StatusFrame(tk.Frame):
"""状态显示面板"""
def update_status(self, data):
"""更新状态显示"""
pass
class ControlFrame(tk.Frame):
"""控制面板"""
def create_buttons(self):
"""创建控制按钮"""
pass
```
### 1.3 控制器模块 (Controller) - 详细设计
#### 状态管理机制
1. 使用有限状态机(FSM)管理电源系统状态
2. 状态转换规则:
- 仅当所有预检通过才允许上电
- 故障时自动进入安全状态
- 人工确认后才能复位
#### 命令处理流程
```mermaid
sequenceDiagram
用户->>界面: 点击控制按钮
界面->>控制器: 发送控制命令
控制器->>业务逻辑: 验证命令有效性
业务逻辑->>通信模块: 发送UDP指令
通信模块->>下位机: 传输控制报文
下位机-->>通信模块: 返回应答
通信模块->>控制器: 更新命令状态
控制器->>界面: 显示操作结果
控制器->>数据库: 记录操作日志
```
```python
class PowerManagerController:
"""主控制器"""
def __init__(self):
self.message_handler = PowerMessageHandler("192.168.1.100", 5000)
self.view = MainWindow()
# 绑定事件
self.bind_events()
def bind_events(self):
"""绑定UI事件"""
self.view.control_frame.on_button_click = self.handle_control_command
def handle_control_command(self, cmd):
"""处理控制命令"""
pass
def start(self):
"""启动应用程序"""
self.message_handler.connect()
self.view.mainloop()
```
## 2. 接口规范
### 2.1 模块交互架构
```mermaid
classDiagram
class Communication {
+send_command(cmd: dict) bool
+get_status() dict
+subscribe(callback: function)
+on_message_received(data: bytes)
}
class Controller {
+handle_ui_event(event: dict)
+update_system_status(status: dict)
+log_operation(log: dict)
}
class UI {
+update_status(data: dict)
+show_alert(level: int, msg: str)
+log_message(source: str, msg: str)
}
Communication --> Controller : 状态更新通知
Controller --> UI : 界面更新指令
UI --> Controller : 用户操作事件
```
### 2.2 详细接口定义
#### 通信模块 → 控制器
```python
# 状态更新通知
{
"msg_type": "status_update",
"timestamp": "2025-05-04T10:20:00",
"data": {
"voltage": 220.5,
"current": 15.2,
"breaker_state": True
}
}
# 错误通知
{
"msg_type": "error",
"code": 1001,
"message": "校验失败"
}
```
#### 控制器 → 界面
```python
# 状态显示更新
{
"component": "voltage_meter",
"value": 220.5,
"unit": "V",
"normal_range": [210, 230]
}
# 告警信息
{
"level": 2, # 1-警告 2-错误 3-严重
"message": "A相电压过高",
"suggestion": "检查A相负载"
}
```
#### 界面 → 控制器
```python
# 按钮操作事件
{
"event_type": "button_click",
"button_id": "power_on",
"timestamp": "2025-05-04T10:20:01"
}
# 参数设置事件
{
"event_type": "param_set",
"param": "voltage_limit",
"value": 230,
"unit": "V"
}
```
### 2.3 错误处理规范
1. 通信错误:自动重试3次后通知控制器
2. 业务逻辑错误:记录详细日志并通知界面
3. 界面错误:显示友好提示并保持最后正常状态
## 3. 数据库设计
(之前已完成的数据库表结构...)
@@ -0,0 +1,651 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pPowerManger 代码审查与架构优化报告</title>
<style>
:root{
--bg:#0f1419; --panel:#171e26; --panel2:#1d2630; --border:#2a3642;
--text:#d8e0e8; --text-dim:#8fa0b0; --accent:#4da3ff; --accent2:#37c8a0;
--p0:#ff5c5c; --p1:#ffb347; --p2:#6fbf73; --code-bg:#101820;
}
*{margin:0;padding:0;box-sizing:border-box;}
body{background:var(--bg);color:var(--text);font-family:"Microsoft YaHei","PingFang SC","Segoe UI",sans-serif;line-height:1.75;font-size:15px;}
.layout{display:flex;max-width:1440px;margin:0 auto;}
nav{position:sticky;top:0;height:100vh;width:250px;flex-shrink:0;overflow-y:auto;padding:28px 18px;border-right:1px solid var(--border);background:var(--panel);}
nav h3{font-size:13px;color:var(--text-dim);letter-spacing:2px;margin-bottom:14px;}
nav a{display:block;color:var(--text-dim);text-decoration:none;padding:5px 10px;border-radius:6px;font-size:13.5px;border-left:2px solid transparent;}
nav a:hover{color:var(--accent);background:var(--panel2);}
main{flex:1;padding:44px 56px 120px;max-width:1140px;}
header.cover{border-bottom:1px solid var(--border);padding-bottom:30px;margin-bottom:36px;}
header.cover .tag{color:var(--accent);font-size:13px;letter-spacing:3px;}
header.cover h1{font-size:30px;margin:10px 0 8px;font-weight:600;}
header.cover .meta{color:var(--text-dim);font-size:13.5px;}
h2{font-size:22px;margin:52px 0 18px;padding-left:14px;border-left:4px solid var(--accent);font-weight:600;}
h3{font-size:17px;margin:30px 0 12px;color:var(--accent);font-weight:600;}
h4{font-size:15.5px;margin:20px 0 8px;color:var(--text);}
p{margin:10px 0;color:var(--text);}
.card{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:20px 24px;margin:16px 0;}
table{width:100%;border-collapse:collapse;margin:14px 0;font-size:13.5px;}
th{background:var(--panel2);color:var(--accent);text-align:left;padding:9px 12px;border:1px solid var(--border);font-weight:600;white-space:nowrap;}
td{padding:8px 12px;border:1px solid var(--border);vertical-align:top;}
tr:nth-child(even) td{background:rgba(255,255,255,0.02);}
code{font-family:Consolas,"Courier New",monospace;background:var(--code-bg);color:#9fd3ff;padding:1px 6px;border-radius:4px;font-size:13px;}
pre{background:var(--code-bg);border:1px solid var(--border);border-radius:8px;padding:14px 18px;overflow-x:auto;margin:12px 0;}
pre code{background:none;padding:0;color:#c8d8e8;font-size:13px;line-height:1.6;}
.badge{display:inline-block;padding:1px 10px;border-radius:10px;font-size:12px;font-weight:600;margin-right:6px;white-space:nowrap;}
.p0{background:rgba(255,92,92,.15);color:var(--p0);border:1px solid var(--p0);}
.p1{background:rgba(255,179,71,.12);color:var(--p1);border:1px solid var(--p1);}
.p2{background:rgba(111,191,115,.12);color:var(--p2);border:1px solid var(--p2);}
.loc{color:var(--text-dim);font-size:12.5px;font-family:Consolas,monospace;}
ul,ol{margin:8px 0 8px 24px;}
li{margin:5px 0;}
.flow{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:14px 0;font-size:13px;}
.node{background:var(--panel2);border:1px solid var(--accent);border-radius:8px;padding:8px 14px;color:var(--text);}
.node.green{border-color:var(--accent2);}
.node.orange{border-color:var(--p1);}
.arrow{color:var(--accent);font-weight:bold;}
.note{border-left:3px solid var(--accent2);background:rgba(55,200,160,.06);padding:12px 18px;border-radius:0 8px 8px 0;margin:14px 0;font-size:14px;}
.warn{border-left:3px solid var(--p1);background:rgba(255,179,71,.06);padding:12px 18px;border-radius:0 8px 8px 0;margin:14px 0;font-size:14px;}
.danger{border-left:3px solid var(--p0);background:rgba(255,92,92,.07);padding:12px 18px;border-radius:0 8px 8px 0;margin:14px 0;font-size:14px;}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.kpi{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:18px 0;}
.kpi .item{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:16px;text-align:center;}
.kpi .num{font-size:26px;font-weight:700;color:var(--accent);}
.kpi .lbl{font-size:12.5px;color:var(--text-dim);margin-top:4px;}
.phase{border-left:3px solid var(--accent);padding:6px 0 6px 20px;margin:18px 0;position:relative;}
.phase::before{content:"";position:absolute;left:-7px;top:12px;width:11px;height:11px;border-radius:50%;background:var(--accent);}
.phase h4{margin:0 0 4px;color:var(--accent);}
.small{font-size:13px;color:var(--text-dim);}
@media (max-width:1000px){nav{display:none;}main{padding:24px;}.kpi{grid-template-columns:repeat(2,1fr);}.grid2{grid-template-columns:1fr;}}
</style>
</head>
<body>
<div class="layout">
<nav>
<h3>目录</h3>
<a href="#s1">1 审查概述</a>
<a href="#s2">2 现状架构分析</a>
<a href="#s3">3 代码审查意见</a>
<a href="#s3-1">3.1 确定性缺陷(Bug 级)</a>
<a href="#s3-2">3.2 状态机与控制逻辑</a>
<a href="#s3-3">3.3 通信逻辑</a>
<a href="#s3-4">3.4 故障码体系</a>
<a href="#s3-5">3.5 数据管理与并发</a>
<a href="#s3-6">3.6 上位机与 Web 服务</a>
<a href="#s3-7">3.7 构建工程与配置管理</a>
<a href="#s4">4 架构优化方案</a>
<a href="#s4-1">4.1 目标分层架构</a>
<a href="#s4-2">4.2 状态机重构</a>
<a href="#s4-3">4.3 通信层重构</a>
<a href="#s4-4">4.4 故障码统一方案</a>
<a href="#s4-5">4.5 上位机/下位机管理</a>
<a href="#s5">5 设备上电管理与状态机转移重构详细设计</a>
<a href="#s6">6 特性一致性保障策略</a>
<a href="#s7">7 测试方案</a>
<a href="#s8">8 实施路线图</a>
<a href="#s9">9 附录:问题清单</a>
</nav>
<main>
<header class="cover">
<div class="tag">CODE REVIEW &amp; ARCHITECTURE OPTIMIZATION</div>
<h1>pPowerManger 电源管理软件<br>代码审查与架构优化报告</h1>
<div class="meta">项目:H100PowerManger(UUV 复合能源功率管理)&nbsp;|&nbsp;范围:src/pPowerManger、src/pPowerMangerHost、test、ci&nbsp;|&nbsp;日期:2026-09-03&nbsp;|&nbsp;版本:V1.0</div>
</header>
<!-- ================= 1 ================= -->
<h2 id="s1">1 审查概述</h2>
<p>pPowerManger 是 H100 水下无人航行器复合能源系统的电源管理进程,运行于 MOOS 中间件之上,承担上位机指令接收、下位机(CCU/配电)设备管理、14 工况状态机调度、故障检测与上报、数据持久化及本地 Web 监视等职责。本次审查覆盖下位机核心代码约 2.4 万行(不含第三方 mongoose 库约 2.8 万行),重点评估代码架构、控制逻辑、通信逻辑、故障管理与测试体系,并给出以保持外部特性严格一致为前提的优化方案。</p>
<div class="kpi">
<div class="item"><div class="num">14</div><div class="lbl">工况状态(tinyfsm)</div></div>
<div class="item"><div class="num">4</div><div class="lbl">并发线程(无统一模型)</div></div>
<div class="item"><div class="num">3</div><div class="lbl">套互不统一的故障编码</div></div>
<div class="item"><div class="num">0</div><div class="lbl">状态机 / 上位机测试用例</div></div>
</div>
<div class="card">
<h4>总体结论</h4>
<p>现有代码功能可用,但工程化程度不足,核心问题集中在五条主线:</p>
<ol>
<li><b>存在多个确定性缺陷</b>:Lifting(起吊准备)状态永远无法到达;浮力调节状态反馈因赋值误写为比较而永不更新;MOOS 邮件偏斜检查导致整批邮件被丢弃等(见 3.1)。</li>
<li><b>状态机退化</b>:tinyfsm 仅被用作事件路由外壳,真正的转移逻辑集中在基类两个巨型 switch 中;无 guard 条件;故障检测与 FaultState 完全脱钩。</li>
<li><b>通信层职责混杂</b>:协议知识(JSON 键名、设备 ID、帧格式)散落在 Driver、UpperCommManager、udpComm 三处;4 个线程对共享数据加锁纪律不一致,存在数据竞争与忙等阻塞。</li>
<li><b>故障码体系三分天下</b>:电池故障表、配电故障表、0xLSSS 系统故障魔数三套编码并存,双故障存储导致单一事实源被破坏,上报与显示映射脱节。</li>
<li><b>测试存在三大空白</b>:状态机迁移、故障注入、上位机链路均无测试,重构缺乏安全网。</li>
</ol>
<p>优化应以"先建测试基线、再做特性等价重构"为总原则,分四个阶段实施(见第 7、8 章)。</p>
</div>
<!-- ================= 2 ================= -->
<h2 id="s2">2 现状架构分析</h2>
<h3>2.1 进程与通信拓扑</h3>
<div class="flow">
<span class="node green">Web 页面(浏览器)</span><span class="arrow">⇄ WebSocket</span>
<span class="node green">pPowerMangerHost(上位机桥)</span><span class="arrow">⇄ MOOSDB (TCP:9000, JSON 字符串变量)</span>
<span class="node">pPowerManger(本进程)</span><span class="arrow">⇄ UDP 二进制帧 (5001↔7000)</span>
<span class="node orange">pCCU / pCanBridge</span><span class="arrow">⇄ CAN</span>
<span class="node orange">电池 / 燃料电池 / 配电设备</span>
</div>
<p>另有两条旁路:pPowerManger 内嵌 HTTP 服务(端口 8000/8090)直读内部对象提供本地监视页面;上位机 HostSim 自带 Web 服务(端口 18080)+ SQLite 历史库。上位机与下位机之间<b>不存在直接 socket 连接</b>,全部交互以 MOOSDB 上的 JSON 字符串变量为协议载体(<code>uPower_*_cmd</code> 下行、<code>uPower_*_fb</code> 上行)。</p>
<h3>2.2 下位机内部模块</h3>
<table>
<tr><th>模块</th><th>文件</th><th>规模</th><th>实际职责</th><th>主要问题</th></tr>
<tr><td>主应用</td><td class="loc">PowerManger.h/.cpp</td><td class="loc">~680 行</td><td>MOOSApp 框架、线程拉起、全局对象持有</td><td>上帝对象,持有全部管理器指针并被 FSM 反向穿透</td></tr>
<tr><td>状态机</td><td class="loc">fsm/PowerManagerFsm.cpp + 14 状态</td><td class="loc">~3700 行</td><td>事件路由 + 设备指令处理 + 故障编码 + 状态拷贝</td><td>上帝类;转移逻辑退化到基类 switch;无 guard</td></tr>
<tr><td>上位机通信</td><td class="loc">UpperCommManager.cpp</td><td class="loc">261 行</td><td>MOOS 邮件 JSON 解析、周期状态广播</td><td>解析失败误报 Unknown key;无连接管理</td></tr>
<tr><td>下位机通信</td><td class="loc">LowerCommManager.cpp</td><td class="loc">890 行</td><td>UDP 监听线程、帧校验入队、状态注入、断路器闭环操作</td><td>回调直改全局数据;忙等最长约 7 s;35 个操作函数重复</td></tr>
<tr><td>协议编解码</td><td class="loc">udpcomm/udpComm.cpp, ccuUdpMsg.h</td><td class="loc">~600 行</td><td>UDP 二进制帧打包/解析、校验</td><td>硬编码帧长;死分支;每帧堆分配</td></tr>
<tr><td>设备策略表</td><td class="loc">driver.cpp</td><td class="loc">1094 行</td><td>68 字段 DriverTable、9 张工况设备表、JSON 编解码</td><td>9 张表约 600 行逐字段重复赋值;错误码形同虚设</td></tr>
<tr><td>系统数据</td><td class="loc">systemData.h, pmSysvariable.h</td><td class="loc">~2250 行</td><td>协议镜像结构体 + 全局数据池单例</td><td>头文件内实现;百字段全局池;锁纪律不一致</td></tr>
<tr><td>故障码</td><td class="loc">faultCode.h</td><td class="loc">125 行</td><td>电池 54 项 + 配电 45 项故障表</td><td>与第三套 0xLSSS 魔数并存;上报/显示映射脱节</td></tr>
<tr><td>本地 Web</td><td class="loc">httpserver/(mongoose + 内嵌页面)</td><td class="loc">~7000 行</td><td>dashboard/日志/测试注入/电池/燃料电池页面</td><td>与上位机 Web 重复建设;HTTP 回调越权直改状态</td></tr>
</table>
<h3>2.3 线程模型</h3>
<table>
<tr><th>线程</th><th>周期</th><th>主要工作</th><th>风险</th></tr>
<tr><td>MOOS 主线程</td><td>4 Hz</td><td>OnNewMail → 指令解析 → FSM dispatch;Iterate → CycleTriggeredEvent → 故障检测</td><td>断路器闭环忙等约 7 s 会卡住整个邮件循环</td></tr>
<tr><td>commLoop 线程</td><td>1 Hz</td><td>心跳/周期指令下发、队列清理</td><td>内含 usleep/MOOSPause;与监听线程并发清队列</td></tr>
<tr><td>UDP 监听线程</td><td>阻塞读</td><td>收帧 → 校验入队 → 出队写 systemData / SQLite</td><td>直接写业务数据;退出时可能挂在 recv 上</td></tr>
<tr><td>HTTP 线程</td><td>事件驱动</td><td>Web 页面与 REST</td><td>/fcs/control 回调里加锁直改 PowerManger 状态</td></tr>
</table>
<p>共享数据 <code>m_msCmd</code>、<code>m_depth</code>、<code>m_insData</code>、<code>m_thrustRpm</code> 等由 MOOS 线程无锁写、其余线程读;<code>m_stateMutex</code> 仅保护部分字段;<code>updateSystemData</code> 持 <code>m_dataMutex</code> 再取 <code>faultCodeMutex</code>,锁顺序仅靠注释约定(<span class="loc">PowerManger.h:92-93</span>)。整体属"能跑但不具备可论证的线程安全性"。</p>
<!-- ================= 3 ================= -->
<h2 id="s3">3 代码审查意见</h2>
<h3 id="s3-1">3.1 确定性缺陷(Bug 级,建议立即修复)</h3>
<p>以下问题经逐行核对确认,均有明确代码证据,与架构无关,应在重构前先以最小改动修复并补充回归用例。</p>
<table>
<tr><th>#</th><th>级别</th><th>问题</th><th>位置</th><th>影响与修复建议</th></tr>
<tr>
<td>B1</td><td><span class="badge p0">P0</span></td>
<td><b>Lifting(起吊准备)状态永远无法到达</b>:<code>handleWorkCmd</code> 的 case 12 只有日志,缺少 <code>transit&lt;Lifting&gt;()</code> 与 <code>break</code>,直接坠入 default</td>
<td class="loc">fsm/PowerManagerFsm.cpp:2934-2936</td>
<td>上位机"起吊准备"指令被静默吞掉,Lifting 为死状态。补 <code>transit&lt;Lifting&gt;(); break;</code>。</td>
</tr>
<tr>
<td>B2</td><td><span class="badge p0">P0</span></td>
<td><b>赋值误写为比较</b>:<code>s.buoyage == 1;</code>(共 4 处,值 1/3/2/0),语句无副作用</td>
<td class="loc">fsm/PowerManagerFsm.cpp:1471,1475,1482,1486</td>
<td>浮力调节(buoyage)状态反馈永远不更新,上报恒为初值。改为 <code>=</code>。</td>
</tr>
<tr>
<td>B3</td><td><span class="badge p0">P0</span></td>
<td><b>MOOS 邮件整批丢弃</b>:OnNewMail 中任一邮件偏斜超差即 <code>return true</code>,同批其余邮件全部丢失</td>
<td class="loc">PowerManger.cpp:121-125</td>
<td>网络抖动时会成片丢失上位机指令。改为跳过该条、继续处理后续邮件。</td>
</tr>
<tr>
<td>B4</td><td><span class="badge p1">P1</span></td>
<td><b>JSON 键名不匹配导致指令静默丢失</b>:发送端键 <code>"mastLiftingServo_uint8"</code>,接收端解析键 <code>"mastLiftingServo_uint8_uint8"</code></td>
<td class="loc">driver.cpp:844 vs 924</td>
<td>桅杆升降舵指令永远解析失败。统一键名,并增加"未知键/未命中"告警日志。</td>
</tr>
<tr>
<td>B5</td><td><span class="badge p1">P1</span></td>
<td><b>故障检测函数重复执行</b>:<code>basicNavigationFault(); extendedNavigationFault();</code> 连续调用两遍</td>
<td class="loc">fsm/PowerManagerFsm.cpp:52-55</td>
<td>4 Hz 下双倍开销,且对带副作用的故障逻辑是隐患。删除重复行。</td>
</tr>
<tr>
<td>B6</td><td><span class="badge p1">P1</span></td>
<td><b>三个断路器故障永不上报</b>:fbReserved/tyzReserved/reserved 调用的是无 faultCode 参数的重载,disfaultMap 中 code 10-12 成死码</td>
<td class="loc">systemData.h:1026-1028</td>
<td>补齐故障码入参,或删除死码并同步前端映射。</td>
</tr>
<tr>
<td>B7</td><td><span class="badge p1">P1</span></td>
<td><b>数组越界风险</b>:故障注入 <code>batfaultMap[e.eventId].code</code> 对来自外部的 eventId 无边界检查;<code>systemData.h:524</code> 循环 <code>i&lt;2</code> 越界访问 <code>reserved2[1]</code>(cppcheck error 级)</td>
<td class="loc">fsm/PowerManagerFsm.cpp:3053;systemData.h:524</td>
<td>注入接口可被越界触发,属安全隐患。加边界校验;修正循环上界。</td>
</tr>
<tr>
<td>B8</td><td><span class="badge p1">P1</span></td>
<td><b>TestEvent 故障注入 switch 缺 break</b>:case 15 坠入 default;另有 <code>react(TestEvent)</code> 直接构造 FaultEvent 调故障函数,绕过事件机制</td>
<td class="loc">fsm/PowerManagerFsm.cpp:2988-2989, 3084-3092</td>
<td>注入 15 号故障时行为未定义。补 break,统一走事件派发。</td>
</tr>
<tr>
<td>B9</td><td><span class="badge p2">P2</span></td>
<td><b>未初始化返回</b>(cppcheck error 级):<code>uPlan_taskStart.h:29</code> 返回未初始化 <code>mission</code>;<code>uExternComm_setDeviceSwitch.h:39</code> 可能返回未初始化 <code>cmd.cmd</code>;<code>uPower_pmState.h:14</code> 对含 <code>std::vector</code> 的结构体 memset</td>
<td class="loc">upmsg/ 多个头文件</td>
<td>协议层偶发脏数据。统一在结构体定义处给默认值,禁止对非 POD memset。</td>
</tr>
<tr>
<td>B10</td><td><span class="badge p2">P2</span></td>
<td><b>注释与代码不符</b>:FaultState.cpp:46 注释"5 秒超时"实为 20 秒(:53),且与 Lifting.cpp:40、PowerManagerFsm.cpp 内 5 处同逻辑代码的 5 秒超时不一致;同一段超时代码共复制 7 份</td>
<td class="loc">FaultState.cpp:46/53 等</td>
<td>误导维护且各副本超时阈值漂移。随重构统一为命名常量。</td>
</tr>
<tr>
<td>B11</td><td><span class="badge p1">P1</span></td>
<td><b>模板残留致每条正常指令误报 "Unhandled Mail"</b>:OnNewMail 的 for 循环内保留了 MOOS 模板代码 <code>if(key=="FOO")...else if(key!="APPCAST_REQ") reportRunWarning(...)</code>,除 APPCAST_REQ 外的所有邮件(含已正常处理的 uPower_*_cmd/fb)都被计入 run warning</td>
<td class="loc">PowerManger.cpp:142-146</td>
<td>AppCast 告警计数被污染、日志刷屏。删除该模板残留,仅在 processUpperMsg 返回 false 时告警。</td>
</tr>
<tr>
<td>B12</td><td><span class="badge p1">P1</span></td>
<td><b>畸形 JSON 可致崩溃</b>:<code>loadDriverIdFromJson</code> 对 <code>root[field].asUInt()</code> 无类型检查(<code>JSON_USE_EXCEPTION=1</code>),调用处无 try/catch;<code>SafeReadUInt</code> 用 <code>std::cerr</code> 报错而非日志框架</td>
<td class="loc">driver.cpp:935-936;UpperCommManager.cpp:48</td>
<td>恶意/畸形指令使 jsoncpp 抛异常向上传播。统一走 SafeReadUInt 并捕获异常、改走日志。</td>
</tr>
<tr>
<td>B13</td><td><span class="badge p1">P1</span></td>
<td><b>HTTP 服务在配置就绪前启动</b>:<code>m_httpServer->start()</code> 在构造函数调用,此时 mission 配置未加载、<code>m_db</code> 为 nullptr、UDP 未 bind,控制接口(/fcs/control 等)已对外可访问</td>
<td class="loc">PowerManger.cpp:83-87</td>
<td>存在"配置生效前已暴露控制面"窗口。将 start() 移到 OnStartUp 末尾(DB/端口就绪后)。</td>
</tr>
<tr>
<td>B14</td><td><span class="badge p2">P2</span></td>
<td><b>死代码与孤儿成员</b>:<code>FsmLoop()</code>/<code>_FsmCB</code> 无任何线程启动;<code>m_deviceCmdQuenue</code> 及 push/pop/isEmpty 三接口无消费者;<code>buildReport()</code> 为 MOOS 模板占位;<code>SKEW_TOLERANCE 5</code> 宏未用而 <code>m_skew=10</code> 硬编码</td>
<td class="loc">PowerManger.cpp:18/339;PowerManger.h:48/121</td>
<td>误导维护。随阶段 3 一并清理,SKEW 阈值统一为命名常量。</td>
</tr>
</table>
<h3 id="s3-2">3.2 状态机与控制逻辑</h3>
<h4>3.2.1 现状转移关系(经代码还原)</h4>
<table>
<tr><th>源状态</th><th>事件</th><th>目标状态</th><th>说明</th></tr>
<tr><td>InitState</td><td>entry() 内直接切换</td><td>StandbyState</td><td>初始化完成即转</td></tr>
<tr><td>任意状态</td><td>MasterCommandEvent(workCMD 1~11)</td><td>Standby / ShoreBasedReady / WaterBasedReady / RemoteControl / Cruise / HighSpeed / FloatDown / FloatAdjust / UnderwaterSurvey / SurfaceSurvey / DJMode</td><td class="loc">PowerManagerFsm.cpp:2887-2941;workCMD=12 无效(B1)</td></tr>
<tr><td>任意状态</td><td>TaskStartEvent</td><td>THROW_LOAD→FaultState;FLOAT_UP/SAT_COMM 等→FloatDown;RECYCLE/SAIL→CruiseMode;HOVER→FloatAdjust;TOUR→UnderwaterSurvey</td><td class="loc">PowerManagerFsm.cpp:948-985</td></tr>
<tr><td>任意状态</td><td>TaskStopEvent</td><td>RemoteControl</td><td class="loc">基类 react,cpp:13</td></tr>
<tr><td>StandbyState</td><td>StandbyEvent</td><td>StandbyState(自切换)</td><td>语义存疑</td></tr>
<tr><td>FaultState</td><td>—(无显式出口)</td><td>—</td><td>仅能借基类 handleWorkCmd 间接跳出</td></tr>
</table>
<h4>3.2.2 主要架构问题</h4>
<div class="danger">
<b>A1 状态机退化为"事件路由壳"。</b>真正的状态转移表不在各状态类中,而集中在基类 <code>handleWorkCmd</code> 与 <code>react(TaskStartEvent)</code> 两个巨型 switch;14 个状态类的 <code>react(MasterCommandEvent)</code> 是完全相同的复制粘贴。tinyfsm 提供的 <code>transit&lt;S&gt;(action, condition)</code> guard 重载(tinyfsm.hpp:151)全项目零使用——<b>任何状态下都可经 workCMD 直接跳进任何工况</b>(如巡航中直接切起吊),无任何合法性检查。
</div>
<div class="danger">
<b>A2 故障检测与 FaultState 完全脱钩。</b>8 个 <code>xxxFault()</code> 检测函数只写故障码集合,从不触发状态迁移;FaultState 唯一入口是"抛载任务"(TaskStartEvent::THROW_LOAD),即 3 级故障(如深度失效 0x1302)发生时系统仍停留在原工况。FaultState 无显式出口、无故障恢复确认、无故障码清除联动。FaultEvent 无任何 dispatch 点,基类 <code>react(FaultEvent)</code> 为死代码。
</div>
<div class="warn">
<b>A3 entry() 内长阻塞。</b>WaterBasedReady.cpp:50 与 Lifting.cpp:10 在 entry() 中 <code>MOOSPause(10000)</code>;ShoreBasedReady.cpp:24-35 while 轮询等待约 10 s;断路器操作闭环在事件处理路径上忙等约 7 s。状态迁移期间 FSM 无法响应任何新事件,上位机指令被积压在邮件队列。
</div>
<div class="warn">
<b>A4 上帝类与全局穿透。</b>PowerManagerFsm.cpp(3096 行)集中了 10 个高度重复的设备指令处理函数、10 个 getXxxStatus 状态拷贝函数、8 组故障编码函数(约 200 个 0xLSSS 魔法故障码)。状态代码经 <code>static PowerManger* pm</code> 裸指针直接读写宿主几十个公有成员、直接操作断路器、直接 sendNotification,单向依赖原则被彻底打破。
</div>
<ul>
<li><b>A5 语义错位</b>:FloatDown 实际承载"上浮/下潜/卫星通信/卫星标定"四种业务;<code>handelPowerCmd</code>、<code>underWterDevices</code>、<code>mergencyLithiumBattery</code> 等拼写错误已进入接口。</li>
<li><b>A6 死代码</b>:<code>PowerSystemState</code> 枚举(hpp:21)、<code>DeviceCmd</code> 事件(hpp:41)、<code>handleDeviceCmd</code> 空壳(cpp:2966)、<code>m_currentDriver</code>(hpp:87)、<code>checkError()</code> 被注释掏空(PowerManger.cpp:344-356)。</li>
<li><b>A7 日志混乱</b>:基类兜底 react 用 <code>std::cout &lt;&lt; "No Idear"</code>(hpp:94);entry/exit 大量使用 RTTI demangle + ANSI 转义打印,嵌入式目标上属无效开销;<code>printState</code> 用 <code>desp.find("故障")</code> 字符串匹配决定日志级别(PowerManger.cpp:359),极其脆弱。</li>
</ul>
<h3 id="s3-3">3.3 通信逻辑</h3>
<div class="warn">
<b>C1 上位机连接无管理。</b><code>LowerCommManager::m_connectState</code>(h:131)与 <code>PowerManger::connectState</code>(h:136)声明后从未使用;上位机方向无心跳、无在线检测、无断线告警,仅靠 MOOS 库自动重连。下位机方向有 30 s 超时检测(systemData.h:752-761),但超时后仅置标志位,未与故障系统联动。
</div>
<div class="warn">
<b>C2 协议知识三处散养。</b>JSON 键名与设备 ID 映射(协议层知识)写在 Driver(业务策略层)里,与发送端 driver.cpp 各维护一份,已造成 B4 键名失配;UDP 帧格式在 udpComm,设备 ID 0-61 在 driver.cpp:908-930 与 DeviceId 枚举重复硬编码。<code>Driver</code> 实例在 PowerManger 与 UpperCommManager 各持一份,表状态易不一致。
</div>
<ul>
<li><b>C3 校验与错误处理形同虚设</b>:<code>loadDriverTableFromJson</code> 的 error_code 每次被覆盖、从不检查,恒返回 0(driver.cpp:962-1093);<code>sendCcuSetParmCmd</code> 无论发送成败都 return true(udpComm.cpp:609-619);帧长硬编码 18/6/8 且旁边就是 TODO(udpComm.cpp:467/491/515/539)。</li>
<li><b>C4 死分支与静默吞帧</b>:<code>CCU_UDPMSG_START1 == DIS_MSG_HEAD1 == 0x40</code>,udpComm.cpp:47-56 两个 else-if 完全等价;未知 id 走 default 后仍 return true(udpComm.cpp:344-347)。</li>
<li><b>C5 高频路径低效</b>:每帧堆分配(udpComm.cpp:462 等 5 处)、每个 UDP 包 cout 打印一次(LowerCommManager.cpp:381)、发送函数失败后仍 return true(LowerCommManager.cpp:142-144)。</li>
<li><b>C6 废弃函数藏错误数据</b>:<code>sendDisSysCommand</code> 对 bus3 无视入参硬编码 <code>0x55/0xAA</code>(LowerCommManager.cpp:183-189),若被误用将下发错误指令。</li>
<li><b>C7 双 Web 重复建设</b>:下位机 httpserver(8000)与上位机 HostSim Web(18080)都用 mongoose + /ws + 内嵌页面提供状态监视;下位机 /fcs/control 在 HTTP 回调中直改状态(httpserver.cpp:150),绕过 MOOS 消息机制,属越权控制通道。</li>
</ul>
<h3 id="s3-4">3.4 故障码体系</h3>
<table>
<tr><th>编码方案</th><th>定义位置</th><th>格式</th><th>使用方</th><th>问题</th></tr>
<tr><td>电池故障表</td><td class="loc">faultCode.h:17-76,batfaultMap[54]</td><td>单字节 0x01-0xC9,带 1-4 级</td><td>仅故障注入使用</td><td>level 字段与描述矛盾(:35);注入越界风险(B7)</td></tr>
<tr><td>配电故障表</td><td class="loc">faultCode.h:78-124,disfaultMap[45]</td><td>十进制 1-45</td><td>断路器故障上报</td><td>code 10-12 死码(B6)</td></tr>
<tr><td>系统故障码</td><td class="loc">无定义文件,散落在 PowerManagerFsm.cpp:1676 起</td><td>16 位魔数 0xLSSS(高半字节=等级)</td><td>8 个检测函数 + getFaultLevel</td><td>约 200 个裸字面量无注释无枚举;等级提取依赖位运算巧合</td></tr>
</table>
<ul>
<li><b>F1 双重故障存储</b>:真实故障进 <code>SystemData::sysFaultCodes</code>(set,自恢复即自动清除,无锁存/确认);注入故障进 <code>PowerManger::faultCode</code>(vector,不去重、只能整体 clear)。<code>getFaultLevel()</code>(PowerManger.cpp:448)与上报用的 <code>data.getFaultCodeLevel()</code>(uPower_pmState.h:50)读的是<b>不同集合</b>,同一件事故障等级可能不一致。</li>
<li><b>F2 死赋值</b>:UpperCommManager.cpp:240 把 <code>getFaultCodes()</code> 赋给 state,但 <code>uPower_pmState.h:41-73 buildMsg()</code> 忽略该赋值,改用 <code>data.getFaultCode()</code>。</li>
<li><b>F3 映射脱节</b>:故障码→中文描述的映射 C++ 侧仅用于注入,上报只发数字码;显示文本由前端 JS 重写一份(httpserver/fuelcell.h:834-840);0xLSSS 系统故障码<b>没有任何文本映射</b>。</li>
<li><b>F4 条件编译陷阱(经核实比初版更严重)</b>:<code>DEBUG</code> 宏在全部构建中均未被定义(CMake 无 <code>-DDEBUG</code>),而 systemData.h:776 的 <code>#ifdef DEBUG</code> 依赖它——因此燃料电池 4 级故障字 <code>fc_fault_level_1..4</code> 在<b>所有构建</b>下都永不更新(恒为 0),并非仅"发布版";systemData.h:7 的 <code>#define DEBUFG</code> 是拼写错误且定义了无用宏。同理 <code>#ifdef _DEBUG</code>(UpperCommManager.cpp 全篇)在 Linux 下也永不生效,其调试追踪成员均为死代码。</li>
<li><b>F5 无故障事件持久化</b>:SQLite 仅随帧存原始故障字段,无独立故障事件表,故障发生/恢复时间不可追溯,无法支撑事后分析。</li>
</ul>
<h3 id="s3-5">3.5 数据管理与并发</h3>
<ul>
<li><b>D1 全局数据池</b>:SystemData(systemData.h,1219 行头文件内实现)是百字段单例,字段基本是 pmSysvariable 协议镜像的 float 转换副本,<code>updateSystemData()</code> 逐字段拷贝数百行——协议每改一处需同步三处(pmSysvariable / systemData / updateSystemData)。构造函数 60+ 项手工初始化(:660-690)。</li>
<li><b>D2 锁纪律不一致</b>:提供了部分加锁访问器(:694-733),但 FSM 大量裸读绕过锁(PowerManagerFsm.cpp:1813/1920/1930);TestEvent 直接无锁写 <code>fc_fault_level_1</code>(:3060);<code>msg_*_Update_time</code> 无锁写、另一把锁下读(LowerCommManager.cpp:229-233)。</li>
<li><b>D3 整数截断风险</b>:对 double 使用未限定的 <code>abs()</code>(systemData.h:757),可能解析为 C 的 <code>int abs</code> 截断时间差,应使用 <code>std::fabs</code>。</li>
<li><b>D4 双重时间基准</b>:commLoop 每 1 s <code>clearMsgQueue()</code>(LowerCommManager.cpp:124)与监听线程并发清队列,语义冗余且可能丢未消费帧。</li>
</ul>
<h3 id="s3-6">3.6 上位机与 Web 服务</h3>
<p>上位机 HostSim 的设计相对干净:网页指令经 WebSocket 入 <code>m_cmdQueue</code>、Iterate 中 <code>drainCommands()</code> 转发 MOOSDB(HostSim.cpp:213-228);上行订阅 12 个反馈变量,dirty 后 <code>broadcast(buildSnapshot())</code> 推送,并以 SQLite 表 <code>fb_log</code> 保留 24 h 历史(FeedbackStore.cpp:62-69),提供 <code>/api/history</code>、<code>/api/rawlog</code>。可作为重构时"松耦合桥"的保留样板。</p>
<ul>
<li><b>H1 功能重叠</b>:与下位机 httpserver 在状态监视上重复;建议下位机 Web 退化为纯调试用途(仅本机回环监听),状态监视统一归上位机。</li>
<li><b>H2 越权通道</b>:下位机 /fcs/control 直改内部状态,破坏"一切控制经 MOOS 指令"的单入口原则,应迁移为向 MOOSDB 发指令或加鉴权。</li>
<li><b>H3 页面硬编码</b>:1912 行 data.h 等 7 个页面以 <code>R"(...)"</code> 字符串嵌在头文件,无构建期资源管线(上位机已有 webassets_gen.h 方案可复用)。</li>
</ul>
<h3 id="s3-7">3.7 构建工程与配置管理</h3>
<ul>
<li><b>E1 编译告警默认关闭、无 sanitizer 门禁</b>:CMakeLists 仅加 <code>-fPIC -g -Wdeprecated-declarations</code>,<code>-Wall</code> 需手动开 WALL_ON 且写法含语法错误(<code>"-Wall" -C++11</code>,CMakeLists.txt:86);Release 依赖 CMake 默认 <code>-O3</code> 未显式声明;无 ASan/UBSan/TSan 构建类型。建议:开启 <code>-Wall -Wextra</code> 并清零告警,新增 sanitizer 构建目标,测试二进制输出到 build 目录。</li>
<li><b>E2 依赖不可复现、GLOB 反模式</b>:<code>FILE(GLOB ...)</code> 收集头/库目录(CMakeLists.txt:49/53),新增文件不触发重配;mongoose/sqlite3/loguru/jsoncpp 以源码 vendor 进 src 且无版本号与许可证记录;<code>CMAKE_MINIMUM_REQUIRED(VERSION 3.5)</code> 过老。建议 GLOB 改显式列表,第三方库固定版本并登记 LICENSE。</li>
<li><b>E3 配置解析无校验</b>:OnStartUp 用 <code>atoi</code> 解析 ccuport/iport(PowerManger.cpp:229/233),非法值静默为 0,端口无 1-65535 范围检查;缺配置块仅 warning。建议显式校验非法值并告警。</li>
<li><b>E4 SQLite 高频路径重复 prepare</b>:<code>insertGeneric</code> 每次插入重拼 SQL 并 prepare(SQLite.cpp:610-625),<code>onFrame</code> 每帧同样 prepare;建议缓存 prepared statement,故障事件表纳入 SCHEMA_VERSION 迁移机制。</li>
<li><b>E5 日志/错误输出三套并存</b>:loguru、<code>std::cerr</code>(driver.cpp:887/904/957)、<code>std::cout</code>(LowerCommManager.cpp:373/381)混用,无法按级别关停。建议统一 loguru,cout/cerr 清零。</li>
</ul>
<!-- ================= 4 ================= -->
<h2 id="s4">4 架构优化方案</h2>
<p>优化总原则:<b>外部特性严格不变</b>(MOOS 变量名与 JSON 契约、UDP 帧格式、Web 接口、故障码取值、状态迁移外部可观测序列均保持),<b>内部结构彻底重整</b>。所有重构以第 6 章的一致性保障与第 7 章的测试基线为前提。</p>
<h3 id="s4-1">4.1 目标分层架构</h3>
<div class="card">
<pre><code>┌─────────────────────────────────────────────────────────────┐
│ 接口层 (Interface) UpperCommManager │ HttpServer(调试) │ ← 只做协议适配,不含业务判断
├─────────────────────────────────────────────────────────────┤
│ 应用层 (Application) PowerManagerApp │ ← 协调者:指令→校验→派发到领域服务
│ ├─ CommandService(指令接收、校验、应答) │
│ ├─ FaultManager (故障检测、锁存、上报、事件持久化) │
│ └─ StateMachineService(工况状态机,表驱动 + guard) │
├─────────────────────────────────────────────────────────────┤
│ 领域层 (Domain) DeviceRegistry │ PowerPolicy(Driver表) │ ← 纯 C++,无 MOOS/网络依赖,可单测
│ SystemState(线程安全的状态仓储) │
├─────────────────────────────────────────────────────────────┤
│ 基础设施层 (Infra) UdpLink(收发) │ ProtocolCodec(编解码) │ ← 唯一知道帧格式/JSON键名的地方
│ SQLiteStore │ MoosGateway │
└─────────────────────────────────────────────────────────────┘</code></pre>
</div>
<ul>
<li><b>依赖方向单向化</b>:接口层 → 应用层 → 领域层 ← 基础设施层(依赖倒置)。状态机不再持有 <code>PowerManger*</code> 裸指针,改为构造时注入 <code>ISystemState</code>、<code>IDeviceCommand</code>、<code>IFaultSink</code> 三个窄接口。</li>
<li><b>线程模型统一</b>:确立"单线程事件循环 + 生产者队列"模型——UDP 监听线程、HTTP 线程只做<b>收包→解析→投递事件队列</b>,所有业务处理(FSM dispatch、故障检测、状态写入)在 MOOS Iterate 所在线程串行执行,从根上消除数据竞争。断路器闭环操作改为异步状态(发送→等待反馈事件→超时事件),消灭 7 s 忙等。</li>
<li><b>Driver 拆分</b>:模式设备策略表(领域层 PowerPolicy)与 JSON 键名/设备 ID 映射(基础设施层 ProtocolCodec)分离;Driver 单例化并归属 CommandService,消除双实例不一致。</li>
</ul>
<h3 id="s4-2">4.2 状态机重构方案</h3>
<h4>4.2.1 转移表显式化</h4>
<p>将隐藏在两个 switch 中的转移逻辑提取为<b>静态转移表 + guard 函数</b>,作为唯一事实源,并据此生成文档与测试用例:</p>
<pre><code>// 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, "新增:故障确认后恢复" },
...
};</code></pre>
<p>guard 第一阶段全部保持现状(无条件),仅把"可迁移性"显式化;第二阶段再与总体组确认后补充工况互斥规则(如 HighSpeed 与 DJMode 互斥需先回 Standby)。</p>
<h4>4.2.2 FaultState 与故障系统联动</h4>
<ol>
<li><b>入口</b>:FaultManager 检测到 3 级及以上故障(或抛载任务)→ 投递 <code>FaultEvent</code> → FSM 迁移 FaultState,保持现有"抛载进 FaultState"路径不变。</li>
<li><b>entry 动作异步化</b>:断路器断开序列改为 FaultState 内部的子步骤计时状态(step + deadline),每拍 Iterate 推进,取代 MOOSPause(10000) 与 20 s 忙等;超时阈值统一为命名常量并修正注释。</li>
<li><b>出口</b>:显式定义恢复路径——故障码集合清空且收到上位机确认指令(workCMD=4 RemoteControl,保持现有指令集不变)→ 退出 FaultState。该路径当前已可经基类 handleWorkCmd 间接实现,重构只是将其显式化并纳入转移表,<b>不改变外部行为</b>。</li>
</ol>
<h4>4.2.3 代码组织</h4>
<ul>
<li>PowerManagerFsm.cpp 拆分:<code>DeviceCommandHandlers.cpp</code>(10 个设备指令函数,提取公共模板消除重复)、<code>StatusSnapshot.cpp</code>(10 个 getXxxStatus)、<code>FaultEncoding.cpp</code>(故障编码,配合 4.4 迁移到 FaultManager)。</li>
<li>14 个状态类删除重复的 <code>react(MasterCommandEvent)</code>,统一由基类按转移表处理;状态类只保留 entry/exit 与状态特有事件。</li>
<li>命名修正(handelPowerCmd→handlePowerCmd 等)仅在内部接口进行,MOOS 变量名、JSON 键名等外部标识<b>一律不改</b>。</li>
</ul>
<h3 id="s4-3">4.3 通信层重构方案</h3>
<h4>4.3.1 协议单一事实源</h4>
<p>建立 <code>protocol/</code> 目录,以一份定义文件同时生成/校验编解码两侧,杜绝键名失配(B4)类问题:</p>
<ul>
<li><code>protocol/MoosVariables.def</code>:全部 MOOS 变量名 + JSON schema(键名、类型、取值范围),UpperCommManager 与 Driver 的 JSON 编解码均引用之;增加"未知键/解析失败"统一告警(修复 C3、B4 的静默丢失)。</li>
<li><code>protocol/DeviceId.h</code>:设备 ID 唯一枚举,driver.cpp:908-930 的 0-61 硬编码与 DeviceId 枚举合并;UDP 帧长由结构体 sizeof 计算,删除 18/6/8 魔法数。</li>
<li>修正 udpComm.cpp:47-56 等价死分支与 default 静默吞帧:未知帧计入统计并日志告警。</li>
</ul>
<h4>4.3.2 上下行链路管理</h4>
<table>
<tr><th>链路</th><th>现状</th><th>优化后</th><th>外部行为变化</th></tr>
<tr><td>上位机(MOOS)</td><td>无连接管理,1 s 周期广播状态</td><td>启用已声明未使用的 connectState:以"周期广播正常发出 + 收到上位机任何邮件"维护在线标志;掉线仅记录与上报,不触发控制动作</td><td>无(只新增可观测性)</td></tr>
<tr><td>下位机(UDP)</td><td>30 s 超时置 isTimeout,超时母线发清零指令</td><td>超时事件接入 FaultManager(通信类故障码,新增码段,不占用现有码值);清零指令逻辑保持不变</td><td>故障上报新增通信超时码(经总体确认后启用)</td></tr>
<tr><td>断路器闭环</td><td>事件路径忙等约 7 s + 600 ms 复位</td><td>异步化:发送→注册期望反馈→超时定时器,状态机以子状态跟踪;指令时序与重发次数严格保持现状</td><td>无(时序一致,仅不再阻塞线程)</td></tr>
<tr><td>Web 通道</td><td>下位机 8000 全功能 + /fcs/control 越权直改</td><td>下位机 Web 保留页面但控制类接口改为转发 MOOS 指令(等价于上位机下发);上位机 Web 方案不变</td><td>接口路径与参数不变,仅内部实现改道</td></tr>
</table>
<h4>4.3.3 工程细节修复</h4>
<ul>
<li>发送路径消除每帧堆分配(栈对象 + sendto);删除每包 cout(LowerCommManager.cpp:381),统一走 loguru 并按级别可关。</li>
<li>UDP 监听线程退出改为 socket 超时 + quit 标志,保证进程可干净退出。</li>
<li>移除 <code>sendDisSysCommand</code> 废弃函数(含 0x55/0xAA 硬编码),或修正后纳入 ProtocolCodec。</li>
</ul>
<h3 id="s4-4">4.4 故障码统一方案</h3>
<div class="card">
<h4>目标:一套编码、一个存储、一处映射</h4>
<p><b>1. 统一故障码定义文件 <code>fault/FaultCodeDef.h</code></b>(生成式,唯一事实源):</p>
<pre><code>// 每条故障一行:枚举名, 码值(保持现有取值不变), 等级, 来源域, 中文描述
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 个字面量全部收编为枚举,码值、等级语义与现状逐条核对一致</code></pre>
<p>由此单文件自动生成:C++ 枚举、码值→描述映射表(供上报与前端共用,替代 fuelcell.h:834 的 JS 重写)、测试用的完整性校验数据。电池 0x01-0xC9、配电 1-45、系统 0xLSSS 三段码值全部原样保留,<b>上位机与前端所见故障码不变</b>。</p>
<p><b>2. 合并故障存储为 FaultManager 单一集合</b>:</p>
<ul>
<li>统一存放真实故障与注入故障(注入故障带 source 标记区分),上报接口(uPower_pmState)与等级查询(getFaultLevel)改为读同一集合,消除 F1/F2 的双源不一致;对外 JSON 字段名与格式不变。</li>
<li>锁存语义保持现状(自恢复即自动清除),但 FaultManager 内部记录"发生/恢复"事件并写入 SQLite 新增 <code>fault_event(ts, code, level, event, source)</code> 表——<b>新增表不影响现有表结构</b>。</li>
<li>故障码访问统一加 <code>faultCodeMutex</code>,消除 TestEvent 无锁写。</li>
</ul>
<p><b>3. 修复编码层缺陷</b>:删除 systemData.h:776 的 <code>#ifdef DEBUG</code>(发布版也更新燃料电池 4 级故障字)、修正 DEBUFG 拼写、补齐 B6 三个断路器的故障码入参、faultCode.h:35 的 level 字段与描述对齐。</p>
</div>
<h3 id="s4-5">4.5 上位机 / 下位机管理方案</h3>
<table>
<tr><th>主题</th><th>职责划分(优化后)</th></tr>
<tr><td>控制通道</td><td><b>唯一入口</b>:一切控制指令(网页、调试页、测试注入)最终都变为 MOOSDB 上的 <code>uPower_*_cmd</code> JSON,由 CommandService 统一校验、应答、记录。下位机 HTTP 控制接口仅做转发。</td></tr>
<tr><td>状态监视</td><td>上位机 HostSim 为主(跨机、带历史库 /api/history);下位机 Web 退化为本机调试页(日志查看、测试注入保留),dashboard/data 等监视页标注"调试用"。</td></tr>
<tr><td>指令校验</td><td>CommandService 对 workCMD/powerCMD/modCMD 做范围校验 + 当前状态下合法性校验(按转移表),非法指令回执拒绝原因(新增 JSON 字段,缺省不发送,兼容旧上位机)。</td></tr>
<tr><td>指令清空哨兵</td><td>workCMD=99 等魔法哨兵改为命名常量 <code>CMD_NONE=99</code>,取值不变,仅消除魔法数。</td></tr>
<tr><td>页面资源</td><td>下位机 7 个硬编码页面迁移到 CMake 资源管线(复用上位机 webassets_gen.h 方案),HTML 独立成文件,便于前端维护;URL 与页面功能不变。</td></tr>
</table>
<!-- ================= 5(新增详细设计章) ================= -->
<h2 id="s5">5 设备上电管理与状态机转移重构详细设计</h2>
<h3>5.1 总体设计判断</h3>
<p>设备上电/管理与状态机转移是同一个问题的两面——<b>"声明"与"执行"混在一起</b>。9 张工况设备表(driver.cpp 约 600 行逐字段赋值)本质是"数据",却写成了代码;状态 entry() 里的上电时序本质是"流程",却硬编码成一串串 <code>setDeviceState + MOOSPause + operate_*_Breaker</code>;状态转移本质是"一张表",却隐式埋在 <code>handleWorkCmd</code>/<code>react(TaskStartEvent)</code> 两个 switch 里。重构的共同方向:<b>把数据从代码里剥离出来,把流程交给统一执行器,把转移显式化</b>。</p>
<h3>5.2 设备上电与管理重构</h3>
<h4>5.2.1 现状代码证据</h4>
<ul>
<li><b>工况表即代码</b>:<code>Driver::getSTANDBYTable()</code>(driver.cpp:151-228)等 9 个函数,每个约 70 行 <code>driverTable->depth1 = 1;</code> 式逐字段赋值,<code>getCRUISE/getHIGH_SPEED/getASCEND_DESCEND</code> 内容几乎一致,且混用裸 0/1 与 <code>DRIVER_*</code> 宏。</li>
<li><b>上电时序硬编码且阻塞</b>:<code>CruiseMode::entry()</code> 的典型模式是:查当前设备状态 → <code>operate_HVA_actuatorCircuit_Breaker(0)</code>(内部忙等最长 6 s)→ <code>MOOSPause(3)</code> → 连续 6 次 <code>setDeviceState</code> → 上报,每个状态 entry 都是这段逻辑的变体手写。</li>
<li><b>断路器闭环时序散落</b>:<code>operateBreakerGeneric</code>(LowerCommManager.cpp:843-890)的"首次发包 → 100 ms 等待 → 轮询反馈 → 200 ms 重发 → 6 s 超时 → 3×200 ms 复位"被 35 个 <code>operate_*</code> 函数复用,但在事件处理路径上同步执行,阻塞 MOOS 线程。</li>
<li><b>校验逻辑十份复制</b>:<code>WaterBasedReady::react(DevCmdExt)</code> 中的校验链(Debug 模式豁免 → 深度上电保护 → 前视声纳先关机再断电并 MOOSPause(10000) → 写设备状态)在 10 个 <code>handleXxxCmd</code> 中各有变体,细微偏差正是 B4(桅杆键名失配)类缺陷的温床。</li>
</ul>
<h4>5.2.2 目标结构</h4>
<div class="card">
<pre><code>现状(分散) 目标(收敛)
┌──────────────────────────────┐ ┌────────────────────────────────┐
│ 9 张工况表 = 600 行赋值函数 │ │ ① 设备描述表 DeviceDef │
│ entry() 硬编码时序+忙等6~20s │ 收敛 │ 68 设备 × 属性(域/断路器通道/ │
│ 10 个 handleXxxCmd 校验复制 │ ───────────► │ 保护规则/9 工况默认值) │
│ 35 个 operate_*_Breaker │ │ ② 功率时序执行器 PowerSequencer │
└──────────────────────────────┘ │ 声明式步骤,异步分步执行 │
│ ③ 统一指令管线:模式校验→深度 │
│ 保护→前置动作→执行→回执 │
│ ④ DeviceManager 唯一写口 │
│ m_subDisSysCmd 只经它修改 │
└────────────────────────────────┘</code></pre>
</div>
<h4>5.2.3 重构四件事</h4>
<ol>
<li><b>工况表从函数变成数据。</b>9 个 <code>getXxxTable()</code> 函数替换为静态二维表 <code>kModeDeviceTable[9][68]</code>(或按设备分组的声明式定义),查表替代函数调用。迁移时用程序把现有 9 个函数的输出快照为 golden data,重构后逐字节比对——这是"特性严格一致"最直接的保证。</li>
<li><b>上电时序从阻塞硬编码变成声明式步骤 + 异步执行器。</b>每个工况声明一个步骤序列,例如 CruiseMode:<code>[条件:任一bowCover≠0] → 断启闭机构断路器(超时6s) → 延时3s → 批量下电bowCover1~6 → 上报</code>。PowerSequencer 每拍 Iterate 推进一步,<code>operateBreakerGeneric</code> 的时序参数(100 ms 首等、200 ms 重发、6 s 超时、3×200 ms 复位)原样保留为步骤属性,但不再阻塞线程;entry() 只提交序列即返回,状态机始终可响应事件。</li>
<li><b>设备指令校验收敛为一条管线。</b>所有设备指令走同一链路:模式校验(Normal 禁操作)→ 域保护(水下设备深度检查,Debug 豁免)→ 设备专属前置动作(forwardSonar 先关机再断电等,注册在设备描述表)→ 经 Sequencer 执行 → 回执。各状态只声明"本状态允许操作哪些设备域",不再各写一份校验。</li>
<li><b>设备状态唯一写口。</b><code>m_subDisSysCmd</code> 现在被 FSM、HTTP 回调、测试注入直接修改,重构后只能经 DeviceManager 写入;外部可观测的写序列(日志、上报、SQLite 落库)保持一致。</li>
</ol>
<h3>5.3 状态机转移重构</h3>
<h4>5.3.1 核心问题:转移关系隐式化</h4>
<p>转移关系不存在于任何一张表中,而是从两个 switch 的行为里"涌现"出来的——这正是 case 12 漏写 <code>transit</code> 导致 Lifting 不可达(B1)这类缺陷的结构性根源。重构目标是把转移机制从"隐式 switch"变为"显式表驱动":</p>
<div class="card">
<pre><code>现状:隐式转移 目标:显式转移
MasterCommandEvent 到达 事件统一规范化
│ │
handleWorkCmd 巨型 switch 查转移表 TransitionRule[]
(14 状态 × 14 份复制 react) (源状态×事件→目标,唯一事实源)
│ │
无 guard 直接 transit guard 校验
(case 12 漏写 = Lifting 死状态) (一期空 guard 记日志,二期补互斥)
│ │
entry() 阻塞上电 CHANING 过渡子状态
(期间 FSM 不响应任何事件) (上电序列异步执行,完成即落定)
│ │
FaultState 孤岛 FaultEvent 显式迁移
(故障检测不触发迁移,无出口) (3 级以上故障进入,确认后退出)</code></pre>
</div>
<h4>5.3.2 重构五步走</h4>
<ol>
<li><b>把转移关系从代码里"挖"出来变成表。</b>逐行核对 <code>handleWorkCmd</code>(workCMD 1~12)、<code>react(TaskStartEvent)</code>(THROW_LOAD/FLOAT_UP/RECYCLE/SAIL/HOVER/TOUR 等)、<code>react(TaskStopEvent)</code> 与 InitState 自迁移,生成 <code>TransitionRule[]</code> 静态表,每行标注原代码位置(如 <code>workCMD=5 → CruiseMode, Fsm.cpp:2903</code>)。该表同时是实现、文档与测试基准。<code>handleWorkCmd</code> 的 switch 删除,14 个状态中复制粘贴的 <code>react(MasterCommandEvent)</code> 全部删除,统一由基类查表处理;workCMD=99 清空哨兵语义保留,仅改为命名常量。</li>
<li><b>guard 分两期落地。</b>一期所有 guard 为 nullptr(无条件),<b>严格保持现状转移语义</b>——这是特性一致的红线;基类在每次迁移时记录"源状态→事件→目标"日志。二期依据日志与总体组确认应禁止的迁移(如巡航中直接切 DJMode),再逐步补 guard。重构本身不引入行为变化,行为收紧是另一个独立、可评审的决策。</li>
<li><b>entry() 阻塞上电改为 CHANING 过渡子状态。</b>现状代码已有雏形——每个 entry 开头 <code>workCondition = CHANING</code>、干完活才置为目标工况,只是"干活"方式是阻塞的。重构将其正式化:迁移发生时进入目标状态的过渡形态,上电序列交 PowerSequencer 异步执行,序列完成事件使状态落定;期间 FSM 照常响应事件(如 FaultEvent 可打断上电)。上位机看到的 workCondition 变化序列与现状完全一致。</li>
<li><b>FaultState 接入主转移图。</b>故障检测发现 3 级以上故障 → FaultManager 派发 FaultEvent → 转移表新增 <code>ANY × FaultEvent → FaultState</code> 规则(保留现有 THROW_LOAD 入口不变);FaultState 增加显式出口:故障码集合清空 + 上位机确认(复用现有 workCMD=4 指令,不新增协议)→ RemoteControl;FaultState 内的断路器断开序列同样改为 Sequencer 步骤,消灭 20 s 忙等。</li>
<li><b>状态类只做"差异"。</b>状态类只保留本状态特有内容:entry 提交的上电序列、特有事件处理(如 DJMode 专属指令)、exit 清理;公共行为上收基类。CruiseMode/HighSpeedMode 等任务态的共性(均响应 TaskStopEvent→RemoteControl)通过继承 MissionStateBase 表达,利用 tinyfsm 原生支持的状态继承。</li>
</ol>
<h3>5.4 两块重构的衔接与线程模型</h3>
<div class="note">
衔接点在 CHANING 子状态:<b>状态机管"去哪里",PowerSequencer 管"到了之后按什么时序上电",DeviceManager 管"每一笔设备状态怎么写"</b>。三者通过事件队列串行协作,全部运行在 MOOS 主线程上,从根上消除现有四个线程交叉读写全局数据的问题。UDP 监听线程与 HTTP 线程退化为纯生产者:收包→解析→投递事件队列,不再直接触碰业务状态。
</div>
<h3>5.5 迁移顺序与一致性验证</h3>
<ol>
<li><b>快照基线</b>:采集 9 张工况表输出、标准指令脚本下的状态上报时序,作为 golden data;</li>
<li><b>设备描述表与指令管线</b>(风险最低,先行);</li>
<li><b>PowerSequencer 替换阻塞上电</b>;</li>
<li><b>转移表替换 switch</b>。</li>
</ol>
<p>每一步均以 golden data diff 为空为通过条件,diff 非空则不进入下一步。B1(Lifting 不可达)等已确认缺陷在挖掘转移表时即会暴露,按第 6 章约定单独登记为行为变更点修复。</p>
<!-- ================= 6 ================= -->
<h2 id="s6">6 特性一致性保障策略</h2>
<p>"优化后与原代码特性严格一致"需要通过可执行的基线来保证,而不是靠评审印象。建议按以下四层契约冻结外部行为:</p>
<table>
<tr><th>契约层</th><th>冻结内容</th><th>验证手段</th></tr>
<tr><td>MOOS 接口契约</td><td>全部订阅/发布变量名、JSON 键名与类型、周期广播频率(1 s)、应答时序</td><td>契约测试:录制现有系统真实 MOOS 流量为"黄金报文集",重构后回放比对(键集合、类型、取值范围)</td></tr>
<tr><td>UDP 帧契约</td><td>6 类反馈帧 ID、帧头 0x40 0x40、#pragma pack(1) 布局、校验算法、帧长</td><td>字节级编解码往返测试(现有 udp_feeder/full_feeder 扩展为参数化用例);结构体 static_assert 尺寸断言</td></tr>
<tr><td>行为契约</td><td>状态迁移外部可观测序列(指令→状态上报序列)、断路器操作时序(重发间隔、6 s 超时、600 ms 复位)、故障码取值与上报内容</td><td>黄金主测试:对现状系统跑标准指令脚本,录制 <code>uPower_currentState_st</code> 等上报时序为基线;重构后同脚本回放,时序逐拍比对</td></tr>
<tr><td>Web/存储契约</td><td>URL 路由、REST 参数、SQLite 现有表结构</td><td>HTTP 接口冒烟用例;数据库 schema 比对脚本</td></tr>
</table>
<div class="note">
<b>已知缺陷的处理约定</b>:3.1 节 B1-B10 属"现状特性"的一部分还是"应修复的缺陷",须逐项与总体确认。建议原则:外部可观测且显然错误的行为(如 buoyage 恒不更新、Lifting 不可达、桅杆指令丢失)按缺陷修复并在报告中显式登记"行为变更点";纯内部问题(重复调用、死代码)直接修复,行为不变。所有行为变更点单独列入变更清单,重构完成时逐项回归确认。
</div>
<!-- ================= 7 ================= -->
<h2 id="s7">7 测试方案</h2>
<h3>7.1 测试金字塔与补齐重点</h3>
<table>
<tr><th>层级</th><th>现状</th><th>补齐内容</th><th>框架/工具</th></tr>
<tr><td>单元测试</td><td>GTest 56 例:Driver 表、SystemData、故障码表、upmsg 往返、UDP 校验和、pack 布局</td><td>① 状态机迁移测试:转移表全覆盖(每个规则至少 1 例)+ guard 边界;② FaultManager:检测→锁存→上报→清除全链路,含 B6/B7 回归;③ ProtocolCodec:JSON 键名完整性(防 B4 复发)、帧编解码参数化</td><td>GTest;领域层零 MOOS 依赖后可脱离 MOOSDB 运行</td></tr>
<tr><td>集成测试</td><td>udp_feeder/full_feeder 字节级仿真 CCU+4 路配电 + check_db.py 验证落库</td><td>① 故障注入 feeder:丢包、坏校验和、越界值、故障位置位、30 s 静默(超时路径);② MOOS 端到端:上位机指令→状态迁移→上报序列黄金比对(第 6 章基线);③ 断路器闭环时序仿真</td><td>Python 脚本 + 现有 feeder 扩展;MOOSDB 冒烟沿用</td></tr>
<tr><td>系统/回归</td><td>CI 有构建+冒烟,但集成失败不阻塞(build-test.sh:181),已知 std::bad_alloc 被容忍</td><td>① 集成失败纳入 ALL_OK 阻塞;② cppcheck/clang-tidy 接入 CI(error 级清零);③ 测试二进制输出改到 build 目录,清理 test/ 下 in-source 构建残留</td><td>Gitea Actions 现有流水线扩展</td></tr>
<tr><td>上位机测试</td><td>零</td><td>HostSim 指令队列 drain、FeedbackStore 落盘/24 h 清理、/api/history 时序提取、WS 快照广播</td><td>GTest(队列/存储)+ Python(HTTP/WS 接口)</td></tr>
</table>
<h3>7.2 关键专项测试</h3>
<ul>
<li><b>状态机迁移矩阵</b>:以 4.2.1 转移表为基准自动生成用例骨架——14 状态 × 11 事件全组合标注"允许/拒绝/保持",重构前后两轮运行,输出 diff 必须为空(除已登记的行为变更点)。</li>
<li><b>故障注入矩阵</b>:覆盖三级来源——UDP 层(坏帧/丢帧/超时)、设备层(断路器脱扣、绝缘低、BMS 各级故障位)、系统层(深度失效、功率不足等 0xLSSS 码);断言故障码上报内容、FaultState 迁移、SQLite fault_event 落库三处一致。</li>
<li><b>并发压力</b>:UDP 监听线程 100 Hz 灌包 + MOOS 指令并发注入,ThreadSanitizer 构建跑 10 min,数据竞争报告清零;队列上限 100 的溢出行为固定为"丢最旧 + 计数告警"并测试。</li>
<li><b>长稳</b>:feeder 连续 24 h 正常流量 + 周期故障注入,监控内存(消除每帧 new/delete 后应无增长)、句柄、落库行数。</li>
</ul>
<h3>7.3 验收标准</h3>
<ol>
<li>黄金报文比对:MOOS/UDP 契约测试 100% 通过,diff 为空;</li>
<li>状态迁移矩阵:除已登记变更点(B1 修复后 Lifting 可达等)外零差异;</li>
<li>新增单测覆盖率:fsm/、FaultManager、ProtocolCodec 行覆盖 ≥ 80%;</li>
<li>cppcheck error 级清零,TSan 报告清零;</li>
<li>CI 全链路(构建→单测→集成→黄金比对)绿灯且失败阻塞。</li>
</ol>
<!-- ================= 8 ================= -->
<h2 id="s8">8 实施路线图</h2>
<div class="phase">
<h4>阶段 0:缺陷修复与测试基线(先行,约 2 周)</h4>
<p class="small">最小改动修复 B1-B14 并逐项登记行为变更;开启 <code>-Wall -Wextra</code> 清零告警、接入 sanitizer 构建目标与 cppcheck(error 级阻塞);配置项端口校验;搭建黄金报文基线与状态迁移矩阵录制工具。<b>产出:行为基线库 + 变更清单 + 可运行的安全网。</b></p>
</div>
<div class="phase">
<h4>阶段 1:故障码与数据层统一(约 2-3 周)</h4>
<p class="small">落地 FaultCodeDef.h 生成式定义;合并双故障存储为 FaultManager;新增 fault_event 持久化表;修复条件编译与死码。该阶段相对独立、风险最低,先行可立即改善可维护性。<b>产出:统一故障子系统 + 故障注入测试矩阵。</b></p>
</div>
<div class="phase">
<h4>阶段 2:通信层与线程模型(约 3-4 周)</h4>
<p class="small">建立 protocol/ 单一事实源;UDP 监听线程改为纯生产者;断路器闭环异步化;连接状态管理启用;下位机 HTTP 控制接口改道 MOOS。<b>产出:单线程事件循环模型 + 契约测试全绿。</b></p>
</div>
<div class="phase">
<h4>阶段 3:状态机重构(约 3-4 周)</h4>
<p class="small">转移表显式化;PowerManagerFsm 上帝类拆分;状态类去重;FaultState 出入路径显式化;entry 阻塞消除。每步以迁移矩阵 diff 为空为准入。<b>产出:表驱动状态机 + 迁移矩阵零差异报告。</b></p>
</div>
<div class="phase">
<h4>阶段 4:上位机与收尾(约 2 周)</h4>
<p class="small">上位机测试补齐;下位机页面资源管线迁移;长稳与并发压力测试;文档更新(转移表自动生成状态图)。<b>产出:完整测试体系 + 验收报告。</b></p>
</div>
<div class="warn">
<b>风险控制</b>:每阶段结束必须 CI 全绿(含黄金比对)方可合并;协议/故障码/状态序列的任何外部可见变更必须已在变更清单登记并经确认;保持 pCCU、pMotor、pELoad 等周边进程接口不动,重构范围严格限定在 pPowerManger 与 pPowerMangerHost。
</div>
<!-- ================= 9 ================= -->
<h2 id="s9">9 附录:问题清单汇总</h2>
<table>
<tr><th>编号</th><th>级别</th><th>类别</th><th>问题摘要</th><th>位置</th><th>处置</th></tr>
<tr><td>B1</td><td><span class="badge p0">P0</span></td><td>Bug</td><td>Lifting 状态不可达(case 12 无 transit/break)</td><td class="loc">PowerManagerFsm.cpp:2934</td><td>阶段0修复(行为变更登记)</td></tr>
<tr><td>B2</td><td><span class="badge p0">P0</span></td><td>Bug</td><td>s.buoyage == 赋值误写为比较 ×4</td><td class="loc">PowerManagerFsm.cpp:1471-1486</td><td>阶段0修复(行为变更登记)</td></tr>
<tr><td>B3</td><td><span class="badge p0">P0</span></td><td>Bug</td><td>邮件偏斜 return 导致整批丢弃</td><td class="loc">PowerManger.cpp:121-125</td><td>阶段0修复</td></tr>
<tr><td>B4</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>桅杆升降舵 JSON 键名失配,指令静默丢失</td><td class="loc">driver.cpp:844/924</td><td>阶段0修复(行为变更登记)</td></tr>
<tr><td>B5</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>故障检测函数重复调用两遍</td><td class="loc">PowerManagerFsm.cpp:52-55</td><td>阶段0修复</td></tr>
<tr><td>B6</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>3 个断路器故障码死码</td><td class="loc">systemData.h:1026-1028</td><td>阶段0修复</td></tr>
<tr><td>B7</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>故障注入数组越界 + systemData.h:524 越界</td><td class="loc">PowerManagerFsm.cpp:3053</td><td>阶段0修复</td></tr>
<tr><td>B8</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>TestEvent switch 缺 break;绕过事件机制</td><td class="loc">PowerManagerFsm.cpp:3084</td><td>阶段0修复</td></tr>
<tr><td>B9</td><td><span class="badge p2">P2</span></td><td>Bug</td><td>协议结构体未初始化返回 / memset 非 POD</td><td class="loc">upmsg/ 多处</td><td>阶段0修复</td></tr>
<tr><td>B10</td><td><span class="badge p2">P2</span></td><td>规范</td><td>超时常量与注释不符(FaultState 20s vs 其余 5s),复制 7 份</td><td class="loc">FaultState.cpp:46/53 等</td><td>阶段3统一</td></tr>
<tr><td>B11</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>模板残留致每条正常指令误报 "Unhandled Mail" run warning</td><td class="loc">PowerManger.cpp:142-146</td><td>阶段0修复</td></tr>
<tr><td>B12</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>畸形 JSON 类型致 jsoncpp 抛异常(asUInt 无类型检查、无 catch)</td><td class="loc">driver.cpp:935 / UpperCommManager.cpp:48</td><td>阶段0修复</td></tr>
<tr><td>B13</td><td><span class="badge p1">P1</span></td><td>Bug</td><td>HTTP 服务在配置/DB 就绪前于构造函数启动,提前暴露控制面</td><td class="loc">PowerManger.cpp:83-87</td><td>阶段0修复</td></tr>
<tr><td>B14</td><td><span class="badge p2">P2</span></td><td>规范</td><td>FsmLoop/_FsmCB、m_deviceCmdQuenue 无消费者;buildReport 占位;SKEW_TOLERANCE 宏未用</td><td class="loc">PowerManger.h:48/121 等</td><td>阶段3清理</td></tr>
<tr><td>A1</td><td><span class="badge p0">P0</span></td><td>架构</td><td>状态机退化为路由壳,转移逻辑集中在基类 switch</td><td class="loc">fsm/ 全局</td><td>阶段3重构</td></tr>
<tr><td>A2</td><td><span class="badge p0">P0</span></td><td>架构</td><td>故障检测与 FaultState 脱钩;FaultState 无出口</td><td class="loc">PowerManagerFsm.cpp:948-953</td><td>阶段1+3</td></tr>
<tr><td>A3</td><td><span class="badge p1">P1</span></td><td>架构</td><td>entry() 内 MOOSPause/忙等最长 10-20 s</td><td class="loc">WaterBasedReady.cpp:50 等</td><td>阶段3异步化</td></tr>
<tr><td>A4</td><td><span class="badge p1">P1</span></td><td>架构</td><td>3096 行上帝类;static pm 裸指针全局穿透</td><td class="loc">PowerManagerFsm.hpp:86</td><td>阶段3拆分</td></tr>
<tr><td>A5-A7</td><td><span class="badge p2">P2</span></td><td>规范</td><td>语义错位/拼写错误/死代码/日志混乱</td><td class="loc">多处</td><td>阶段3清理</td></tr>
<tr><td>C1</td><td><span class="badge p1">P1</span></td><td>通信</td><td>上位机无连接管理,connectState 死字段</td><td class="loc">LowerCommManager.h:131</td><td>阶段2启用</td></tr>
<tr><td>C2</td><td><span class="badge p1">P1</span></td><td>通信</td><td>协议知识三处散养;Driver 双实例</td><td class="loc">driver.cpp / UpperCommManager.h:93</td><td>阶段2收敛</td></tr>
<tr><td>C3-C6</td><td><span class="badge p1">P1</span></td><td>通信</td><td>错误码虚设/死分支/硬编码帧长/废弃函数错误数据</td><td class="loc">udpComm.cpp / driver.cpp</td><td>阶段2修复</td></tr>
<tr><td>C5</td><td><span class="badge p2">P2</span></td><td>性能</td><td>每帧堆分配、每包 cout</td><td class="loc">udpComm.cpp:462 等</td><td>阶段2优化</td></tr>
<tr><td>C7</td><td><span class="badge p1">P1</span></td><td>架构</td><td>双 Web 重复建设;/fcs/control 越权直改状态</td><td class="loc">httpserver.cpp:150</td><td>阶段2+4</td></tr>
<tr><td>F1</td><td><span class="badge p0">P0</span></td><td>故障</td><td>双重故障存储,等级查询与上报读不同集合</td><td class="loc">PowerManger.cpp:448 / uPower_pmState.h:50</td><td>阶段1合并</td></tr>
<tr><td>F2</td><td><span class="badge p1">P1</span></td><td>故障</td><td>上报死赋值</td><td class="loc">UpperCommManager.cpp:240</td><td>阶段1清理</td></tr>
<tr><td>F3</td><td><span class="badge p1">P1</span></td><td>故障</td><td>三套编码并存;系统码无文本映射;前端 JS 重写映射</td><td class="loc">faultCode.h / fuelcell.h:834</td><td>阶段1统一</td></tr>
<tr><td>F4</td><td><span class="badge p1">P1</span></td><td>故障</td><td>DEBUG 宏全构建未定义,4 级故障字恒为 0;DEBUFG 拼写;_DEBUG 死代码</td><td class="loc">systemData.h:7,776 等</td><td>阶段0修复</td></tr>
<tr><td>F5</td><td><span class="badge p2">P2</span></td><td>故障</td><td>无故障事件持久化表</td><td class="loc">SQLite 层</td><td>阶段1新增</td></tr>
<tr><td>D1-D4</td><td><span class="badge p1">P1</span></td><td>并发</td><td>全局数据池/锁纪律不一致/abs 截断/并发清队列</td><td class="loc">systemData.h 等</td><td>阶段2统一线程模型</td></tr>
<tr><td>H1-H3</td><td><span class="badge p2">P2</span></td><td>上位机</td><td>功能重叠/越权通道/页面硬编码</td><td class="loc">HostSim / httpserver</td><td>阶段4</td></tr>
<tr><td>E1</td><td><span class="badge p1">P1</span></td><td>构建</td><td>-Wall 未启用且写法错误;无 sanitizer;测试输出残留</td><td class="loc">CMakeLists.txt:80-86</td><td>阶段0接入</td></tr>
<tr><td>E2</td><td><span class="badge p2">P2</span></td><td>构建</td><td>GLOB 反模式;第三方库无版本/许可证记录;CMAKE 版本过老</td><td class="loc">CMakeLists.txt:9,49,53</td><td>阶段0接入</td></tr>
<tr><td>E3</td><td><span class="badge p1">P1</span></td><td>配置</td><td>ccuport/iport atoi 无校验、无范围检查</td><td class="loc">PowerManger.cpp:229/233</td><td>阶段0修复</td></tr>
<tr><td>E4</td><td><span class="badge p2">P2</span></td><td>性能</td><td>SQLite insertGeneric/onFrame 高频重复 prepare</td><td class="loc">SQLite.cpp:610-625</td><td>阶段1优化</td></tr>
<tr><td>E5</td><td><span class="badge p2">P2</span></td><td>规范</td><td>日志/错误输出三套并存(loguru/cerr/cout)</td><td class="loc">driver.cpp / LowerCommManager.cpp</td><td>阶段3统一</td></tr>
<tr><td>T1</td><td><span class="badge p0">P0</span></td><td>测试</td><td>状态机/故障注入/上位机三大测试空白</td><td class="loc">test/</td><td>阶段0起持续</td></tr>
<tr><td>T2</td><td><span class="badge p1">P1</span></td><td>CI</td><td>集成失败不阻塞;cppcheck 未接入;in-source 构建残留</td><td class="loc">ci/build-test.sh:181</td><td>阶段0</td></tr>
</table>
<p class="small" style="margin-top:40px;border-top:1px solid var(--border);padding-top:16px;">
本报告基于 2026-09-03 工作区代码静态审查生成,所有问题均标注文件与行号,关键缺陷(B1、B2、B3、B5)已经人工逐行复核。报告未对任何源代码做修改。
</p>
</main>
</div>
</body>
</html>
Binary file not shown.
Binary file not shown.
+161
View File
@@ -0,0 +1,161 @@
// MOOS file
// 板卡 (RK3588 / Ubuntu 20.04) 专用 mission 配置
// 七个 systemd 服务(moosdb / pPowerManger / pPowerMangerHost / pCCU /
// pCanBridge / pMotor / pELoad)共用本文件。
// 由 scripts/build-board.sh 同步到板卡 /root/work/h100/missions/h100.moos
ServerHost = localhost
ServerPort = 9000
Community = h100
ProcessConfig = pPowerManger
{
AppTick = 4
CommsTick = 4
log_level = INFO
// 日志文件路径(目录需已存在,由 build-board.sh 部署时创建)
logpath = /root/work/h100/data/pPowerManger.log
// 数据库存储路径(目录需已存在,由 build-board.sh 部署时创建)
dbpath = /root/work/h100/data/power_data.db
// CCU 地址(pPowerManger -> pCCU 的 UDP 链路);不配置则默认 127.0.0.1:7000
ccuhost = 127.0.0.1
ccuport = 7000
// 本地输入端口(接收 CCU 数据);默认 5001。
// 5001 让给 pCCU 的 rcu 链路接收真实CCU(192.168.0.140) 固定上报的
// 配电反馈,故本进程接收端口改为 5002(pCCU 转发的反馈与状态报文发往此处)
iport = 5002
}
ProcessConfig = pPowerMangerHost
{
AppTick = 4
CommsTick = 4
WEB_PORT = 18080
// 反馈落盘(SQLite),路径可配置;目录不存在会自动创建
DB_FILE = /root/work/h100/data/feedback_data.db
FB_LOG_ENABLED = true
FB_LOG_KEEP_SECONDS = 86400 // 历史保留时长(秒),24h
FB_LOG_PRUNE_INTERVAL_SECONDS = 60 // 清理检查周期(秒)
}
ProcessConfig = pCCU
{
AppTick = 4
CommsTick = 4
// FC 链路(本机监听 FC 状态;发送 FC 控制指令给燃料电池控制器)
fc_local_port = 6000
fc_remote_ip = 192.168.1.162
fc_remote_port = 7000
// PM 链路(监听 pPowerManger 指令;向 pPowerManger 周期发送 PM 状态)
// pPowerManger 与 pCCU 同机部署;pPowerManger 接收端口为 5002(iport),
// 故 PM 状态发送目标用 127.0.0.1:5002。
pm_local_port = 7000
pm_remote_ip = 127.0.0.1
pm_remote_port = 5002
//======== 真实CCU 桥接链路(配电协议) ========
// 真实CCU(192.168.0.140) 设备侧固定上报到 192.168.0.223:5001
// (与 pPowerManger 默认 CCU 配置一致:CCUPORT=7000 为其监听口,
// 上报目标为控制主机 5001),设备侧不可修改。
// 故 pCCU 的 rcu 链路直接绑定 5001 接收其反馈:
// 配电反馈 0x0005~0x0008:原帧转发 pPowerManger(127.0.0.1:5002) + 网页显示
// 配电指令 0x0001~0x0004:转发目标为真实CCU 监听口 192.168.0.140:7000
// 其余帧(旧协议 0x20 0x20 状态帧、无效帧)落库忽略
rcu_enable = true
rcu_local_port = 5001
rcu_remote_ip = 192.168.0.140
rcu_remote_port = 7000
//======== 锂电池 ========
// CAN 总线 BMS 协议(docs/BMS_协议字段定义.xlsx),
// 经 pCanBridge 发布的 CAN_0x* 消息透传,无需额外配置。
dbpath = /root/work/h100/data/pccu_data.db
logpath = /root/work/h100/data/pCCU.log
web_port = 8080
web_enable = true
}
ProcessConfig = pCanBridge
{
AppTick = 4
CommsTick = 4
// CAN<->MOOSDB 多通道透传:CANET(TCP Server 模式)
// 每通道一条独立 TCP 连接;工作端口:CAN0=4001,CAN1=4002,...,CAN7=4008
channel = CAN0,192.168.0.222,4001
channel = CAN1,192.168.0.222,4002
// channel = CAN1,192.168.0.222,4002
// CAN_TX 下行默认通道(pCCU 未指定 m_sSrcAux 时使用;可省略)
default_channel = CAN0
// CAN 帧 SQLite 落库路径(can_frame 表,channel 列区分通道)
dbpath = /root/work/h100/data/pCanBridge_data.db
}
ProcessConfig = pMotor
{
AppTick = 4
CommsTick = 4
// 推进电机控制器:CAN1 通道(经 pCanBridge 透传,docs/推进电机通信协议20260321.docx)
// 下行 500ms 周期指令帧 0x18EF2010(网页 /api/motor_cmd 控制启停/复位/转速),
// 上行订阅 CAN_0x* 解码故障/转速/母线电压/温度,网页 18081 展示
can_channel = CAN1
// 冗余 CAN 通道(配置后指令帧同内容发 0x18EF2011;留空=不发送)
// red_channel = CAN2
cmd_period_ms = 500
reset_hold_sec = 5
web_port = 18081
web_enable = true
logpath = /root/work/h100/data/pMotor.log
}
ProcessConfig = pELoad
{
AppTick = 10
CommsTick = 4
//======== 电子负载(IT6000C)以太网地址 ========
// 仪器 LAN 配置的 IP 与 Raw Socket 端口号(出厂默认 30000)
eload_ip = 192.168.200.100
eload_port = 30000
//======== 功率控制 ========
// MOOS 功率消息变量名(实时电机功率,W,正值=负载吸收)
power_var = MOTOR_POWER
ctrl_period_ms = 100
power_stale_sec = 3
current_sign = negative
nominal_volt = 500
max_power = 0
max_current = 0
slew_limit_wps = 0
func_priority = cc
// CV 优先时的目标电压(V),func_priority=cv 时生效(恒压吸收)
volt_set = 0
//======== 数据存储 ========
dbpath = /root/work/h100/data/pELoad.db
log_keep_hours = 72
meas_log_period_ms = 100
// 测量命令来源:fetch=FETCh(读设备表头缓存,不触发测量,响应快,推荐);
// meas=MEAS(每次触发设备重新测量,响应慢,易超时)
meas_source = fetch
//======== 网页(避开 8080/8090/18080/18081)========
web_port = 18082
web_enable = true
logpath = /root/work/h100/data/pELoad.log
}
+5
View File
@@ -2,4 +2,9 @@ ProcessConfig = pPowerManger
{
AppTick = 4
CommsTick = 4
// CCU 地址;不配置则默认 127.0.0.1:7000
// ccuhost = 127.0.0.1
// ccuport = 7000
// 本地输入端口(接收 CCU 数据);不配置则默认 5001
// iport = 5001
}
+310
View File
@@ -0,0 +1,310 @@
#!/bin/bash
#=======================================================================
# FILE: scripts/build-board.sh
# DESC: 把源码同步到目标板卡 (RK3588 / Ubuntu 22.04),在板卡原生编译
# (aarch64 直编,比本地 QEMU 模拟快约 10 倍),并把产物部署到
# /root/work/h100/bin/,可选重启 systemd 服务。
#
# 用法:
# ./scripts/build-board.sh # 同步+编译+部署(不重启)
# ./scripts/build-board.sh --start # 编译后重启全部服务(含 pCanBridge/pMotor/pELoad)
# ./scripts/build-board.sh --clean # 板卡上重新 cmake 再编译
# ./scripts/build-board.sh --jobs 4 # 指定并行度(默认板卡 nproc)
#
# 参数:
# --host <ip> 目标主机(默认 192.168.0.223)
# --user <user> 登录用户(默认 root)
# --board-src <dir> 板卡源码目录(默认 /root/work/h100/src)
# --board-bin <dir> 板卡部署目录(默认 /root/work/h100/bin)
# --jobs <n> 编译并行度(默认板卡 nproc)
# --start 编译部署后重启服务
# --clean 先重新 cmake(配置变更时用)
# --ccu-host <ip> 覆盖 mission 中 pPowerManger 的 CCU 地址 (ccuhost)
# --ccu-port <n> 覆盖 mission 中 pPowerManger 的 CCU 端口 (ccuport)
# -h, --help 显示帮助
#
# 前置: 板卡已配免密 SSH,且有编译环境(cmake/g++/MOOS/jsoncpp),
# 源码已能本地编译(本脚本只负责同步+板卡编译)。
#
# !!!! 板卡网络配置(2026-08-26 联调定型,勿随意修改) !!!!
#
# 板卡与 FC 的组网为"单网卡 + /23 掩码"方案(依据《超滑FC和复合管控器
# 通讯协议-0821》1.1.1:CCU 192.168.0.140/23 <-> FC 192.168.1.162):
#
# enP3p49s0 192.168.0.223/23 (255.255.254.0) <- 唯一业务网口,接交换机
# /23 使 192.168.0.x 与 192.168.1.x 同子网直达,
# FC(192.168.1.162) 无需网关即可互通。
# NM 配置档案: "有线连接 1"(静态 manual,已持久化)
# enP4p65s0 已弃用(旧双网卡方案的 FC 专口,物理链路已断)
# 回环 lo pPowerManger(5001) <-> pCCU(7000) 同机 UDP 互通
#
# 关联端口:
# FC 状态: FC:7000 -> 板卡:6000 (pCCU 监听)
# FC 控制: 板卡:6000 -> FC:7000 (pCCU 发送)
# PM 指令: pPowerManger:5001 -> pCCU:7000 (127.0.0.1,
# 见 missions/h100.moos ccuhost/ccuport,默认值在 PowerManger.h CCUHOST)
# PM 状态: pCCU:7000 -> pPowerManger:5001 (127.0.0.1)
#
# 排障提示:
# - 若收不到 FC 状态,先在板卡上 `ethtool enP3p49s0` 看 Link detected,
# 再 `ping 192.168.1.162`;ping 不通通常是物理链路/交换机问题,
# 不要改回 /24 掩码或启用 enP4p65s0 来"修"网络。
# - 改 IP/掩码须用 `nmcli con mod "有线连接 1" ...` 持久化,
# 并保持掩码 /23。
#=======================================================================
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
HOST="192.168.0.223"
USER="root"
BOARD_SRC="/root/work/h100/src"
BOARD_BIN="/root/work/h100/bin"
JOBS=""
DO_START=0
DO_CLEAN=0
CCU_HOST=""
CCU_PORT=""
SERVICES=(moosdb pPowerManger pPowerMangerHost pCCU pCanBridge pMotor pELoad)
info() { printf '\033[1;36m[build-board]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[build-board]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[build-board]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[build-board]\033[0m %s\n' "$*"; }
usage() {
sed -n '2,31p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
}
#-------------------------------------------------------------------
# 参数解析
#-------------------------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
--host) [ $# -ge 2 ] || { err "--host 需要参数"; exit 1; }; HOST="$2"; shift 2 ;;
--user) [ $# -ge 2 ] || { err "--user 需要参数"; exit 1; }; USER="$2"; shift 2 ;;
--board-src) [ $# -ge 2 ] || { err "--board-src 需要参数"; exit 1; }; BOARD_SRC="$2"; shift 2 ;;
--board-bin) [ $# -ge 2 ] || { err "--board-bin 需要参数"; exit 1; }; BOARD_BIN="$2"; shift 2 ;;
--jobs) [ $# -ge 2 ] || { err "--jobs 需要参数"; exit 1; }; JOBS="$2"; shift 2 ;;
--start) DO_START=1; shift ;;
--clean) DO_CLEAN=1; shift ;;
--ccu-host) [ $# -ge 2 ] || { err "--ccu-host 需要参数"; exit 1; }; CCU_HOST="$2"; shift 2 ;;
--ccu-port) [ $# -ge 2 ] || { err "--ccu-port 需要参数"; exit 1; }; CCU_PORT="$2"; shift 2 ;;
-h|--help) usage ;;
*) err "未知参数: $1(见 --help)"; exit 1 ;;
esac
done
SSH_TARGET="${USER}@${HOST}"
SSH="ssh -o BatchMode=yes -o ConnectTimeout=5"
case "${CCU_PORT}" in
""|*[!0-9]*) [ -z "${CCU_PORT}" ] || { err "--ccu-port 必须是数字端口: ${CCU_PORT}"; exit 1; } ;;
esac
#-------------------------------------------------------------------
# 1. 前置检查
#-------------------------------------------------------------------
if ! ${SSH} "${SSH_TARGET}" 'true' 2>/dev/null; then
err "无法免密 SSH 到 ${SSH_TARGET}"
err "请先配置免密登录: ssh-copy-id ${SSH_TARGET}"
exit 1
fi
ok "SSH 连接正常: ${SSH_TARGET}"
#-------------------------------------------------------------------
# mission 中插入/替换 "key = value"(键已存在则替换,否则插到第一个
# 顶层 '}' 之前,即 pPowerManger 配置块末尾)
#-------------------------------------------------------------------
upsert_moos_param() {
local f="$1" key="$2" val="$3"
if grep -qE "^[[:space:]]*${key}[[:space:]]*=" "${f}"; then
sed -i -E "s|^([[:space:]]*)${key}[[:space:]]*=.*|\1${key} = ${val}|" "${f}"
else
awk -v kv=" ${key} = ${val}" \
'!d && /^[[:space:]]*}[[:space:]]*$/ { print kv; d=1 } { print }' \
"${f}" > "${f}.tmp" && mv "${f}.tmp" "${f}"
fi
}
#-------------------------------------------------------------------
# 2. 同步源码到板卡(排除构建产物 / git / 数据)
#-------------------------------------------------------------------
info "同步源码到板卡 ${BOARD_SRC} ..."
rsync -az --delete \
--exclude='build/' \
--exclude='build-arm64/' \
--exclude='build-native/' \
--exclude='build-nosa-test/' \
--exclude='bin/' \
--exclude='.git/' \
--exclude='.cache/' \
--exclude='.kilo/' \
--exclude='.vscode/' \
--exclude='docs/' \
--exclude='reports/' \
--exclude='*.log' \
--exclude='*.db' \
--exclude='CMakeCache.txt' \
"${PROJECT_ROOT}/" "${SSH_TARGET}:${BOARD_SRC}/" || { err "rsync 源码失败"; exit 1; }
ok "源码已同步"
if [ -f "${PROJECT_ROOT}/missions/h100.moos" ]; then
info "同步 mission h100.moos ..."
local_mission="${PROJECT_ROOT}/missions/h100.moos"
tmp_moos=""
if [ -n "${CCU_HOST}" ] || [ -n "${CCU_PORT}" ]; then
tmp_moos="$(mktemp)"
cp "${local_mission}" "${tmp_moos}"
[ -n "${CCU_HOST}" ] && upsert_moos_param "${tmp_moos}" ccuhost "${CCU_HOST}"
[ -n "${CCU_PORT}" ] && upsert_moos_param "${tmp_moos}" ccuport "${CCU_PORT}"
local_mission="${tmp_moos}"
info "CCU 地址覆盖: ccuhost=${CCU_HOST:-<保持原值>} ccuport=${CCU_PORT:-<保持原值>}"
fi
rsync -az "${local_mission}" \
"${SSH_TARGET}:/root/work/h100/missions/h100.moos" || { err "rsync mission 失败"; [ -n "${tmp_moos}" ] && rm -f "${tmp_moos}"; exit 1; }
[ -n "${tmp_moos}" ] && rm -f "${tmp_moos}"
ok "mission 已同步"
fi
#-------------------------------------------------------------------
# 3. 板卡原生编译
#-------------------------------------------------------------------
if [ "${DO_CLEAN}" = "1" ]; then
info "重新 cmake(--clean)..."
${SSH} "${SSH_TARGET}" "rm -rf ${BOARD_SRC}/build-native && mkdir -p ${BOARD_SRC}/build-native && \
cd ${BOARD_SRC}/build-native && cmake -DCMAKE_BUILD_TYPE=Release .." \
|| { err "cmake 失败"; exit 1; }
ok "cmake 完成"
else
${SSH} "${SSH_TARGET}" "mkdir -p ${BOARD_SRC}/build-native"
# 若首次未配置过则先 cmake
if ! ${SSH} "${SSH_TARGET}" "[ -f ${BOARD_SRC}/build-native/Makefile ]"; then
info "首次编译,先 cmake ..."
${SSH} "${SSH_TARGET}" "cd ${BOARD_SRC}/build-native && cmake -DCMAKE_BUILD_TYPE=Release .." \
|| { err "cmake 失败"; exit 1; }
ok "cmake 完成"
fi
fi
if [ -z "${JOBS}" ]; then
JOBS="$(${SSH} "${SSH_TARGET}" nproc)"
fi
info "板卡编译 (make -j${JOBS}) ..."
if ! ${SSH} "${SSH_TARGET}" "cd ${BOARD_SRC}/build-native && make -j${JOBS}"; then
err "板卡编译失败(见上方输出)"
exit 1
fi
ok "板卡编译完成"
#-------------------------------------------------------------------
# 4. 部署产物到板卡运行目录
#-------------------------------------------------------------------
info "部署产物到 ${BOARD_BIN}/ ..."
${SSH} "${SSH_TARGET}" "mkdir -p ${BOARD_BIN} && \
cp -f ${BOARD_SRC}/bin/pPowerManger ${BOARD_BIN}/pPowerManger && \
cp -f ${BOARD_SRC}/bin/pPowerMangerHost ${BOARD_BIN}/pPowerMangerHost && \
cp -f ${BOARD_SRC}/bin/pCCU ${BOARD_BIN}/pCCU && \
cp -f ${BOARD_SRC}/bin/pCanBridge ${BOARD_BIN}/pCanBridge && \
cp -f ${BOARD_SRC}/bin/pMotor ${BOARD_BIN}/pMotor && \
cp -f ${BOARD_SRC}/bin/pELoad ${BOARD_BIN}/pELoad && \
chmod +x ${BOARD_BIN}/pPowerManger ${BOARD_BIN}/pPowerMangerHost ${BOARD_BIN}/pCCU ${BOARD_BIN}/pCanBridge ${BOARD_BIN}/pMotor ${BOARD_BIN}/pELoad" \
|| { err "部署产物失败"; exit 1; }
ok "产物已部署"
#-------------------------------------------------------------------
# 4.5 安装/刷新 pCanBridge systemd unit(幂等,其它 5 个 unit 已装)
#-------------------------------------------------------------------
info "安装 pCanBridge.service ..."
${SSH} "${SSH_TARGET}" "cat > /etc/systemd/system/pCanBridge.service" <<EOF
[Unit]
Description=pCanBridge for H100 Power Manager
After=moosdb.service
Requires=moosdb.service
[Service]
Type=simple
WorkingDirectory=${BOARD_BIN}
ExecStart=${BOARD_BIN}/pCanBridge --alias=pCanBridge /root/work/h100/missions/h100.moos
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
${SSH} "${SSH_TARGET}" "systemctl daemon-reload && systemctl reset-failed pCanBridge 2>/dev/null || true"
ok "pCanBridge.service 已安装"
#-------------------------------------------------------------------
# 4.6 安装/刷新 pMotor systemd unit(幂等)
#-------------------------------------------------------------------
info "安装 pMotor.service ..."
${SSH} "${SSH_TARGET}" "cat > /etc/systemd/system/pMotor.service" <<EOF
[Unit]
Description=pMotor Propulsion Motor for H100 Power Manager
After=moosdb.service pCanBridge.service
Requires=moosdb.service
[Service]
Type=simple
WorkingDirectory=${BOARD_BIN}
ExecStart=${BOARD_BIN}/pMotor --alias=pMotor /root/work/h100/missions/h100.moos
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
${SSH} "${SSH_TARGET}" "systemctl daemon-reload && systemctl reset-failed pMotor 2>/dev/null || true"
ok "pMotor.service 已安装"
#-------------------------------------------------------------------
# 4.7 安装/刷新 pELoad systemd unit(幂等)
# 电子负载(IT6000C)控制程序:以太网直连设备,不依赖 pCanBridge
#-------------------------------------------------------------------
info "安装 pELoad.service ..."
${SSH} "${SSH_TARGET}" "cat > /etc/systemd/system/pELoad.service" <<EOF
[Unit]
Description=pELoad Electronic Load (IT6000C) for H100 Power Manager
After=moosdb.service
Requires=moosdb.service
[Service]
Type=simple
WorkingDirectory=${BOARD_BIN}
ExecStart=${BOARD_BIN}/pELoad --alias=pELoad /root/work/h100/missions/h100.moos
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
${SSH} "${SSH_TARGET}" "systemctl daemon-reload && systemctl reset-failed pELoad 2>/dev/null || true"
ok "pELoad.service 已安装"
echo ""
ok "产物: ${BOARD_BIN}/pPowerManger (aarch64)"
${SSH} "${SSH_TARGET}" "file ${BOARD_BIN}/pPowerManger | cut -c1-60"
#-------------------------------------------------------------------
# 5. 可选重启服务
#-------------------------------------------------------------------
if [ "${DO_START}" = "1" ]; then
info "重启服务 ..."
${SSH} "${SSH_TARGET}" "systemctl restart ${SERVICES[*]}" \
|| { err "重启服务失败"; exit 1; }
sleep 3
ok "服务状态: $(${SSH} "${SSH_TARGET}" "systemctl is-active ${SERVICES[*]}")"
else
echo ""
info "未重启服务。需要重启请加 --start 或手动:"
echo " systemctl restart ${SERVICES[*]}"
fi
echo ""
ok "完成。"
+147
View File
@@ -0,0 +1,147 @@
#!/bin/bash
#=======================================================================
# FILE: scripts/clean-data.sh
# DESC: 清理目标板卡 (RK3588 / Ubuntu 22.04) 上的数据库与日志文件。
# 默认清理板卡运行目录 /root/work/h100/data/ 下的:
# power_data.db (+ -wal/-shm) 主机功率数据库
# feedback_data.db 反馈落盘数据库
# pccu_data.db (+ -wal/-shm) pCCU 帧日志数据库
# pCanBridge_data.db (+ -wal/-shm) pCanBridge CAN 帧数据库
# pPowerManger.log / pCCU.log 运行日志
# 默认先停止各服务再清理(避免 SQLite WAL 仍在写入)。
#
# 用法:
# ./scripts/clean-data.sh # 停服务 -> 清理 data 目录(不重启)
# ./scripts/clean-data.sh --start # 清理后重新启动各服务
# ./scripts/clean-data.sh --no-stop # 不停服务直接清理(慎用)
# ./scripts/clean-data.sh --stale # 额外清理 bin/ 与 src/bin/ 下的旧 db/log
#
# 参数:
# --host <ip> 目标主机(默认 192.168.0.223)
# --user <user> 登录用户(默认 root)
# --board-dir <dir> 板卡部署根目录(默认 /root/work/h100)
# --start 清理后重启服务
# --no-stop 不停服务直接清理(慎用)
# --stale 额外清理历史遗留的 bin/、src/bin/ 下旧数据库与日志
# -h, --help 显示帮助
#
# 前置: 板卡已配免密 SSH(ssh-copy-id root@<host>)。
#=======================================================================
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
HOST="192.168.0.223"
USER="root"
BOARD_DIR="/root/work/h100"
DO_START=0
DO_STOP=1
DO_STALE=0
SERVICES=(moosdb pPowerManger pPowerMangerHost pCCU pCanBridge)
# 板卡 data 目录下需要清理的文件(含 SQLite WAL/SHM)
DATA_FILES=(
power_data.db power_data.db-wal power_data.db-shm
feedback_data.db
pccu_data.db pccu_data.db-wal pccu_data.db-shm
pCanBridge_data.db pCanBridge_data.db-wal pCanBridge_data.db-shm
pPowerManger.log pCCU.log
)
# 历史遗留目录(旧部署位置),仅当 --stale 时清理
STALE_DIRS=("${BOARD_DIR}/bin" "${BOARD_DIR}/src/bin")
info() { printf '\033[1;36m[clean-data]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[clean-data]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[clean-data]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[clean-data]\033[0m %s\n' "$*"; }
usage() {
sed -n '2,31p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
}
#-------------------------------------------------------------------
# 参数解析
#-------------------------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
--host) [ $# -ge 2 ] || { err "--host 需要参数"; exit 1; }; HOST="$2"; shift 2 ;;
--user) [ $# -ge 2 ] || { err "--user 需要参数"; exit 1; }; USER="$2"; shift 2 ;;
--board-dir) [ $# -ge 2 ] || { err "--board-dir 需要参数"; exit 1; }; BOARD_DIR="$2"; shift 2 ;;
--start) DO_START=1; shift ;;
--no-stop) DO_STOP=0; shift ;;
--stale) DO_STALE=1; shift ;;
-h|--help) usage ;;
*) err "未知参数: $1(见 --help)"; exit 1 ;;
esac
done
STALE_DIRS=("${BOARD_DIR}/bin" "${BOARD_DIR}/src/bin")
SSH_TARGET="${USER}@${HOST}"
SSH="ssh -o BatchMode=yes -o ConnectTimeout=5"
#-------------------------------------------------------------------
# 1. 前置检查
#-------------------------------------------------------------------
if ! ${SSH} "${SSH_TARGET}" 'true' 2>/dev/null; then
err "无法免密 SSH 到 ${SSH_TARGET}"
err "请先配置免密登录: ssh-copy-id ${SSH_TARGET}"
exit 1
fi
ok "SSH 连接正常: ${SSH_TARGET}"
#-------------------------------------------------------------------
# 2. 停止服务(默认)
#-------------------------------------------------------------------
if [ "${DO_STOP}" = "1" ]; then
info "停止服务 ${SERVICES[*]} ..."
# 先停应用,最后停 moosdb
${SSH} "${SSH_TARGET}" "systemctl stop pCanBridge pPowerMangerHost pPowerManger pCCU moosdb 2>/dev/null; true"
ok "服务已停止"
else
warn "--no-stop:不停服务直接清理,可能产生脏数据(SQLite 仍在写入)"
fi
#-------------------------------------------------------------------
# 3. 清理 data 目录
#-------------------------------------------------------------------
BOARD_DATA="${BOARD_DIR}/data"
info "清理 ${BOARD_DATA}/ ..."
for f in "${DATA_FILES[@]}"; do
${SSH} "${SSH_TARGET}" "rm -f ${BOARD_DATA}/${f}" \
&& ok "删除 ${f}" || warn "删除 ${f} 失败(可能不存在)"
done
#-------------------------------------------------------------------
# 4. 可选: 清理历史遗留目录
#-------------------------------------------------------------------
if [ "${DO_STALE}" = "1" ]; then
for d in "${STALE_DIRS[@]}"; do
info "清理历史遗留 ${d}/ 下的 db/log ..."
${SSH} "${SSH_TARGET}" "rm -f ${d}/power_data.db ${d}/power_data.db-wal ${d}/power_data.db-shm ${d}/feedback_data.db ${d}/pPowerManger.log 2>/dev/null; true"
ok "${d} 已清理"
done
fi
#-------------------------------------------------------------------
# 5. 可选: 重启服务
#-------------------------------------------------------------------
if [ "${DO_START}" = "1" ]; then
info "启动服务 (moosdb -> pPowerManger / pPowerMangerHost / pCCU / pCanBridge) ..."
${SSH} "${SSH_TARGET}" "systemctl start moosdb && sleep 1 && \
systemctl start pPowerManger pPowerMangerHost pCCU pCanBridge" \
|| { err "启动失败"; exit 1; }
ok "服务已启动"
else
echo ""
info "清理完成,服务未启动。需要重启请加 --start 或手动:"
echo " systemctl start moosdb pPowerManger pPowerMangerHost pCCU pCanBridge"
fi
echo ""
ok "完成。"
+133
View File
@@ -0,0 +1,133 @@
#!/bin/bash
#=======================================================================
# FILE: scripts/fetch-data.sh
# DESC: 从目标板卡 (RK3588 / Ubuntu 22.04) 下载数据库与日志到本地。
# 默认抓取板卡运行目录 /root/work/h100/data/ 下的:
# power_data.db (+ -wal/-shm) 主机功率数据库
# feedback_data.db 反馈落盘数据库
# pccu_data.db (+ -wal/-shm) pCCU 帧日志数据库
# pCanBridge_data.db (+ -wal/-shm) pCanBridge CAN 帧数据库
# pPowerManger.log / pCCU.log 运行日志
# 可选 --journal 一并导出各 systemd 服务的 journal 日志。
#
# 用法:
# ./scripts/fetch-data.sh # 下载 db + 日志到本地 data/board-<时间>/
# ./scripts/fetch-data.sh --journal # 额外导出 systemd journal
# ./scripts/fetch-data.sh --out-dir /tmp/xx # 指定本地保存目录
#
# 参数:
# --host <ip> 目标主机(默认 192.168.0.223)
# --user <user> 登录用户(默认 root)
# --board-dir <dir> 板卡部署根目录(默认 /root/work/h100)
# --out-dir <dir> 本地保存目录(默认 data/board-<时间>)
# --journal 额外导出各服务的 systemd journal 日志
# -h, --help 显示帮助
#
# 前置: 板卡已配免密 SSH(ssh-copy-id root@<host>)。
#=======================================================================
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
HOST="192.168.0.223"
USER="root"
BOARD_DIR="/root/work/h100"
OUT_DIR=""
DO_JOURNAL=0
SERVICES=(moosdb pPowerManger pPowerMangerHost pCCU pCanBridge)
# 板卡 data 目录下需要下载的文件(含 SQLite WAL/SHM 以保证数据完整)
DATA_FILES=(
power_data.db power_data.db-wal power_data.db-shm
feedback_data.db
pccu_data.db pccu_data.db-wal pccu_data.db-shm
pCanBridge_data.db pCanBridge_data.db-wal pCanBridge_data.db-shm
pPowerManger.log pCCU.log
)
info() { printf '\033[1;36m[fetch-data]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[fetch-data]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[fetch-data]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[fetch-data]\033[0m %s\n' "$*"; }
usage() {
sed -n '2,28p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
}
#-------------------------------------------------------------------
# 参数解析
#-------------------------------------------------------------------
while [ $# -gt 0 ]; do
case "$1" in
--host) [ $# -ge 2 ] || { err "--host 需要参数"; exit 1; }; HOST="$2"; shift 2 ;;
--user) [ $# -ge 2 ] || { err "--user 需要参数"; exit 1; }; USER="$2"; shift 2 ;;
--board-dir) [ $# -ge 2 ] || { err "--board-dir 需要参数"; exit 1; }; BOARD_DIR="$2"; shift 2 ;;
--out-dir) [ $# -ge 2 ] || { err "--out-dir 需要参数"; exit 1; }; OUT_DIR="$2"; shift 2 ;;
--journal) DO_JOURNAL=1; shift ;;
-h|--help) usage ;;
*) err "未知参数: $1(见 --help)"; exit 1 ;;
esac
done
SSH_TARGET="${USER}@${HOST}"
SSH="ssh -o BatchMode=yes -o ConnectTimeout=5"
SCP="scp -o BatchMode=yes -o ConnectTimeout=5"
#-------------------------------------------------------------------
# 1. 前置检查
#-------------------------------------------------------------------
if ! ${SSH} "${SSH_TARGET}" 'true' 2>/dev/null; then
err "无法免密 SSH 到 ${SSH_TARGET}"
err "请先配置免密登录: ssh-copy-id ${SSH_TARGET}"
exit 1
fi
ok "SSH 连接正常: ${SSH_TARGET}"
if [ -z "${OUT_DIR}" ]; then
OUT_DIR="${PROJECT_ROOT}/data/board-$(date +%Y%m%d-%H%M%S)"
fi
mkdir -p "${OUT_DIR}" || { err "无法创建本地目录 ${OUT_DIR}"; exit 1; }
info "本地保存目录: ${OUT_DIR}"
#-------------------------------------------------------------------
# 2. 下载 data 目录下的数据库与日志
#-------------------------------------------------------------------
BOARD_DATA="${BOARD_DIR}/data"
missing=0
for f in "${DATA_FILES[@]}"; do
if ${SSH} "${SSH_TARGET}" "[ -f ${BOARD_DATA}/${f} ]" 2>/dev/null; then
info "下载 ${f} ..."
${SCP} -q "${SSH_TARGET}:${BOARD_DATA}/${f}" "${OUT_DIR}/" \
|| { err "下载 ${f} 失败"; exit 1; }
ok "${f}"
else
warn "板卡上不存在: ${BOARD_DATA}/${f}(跳过)"
missing=$((missing+1))
fi
done
[ "${missing}" -eq "${#DATA_FILES[@]}" ] && { err "板卡 data 目录下没有任何可下载文件"; exit 1; }
#-------------------------------------------------------------------
# 3. 可选: 导出 systemd journal
#-------------------------------------------------------------------
if [ "${DO_JOURNAL}" = "1" ]; then
for s in "${SERVICES[@]}"; do
info "导出 journal: ${s} ..."
${SSH} "${SSH_TARGET}" "journalctl --no-pager -u ${s} 2>/dev/null" > "${OUT_DIR}/${s}.journal.log" \
|| { warn "导出 ${s} journal 失败(可能无记录)"; }
ok "${s}.journal.log"
done
fi
#-------------------------------------------------------------------
# 4. 完成
#-------------------------------------------------------------------
echo ""
ok "下载完成。本地文件:"
ls -lah "${OUT_DIR}" | sed 's/^/ /'
echo ""
info "查看数据库可用: sqlite3 ${OUT_DIR}/power_data.db .tables"
+306
View File
@@ -0,0 +1,306 @@
#!/bin/bash
#=======================================================================
# FILE: scripts/run_host_sim.sh
# DESC: 运行与测试 pPowerMangerHost(模拟 pPowerManger 上位机的网页程序)
#
# 用法:
# ./scripts/run_host_sim.sh run 启动 MOOSDB + pPowerManger
# + pPowerMangerHost(前台运行,
# Ctrl-C 退出并清理)
# ./scripts/run_host_sim.sh test 自动化测试 pPowerMangerHost:
# 网页 HTTP / WebSocket 反馈 /
# 指令下发到 MOOSDB
# ./scripts/run_host_sim.sh build 仅编译需要的目标
# ./scripts/run_host_sim.sh stop 清理残留进程
# ./scripts/run_host_sim.sh help 显示帮助
#
# 环境变量(可覆盖):
# SERVER_HOST MOOSDB 地址(默认 localhost)
# SERVER_PORT MOOSDB 端口(默认 9000)
# WEB_PORT 网页监听端口(默认 18080)
# WITH_POWERMANGER run 模式是否同时启动 pPowerManger(默认 1)
#=======================================================================
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BIN_DIR="${ROOT}/bin"
MOOS_TEMPLATE="${ROOT}/src/pPowerMangerHost/pPowerMangerHost.moos"
SERVER_HOST="${SERVER_HOST:-localhost}"
SERVER_PORT="${SERVER_PORT:-9000}"
WEB_PORT="${WEB_PORT:-18080}"
WITH_POWERMANGER="${WITH_POWERMANGER:-1}"
MOOSDB_BIN="${MOOSDB_BIN:-$(command -v MOOSDB || true)}"
POKE_BIN="$(command -v uPokeDB || true)"
PM_BIN="${BIN_DIR}/pPowerManger"
HOST_BIN="${BIN_DIR}/pPowerMangerHost"
MOOSDB_PID=""
PM_PID=""
HOST_PID=""
WORKDIR=""
MISSION=""
info() { printf '\033[1;36m[host-sim]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[host-sim]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[host-sim]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[host-sim]\033[0m %s\n' "$*"; }
usage() {
sed -n '2,23p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
}
# 生成临时 mission 文件(同时含 pPowerManger 与 pPowerMangerHost 两个配置块)
make_mission() {
WORKDIR="$(mktemp -d)"
MISSION="${WORKDIR}/host_sim.moos"
cat > "${MISSION}" <<EOF
ServerHost = ${SERVER_HOST}
ServerPort = ${SERVER_PORT}
Community = h100hostsim
ProcessConfig = pPowerManger
{
AppTick = 4
CommsTick = 4
}
ProcessConfig = pPowerMangerHost
{
AppTick = 4
CommsTick = 4
WEB_PORT = ${WEB_PORT}
}
EOF
}
kill_proc() {
[ -n "$1" ] && kill "$1" 2>/dev/null && wait "$1" 2>/dev/null
}
cleanup() {
kill_proc "${HOST_PID}"
kill_proc "${PM_PID}"
kill_proc "${MOOSDB_PID}"
[ -n "${WORKDIR}" ] && rm -rf "${WORKDIR}"
}
build_targets() {
info "编译 pPowerManger / pPowerMangerHost ..."
mkdir -p "${ROOT}/build"
cd "${ROOT}/build"
cmake -DCMAKE_BUILD_TYPE=Release "${ROOT}" >/dev/null 2>&1 || { err "cmake 失败"; return 1; }
make -j2 pPowerManger pPowerMangerHost >/dev/null 2>&1 || { err "make 失败"; return 1; }
cd "${ROOT}"
ok "编译完成"
}
check_binaries() {
local missing=0
[ -n "${MOOSDB_BIN}" ] || { err "未找到 MOOSDB(请加入 PATH)"; missing=1; }
[ -x "${HOST_BIN}" ] || { err "未找到 ${HOST_BIN}(请先运行: $0 build)"; missing=1; }
if [ "${WITH_POWERMANGER}" = "1" ]; then
[ -x "${PM_BIN}" ] || { err "未找到 ${PM_BIN}(请先运行: $0 build)"; missing=1; }
fi
return "${missing}"
}
start_moosdb() {
make_mission
info "启动 MOOSDB (${SERVER_HOST}:${SERVER_PORT}) ..."
"${MOOSDB_BIN}" --moos_no_colour "${MISSION}" > "${WORKDIR}/moosdb.log" 2>&1 &
MOOSDB_PID=$!
sleep 1
kill -0 "${MOOSDB_PID}" 2>/dev/null || { err "MOOSDB 未存活"; return 1; }
}
start_pm() {
info "启动 pPowerManger ..."
( cd "${WORKDIR}" && exec "${PM_BIN}" "${MISSION}" ) > "${WORKDIR}/pm.log" 2>&1 &
PM_PID=$!
sleep 1
kill -0 "${PM_PID}" 2>/dev/null || warn "pPowerManger 未存活(无 CCU 硬件属正常)"
}
start_host() {
info "启动 pPowerMangerHost (网页端口 ${WEB_PORT}) ..."
( cd "${WORKDIR}" && exec "${HOST_BIN}" "${MISSION}" ) > "${WORKDIR}/host.log" 2>&1 &
HOST_PID=$!
sleep 2
kill -0 "${HOST_PID}" 2>/dev/null || { err "pPowerMangerHost 未存活"; return 1; }
}
#-----------------------------------------------------------------------
# 测试项
#-----------------------------------------------------------------------
test_http() {
info "测试: 网页 HTTP (http://${SERVER_HOST}:${WEB_PORT}/) ..."
local code
code="$(curl -s -o /dev/null -w '%{http_code}' "http://${SERVER_HOST}:${WEB_PORT}/" 2>/dev/null || echo 000)"
if [ "${code}" = "200" ]; then
ok "网页 HTTP 200 通过"
return 0
else
err "网页 HTTP 失败 (code=${code})"
return 1
fi
}
# 反馈链路:向 MOOSDB 注入一条假的反馈变量 → 宿主订阅后应经 WS 推给浏览器
test_monitor_loop() {
info "测试: 反馈链路 (MOOSDB -> pPowerMangerHost -> WebSocket) ..."
if [ -z "${POKE_BIN}" ]; then
warn "未找到 uPokeDB,跳过反馈链路测试"
return 0
fi
if ! python3 -c "import websockets" >/dev/null 2>&1; then
warn "未安装 python3 websockets,跳过反馈链路测试"
return 0
fi
local fb_key="uPower_pmState_fb"
local fb_val='{"workCondition_uint8":9,"currentMod_uint8":1,"powerState_uint8":1,"faultLevel_uint8":0,"soc_uint16":88,"sustainableTime_int64":3600,"leakage_uint8":0,"info_str":"host-sim-test"}'
# 先让 WS 客户端连接等待快照,再注入反馈
FB_KEY="${fb_key}" python3 - "${WEB_PORT}" > "${WORKDIR}/ws_fb.log" 2>&1 <<'PY' &
import asyncio, json, os, sys
import websockets
PORT = int(sys.argv[1])
KEY = os.environ["FB_KEY"]
async def main():
uri = f"ws://localhost:{PORT}/ws"
async with websockets.connect(uri) as ws:
try:
while True:
msg = await asyncio.wait_for(ws.recv(), timeout=8)
snap = json.loads(msg)
if isinstance(snap, dict) and KEY in snap:
print(f"FEEDBACK_OK {KEY}")
return
except asyncio.TimeoutError:
pass
print("FEEDBACK_TIMEOUT")
asyncio.run(main())
PY
WS_PID=$!
sleep 1
"${POKE_BIN}" --host="${SERVER_HOST}" --port="${SERVER_PORT}" "${fb_key}=${fb_val}" >/dev/null 2>&1
wait "${WS_PID}" 2>/dev/null
if grep -q "FEEDBACK_OK" "${WORKDIR}/ws_fb.log" 2>/dev/null; then
ok "反馈链路通过(${fb_key} 已推送到浏览器)"
return 0
else
err "反馈链路失败(WS 未收到 ${fb_key})"
cat "${WORKDIR}/ws_fb.log" >&2
return 1
fi
}
# 指令下发:网页 WS 下发 -> 宿主 Notify -> MOOSDB -> (宿主订阅回环) -> 推回浏览器
# 利用宿主订阅了 uPower_pmState_fb,下发该变量后经 MOOSDB 回环,能收到即证明
# 指令确实进入了 MOOSDB。
test_command_notify() {
info "测试: 指令下发 (WebSocket -> pPowerMangerHost -> MOOSDB) ..."
if ! python3 -c "import websockets" >/dev/null 2>&1; then
warn "未安装 python3 websockets,跳过指令下发校验"
return 0
fi
python3 - "${WEB_PORT}" > "${WORKDIR}/ws_cmd.log" 2>&1 <<'PY'
import asyncio, json, sys
import websockets
PORT = int(sys.argv[1])
KEY = "uPower_pmState_fb"
TAG = "cmd-loopback-test"
async def main():
uri = f"ws://localhost:{PORT}/ws"
async with websockets.connect(uri) as ws:
await ws.send(json.dumps({"key": KEY, "value": {"info_str": TAG, "workCondition_uint8": 7}}))
try:
while True:
msg = await asyncio.wait_for(ws.recv(), timeout=8)
snap = json.loads(msg)
if isinstance(snap, dict) and KEY in snap:
obj = json.loads(snap[KEY])
if obj.get("info_str") == TAG:
print("CMD_OK")
return
except asyncio.TimeoutError:
pass
print("CMD_TIMEOUT")
asyncio.run(main())
PY
local r=$?
if [ ${r} -eq 0 ] && grep -q "CMD_OK" "${WORKDIR}/ws_cmd.log" 2>/dev/null; then
ok "指令下发到 MOOSDB 通过(uPower_pmState_fb 已回环)"
return 0
else
err "指令下发校验失败:未收到回环快照"
cat "${WORKDIR}/ws_cmd.log" >&2
return 1
fi
}
#-----------------------------------------------------------------------
# 命令入口
#-----------------------------------------------------------------------
cmd_run() {
check_binaries || return 1
start_moosdb || return 1
[ "${WITH_POWERMANGER}" = "1" ] && start_pm
start_host || return 1
info "全部启动完成。"
info "浏览器访问: http://${SERVER_HOST}:${WEB_PORT}/"
info "按 Ctrl-C 退出并清理。"
echo ""
trap 'info "退出清理..."; cleanup; exit 0' INT TERM
while kill -0 "${HOST_PID}" 2>/dev/null; do sleep 1; done
info "pPowerMangerHost 已退出。"
cleanup
}
cmd_test() {
check_binaries || return 1
start_moosdb || return 1
start_host || return 1
local pass=0 fail=0 r
test_http; r=$?; [ $r -eq 0 ] && pass=$((pass+1)) || fail=$((fail+1))
test_monitor_loop; r=$?; [ $r -eq 0 ] && pass=$((pass+1)) || fail=$((fail+1))
test_command_notify;r=$?; [ $r -eq 0 ] && pass=$((pass+1)) || fail=$((fail+1))
echo ""
info "==== 测试结果: 通过 ${pass} / 失败 ${fail} ===="
cleanup
[ "${fail}" -eq 0 ]
}
cmd_stop() {
pkill -f "${HOST_BIN}" 2>/dev/null
pkill -f "${PM_BIN}" 2>/dev/null
pkill -x MOOSDB 2>/dev/null
ok "已清理残留进程"
}
case "${1:-run}" in
run) cmd_run ;;
test) cmd_test ;;
build) build_targets ;;
stop) cmd_stop ;;
help|-h|--help) usage ;;
*) usage ;;
esac
+176
View File
@@ -0,0 +1,176 @@
#!/bin/bash
#=======================================================================
# FILE: scripts/sync-board-time.sh
# DESC: 校正 223 板卡(RK3588,默认 192.168.0.223)的系统时间:以
# 本机时间(或 --time 指定时间)为基准,经 SSH 设置板卡系统时
# 钟,并尽力写入硬件时钟 (RTC)。板卡网络隔离无 NTP,用本脚本对时。
#
# 用法:
# ./scripts/sync-board-time.sh # 用本机时间校正板卡
# ./scripts/sync-board-time.sh status # 只查看时间与偏差
# ./scripts/sync-board-time.sh --time "2026-09-01 12:00:00"
# # 用指定时间校正(本地时区)
#
# 参数:
# --host <ip> 目标主机(默认 192.168.0.223)
# --user <user> 登录用户(默认 root)
# --time <str> 手动指定基准时间(date 可解析的格式,按本地时区解释)
# --keep-ntp 不执行 timedatectl set-ntp false
# --no-rtc 不写硬件时钟(跳过 hwclock --systohc)
# -h, --help 显示帮助
#
# 说明:
# - 需要 root 登录与免密 SSH(ssh-copy-id root@<host>)。
# - 对时经 SSH 往返时延 (RTT) 折半补偿,精度约 ±0.1s(受网络抖动影响)。
# - 板卡若无可用 RTC,hwclock 失败仅告警;重启后时间可能回跳。
# - 对时会调整系统时钟,运行中的服务日志时间戳会跳变,属正常现象。
#=======================================================================
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HOST="192.168.0.223"
USER="root"
SET_TIME=""
KEEP_NTP=0
NO_RTC=0
info() { printf '\033[1;36m[time]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[time]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[time]\033[0m %s\n' "$*"; }
err() { printf '\033[1;31m[time]\033[0m %s\n' "$*"; }
usage() {
sed -n '2,26p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
}
#-------------------------------------------------------------------
# 参数解析
#-------------------------------------------------------------------
ACTION=""
while [ $# -gt 0 ]; do
case "$1" in
--host) [ $# -ge 2 ] || { err "--host 需要参数"; exit 1; }; HOST="$2"; shift 2 ;;
--user) [ $# -ge 2 ] || { err "--user 需要参数"; exit 1; }; USER="$2"; shift 2 ;;
--time) [ $# -ge 2 ] || { err "--time 需要参数"; exit 1; }; SET_TIME="$2"; shift 2 ;;
--keep-ntp) KEEP_NTP=1; shift ;;
--no-rtc) NO_RTC=1; shift ;;
status|help|-h|--help) ACTION="$1"; shift ;;
"") shift ;;
*) err "未知参数: $1(见 help)"; exit 1 ;;
esac
done
[ -n "${ACTION}" ] || ACTION="sync"
SSH_TARGET="${USER}@${HOST}"
SSH="ssh -o BatchMode=yes -o ConnectTimeout=5"
#-------------------------------------------------------------------
# 读取板卡时间并测 RTT
# 输出: "<epoch>|<板卡可读时间> <t0> <t1>"(t0/t1 为本机采样时刻)
#-------------------------------------------------------------------
read_board_time() {
local t0 t1 out
t0="$(date +%s.%N)"
out="$(${SSH} "${SSH_TARGET}" 'date "+%s.%N|%F %T %Z"' 2>/dev/null)" || {
err "无法免密 SSH 到 ${SSH_TARGET}(先配置免密: ssh-copy-id ${SSH_TARGET})" >&2
return 1
}
t1="$(date +%s.%N)"
printf '%s %.3f %.3f\n' "${out}" "${t0}" "${t1}"
}
# 从 read_board_time 输出解析: set -- <out> 后取 $1/$2/$3
parse_board_time() {
B_EPOCH="${1%%|*}"
B_DATE="${1#*|}"
T0="$2"
T1="$3"
MID="$(awk -v t0="${T0}" -v t1="${T1}" 'BEGIN{printf "%.3f", t0 + (t1-t0)/2}')"
DIFF="$(awk -v b="${B_EPOCH}" -v m="${MID}" 'BEGIN{printf "%+.3f", b - m}')"
}
#-------------------------------------------------------------------
# 命令: status
#-------------------------------------------------------------------
cmd_status() {
local out
out="$(read_board_time)" || return 1
parse_board_time ${out}
local rtt
rtt="$(awk -v t0="${T0}" -v t1="${T1}" 'BEGIN{printf "%.3f", t1-t0}')"
printf '本机时间: %s (RTT %.3fs)\n' "$(date -d "@${MID}" '+%F %T')" "${rtt}"
printf '板卡时间: %s\n' "${B_DATE}"
printf '偏差: 板卡比本机快 %s 秒\n' "${DIFF}"
}
#-------------------------------------------------------------------
# 命令: sync(默认)
#-------------------------------------------------------------------
cmd_sync() {
local out target board_reply
# 前置检查:SSH 连通 + 板卡 root 权限
out="$(read_board_time)" || return 1
parse_board_time ${out}
if ! ${SSH} "${SSH_TARGET}" '[ "$(id -u)" = 0 ]' 2>/dev/null; then
err "板卡登录用户需要 root 权限(当前 --user ${USER})"
return 1
fi
ok "SSH 连接正常: ${SSH_TARGET}"
info "当前偏差: 板卡比本机快 ${DIFF} 秒"
# 关闭 NTP 自动同步(板卡网络隔离本就无 NTP 源,防止干扰手动对时)
if [ "${KEEP_NTP}" = "0" ]; then
info "关闭板卡 NTP 自动同步 (timedatectl set-ntp false) ..."
${SSH} "${SSH_TARGET}" "command -v timedatectl >/dev/null 2>&1 && timedatectl set-ntp false || true"
fi
# 计算目标时间:手动指定值直接用;否则取本机时间 + RTT/2 补偿
if [ -n "${SET_TIME}" ]; then
target="$(date -d "${SET_TIME}" '+%s.%N' 2>/dev/null)" || {
err "无法解析时间: ${SET_TIME}"; return 1;
}
info "基准时间(手动): ${SET_TIME}"
else
local t2
t2="$(date +%s.%N)"
target="$(awk -v t2="${t2}" -v t0="${T0}" -v t1="${T1}" 'BEGIN{printf "%.3f", t2 + (t1-t0)/2}')"
info "基准时间(本机 + RTT/2 补偿)"
fi
info "设置板卡系统时间 -> $(date -d "@${target}" '+%F %T') ..."
board_reply="$(${SSH} "${SSH_TARGET}" "date -s @${target} 2>&1")" || {
err "date -s 失败: ${board_reply}"
return 1
}
ok "板卡返回: ${board_reply}"
# 写硬件时钟(无 RTC 时仅告警)
if [ "${NO_RTC}" = "0" ]; then
info "同步硬件时钟 (hwclock --systohc) ..."
if ${SSH} "${SSH_TARGET}" "hwclock --systohc" 2>/dev/null; then
ok "RTC 已同步"
else
warn "hwclock 失败(板卡可能无 RTC 或驱动未加载),重启后时间可能回跳"
fi
fi
# 校验
out="$(read_board_time)" || return 1
parse_board_time ${out}
info "校正后偏差: 板卡比本机快 ${DIFF} 秒"
ok "板卡时间: ${B_DATE}"
}
#-------------------------------------------------------------------
# 命令入口
#-------------------------------------------------------------------
case "${ACTION}" in
sync) cmd_sync ;;
status) cmd_status ;;
help|-h|--help) usage ;;
*) usage ;;
esac
+5
View File
@@ -19,6 +19,11 @@ INCLUDE_DIRECTORIES(${LOCAL_LIBRARY_DIRS})
ADD_SUBDIRECTORY(pPowerManger)
# ADD_SUBDIRECTORY(appPowerMangerWebUI)
add_subdirectory(pPMtest)
add_subdirectory(pPowerMangerHost)
add_subdirectory(pCCU)
add_subdirectory(pCanBridge)
add_subdirectory(pMotor)
add_subdirectory(pELoad)
##############################################################################
# END of CMakeLists.txt
##############################################################################
-37
View File
@@ -1,37 +0,0 @@
#--------------------------------------------------------
# The CMakeLists.txt for: appPowerMangerWebUI
# Author(s):
#--------------------------------------------------------
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 设置 vcpkg 工具链文件路径
set(CMAKE_TOOLCHAIN_FILE "/home/zjk/project/lib/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE STRING "")
# 查找Boost库
find_package(Boost REQUIRED COMPONENTS system thread)
#find crow库
find_package(Crow CONFIG REQUIRED)
# Find SQLite3 package
find_package(SQLite3 REQUIRED)
SET(SRC
main.cpp
DBImage.cpp
)
ADD_EXECUTABLE(appPowerMangerWebUI ${SRC})
TARGET_LINK_LIBRARIES(appPowerMangerWebUI
${MOOS_LIBRARIES}
mbutil
m
pthread
Crow::Crow
asio::asio
jsoncpp
SQLiteDB
sqlite3
Boost::system
Boost::thread)
-230
View File
@@ -1,230 +0,0 @@
///////////////////////////////////////////////////////////////////////////
//
// MOOS - Mission Oriented Operating Suite
//
// A suit of Applications and Libraries for Mobile Robotics Research
// Copyright (C) 2001-2005 Massachusetts Institute of Technology and
// Oxford University.
//
// This software was written by Paul Newman at MIT 2001-2002 and Oxford
// University 2003-2005. email: pnewman@robots.ox.ac.uk.
//
// This file is part of a MOOS Utility Component.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
////////////////////////// END_GPL //////////////////////////////////
// DBImage.cpp: implementation of the CDBImage class.
//
//////////////////////////////////////////////////////////////////////
#include "DBImage.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
bool CDBImage::Set(MOOSMSG_LIST & InMail)
{
MOOSMSG_LIST::iterator p;
m_Lock.Lock();
//MOOSTrace("Rx'd %d \n",InMail.size());
for(p = InMail.begin();p!=InMail.end();p++)
{
CMOOSMsg & rMsg= *p;
//MOOSTrace("Rx'd %s \n",rMsg.m_sKey.c_str());
if(!m_Mask.empty() && m_Mask.find(rMsg.GetSource())!=m_Mask.end())
{
continue;
}
if(rMsg.IsDataType(MOOS_NOT_SET) && !m_bShowPending)
{
continue;
}
int nNdx = GetIndex(rMsg.m_sKey);
if(nNdx<0)
{
m_IndexMap[rMsg.m_sKey] = m_DBData.size();
m_DBData.push_back(CVar(rMsg));
}
else
{
bool bChanging = !( rMsg.GetTime()==m_DBData[nNdx].GetTimeVal());
m_DBData[nNdx]= CVar(rMsg);
m_DBData[nNdx].Changed(bChanging);
}
}
m_Lock.UnLock();
return true;
}
int CDBImage::GetIndex(const std::string & sName)
{
std::map<std::string,int>::iterator q = m_IndexMap.find(sName);
if(q!=m_IndexMap.end())
{
int n = q->second;
return n;
}
else
{
return -1;
}
}
bool CDBImage:: HasChanged(int n)
{
if(n >= static_cast<int> (m_DBData.size()) || n<0)
return false;
return m_DBData[n].IsChanged();
}
bool CDBImage::Get(CVar & rVar, int n)
{
m_Lock.Lock();
if(n>=static_cast<int> (m_DBData.size()) || n<0)
{
m_Lock.UnLock();
return false;
}
rVar = m_DBData[n];
m_Lock.UnLock();
return true;
}
bool CDBImage::SetProcInfo(MOOSMSG_LIST & InMail)
{
m_Lock.Lock();
// TODO: Add your control notification handler code here
m_nClients = InMail.size();
m_Processes.clear();
MOOSMSG_LIST::iterator p;
for(p=InMail.begin();p!=InMail.end();p++)
{
CMOOSMsg & rMsg = *p;
std::string sTmp = rMsg.m_sVal;
std::string sWho = MOOSChomp(sTmp,":");
if(m_Processes.find(sWho)==m_Processes.end())
{
ProcInfo Info;
m_Processes[sWho] = Info;
}
std::string sSubscribed,sPublished;
sSubscribed = MOOSChomp(sTmp,"PUBLISHED=");
sPublished = sTmp;
MOOSChomp(sSubscribed,"SUBSCRIBED=");
while(!sSubscribed.empty())
{
sTmp = MOOSChomp(sSubscribed,",");
if(!sTmp.empty())
{
m_Processes[sWho].m_Subscribed.push_front(sTmp);
}
}
while(!sPublished.empty())
{
sTmp = MOOSChomp(sPublished,",");
if(!sTmp.empty())
{
m_Processes[sWho].m_Published.push_front(sTmp);
}
}
}
m_Lock.UnLock();
return true;
}
bool CDBImage::GetProcInfo(const std::string & sWhat,STRING_LIST & sSubs,STRING_LIST & sPubs)
{
bool bRes = false;
m_Lock.Lock();
PROC_INFO_MAP::iterator q = m_Processes.find(sWhat);
if(q!=m_Processes.end())
{
sSubs = q->second.m_Subscribed;
sPubs = q->second.m_Published;
bRes = true;
}
m_Lock.UnLock();
return bRes;
}
bool CDBImage::GetProcesses(STRING_LIST & sProcs)
{
m_Lock.Lock();
sProcs.clear();
PROC_INFO_MAP::iterator q;
for(q=m_Processes.begin();q!=m_Processes.end();q++)
{
sProcs.push_back(q->first.c_str());
}
m_Lock.UnLock();
return true;
}
bool CDBImage::SetMask(std::set<std::string> Mask)
{
m_Mask = Mask;
return true;
}
bool CDBImage::Clear()
{
m_Lock.Lock();
m_DBData.clear();
m_IndexMap.clear();
m_Lock.UnLock();
return true;
}
-170
View File
@@ -1,170 +0,0 @@
///////////////////////////////////////////////////////////////////////////
//
// MOOS - Mission Oriented Operating Suite
//
// A suit of Applications and Libraries for Mobile Robotics Research
// Copyright (C) 2001-2005 Massachusetts Institute of Technology and
// Oxford University.
//
// This software was written by Paul Newman at MIT 2001-2002 and Oxford
// University 2003-2005. email: pnewman@robots.ox.ac.uk.
//
// This file is part of a MOOS Utility Component.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
////////////////////////// END_GPL //////////////////////////////////
// DBImage.h: interface for the CDBImage class.
//
//////////////////////////////////////////////////////////////////////
#ifdef _WIN32
#pragma warning(disable : 4786)
#endif
#if !defined(AFX_DBIMAGE_H__E4F9EC4D_7049_4EC8_86BA_0AF21BBCA23C__INCLUDED_)
#define AFX_DBIMAGE_H__E4F9EC4D_7049_4EC8_86BA_0AF21BBCA23C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "MOOS/libMOOS/MOOSLib.h"
#include <string>
#include <map>
#include <vector>
class CDBImage
{
public:
bool Clear();
CDBImage()
{
m_bShowPending = false;
}
struct ProcInfo
{
std::string m_sName;
STRING_LIST m_Published;
STRING_LIST m_Subscribed;
};
typedef std::map<std::string,ProcInfo> PROC_INFO_MAP;
class CVar
{
public:
CVar()
{
m_dfTime = -1;
}
CVar(CMOOSMsg & rMsg)
{
m_dfTime = rMsg.m_dfTime;
switch(rMsg.m_cDataType)
{
case MOOS_STRING:
m_eType = STR;
break;
case MOOS_DOUBLE:
m_eType = DBL;
break;
case MOOS_BINARY_STRING:
m_eType = BIN;
break;
default:
m_eType = UNKNOWN;
}
m_sStr = rMsg.GetAsString();
m_sCommunity = rMsg.m_sOriginatingCommunity;
m_sName = rMsg.m_sKey;
m_sFreq = MOOSFormat("%.1f",rMsg.m_dfVal2);
m_sSource = rMsg.m_sSrc;
m_bChanged = false;
}
std::string GetTime(){return MOOSFormat("%.3f",m_dfTime);};
double GetTimeVal(){return m_dfTime;};
std::string GetValue(){return m_eType==UNKNOWN?"":m_sStr;};
std::string GetCommunity(){return m_sCommunity;};
std::string GetName(){return m_sName;};
std::string GetFrequency(){return m_sFreq;};
std::string GetSource(){return m_sSource;};
bool IsChanged(){return m_bChanged;};
void Changed(bool b){m_bChanged = b;};
std::string GetType()
{
switch(m_eType)
{
case STR:
return "$";
case DBL:
return "D";
case BIN:
return "B";
default:
return "?";
}
}
protected:
double m_dfTime;
std::string m_sStr;
enum Type{ STR,DBL,BIN,UNKNOWN};
Type m_eType;
std::string m_sCommunity;
std::string m_sSrc;
std::string m_sName;
std::string m_sFreq;
std::string m_sSource;
bool m_bChanged;
};
bool Set(MOOSMSG_LIST & InMail);
bool Get(CVar & rVar, int n);
bool HasChanged(int n);
bool SetProcInfo(MOOSMSG_LIST & InMail) ;
bool GetProcesses(STRING_LIST & sProcs);
int GetNumVariables(){return m_DBData.size();}
bool GetProcInfo(const std::string & sWhat,STRING_LIST & sSubs,STRING_LIST & sPubs);
bool SetMask(std::set<std::string> Mask);
bool ShowPending(bool b){m_bShowPending = b;return true;};
protected:
int GetIndex(const std::string & sName);
std::vector<CVar> m_DBData;
std::map<std::string,int> m_IndexMap;
std::set<std::string> m_Mask;
bool m_bShowPending ;
int m_nClients;
int m_nVariables;
PROC_INFO_MAP m_Processes;
CMOOSLock m_Lock;
};
#endif // !defined(AFX_DBIMAGE_H__E4F9EC4D_7049_4EC8_86BA_0AF21BBCA23C__INCLUDED_)
-305
View File
@@ -1,305 +0,0 @@
#include <string>
#include "crow.h"
#include <iostream>
#include "json/json.h"
#include <sys/inotify.h>
#include <unistd.h>
#include <dirent.h>
#include <fstream>
#include <vector>
#include <thread>
#include <mutex>
#include <memory>
#include "../pPowerManger/SQLiteDB.h"
#include "../pPowerManger/pmSysvariable.h"
#include <MOOS/libMOOS/Comms/MOOSAsyncCommClient.h>
#include "DBImage.h"
using namespace std;
// 定义全局变量
ccuState ccustate;
StateData stateData;
mutex wsMutex;
vector<crow::websocket::connection*> wsConnections;
shared_ptr<SQLiteDB> db;
shared_ptr<MOOS::MOOSAsyncCommClient> moosClient;
shared_ptr<CDBImage> dbImage;
bool bContinue = true;
// WebSocket消息处理
void sendStateUpdate() {
crow::json::wvalue msg;
string time;
{
lock_guard<mutex> lock(wsMutex);
db->getStateData(stateData, time);
}
// 基本状态信息
msg["state"] = {
{"time", time},
{"state_id", stateData.state_id},
{"state_name", stateData.state_name},
{"event", stateData.event},
{"event_id", stateData.event_id},
{"fault_code", stateData.fault_code},
{"note", stateData.note}
};
// 数据库状态信息
msg["db_status"] = {
{"connected", db->isDatabaseConnected("/home/zjk/project/H100/H100PowerManger/data", "power_data.db")},
{"database", db->getDBPatch()},
{"size", db->getDatabaseSize()}
};
// MOOS数据库状态信息
msg["moos_db_status"] = {
{"connected", moosClient->IsConnected()},
{"host", "localhost"},
{"port", 9000}
};
// 进程详细信息
{
lock_guard<mutex> lock(wsMutex);
STRING_LIST processes;
dbImage->GetProcesses(processes);
crow::json::wvalue::list processList;
for(const auto& proc : processes) {
STRING_LIST subs, pubs;
if(dbImage->GetProcInfo(proc, subs, pubs)) {
crow::json::wvalue processInfo;
processInfo["name"] = proc;
std::vector<std::string> subs_vec(subs.begin(), subs.end());
std::vector<std::string> pubs_vec(pubs.begin(), pubs.end());
processInfo["subscribed"] = subs_vec;
processInfo["published"] = pubs_vec;
processList.push_back(std::move(processInfo));
}
}
msg["processes"] = {
{"count", processes.size()},
{"list", std::move(processList)}
};
}
// 数据库变量信息
{
lock_guard<mutex> lock(wsMutex);
int varCount = dbImage->GetNumVariables();
crow::json::wvalue::list varList;
// 遍历所有变量
for(int i = 0; i < varCount; i++) {
CDBImage::CVar var;
if(dbImage->Get(var, i)) {
varList.push_back({
{"name", var.GetName()},
{"value", var.GetValue()},
{"time", var.GetTime()},
{"type", var.GetType()},
{"source", var.GetSource()}
});
}
}
msg["variables"] = {
{"count", varCount},
{"data", std::move(varList)} // 包含所有变量的数组
};
}
// 发送WebSocket消息
{
lock_guard<mutex> lock(wsMutex);
for(auto conn : wsConnections) {
conn->send_text(msg.dump());
}
}
}
// 订阅MOOS变量
bool FetchLoop(MOOS::MOOSAsyncCommClient* comms, CDBImage* m_DBImage)
{
int nCounts = 0;
while(true) // 外部需要控制循环退出条件
{
if(comms->IsConnected())
{
// 每5次循环获取一次进程摘要
if(nCounts++ % 5 == 0)
{
MOOSMSG_LIST procMail;
if(comms->ServerRequest("PROC_SUMMARY", procMail))
{
m_DBImage->SetProcInfo(procMail);
// 打印进程信息
// STRING_LIST processes;
// m_DBImage->GetProcesses(processes);
// std::cout << "Active processes: " << processes.size() << std::endl;
}
}
// 每次循环都获取数据库摘要
MOOSMSG_LIST dbMail;
if(comms->ServerRequest("ALL", dbMail))
{
m_DBImage->Set(dbMail);
// 打印数据库变量信息
int varCount = m_DBImage->GetNumVariables();
// std::cout << "Database variables: " << varCount << std::endl;
// 打印最近更新的变量
if(varCount > 0) {
CDBImage::CVar lastVar;
if(m_DBImage->Get(lastVar, varCount-1)) {
// std::cout << "Last updated variable: "
// << lastVar.GetName() << " = "
// << lastVar.GetValue() << std::endl;
}
}
}
}
// 保持大约4Hz的频率
MOOSPause(250);
}
return true;
}
int main() {
crow::SimpleApp app;
db = make_shared<SQLiteDB>("/home/zjk/project/H100/H100PowerManger/data/power_data.db");
moosClient = make_shared<MOOS::MOOSAsyncCommClient>();
dbImage = make_shared<CDBImage>();
moosClient->Run("localhost", 9000, "appPowerMangerWebUI");
// WebSocket路由
CROW_WEBSOCKET_ROUTE(app, "/ws/state")
.onopen([&](crow::websocket::connection& conn){
lock_guard<mutex> lock(wsMutex);
wsConnections.push_back(&conn);
cout << "New WebSocket connection" << endl;
})
.onclose([&](crow::websocket::connection& conn, const string& /*reason*/, uint16_t /*code*/){
lock_guard<mutex> lock(wsMutex);
wsConnections.erase(
remove(wsConnections.begin(), wsConnections.end(), &conn),
wsConnections.end());
cout << "WebSocket connection closed" << endl;
})
.onmessage([&](crow::websocket::connection& conn, const string& data, bool is_binary){
cout << "Received WebSocket message: " << data << endl;
});
// 状态更新定时器
thread([](){
while(true) {
sendStateUpdate();
this_thread::sleep_for(chrono::seconds(1));
}
}).detach();
//MOOS数据库更新定时器
std::thread fetchThread([&](){
while(bContinue) {
FetchLoop(moosClient.get(), dbImage.get());
}
});
// 绑定主页路由
CROW_ROUTE(app, "/")([]() {
auto page = crow::mustache::load_text("index.html");
return page;
});
// MOOS通知API
CROW_ROUTE(app, "/api/moos-notify")
.methods("POST"_method)
([](const crow::request& req) {
auto json = crow::json::load(req.body);
if (!json) {
return crow::response(400, "Invalid JSON");
}
string var = json["var"].s();
string key = json["key"].s();
if (var.empty() || key.empty()) {
return crow::response(400, "Var and Key are required");
}
try {
moosClient->Notify(var, key);
return crow::response(200, "{\"success\":true}");
} catch (const exception& e) {
return crow::response(500,
crow::json::wvalue{{"success", false}, {"message", e.what()}}.dump());
}
});
// 下载数据库API
CROW_ROUTE(app, "/api/download-db")
([]() {
const std::string dbPath = "/home/zjk/project/H100/H100PowerManger/data/power_data.db";
const size_t chunkSize = 4096; // 4KB chunks
std::ifstream file(dbPath, std::ios::binary | std::ios::ate);
if (!file) {
return crow::response(404, "Database file not found");
}
// 获取文件大小
std::streamsize fileSize = file.tellg();
file.seekg(0, std::ios::beg);
if (fileSize == 0) {
return crow::response(500, "Database file is empty");
}
// 创建响应
crow::response res;
res.code = 200;
res.set_header("Content-Type", "application/octet-stream");
res.set_header("Content-Disposition", "attachment; filename=power_data.db");
res.set_header("Content-Length", std::to_string(fileSize));
try {
// 分块读取和写入文件
auto buffer = std::make_unique<char[]>(chunkSize);
size_t totalSent = 0;
while (totalSent < fileSize) {
size_t remaining = fileSize - totalSent;
size_t readSize = std::min(remaining, chunkSize);
file.read(buffer.get(), readSize);
if (file.gcount() <= 0) break;
res.write(std::string(buffer.get(), file.gcount()));
totalSent += file.gcount();
}
file.close();
return res;
} catch (const std::exception& e) {
file.close();
return crow::response(500, std::string("Download failed: ") + e.what());
}
});
// 启动服务器
app.port(18080)
.multithreaded()
.concurrency(16)
.run();
return 0;
}
-108
View File
@@ -1,108 +0,0 @@
#--------------------------------------------------------
# The CMakeLists.txt for: pPowerManger
# Author(s):
#--------------------------------------------------------
# Set System Specific Libraries
if (${WIN32})
# Windows Libraries
SET(SYSTEM_LIBS
wsock32 )
else (${WIN32})
# Linux and Apple Libraries
SET(SYSTEM_LIBS
m
pthread )
endif (${WIN32})
# Use local SQLite3 source
SET(SQLITE_SRC
sqlit3/SQLite.cpp
sqlit3/sqlite3.c
)
SET(HTTP_SRC
httpserver/mongoose.c
httpserver/httpserver.cpp)
SET(LOG_SRC
logc/loguru.cpp)
SET(UDP_COMM_SRC
udpcomm/udpComm.cpp)
SET(FSMLIB_SRC
fsm/PowerManagerFsm.cpp
fsm/CruiseMode.cpp
fsm/DJMode.cpp
fsm/FaultState.cpp
fsm/FloatAdjust.cpp
fsm/FloatDown.cpp
fsm/HighSpeedMode.cpp
fsm/InitState.cpp
fsm/RemoteControl.cpp
fsm/ShoreBasedReady.cpp
fsm/StandbyState.cpp
fsm/SurfaceSurvey.cpp
fsm/UnderwaterSurvey.cpp
fsm/WaterBasedReady.cpp
)
SET(SRC
${FSMLIB_SRC}
${SQLITE_SRC}
${HTTP_SRC}
${UDP_COMM_SRC}
${LOG_SRC}
PowerManger.cpp
PowerManger_Info.cpp
# udpComm.cpp
main.cpp
# SQLiteDB.cpp
driver.cpp
LowerCommManager.cpp
UpperCommManager.cpp
# loguru.cpp
upmsg/upmsg_definitions.cpp
)
ADD_EXECUTABLE(pPowerManger ${SRC})
TARGET_INCLUDE_DIRECTORIES(pPowerManger PRIVATE sqlit3)
TARGET_LINK_LIBRARIES(pPowerManger
${MOOS_LIBRARIES}
apputil
mbutil
m
pthread
jsoncpp
${SYSTEM_LIBS}
dl
)
# Add SQLiteDB static library
SET(UDPCOMM_SRC
udpComm.cpp
)
# SET(SQLITEDB_SRC
# SQLiteDB.cpp
# )
SET(DRIVER_SRC
driver.cpp)
# SET(POWERMANGE_SRC
# PowerManger.cpp
# PowerManger_Info.cpp
# udpComm.cpp
# SQLiteDB.cpp
# driver.cpp
# PowerManagerFsm.cpp)
# ADD_LIBRARY(SQLiteDB STATIC ${SQLITEDB_SRC})
# ADD_LIBRARY(UDPComm STATIC ${UDPCOMM_SRC})
# # ADD_LIBRARY(FSMLib STATIC ${FSMLIB_SRC})
# ADD_LIBRARY(Driver STATIC ${DRIVER_SRC})
# ADD_LIBRARY(PowerMange STATIC ${POWERMANGE_SRC})
-929
View File
@@ -1,929 +0,0 @@
#include "LowerCommManager.h"
#include "PowerManger.h"
#include "driver.h"
#include "udpcomm/udpComm.h"
bool _ListenCB(void * pParam)
{
LowerCommManager* pMe = (LowerCommManager* )pParam;
return pMe->listenThreadFunc();
}
LowerCommManager::LowerCommManager(PowerManger* pm, long myPort, long ccuPort, const std::string& cccHost){
m_pm = pm;
m_udpComm = new udpComm();
m_port = myPort;
m_udpComm->ccuHost = cccHost;
m_udpComm->ccuPort = ccuPort;
m_udpComm->udpScoket = new XPCUdpSocket(m_port);
std::cout << MOOS::ConsoleColours::yellow() << "╔════════════════════════╗" << MOOS::ConsoleColours::reset() << std::endl;
std::cout << MOOS::ConsoleColours::yellow() << "║ [LOCAL PORT] "
<< MOOS::ConsoleColours::cyan() << m_port
<< MOOS::ConsoleColours::yellow() << std::setw(15) << " ║" << MOOS::ConsoleColours::reset() << std::endl;
std::cout << MOOS::ConsoleColours::yellow() << "║ [CCU HOST ] "
<< MOOS::ConsoleColours::cyan() << m_udpComm->ccuHost
<< MOOS::ConsoleColours::yellow() << std::setw(8) << " ║" << MOOS::ConsoleColours::reset() << std::endl;
std::cout << MOOS::ConsoleColours::yellow() << "║ [CCU PORT ] "
<< MOOS::ConsoleColours::cyan() << m_udpComm->ccuPort
<< MOOS::ConsoleColours::yellow() << std::setw(15) << " ║" << MOOS::ConsoleColours::reset() << std::endl;
std::cout << MOOS::ConsoleColours::yellow() << "╚════════════════════════╝" << MOOS::ConsoleColours::reset() << std::endl;
}
LowerCommManager::~LowerCommManager(){
try {
stopListenThread();
if (m_udpComm) {
delete m_udpComm;
m_udpComm = nullptr;
}
} catch (...) {
cerr << "Error during cleanup" << endl;
}
}
bool LowerCommManager::initUdpComm(){
if (m_udpComm->udpScoket) {
try {
std::cout << MOOS::ConsoleColours::yellow() << "Attempting to bind UDP socket..." << MOOS::ConsoleColours::reset() << std::endl;
m_udpComm->udpScoket->vBindSocket(); // 可能会抛出 XPCException 或其他 std::exception
std::cout << MOOS::ConsoleColours::green() << "UDP bind succeeded!" << MOOS::ConsoleColours::reset() << std::endl;
LOG_F(INFO, "UDP bind succeeded!");
}
catch (const std::exception& e) {
std::cerr << MOOS::ConsoleColours::red()
<< "[UDP Bind Failed] Exception: " << e.what()
<< MOOS::ConsoleColours::reset() << std::endl;
LOG_F(ERROR, "UDP bind failed: %s", e.what());
return false;
}
catch (...) {
std::cerr << MOOS::ConsoleColours::red()
<< "[UDP Bind Failed] Unknown exception occurred!"
<< MOOS::ConsoleColours::reset() << std::endl;
LOG_F(ERROR, "Unknown exception occurred during UDP bind");
return false;
}
} else {
std::cerr << MOOS::ConsoleColours::red()
<< "[UDP Init Failed] udpSocket is null!"
<< MOOS::ConsoleColours::reset() << std::endl;
LOG_F(ERROR, "udpSocket is null!");
return false;
}
return true;
}
void LowerCommManager::processPeriodicTasks() {
sendCcuCommand(m_pm->m_CcuColCmd);
m_pm->m_db->insertData(m_pm->m_CcuColCmd);
//延时5ms
usleep(5000);
if(m_pm->m_systemData->device_common_status.dis_hv_c1.isTimeout )
{
disHighVolBusCmd clr1{};
m_udpComm->sendDisSysCmd(clr1);
MOOSPause(200);
}
if(m_pm->m_systemData->device_common_status.dis_lv_c1.isTimeout )
{
disHighBVolBusCmd clr3{};
m_udpComm->sendDisSysCmd(clr3);
MOOSPause(200);
}
m_udpComm->clearMsgQueue();
}
void LowerCommManager::operate_hold()
{
disHighVolBusCmd clr1{};
disHighAVolBusCmd clr2{};
disHighBVolBusCmd clr3{};
disLowBusCmd clr4{};
m_udpComm->sendDisSysCmd(clr1);
MOOSPause(200);
m_udpComm->sendDisSysCmd(clr2);
MOOSPause(200);
m_udpComm->sendDisSysCmd(clr3);
MOOSPause(200);
m_udpComm->sendDisSysCmd(clr4);
}
bool LowerCommManager::sendCcuCommand(const ccuColCmd cmd){
// m_pm->m_db->insertCcuSysCmd(cmd);
if(!m_udpComm->sendCcuColCmd(cmd))
cout << "send ccu command failed!" << endl;
return true;
}
bool LowerCommManager::sendDisSysCommand(const disSysBreakerList& cmd){
//根据目标状态进行断路器操作
//a. 高压母线的断路器
disHighVolBusCmd a;
a.fuelCellCircuitBreaker = getBreakerOption(cmd.bus1Breaker.fuelCellCircuitBreaker);
a.lithiumBatteryGroupInstrumentCircuitBreaker = getBreakerOption(cmd.bus1Breaker.lithiumBatteryGroupInstrumentCircuitBreaker);
a.dcDc5ModuleCircuitBreaker = getBreakerOption(cmd.bus1Breaker.dcDc5ModuleCircuitBreaker);
a.powerLithiumBatteryCircuitBreaker = getBreakerOption(cmd.bus1Breaker.powerLithiumBatteryCircuitBreaker);
a.propulsionMotorCircuitBreaker = getBreakerOption(cmd.bus1Breaker.propulsionMotorCircuitBreaker);
a.bowHighVoltageDistributionBoxCircuitBreaker = getBreakerOption(cmd.bus1Breaker.bowHighVoltageDistributionBoxCircuitBreaker);
a.tyzReservedCircuitBreaker = getBreakerOption(cmd.bus1Breaker.tyzReservedCircuitBreaker);
a.sternFTDevice45CircuitBreaker = getBreakerOption(cmd.bus1Breaker.sternFTDevice45CircuitBreaker);
a.sternRudderSwitch1CircuitBreaker = getBreakerOption(cmd.bus1Breaker.sternRudderSwitch1CircuitBreaker);
a.sternRudderSwitch2CircuitBreaker = getBreakerOption(cmd.bus1Breaker.sternRudderSwitch2CircuitBreaker);
a.reservedCircuitBreaker = getBreakerOption(cmd.bus1Breaker.reservedCircuitBreaker);
a.fbReservedCircuitBreaker = getBreakerOption(cmd.bus1Breaker.fbReservedCircuitBreaker);
a.dcdc5Module = getBreakerOption(cmd.bus1Breaker.dcdcmodel);
a.coolingSystem = getBreakerOption(cmd.bus1Breaker.coolingSystem);
m_udpComm->sendDisSysCmd(a);
m_pm->m_db->insertData(a);
usleep(5000);
//b. 高压汇流排A断路器
disHighAVolBusCmd b;
b.bowFTDevice123CircuitBreaker = getBreakerOption(cmd.bus2Breaker.bowFTDevice123CircuitBreaker);
b.actuatorCircuitBreaker = getBreakerOption(cmd.bus2Breaker.actuatorCircuitBreaker);
b.mastSteeringGearControlBoxCircuitBreaker = getBreakerOption(cmd.bus2Breaker.mastSteeringGearControlBoxCircuitBreaker);
b.xczCircuitBreaker = getBreakerOption(cmd.bus2Breaker.xczCircuitBreaker);
b.bowRudderControlBoxCircuitBreaker = getBreakerOption(cmd.bus2Breaker.bowRudderControlBoxCircuitBreaker);
b.openWaterCoverStartCylinderCircuitBreaker = getBreakerOption(cmd.bus2Breaker.openWaterCoverStartCylinderCircuitBreaker);
m_udpComm->sendDisSysCmd(b);
m_pm->m_db->insertData(b);
usleep(5000);
//c. 低压主母线断路器
disHighBVolBusCmd c;
c.lithiumBatteryGroupInstrumentCircuitBreaker = 0x55;
c.bowLowVoltageDistributionBoxCircuitBreaker = 0xAA;
c.unit4InstrumentDC48VCircuitBreaker = 0xAA;
c.dcC1DCDistributionPanelCircuitBreaker = 0xAA;
c.reservedCircuitBreaker1 = 0xAA;
c.reservedCircuitBreaker2 = 0xAA;
c.emergencyLithiumBatteryGroup2CircuitBreaker = 0xAA;
m_udpComm->sendDisSysCmd(c);
m_pm->m_db->insertData(c);
usleep(5000);
//d. 闭合低压汇流排断路器
disLowBusCmd d;
d.unit1CircuitBreaker = getBreakerOption(cmd.bus4Breaker.unit1CircuitBreaker);
d.unit2CircuitBreaker = getBreakerOption(cmd.bus4Breaker.unit2CircuitBreaker);
d.unit3CircuitBreaker = getBreakerOption(cmd.bus4Breaker.unit3CircuitBreaker);
d.unit5CircuitBreaker = getBreakerOption(cmd.bus4Breaker.unit5CircuitBreaker);
d.bowPZDeviceCircuitBreaker = getBreakerOption(cmd.bus4Breaker.bowPZDeviceCircuitBreaker);
d.reservedCircuitBreaker1 = getBreakerOption(cmd.bus4Breaker.reservedCircuitBreaker1);
d.reservedCircuitBreaker2 = getBreakerOption(cmd.bus4Breaker.reservedCircuitBreaker2);
d.emergencyLithiumBatteryGroup1CircuitBreaker = getBreakerOption(cmd.bus4Breaker.emergencyLithiumBatteryGroup1CircuitBreaker);
m_udpComm->sendDisSysCmd(d);
m_pm->m_db->insertData(d);
usleep(5000);
return true;
}
void LowerCommManager::clearMsgQueue(){
m_udpComm->clearMsgQueue();
}
bool LowerCommManager::updatePoweSystemStates(){
// std::cout << "updatePoweSystemStates" << std::endl;
if(!m_udpComm->m_qReceiveCcuStateBuffer.empty())
{
double time;
m_udpComm->popMsgFormQueue(time,m_CcuCurrentState);
m_pm->m_pmCurrentState.ccustate = m_CcuCurrentState.data;
m_pm->m_db->insertData(m_pm->m_pmCurrentState.ccustate); //写入数据库
//DEBUG
//写入文件
// std::ofstream outFile("ccuState.txt", std::ios::app);
// coutMsg(m_CcuCurrentState, outFile);
// coutMsg(m_CcuCurrentState,std::cout);
msg_CcuStateFbMsg_Update_time = time;
}
if(!m_udpComm->m_qReceiveDisHighVolBusBuffer.empty())
{
double time;
m_udpComm->popMsgFormQueue(time,m_disHighVolBusCurrentState);
m_pm->m_pmCurrentState.bus1State = m_disHighVolBusCurrentState.data;
m_pm->m_db->insertData(m_pm->m_pmCurrentState.bus1State); //写入数据库
msg_disHighVolBusFbMsg_Update_time = time;
disBus1Breaker* pdis1=new disBus1Breaker;
(m_pm->m_pmCurrentState.bus1State.fuelCellCircuitBreaker == BREAK_ON) ? pdis1 -> fuelCellCircuitBreaker = 1 : pdis1 -> fuelCellCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.powerLithiumBatteryCircuitBreaker == BREAK_ON) ? pdis1 -> powerLithiumBatteryCircuitBreaker = 1 : pdis1 -> powerLithiumBatteryCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.propulsionMotorCircuitBreaker == BREAK_ON) ? pdis1 -> propulsionMotorCircuitBreaker = 1 : pdis1 -> propulsionMotorCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.lithiumBatteryGroupInstrumentCircuitBreaker == BREAK_ON) ? pdis1 -> lithiumBatteryGroupInstrumentCircuitBreaker = 1 : pdis1 -> lithiumBatteryGroupInstrumentCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.dcDc5ModuleCircuitBreaker == BREAK_ON) ? pdis1 -> dcDc5ModuleCircuitBreaker = 1 : pdis1 -> dcDc5ModuleCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.bowHighVoltageDistributionBoxCircuitBreaker == BREAK_ON) ? pdis1 -> bowHighVoltageDistributionBoxCircuitBreaker = 1 : pdis1 -> bowHighVoltageDistributionBoxCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.sternFTDevice45CircuitBreaker == BREAK_ON) ? pdis1 -> sternFTDevice45CircuitBreaker = 1 : pdis1 -> sternFTDevice45CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.sternRudderSwitch1CircuitBreaker == BREAK_ON) ? pdis1 -> sternRudderSwitch1CircuitBreaker = 1 : pdis1 -> sternRudderSwitch1CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.sternRudderSwitch2CircuitBreaker == BREAK_ON) ? pdis1 -> sternRudderSwitch2CircuitBreaker = 1 : pdis1 -> sternRudderSwitch2CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.fbReservedCircuitBreaker == BREAK_ON) ? pdis1 -> fbReservedCircuitBreaker = 1 : pdis1 -> fbReservedCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.tyzReservedCircuitBreaker == BREAK_ON) ? pdis1 -> tyzReservedCircuitBreaker = 1 : pdis1 -> tyzReservedCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus1State.reservedCircuitBreaker == BREAK_ON) ? pdis1 -> reservedCircuitBreaker = 1 : pdis1 -> reservedCircuitBreaker = 0;
pdis1 -> paceholder = 0;
m_pm->m_currentDisBreakerState.bus1Breaker = *pdis1;
delete pdis1;
}
if(!m_udpComm->m_qReceiveDisHighAVolBusBuffer.empty())
{
double time;
m_udpComm->popMsgFormQueue(time,m_disHighAVolBusCurrentState);
m_pm->m_pmCurrentState.bus2State = m_disHighAVolBusCurrentState.data;
m_pm->m_db->insertData(m_pm->m_pmCurrentState.bus2State); //写入数据库
msg_disHighAVolBusFbMsg_Update_time = time;
disBus2Breaker* pdis2=new disBus2Breaker;
(m_pm->m_pmCurrentState.bus2State.bowFTDevice123CircuitBreaker == BREAK_ON)? pdis2 -> bowFTDevice123CircuitBreaker = 1 : pdis2 -> bowFTDevice123CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus2State.actuatorCircuitBreaker == BREAK_ON)? pdis2 -> actuatorCircuitBreaker = 1 : pdis2 -> actuatorCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus2State.mastSteeringGearControlBoxCircuitBreaker == BREAK_ON)? pdis2 -> mastSteeringGearControlBoxCircuitBreaker = 1 : pdis2 -> mastSteeringGearControlBoxCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus2State.xczCircuitBreaker == BREAK_ON)? pdis2 -> xczCircuitBreaker = 1 : pdis2 -> xczCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus2State.bowRudderControlBoxCircuitBreaker == BREAK_ON)? pdis2 -> bowRudderControlBoxCircuitBreaker = 1 : pdis2 -> bowRudderControlBoxCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus2State.openWaterCoverStartCylinderCircuitBreaker == BREAK_ON)? pdis2 -> openWaterCoverStartCylinderCircuitBreaker = 1 : pdis2 -> openWaterCoverStartCylinderCircuitBreaker = 0;
pdis2 -> paceholder = 0;
m_pm->m_currentDisBreakerState.bus2Breaker = *pdis2;
delete pdis2;
}
if(!m_udpComm->m_qReceiveDisHighBVolBusBuffer.empty())
{
double time;
m_udpComm->popMsgFormQueue(time,m_disHighBVolBusCurrentState);
m_pm->m_pmCurrentState.bus3State = m_disHighBVolBusCurrentState.data;
m_pm->m_db->insertData(m_pm->m_pmCurrentState.bus3State); //写入数据库
msg_disHighBVolBusFbMsg_Update_time = time;
disBus3Breaker* pdis3=new disBus3Breaker;
(m_pm->m_pmCurrentState.bus3State.lithiumBatteryGroupInstrumentCircuitBreaker == BREAK_ON)? pdis3 -> lithiumBatteryGroupInstrumentCircuitBreaker = 1 : pdis3 -> lithiumBatteryGroupInstrumentCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus3State.bowLowVoltageDistributionBoxCircuitBreaker == BREAK_ON)? pdis3 -> bowLowVoltageDistributionBoxCircuitBreaker = 1 : pdis3 -> bowLowVoltageDistributionBoxCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus3State.unit4InstrumentDC48VCircuitBreaker == BREAK_ON)? pdis3 -> unit4InstrumentDC48VCircuitBreaker = 1 : pdis3 -> unit4InstrumentDC48VCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus3State.dcC1DCDistributionPanelCircuitBreaker == BREAK_ON)? pdis3 -> dcC1DCDistributionPanelCircuitBreaker = 1 : pdis3 -> dcC1DCDistributionPanelCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus3State.reservedCircuitBreaker1 == BREAK_ON)? pdis3 -> reservedCircuitBreaker1 = 1 : pdis3 -> reservedCircuitBreaker1 = 0;
(m_pm->m_pmCurrentState.bus3State.reservedCircuitBreaker2 == BREAK_ON)? pdis3 -> reservedCircuitBreaker2 = 1 : pdis3 -> reservedCircuitBreaker2 = 0;
(m_pm->m_pmCurrentState.bus3State.emergencyLithiumBatteryGroup2CircuitBreaker == BREAK_ON)? pdis3 -> emergencyLithiumBatteryGroup2CircuitBreaker = 1 : pdis3 -> emergencyLithiumBatteryGroup2CircuitBreaker = 0;
pdis3->paceholder = 0;
m_pm->m_currentDisBreakerState.bus3Breaker = *pdis3;
delete pdis3;
}
if(!m_udpComm->m_qReceiveDisLowBusBuffer.empty())
{
double time;
m_udpComm->popMsgFormQueue(time,m_disLowBusState);
m_pm->m_pmCurrentState.bus4State = m_disLowBusState.data;
m_pm->m_db->insertData(m_pm->m_pmCurrentState.bus4State); //写入数据库
msg_disLowBusFbMsg_Update_time = time;
disBus4Breaker* pdis4=new disBus4Breaker;
(m_pm->m_pmCurrentState.bus4State.unit1CircuitBreaker == BREAK_ON)? pdis4 -> unit1CircuitBreaker = 1 : pdis4 -> unit1CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus4State.unit2CircuitBreaker == BREAK_ON)? pdis4 -> unit2CircuitBreaker = 1 : pdis4 -> unit2CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus4State.unit3CircuitBreaker == BREAK_ON)? pdis4 -> unit3CircuitBreaker = 1 : pdis4 -> unit3CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus4State.unit5CircuitBreaker == BREAK_ON)? pdis4 -> unit5CircuitBreaker = 1 : pdis4 -> unit5CircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus4State.bowPZDeviceCircuitBreaker == BREAK_ON)? pdis4 -> bowPZDeviceCircuitBreaker = 1 : pdis4 -> bowPZDeviceCircuitBreaker = 0;
(m_pm->m_pmCurrentState.bus4State.reservedCircuitBreaker1 == BREAK_ON)? pdis4 -> reservedCircuitBreaker1 = 1 : pdis4 -> reservedCircuitBreaker1 = 0;
(m_pm->m_pmCurrentState.bus4State.reservedCircuitBreaker2 == BREAK_ON)? pdis4 -> reservedCircuitBreaker2 = 1 : pdis4 -> reservedCircuitBreaker2 = 0;
(m_pm->m_pmCurrentState.bus4State.emergencyLithiumBatteryGroup1CircuitBreaker == BREAK_ON)? pdis4 -> emergencyLithiumBatteryGroup1CircuitBreaker = 1 : pdis4 -> emergencyLithiumBatteryGroup1CircuitBreaker = 0;
pdis4 -> paceholder = 0;
m_pm->m_currentDisBreakerState.bus4Breaker = *pdis4;
delete pdis4;
}
m_pm->m_systemData->updateSystemData(m_pm->m_pmCurrentState.ccustate);
m_pm->m_systemData->updateSystemData(m_pm->m_pmCurrentState.bus1State);
m_pm->m_systemData->updateSystemData(m_pm->m_pmCurrentState.bus2State);
m_pm->m_systemData->updateSystemData(m_pm->m_pmCurrentState.bus3State);
m_pm->m_systemData->updateSystemData(m_pm->m_pmCurrentState.bus4State);
m_pm->m_systemData->device_common_status.ccu_status.lastUpdateTime = m_pm->m_lowerCommManager->msg_CcuStateFbMsg_Update_time;
m_pm->m_systemData->device_common_status.dis_hv_c1.lastUpdateTime = m_pm->m_lowerCommManager->msg_disHighVolBusFbMsg_Update_time;
m_pm->m_systemData->device_common_status.dis_hv_c2.lastUpdateTime = m_pm->m_lowerCommManager->msg_disHighAVolBusFbMsg_Update_time;
m_pm->m_systemData->device_common_status.dis_lv_c1.lastUpdateTime = m_pm->m_lowerCommManager->msg_disHighBVolBusFbMsg_Update_time;
m_pm->m_systemData->device_common_status.dis_lv_c2.lastUpdateTime = m_pm->m_lowerCommManager->msg_disLowBusFbMsg_Update_time;
return true;
}
bool LowerCommManager::listenThreadFunc()
{
cout<<MOOS::ConsoleColours::green()<< "LowerCommManager::listenThreadFunc" << MOOS::ConsoleColours::reset() << endl;
unsigned char Buff[1024];
while(!m_ListenThread.IsQuitRequested())
{
try
{
int nRead = m_udpComm->udpScoket->iRecieveMessage(Buff, sizeof(Buff));
cout<<MOOS::ConsoleColours::yellow()<< "RECEVE DATA COUNT:" << nRead << MOOS::ConsoleColours::reset() << endl;
if(nRead > 0)
{
try {
if(!m_udpComm->pushMsgToQueue(Buff))
{
for(int i=0; i<m_udpComm->sError.size(); i++)
{
cout<<MOOS::ConsoleColours::red()<< m_udpComm->sError[i] << MOOS::ConsoleColours::reset() << endl;
}
}
//更新当前状态
if(!updatePoweSystemStates())
{
std::cerr << "Error updating power system states" << std::endl;
}
}
catch(const std::exception& e) {
cerr << "Error processing message: " << e.what() << endl;
}
}
// 打印通信错误信息
if(!m_udpComm->sError.empty())
{
for(int i=0; i<m_udpComm->sError.size(); i++)
{
cout<<MOOS::ConsoleColours::red() << m_udpComm->sError[i] << MOOS::ConsoleColours::reset() << endl;
}
m_udpComm->sError.clear();
}
}
catch(const XPCException& e)
{
cerr << "XPCException in listenThreadFunc" << endl;
// 短暂休眠后继续尝试
MOOSPause(100);
}
catch(const std::exception& e)
{
cerr << "Exception in listenThreadFunc: " << e.what() << endl;
MOOSPause(100);
}
catch(...)
{
cerr << "Unknown exception in listenThreadFunc" << endl;
MOOSPause(100);
}
}
return true;
}
void LowerCommManager::startListenThread()
{
if(!m_ListenThread.IsThreadRunning())
{
m_ListenThread.Initialise(_ListenCB,this);
m_ListenThread.Start();
std::cout << "LowerCommManager::startListenThread" << std::endl;
}
}
void LowerCommManager::stopListenThread()
{
if(m_ListenThread.IsThreadRunning())
{
m_ListenThread.Stop();
std::cout << "LowerCommManager::stopListenThread" << std::endl;
}
}
char LowerCommManager::getBreakerOption(char cmd)
{
if(cmd ==1)
return 0x55;
else if(cmd ==0)
return 0xAA;
else
return 0x00;
}
void LowerCommManager::coutMsg(const msg_CcuStateFbMsg& p, std::ostream& os){
os << std::hex << std::setfill('0') << std::uppercase;
os << "起始符1 (start1): 0x" << std::setw(2) << static_cast<int>(p.header.start1) << "\n";
os << "起始符2 (start2): 0x" << std::setw(2) << static_cast<int>(p.header.start2) << "\n";
os << "标识符 (identifier): 0x" << std::setw(4) << p.header.id << "\n";
os << std::dec << "数据长度 (length): " << p.header.length << "\n";
os << "运行模式 (fc_mode): 0x" << std::hex << std::setw(2) << static_cast<int>(p.data.fc_mode) << "\n";
os << "燃料电池状态 (fc_status): 0x" << std::setw(2) << static_cast<int>(p.data.fc_status) << "\n";
os << "故障等级 (fc_fault_level): 0x" << std::setw(2) << static_cast<int>(p.data.fc_fault_level) << "\n";
os << std::dec;
os << "输出功率限制 (output_power_limit): " << p.data.output_power_limit << " W\n";
os << "累计发电时间 (total_generation_time): " << p.data.total_generation_time * 0.1 << " 小时\n";
os << "发电总功率 (total_generation_power): " << p.data.total_generation_power << " W\n";
os << "一级故障字 (fault_level_1): 0x" << std::hex << p.data.fault_level_1 << "\n";
os << "二级故障字 (fault_level_2): 0x" << p.data.fault_level_2 << "\n";
os << "三级故障字 (fault_level_3): 0x" << p.data.fault_level_3 << "\n";
os << "四级故障字 (fault_level_4): 0x" << p.data.fault_level_4 << "\n";
os << std::dec;
os << "储氢剩余容量 (hydrogen_capacity): " << static_cast<int>(p.data.hydrogen_capacity) << "%\n";
os << "液氧剩余容量 (liquid_oxygen_capacity): " << static_cast<int>(p.data.liquid_oxygen_capacity) << "%\n";
os << "钯膜最高温度 (palladium_temp): " << p.data.palladium_temp << " ℃\n";
os << "主水路压力 (main_pipe_pressure): " << p.data.main_pipe_pressure * 0.01 << " kPa\n";
os << "辅水路压力 (aux_pipe_pressure): " << p.data.aux_pipe_pressure * 0.01 << " kPa\n";
os << "舱室压力1 (cabin_pressure1): " << p.data.cabin_pressure1 * 0.01 << " kPa\n";
os << "舱室压力2 (cabin_pressure2): " << p.data.cabin_pressure2 * 0.01 << " kPa\n";
os << "舱室温度1 (cabin_temp1): " << p.data.cabin_temp1 * 0.01 << " ℃\n";
os << "舱室温度2 (cabin_temp2): " << p.data.cabin_temp2 * 0.01 << " ℃\n";
os << "舱室湿度1 (cabin_humidity1): " << p.data.cabin_humidity1 * 0.01 << " %RH\n";
os << "舱室湿度2 (cabin_humidity2): " << p.data.cabin_humidity2 * 0.01 << " %RH\n";
os << "火焰探测器1状态 (flame_detector1): 0x" << std::hex << static_cast<int>(p.data.flame_detector1) << "\n";
os << "火焰探测器2状态 (flame_detector2): 0x" << static_cast<int>(p.data.flame_detector2) << "\n";
os << std::dec;
os << "氢气浓度1 (h2_concentration1): " << p.data.h2_concentration1 * 0.01 << "% LEL\n";
os << "氢气浓度2 (h2_concentration2): " << p.data.h2_concentration2 * 0.01 << "% LEL\n";
os << "氢气浓度3 (h2_concentration3): " << p.data.h2_concentration3 * 0.01 << "% LEL\n";
os << "氧气浓度1 (o2_concentration1): " << p.data.o2_concentration1 * 0.01 << "% LEL\n";
os << "氧气浓度2 (o2_concentration2): " << p.data.o2_concentration2 * 0.01 << "% LEL\n";
os << "甲醇浓度1 (ch3oh_concentration1): " << p.data.ch3oh_concentration1 * 0.01 << "% LEL\n";
os << "甲醇浓度2 (ch3oh_concentration2): " << p.data.ch3oh_concentration2 * 0.01 << "% LEL\n";
for (int i = 0; i < 10; ++i)
os << "保留字段 reserved1[" << i << "]: 0x" << std::hex << p.data.reserved1[i] << "\n";
os << "仪表舱 氧气浓度 (cabin_ox_concentration): " << p.data.cabin_ox_concentration * 0.1 << "%\n";
os << "仪表舱 温度 (cabin_temperature): " << static_cast<int16_t> (p.data.cabin_temperature) * 0.1 << "°C\n";
os << "仪表舱 湿度 (cabin_humidity): " << p.data.cabin_humidity * 0.1 << "% RH\n";
os << "仪表舱 大气压 (cabin_pressure): " << p.data.cabin_pressure * 0.2 << " kPa\n";
os << "动力舱 氧气浓度 (power_cabin_ox_concentration): " << p.data.power_cabin_ox_concentration * 0.1 << "%\n";
os << "动力舱 温度 (power_cabin_temperature): " << static_cast<int16_t> (p.data.power_cabin_temperature) * 0.1 << "°C\n";
os << "动力舱 湿度 (power_cabin_humidity): " << p.data.power_cabin_humidity * 0.1 << "% RH\n";
os << "动力舱 大气压 (power_cabin_pressure): " << p.data.power_cabin_pressure * 0.2 << " kPa\n";
for (int i = 0; i < 6; ++i)
os << "动力电池报警标志 dyn_alarm_flag[" << i << "]: 0x" << std::hex << static_cast<int>(p.data.dyn_alarm_flag[i]) << "\n";
for (int i = 0; i < 6; ++i)
os << "仪表电池报警标志 ins_alarm_flag[" << i << "]: 0x" << std::hex << static_cast<int>(p.data.ins_alarm_flag[i]) << "\n";
os << "仪表正极/充电继电器状态 (ins_relay_status1): 0x" << std::hex << static_cast<int>(p.data.ins_relay_status1) << "\n";
os << "仪表预充/负极继电器状态 (ins_relay_status2): 0x" << static_cast<int>(p.data.ins_relay_status2) << "\n";
os << "动力正极/充电继电器状态 (dyn_relay_status1): 0x" << static_cast<int>(p.data.dyn_relay_status1) << "\n";
os << "动力预充/负极继电器状态 (dyn_relay_status2): 0x" << static_cast<int>(p.data.dyn_relay_status2) << "\n";
os << std::dec;
os << "仪表最大放电功率 (ins_max_discharge_power): " << p.data.ins_max_discharge_power * 0.05 << " kW\n";
os << "动力最大放电功率 (dyn_max_discharge_power): " << p.data.dyn_max_discharge_power * 0.05 << " kW\n";
os << "仪表SOC (ins_soc): " << static_cast<int>(p.data.ins_soc) << "%\n";
os << "动力SOC (dyn_soc): " << static_cast<int>(p.data.dyn_soc) << "%\n";
os << "仪表电池总能量 (ins_total_energy): " << p.data.ins_total_energy * 0.1 << " kWh\n";
os << "动力电池总能量 (dyn_total_energy): " << p.data.dyn_total_energy * 0.1 << " kWh\n";
os << "仪表输入功率 (ins_power_input): " << p.data.ins_power_input * 0.1 << " kW\n";
os << "动力输入功率 (dyn_power_input): " << p.data.dyn_power_input * 0.1 << " kW\n";
os << "仪表充电状态 (ins_charge_status): 0x" << std::hex << static_cast<int>(p.data.ins_charge_status) << "\n";
os << "动力充电状态 (dyn_charge_status): 0x" << static_cast<int>(p.data.dyn_charge_status) << "\n";
os << std::dec;
os << "仪表LINK电压: " << p.data.ins_voltage_link * 0.01 << " V\n";
os << "仪表PACK电压: " << p.data.ins_voltage_pack * 0.01 << " V\n";
os << "仪表电流: " << static_cast<int16_t> (p.data.ins_current) * 0.05 << " A\n";
os << "仪表正端绝缘电阻: " << p.data.ins_resistance_pos << " kΩ\n";
os << "仪表负端绝缘电阻: " << p.data.ins_resistance_neg << " kΩ\n";
os << "动力LINK电压: " << p.data.dyn_voltage_link * 0.01 << " V\n";
os << "动力PACK电压: " << p.data.dyn_voltage_pack * 0.01 << " V\n";
os << "动力电流: " << static_cast<int16_t> (p.data.dyn_current) * 0.05 << " A\n";
os << "动力正端绝缘电阻: " << p.data.dyn_resistance_pos << " kΩ\n";
os << "动力负端绝缘电阻: " << p.data.dyn_resistance_neg << " kΩ\n";
os << "仪表紧急状态 (ins_emergency_status): 0x" << std::hex << static_cast<int>(p.data.ins_emergency_status) << "\n";
os << "动力紧急状态 (dyn_emergency_status): 0x" << static_cast<int>(p.data.dyn_emergency_status) << "\n";
for (int i = 0; i < 2; ++i)
os << "保留字段 reserved2[" << i << "]: 0x" << std::hex << static_cast<int>(p.data.reserved2[i]) << "\n";
for (int i = 0; i < 6; ++i)
os << "SOC门限值 ins_soc_threshold" << i+1 << ": 0x" << std::hex << static_cast<int>(p.data.ins_soc_threshold1) << "\n";
for (int i = 0; i < 4; ++i)
os << "功率限制 ins_power_limit" << i+1 << ": 0x" << std::hex << static_cast<int>(p.data.ins_power_limit1) << "\n";
os << "设备在线状态1 (device_online_flag1): 0x" << std::hex << p.data.device_online_flag1 << "\n";
os << "设备在线状态2 (device_online_flag2): 0x" << p.data.device_online_flag2 << "\n";
// os << "保留字段 reserved3: 0x" << p.data.reserved1 << "\n";
os << "主机心跳 (reserved4): 0x" << p.data.heartbeat << "\n";
os << "校验和 (checksum): 0x" << p.checkCode << "\n";
os << std::dec;
}
//================================================================================================================
// 高电压母线断路器操作函数
bool LowerCommManager::operate_HV_fuelCellCircuit_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_fuelCellCircuit_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.fuelCellCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.fuelCellCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_powerLithiumBattery_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_powerLithiumBattery_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.powerLithiumBatteryCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.powerLithiumBatteryCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_propulsionMotor_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_propulsionMotor_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.propulsionMotorCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.propulsionMotorCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_lithiumBatteryMeter_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_lithiumBatteryMeter_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.lithiumBatteryGroupInstrumentCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.lithiumBatteryGroupInstrumentCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_dcDc5Module_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_dcDc5Module_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.dcDc5ModuleCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.dcDc5ModuleCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_bowHighVoltageBox_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_bowHighVoltageBox_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.bowHighVoltageDistributionBoxCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.bowHighVoltageDistributionBoxCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_sternFt45_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_sternFt45_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.sternFTDevice45CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.sternFTDevice45CircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_sternRudder1_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_sternRudder1_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.sternRudderSwitch1CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.sternRudderSwitch1CircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_sternRudder2_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_sternRudder2_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.sternRudderSwitch2CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.sternRudderSwitch2CircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_fbReserved_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_fbReserved_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.fbReservedCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.fbReservedCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_tyzReserved_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_tyzReserved_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.tyzReservedCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.tyzReservedCircuitBreaker; }
);
}
bool LowerCommManager::operate_HV_reserved_Breaker(char opt){
return operateBreakerGeneric<disHighVolBusCmd, char>(
"HV_reserved_Breaker",
opt,
[&](disHighVolBusCmd& c){ c.reservedCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus1State.reservedCircuitBreaker; }
);
}
// bool LowerCommManager::operate_HV_dcDc5_Breaker(char opt){
// return operateBreakerGeneric<disHighVolBusCmd, char>(
// "HV_dcDc5_Breaker",
// opt,
// [&](disHighVolBusCmd& c){ c.dcdc5Module = getBreakerOption(opt); },
// [&](){ return m_pm->m_pmCurrentState.bus1State.dcDcFaultWord1&(0x02); }
// );
// }
bool LowerCommManager::operate_HV_dcDc5_Breaker(char opt)
{
disHighVolBusCmd cmd{}; // 默认全零
double t0 = MOOSTime();
bool ok = false;
string name = "DCDC Module";
LOG_F(INFO, "operate %s: opt=%d", name, opt);
cmd.dcdc5Module = getBreakerOption(opt);
m_udpComm->sendDisSysCmd(cmd);
// ok = m_pm->m_pmCurrentState.bus1State.dcDcFaultWord1&(0x02);
ok = m_pm->m_systemData->m_dcdcStatus.workingStatus;
MOOSPause(100);
while ( !ok ) {
if( m_pm->m_systemData->m_dcdcStatus.workingStatus )
{
ok = true;
break;
}
if (!m_udpComm->sendDisSysCmd(cmd)) {
LOG_F(ERROR, "operate %s: sendDisSysCmd failed", name);
ok = false;
break;
}
if (MOOSTime() > t0 + 6) {
LOG_F(ERROR, "operate %s: timeout", name);
ok = false;
break;
}
MOOSPause(200);
}
//继电器复位
disHighVolBusCmd clr{};
for (int i = 0; i < 3; ++i)
{
m_udpComm->sendDisSysCmd(clr);
MOOSPause(200);
}
return ok;
}
// bool LowerCommManager::operate_HV_coolingSystem_Breaker(char opt){
// return operateBreakerGeneric<disHighVolBusCmd, char>(
// "HV_coolingSystem_Breaker",
// opt,
// [&](disHighVolBusCmd& c){ c.coolingSystem = getBreakerOption(opt); },
// [&](){ return (m_pm->m_pmCurrentState.bus1State.dcDcFaultWord1 & 0x20) ? 0x55 : 0xAA; }
// );
// }
bool LowerCommManager::operate_HV_coolingSystem_Breaker(char opt)
{
disHighVolBusCmd cmd{}; // 默认全零
double t0 = MOOSTime();
bool ok = false;
string name = "CoolingSystem";
LOG_F(INFO, "operate %s: opt=%d", name, opt);
cmd.coolingSystem = getBreakerOption(opt);
m_udpComm->sendDisSysCmd(cmd);
// ok = m_pm->m_pmCurrentState.bus1State.dcDcFaultWord1 & 0x60;
ok = m_pm->m_systemData->m_dcdcStatus.coolingPump1Running || m_pm->m_systemData->m_dcdcStatus.coolingPump2Running;
MOOSPause(100);
while ( !ok ) {
if( (m_pm->m_systemData->m_dcdcStatus.coolingPump1Running || m_pm->m_systemData->m_dcdcStatus.coolingPump2Running) )
{
ok = true;
break;
}
if (!m_udpComm->sendDisSysCmd(cmd)) {
LOG_F(ERROR, "operate %s: sendDisSysCmd failed", name);
ok = false;
break;
}
if (MOOSTime() > t0 + 6) {
LOG_F(ERROR, "operate %s: timeout", name);
ok = false;
break;
}
MOOSPause(200);
}
//继电器复位
disHighVolBusCmd clr{};
for (int i = 0; i < 3; ++i)
{
m_udpComm->sendDisSysCmd(clr);
MOOSPause(200);
}
return ok;
}
// 高压汇流排断路器操作函数
bool LowerCommManager::operate_HVA_bowFTDevice123Circuit_Breaker(char opt){
return operateBreakerGeneric<disHighAVolBusCmd, char>(
"HVA_bowFTDevice123Circuit_Breaker",
opt,
[&](disHighAVolBusCmd& c){ c.bowFTDevice123CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus2State.bowFTDevice123CircuitBreaker; }
);
}
bool LowerCommManager::operate_HVA_actuatorCircuit_Breaker(char opt){
return operateBreakerGeneric<disHighAVolBusCmd, char>(
"HVA_actuatorCircuit_Breaker",
opt,
[&](disHighAVolBusCmd& c){ c.actuatorCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus2State.actuatorCircuitBreaker; }
);
}
bool LowerCommManager::operate_HVA_mastSteeringGearControlBoxCircuit_Breaker(char opt){
return operateBreakerGeneric<disHighAVolBusCmd, char>(
"HVA_mastSteeringGearControlBoxCircuit_Breaker",
opt,
[&](disHighAVolBusCmd& c){ c.mastSteeringGearControlBoxCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus2State.mastSteeringGearControlBoxCircuitBreaker; }
);
}
bool LowerCommManager::operate_HVA_xczCircuit_Breaker(char opt){
return operateBreakerGeneric<disHighAVolBusCmd, char>(
"HVA_xczCircuit_Breaker",
opt,
[&](disHighAVolBusCmd& c){ c.xczCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus2State.xczCircuitBreaker; }
);
}
bool LowerCommManager::operate_HVA_bowRudderControlBoxCircuit_Breaker(char opt){
return operateBreakerGeneric<disHighAVolBusCmd, char>(
"HVA_bowRudderControlBoxCircuit_Breaker",
opt,
[&](disHighAVolBusCmd& c){ c.bowRudderControlBoxCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus2State.bowRudderControlBoxCircuitBreaker; }
);
}
bool LowerCommManager::operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(char opt){
return operateBreakerGeneric<disHighAVolBusCmd, char>(
"HVA_openWaterCoverStartCylinderCircuit_Breaker",
opt,
[&](disHighAVolBusCmd& c){ c.openWaterCoverStartCylinderCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus2State.openWaterCoverStartCylinderCircuitBreaker; }
);
}
// 仪表汇流排断路器操作函数
bool LowerCommManager::operate_LVA_unit1Circuit_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_unit1Circuit_Breaker",
opt,
[&](disLowBusCmd& c){ c.unit1CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.unit1CircuitBreaker; }
);
}
bool LowerCommManager::operate_LVA_unit2Circuit_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_unit2Circuit_Breaker",
opt,
[&](disLowBusCmd& c){ c.unit2CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.unit2CircuitBreaker; }
);
}
bool LowerCommManager::operate_LVA_unit3Circuit_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_unit3Circuit_Breaker",
opt,
[&](disLowBusCmd& c){ c.unit3CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.unit3CircuitBreaker; }
);
}
bool LowerCommManager::operate_LVA_unit5Circuit_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_unit5Circuit_Breaker",
opt,
[&](disLowBusCmd& c){ c.unit5CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.unit5CircuitBreaker; }
);
}
bool LowerCommManager::operate_LVA_bowPZDeviceCircuit_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_bowPZDeviceCircuit_Breaker",
opt,
[&](disLowBusCmd& c){ c.bowPZDeviceCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.bowPZDeviceCircuitBreaker; }
);
}
bool LowerCommManager::operate_LVA_reservedCircuit1_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_reservedCircuit1_Breaker",
opt,
[&](disLowBusCmd& c){ c.reservedCircuitBreaker1 = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.reservedCircuitBreaker1; }
);
}
bool LowerCommManager::operate_LVA_reservedCircuit2_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_reservedCircuit2_Breaker",
opt,
[&](disLowBusCmd& c){ c.reservedCircuitBreaker2 = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.reservedCircuitBreaker2; }
);
}
bool LowerCommManager::operate_LVA_mergencyLithiumBatteryGroup1Circuit_Breaker(char opt){
return operateBreakerGeneric<disLowBusCmd, char>(
"LVA_mergencyLithiumBatteryGroup1Circuit_Breaker",
opt,
[&](disLowBusCmd& c){ c.emergencyLithiumBatteryGroup1CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus4State.emergencyLithiumBatteryGroup1CircuitBreaker; }
);
}
// 仪表母线断路器操作函数
bool LowerCommManager::operate_LV_lithiumBatteryGroupInstrumentCircuit_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"lithiumBatteryGroupInstrumentCircuitBreaker",
opt,
[&](disLowMainBusCmd& c){ c.lithiumBatteryGroupInstrumentCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.lithiumBatteryGroupInstrumentCircuitBreaker; }
);
}
bool LowerCommManager::operate_LV_bowLowVoltageDistributionBoxCircuit_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"bowLowVoltageDistributionBoxCircuitBreaker",
opt,
[&](disLowMainBusCmd& c){ c.bowLowVoltageDistributionBoxCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.bowLowVoltageDistributionBoxCircuitBreaker; }
);
}
bool LowerCommManager::operate_LV_unit4Dc48V_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"unit4InstrumentDC48VCircuitBreaker",
opt,
[&](disLowMainBusCmd& c){ c.unit4InstrumentDC48VCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.unit4InstrumentDC48VCircuitBreaker; }
);
}
bool LowerCommManager::operate_LV_dcC1Panel_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"dcC1DCDistributionPanelCircuitBreaker",
opt,
[&](disLowMainBusCmd& c){ c.dcC1DCDistributionPanelCircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.dcC1DCDistributionPanelCircuitBreaker; }
);
}
bool LowerCommManager::operate_LV_reservedCircuit1_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"reservedCircuitBreaker1",
opt,
[&](disLowMainBusCmd& c){ c.reservedCircuitBreaker1 = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.reservedCircuitBreaker1; }
);
}
bool LowerCommManager::operate_LV_reservedCircuit2_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"reservedCircuitBreaker2",
opt,
[&](disLowMainBusCmd& c){ c.reservedCircuitBreaker2 = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.reservedCircuitBreaker2; }
);
}
bool LowerCommManager::operate_LV_mergencyLithiumBatteryGroup2Circuit_Breaker(char opt){
return operateBreakerGeneric<disLowMainBusCmd, char>(
"emergencyLithiumBatteryGroup2CircuitBreaker",
opt,
[&](disLowMainBusCmd& c){ c.emergencyLithiumBatteryGroup2CircuitBreaker = getBreakerOption(opt); },
[&](){ return m_pm->m_pmCurrentState.bus3State.emergencyLithiumBatteryGroup2CircuitBreaker; }
);
}
// 通用模板
template<typename CmdT, typename StateT>
bool LowerCommManager::operateBreakerGeneric(
char const* name,
char opt,
std::function<void(CmdT&)> fillCmd, // 填 cmd 对应字段
std::function<StateT()> readState // 读 pmCurrentState 对应字段
) {
CmdT cmd{}; // 默认全零
double t0 = MOOSTime();
bool ok = false;
LOG_F(INFO, "operate %s: opt=%d", name, opt);
// 第一次发包
fillCmd(cmd);
m_udpComm->sendDisSysCmd(cmd);
MOOSPause(100);
while ( !ok ) {
if( readState() == getBreakerOption(opt) )
{
ok = true;
break;
}
if (!m_udpComm->sendDisSysCmd(cmd)) {
LOG_F(ERROR, "operate %s: sendDisSysCmd failed", name);
ok = false;
break;
}
if (MOOSTime() > t0 + 6) {
LOG_F(ERROR, "operate %s: timeout", name);
ok = false;
break;
}
MOOSPause(200);
}
//继电器复位
CmdT clr{};
for (int i = 0; i < 3; ++i)
{
m_udpComm->sendDisSysCmd(clr);
MOOSPause(200);
}
return ok;
}
-128
View File
@@ -1,128 +0,0 @@
#ifndef LOWER_COMM_MANAGER_H
#define LOWER_COMM_MANAGER_H
#define UNIX
#include "MOOS/libMOOS/Comms/XPCUdpSocket.h"
#include "MOOS/libMOOS/Utils/MOOSThread.h"
#include "MOOS/libMOOS/Utils/MOOSLock.h"
#include "../udpcomm/ccuUdpMsg.h"
#include "pmSysvariable.h"
#include <queue>
#include <map>
#include <functional>
#include <atomic>
#include <vector>
#include "udpcomm/udpComm.h"
#include <functional>
class PowerManger; // 前向声明
class LowerCommManager {
public:
LowerCommManager(PowerManger* pm, long myPort, long ccuPort, const std::string& cccHost);
~LowerCommManager();
//processPeriodicTasks
void processPeriodicTasks();
//clearMsgQueue
void clearMsgQueue();
public:
// 初始化UDP通信
bool initUdpComm();
// 更新设备状态
bool updatePoweSystemStates();
//================================断路器操作函数=======================================
template<typename CmdT, typename StateT>
bool operateBreakerGeneric(
char const* name,
char opt,
std::function<void(CmdT&)> fillCmd, // 填 cmd 对应字段
std::function<StateT()> readState // 读 pmCurrentState 对应字段
);
// 高电压母线
bool operate_HV_fuelCellCircuit_Breaker(char opt);
bool operate_HV_powerLithiumBattery_Breaker(char opt);
bool operate_HV_propulsionMotor_Breaker(char opt);
bool operate_HV_lithiumBatteryMeter_Breaker(char opt);
bool operate_HV_dcDc5Module_Breaker(char opt);
bool operate_HV_bowHighVoltageBox_Breaker(char opt);
bool operate_HV_sternFt45_Breaker(char opt);
bool operate_HV_sternRudder1_Breaker(char opt);
bool operate_HV_sternRudder2_Breaker(char opt);
bool operate_HV_fbReserved_Breaker(char opt);
bool operate_HV_tyzReserved_Breaker(char opt);
bool operate_HV_reserved_Breaker(char opt);
bool operate_HV_dcDc5_Breaker(char opt);
bool operate_HV_coolingSystem_Breaker(char opt);
// 高压汇流排
bool operate_HVA_bowFTDevice123Circuit_Breaker(char opt);
bool operate_HVA_actuatorCircuit_Breaker(char opt);
bool operate_HVA_mastSteeringGearControlBoxCircuit_Breaker(char opt);
bool operate_HVA_xczCircuit_Breaker(char opt);
bool operate_HVA_bowRudderControlBoxCircuit_Breaker(char opt);
bool operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(char opt);
// 仪表母线
bool operate_LV_lithiumBatteryGroupInstrumentCircuit_Breaker(char opt);
bool operate_LV_bowLowVoltageDistributionBoxCircuit_Breaker(char opt);
bool operate_LV_unit4Dc48V_Breaker(char opt);
bool operate_LV_dcC1Panel_Breaker(char opt);
bool operate_LV_reservedCircuit1_Breaker(char opt);
bool operate_LV_reservedCircuit2_Breaker(char opt);
bool operate_LV_mergencyLithiumBatteryGroup2Circuit_Breaker(char opt);
// 仪表汇流排
bool operate_LVA_unit1Circuit_Breaker(char opt);
bool operate_LVA_unit2Circuit_Breaker(char opt);
bool operate_LVA_unit3Circuit_Breaker(char opt);
bool operate_LVA_unit5Circuit_Breaker(char opt);
bool operate_LVA_bowPZDeviceCircuit_Breaker(char opt);
bool operate_LVA_reservedCircuit1_Breaker(char opt);
bool operate_LVA_reservedCircuit2_Breaker(char opt);
bool operate_LVA_mergencyLithiumBatteryGroup1Circuit_Breaker(char opt);
//保持指令
void operate_hold();
//================================复合管控器操作函数====================================
// 发送CCU控制命令
bool sendCcuCommand(const ccuColCmd cmd);
bool sendDisSysCommand(const disSysBreakerList& cmd); //已废弃,不同时操作多个继电器设备
// 启动/停止监听线程
void startListenThread();
void stopListenThread();
void coutMsg(const msg_CcuStateFbMsg& p, std::ostream& os = std::cout);
//转换函数
char getBreakerOption(char cmd);
// 配电和CCU的反馈状态
msg_CcuStateFbMsg m_CcuCurrentState;
msg_disHighVolBusFbMsg m_disHighVolBusCurrentState;
msg_disHighAVolBusFbMsg m_disHighAVolBusCurrentState;
msg_disLowMainBusFbMsg m_disHighBVolBusCurrentState;
msg_disLowBusFbMsg m_disLowBusState;
// 配电和CCU的反馈状态时间戳
double msg_CcuStateFbMsg_Update_time;
// double msg_CcuSetParmFbMsg_Update_time;
double msg_disHighVolBusFbMsg_Update_time;
double msg_disHighAVolBusFbMsg_Update_time;
double msg_disHighBVolBusFbMsg_Update_time;
double msg_disLowBusFbMsg_Update_time;
public:
long m_port;
long m_ccuPort;
std::string m_ccuHost;
std::string m_host;
PowerManger* m_pm;
udpComm* m_udpComm;
CMOOSThread m_ListenThread;
int m_connectState;
// UDP监听线程函数
bool listenThreadFunc();
};
#endif // LOWER_COMM_MANAGER_H
-403
View File
@@ -1,403 +0,0 @@
/************************************************************/
/* NAME: */
/* ORGN: MIT, Cambridge MA */
/* FILE: PowerManger.cpp */
/* DATE: December 29th, 1963 */
/************************************************************/
#include <iterator>
#include "MBUtils.h"
#include "ACTable.h"
#include "PowerManger.h"
#include "logc/loguru.hpp"
#include <memory>
using namespace std;
bool _FsmCB(void * pParam)
{
PowerManger* pMe = (PowerManger* )pParam;
return pMe->FsmLoop();
}
bool _CommCB(void * pParam)
{
PowerManger* pMe = (PowerManger* )pParam;
return pMe->commLoop();
}
//---------------------------------------------------------
// Constructor()
PowerManger::PowerManger()
{
workCondition = 0;
currentMod = 0;
powerState = 0;
faultLevel = 0;
soc = 0;
sustainableTime = 0;
m_faultNum = 0;
m_ticker = 0;
m_leakage = 0;
m_thrustRpm = 0;
m_thrustEnable = 0;
faultCode.clear();
m_skew = 10;
m_pmCurrentState = {};
m_currentDisBreakerState = {};
m_targetDisBreakerState = {};
m_currentDriverState = {};
m_MSDriverCmd = {};
m_subDisSysCmd = {};
m_subDisSysState = {};
m_subDisSysFault = {};
//初始化控制指令
initControlCmd();
//初始化下位机通信
m_lowerCommManager = new LowerCommManager(this,IPORT,CCUPORT,CCUHOST);
m_lowerCommManager->initUdpComm();
m_lowerCommManager->startListenThread();
//初始化上位机通信
m_upperCommManager = new UpperCommManager(this);
//初始化数据库
m_db = new SQLite("power_data.db");
m_db->createTable();
//初始化系统数据
m_systemData = new SystemData();
//初始化http服务器
m_httpServer = new HttpServer(8090);
m_httpServer->setPowerManger(this);
m_httpServer->setLowerCommManager(m_lowerCommManager);
m_httpServer->setSystemData(m_systemData);
m_httpServer->start();
}
//---------------------------------------------------------
// Destructor
PowerManger::~PowerManger()
{
m_commThread.Stop();
delete m_db;
delete m_lowerCommManager;
delete m_upperCommManager;
delete m_systemData;
delete m_httpServer;
}
//---------------------------------------------------------
// Procedure: OnNewMail()
bool PowerManger::OnNewMail(MOOSMSG_LIST &NewMail)
{
AppCastingMOOSApp::OnNewMail(NewMail);
MOOSMSG_LIST::iterator p;
for(p=NewMail.begin(); p!=NewMail.end(); p++)
{
CMOOSMsg &msg = *p;
string key = msg.GetKey();
string strJson = msg.GetString();
double dfT;
msg.IsSkewed(m_curr_time, &dfT);
if(fabs(dfT) > m_skew)
{
LOG_F(INFO, "Message from %s at time %f is Skewed", msg.GetSource().c_str(), dfT);
return true;
}
//处理上位机发送的控制指令
if(!m_upperCommManager->processUpperMsg(key,strJson))
{
LOG_F(ERROR, "上位机消息处理错误: %s", key.c_str());
}
#if 0 // Keep these around just for template
string comm = msg.GetCommunity();
double dval = msg.GetDouble();
string sval = msg.GetString();
string msrc = msg.GetSource();
double mtime = msg.GetTime();
bool mdbl = msg.IsDouble();
bool mstr = msg.IsString();
#endif
if(key == "FOO")
LOG_F(INFO, "Received FOO message");
else if(key != "APPCAST_REQ") // handled by AppCastingMOOSApp
reportRunWarning("Unhandled Mail: " + key);
}
return(true);
}
//---------------------------------------------------------
// Procedure: OnConnectToServer()
bool PowerManger::OnConnectToServer()
{
registerVariables();
return(true);
}
//---------------------------------------------------------
// Procedure: Iterate()
// happens AppTick times per second
//固定为4Hz
bool PowerManger::Iterate()
{
AppCastingMOOSApp::Iterate();
// 触发定时任务
CycleTriggeredEvent cycleEvent;
PowerManagerFsm::dispatch(cycleEvent);
faultLevel = getFaultLevel();
m_systemData->updateCommonStatus();
// m_CcuColCmd.heartbeat
// if (m_ticker++ % 5 == 0)
// {
// m_CcuColCmd.heartbeat++;
// m_lowerCommManager->processPeriodicTasks();
// m_upperCommManager->processPeriodicTasks();
// }
return(true);
}
//---------------------------------------------------------
// Procedure: OnStartUp()
// happens before connection is open
bool PowerManger::OnStartUp()
{
AppCastingMOOSApp::OnStartUp();
STRING_LIST sParams;
m_MissionReader.EnableVerbatimQuoting(false);
if(!m_MissionReader.GetConfiguration(GetAppName(), sParams))
reportConfigWarning("No config block found for " + GetAppName());
STRING_LIST::iterator p;
for(p=sParams.begin(); p!=sParams.end(); p++) {
string orig = *p;
string line = *p;
string param = tolower(biteStringX(line, '='));
string value = line;
bool handled = false;
if(param == "foo") {
handled = true;
}
else if(param == "bar") {
handled = true;
}
if(!handled)
reportUnhandledConfigWarning(orig);
}
registerVariables();
// 初始化状态机
PowerManagerFsm::init(this);
PowerManagerFsm::start();
InitEvent initEvent;
PowerManagerFsm::dispatch(initEvent);
//初始化通信线程
m_commThread.Initialise(_CommCB,this);
m_commThread.Start();
return(true);
}
//---------------------------------------------------------
// Procedure: registerVariables()
void PowerManger::registerVariables()
{
AppCastingMOOSApp::RegisterVariables();
// Register(DB_TO_PM_SUBDISSTATE, 0);
Register(DB_TO_PM_MSCMD, 0);
Register(DB_TO_PM_EQUIPMENTCMD, 0);
Register(m_upperCommManager->m_secdistStateMsg.key_fb,0);
Register(DB_TO_PM_MISSION,0);
Register(DB_TO_PM_DEEPTH,0);
Register(DB_TO_PM_SETOPCONDITION,0);
Register(DB_TO_PM_SETOPMODE,0);
Register(DB_TO_PM_SETPMSYS,0);
Register(DB_TO_PM_SETDEVSWITCH,0);
Register(DB_TO_PM_TASKSTOP,0);
Register(uDetect_xcVoltage_cmd::key,0);
Register(uDetect_zdpVoltage_cmd::key,0);
Register(uDevice_thruster_st::key,0);
// Register(uExternComm_setThrustSpeed_cmd::key,0);
}
//------------------------------------------------------------
// Procedure: buildReport()
bool PowerManger::buildReport()
{
m_msgs << "============================================" << endl;
m_msgs << "File: " << endl;
m_msgs << "============================================" << endl;
ACTable actab(4);
actab << "Alpha | Bravo | Charlie | Delta";
actab.addHeaderLines();
actab << "one" << "two" << "three" << "four";
m_msgs << actab.getFormattedString();
return(true);
}
bool PowerManger::FsmLoop()
{
return true;
}
int PowerManger::checkError()
{
//TODO:更新协议
//1.检查复合管控器状态
// if(m_pmCurrentState.ccustate.fcuGZ_1H != 0 ||
// m_pmCurrentState.ccustate.fcuGZ_1L != 0 ||
// m_pmCurrentState.ccustate.fcuGZ_2H != 0 ||
// m_pmCurrentState.ccustate.fcuGZ_2L != 0 ||
// m_pmCurrentState.ccustate.fcuGZ_3H != 0 ||
// m_pmCurrentState.ccustate.fcuGZ_3L != 0 ||
// m_pmCurrentState.ccustate.insEmergencyStatusWord != 0)
return 0;
}
void PowerManger::printState(const std::string& stateName, const std::string& desp){
if (desp.find("故障") != string::npos) {
LOG_F(ERROR, "STATE %s -> %s", stateName.c_str(), desp.c_str());
} else {
LOG_F(INFO, "STATE %s -> %s", stateName.c_str(), desp.c_str());
}
}
void PowerManger::initControlCmd()
{
//================初始化配电指令=============================
m_targetDisBreakerState.bus1Breaker.fuelCellCircuitBreaker = 0;
m_targetDisBreakerState.bus1Breaker.powerLithiumBatteryCircuitBreaker = 1; //动力锂电池默认接入
m_targetDisBreakerState.bus1Breaker.propulsionMotorCircuitBreaker = 0;
m_targetDisBreakerState.bus1Breaker.lithiumBatteryGroupInstrumentCircuitBreaker = 1; //仪表锂电池默认接入
m_targetDisBreakerState.bus1Breaker.dcDc5ModuleCircuitBreaker = 1; //DC-DC模块默认接入
m_targetDisBreakerState.bus1Breaker.bowHighVoltageDistributionBoxCircuitBreaker = 1; //艏部高压母线断路器默认接入
m_targetDisBreakerState.bus1Breaker.sternFTDevice45CircuitBreaker = 0; // sternFTDevice45CircuitBreaker默认接入
m_targetDisBreakerState.bus1Breaker.sternRudderSwitch1CircuitBreaker = 0;
m_targetDisBreakerState.bus1Breaker.sternRudderSwitch2CircuitBreaker = 0;
m_targetDisBreakerState.bus1Breaker.fbReservedCircuitBreaker = 0;
m_targetDisBreakerState.bus1Breaker.tyzReservedCircuitBreaker = 0;
m_targetDisBreakerState.bus1Breaker.reservedCircuitBreaker = 0;
m_targetDisBreakerState.bus2Breaker.bowFTDevice123CircuitBreaker = 0;
m_targetDisBreakerState.bus2Breaker.actuatorCircuitBreaker = 0;
m_targetDisBreakerState.bus2Breaker.mastSteeringGearControlBoxCircuitBreaker = 0;
m_targetDisBreakerState.bus2Breaker.xczCircuitBreaker = 0;
m_targetDisBreakerState.bus2Breaker.bowRudderControlBoxCircuitBreaker = 0;
m_targetDisBreakerState.bus2Breaker.openWaterCoverStartCylinderCircuitBreaker = 0;
//锂电池默认接入
m_targetDisBreakerState.bus3Breaker.lithiumBatteryGroupInstrumentCircuitBreaker = 1;
m_targetDisBreakerState.bus3Breaker.bowLowVoltageDistributionBoxCircuitBreaker = 1;
m_targetDisBreakerState.bus3Breaker.unit4InstrumentDC48VCircuitBreaker = 1;
m_targetDisBreakerState.bus3Breaker.dcC1DCDistributionPanelCircuitBreaker = 1;
m_targetDisBreakerState.bus3Breaker.reservedCircuitBreaker1 = 0;
m_targetDisBreakerState.bus3Breaker.reservedCircuitBreaker2 = 0;
m_targetDisBreakerState.bus3Breaker.emergencyLithiumBatteryGroup2CircuitBreaker = 1;
m_targetDisBreakerState.bus4Breaker.unit1CircuitBreaker = 1;
m_targetDisBreakerState.bus4Breaker.unit2CircuitBreaker = 1;
m_targetDisBreakerState.bus4Breaker.unit3CircuitBreaker = 1;
m_targetDisBreakerState.bus4Breaker.unit5CircuitBreaker = 1;
m_targetDisBreakerState.bus4Breaker.bowPZDeviceCircuitBreaker = 1;
m_targetDisBreakerState.bus4Breaker.reservedCircuitBreaker1 = 0;
m_targetDisBreakerState.bus4Breaker.reservedCircuitBreaker2 = 0;
m_targetDisBreakerState.bus4Breaker.emergencyLithiumBatteryGroup1CircuitBreaker = 1; //紧急锂电池默认接入
//================初始化复合管控=============================
m_CcuColCmd.fcmode = 0x00; //默认无效
m_CcuColCmd.fcuCmd = 0x00; //无效指令
m_CcuColCmd.fcuPowerEfficiency = 8; //默认为8kw
m_CcuColCmd.insChargeEnable = 0x30; //不允许充电
m_CcuColCmd.insBatCmd = 0x00; //无效指令
m_CcuColCmd.powerBatCmd = 0x00; //无效指令
m_CcuColCmd.powerBatEfficiency = 00; //默认为最大
m_CcuColCmd.heartbeat = 0;
m_CcuColCmd.masterState = 0xAA;//主站状态,运行中
//================初始化上位机指令=========================
m_msCmd.modCMD = 99;
m_msCmd.powerCMD = 99;
m_msCmd.workCMD = 99;
//================初始化设备驱动指令========================
}
unsigned char PowerManger::getFaultLevel()
{
unsigned char maxLevel = 0;
for (auto code : faultCode) {
// 提取0xabcd中的b位 (右移8位后取低4位)
unsigned char level = (code >> 8) & 0xF;
if (level > maxLevel) {
maxLevel = level;
}
}
return maxLevel;
}
bool PowerManger::commLoop()
{
while(!m_commThread.IsQuitRequested())
{
m_CcuColCmd.heartbeat++;
m_lowerCommManager->processPeriodicTasks();
m_upperCommManager->processPeriodicTasks();
// 延时1秒
usleep(1000000);
}
return true;
}
void PowerManger::injectFault(unsigned type,unsigned int faultCode){
LOG_F(INFO, "注入故障: %d %d", type, faultCode);
TestEvent testEvent("故障注入测试",type,faultCode);
PowerManagerFsm::dispatch(testEvent);
}
void PowerManger::pushDeviceCommand(DeviceSwitchCmd cmd) {
std::lock_guard<std::mutex> lock(m_deviceCmdMutex);
m_deviceCmdQuenue.push(cmd);
}
bool PowerManger::popDeviceCommand(DeviceSwitchCmd &cmd) {
std::lock_guard<std::mutex> lock(m_deviceCmdMutex);
if(m_deviceCmdQuenue.empty()) {
return false;
}
cmd = m_deviceCmdQuenue.front();
m_deviceCmdQuenue.pop();
return true;
}
bool PowerManger::isDeviceCmdEmpty() const {
std::lock_guard<std::mutex> lock(m_deviceCmdMutex);
return m_deviceCmdQuenue.empty();
}
-165
View File
@@ -1,165 +0,0 @@
/*
* @Author: zhaojingkui 1553836110@qq.com
* @Date: 2024-10-22 16:47:01
* @LastEditors: zhaojingkui 1553836110@qq.com
* @LastEditTime: 2024-10-22 18:03:29
* @FilePath: /H100PowerManger/src/pPowerManger/PowerManger.h
* @Description:
*
* Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
*/
/************************************************************/
/* NAME: */
/* ORGN: MIT, Cambridge MA */
/* FILE: PowerManger.h */
/* DATE: December 29th, 1963 */
/************************************************************/
#ifndef PowerManger_HEADER
#define PowerManger_HEADER
#define UNIX
#define JSON_USE_EXCEPTION 1
#include <queue>
#include "logc/loguru.hpp"
#include "MOOS/libMOOS/Thirdparty/AppCasting/AppCastingMOOSApp.h"
#include "MOOS/libMOOS/Utils/MOOSThread.h"
#include "MOOS/libMOOS/Utils/MOOSUtilityFunctions.h"
#include "MOOS/libMOOS/Utils/ConsoleColours.h"
#include "../udpcomm/ccuUdpMsg.h"
#include "json/json.h"
#include "udpcomm/udpComm.h"
#include "pmSysvariable.h"
#include "fsm/PowerManagerFsm.hpp"
#include "driver.h"
#include "LowerCommManager.h"
#include "UpperCommManager.h"
#include "sqlit3/SQLite.h"
#include "systemData.h"
#include "httpserver/httpserver.h"
#include "faultCode.h"
#define IPORT 5001
#define CCUPORT 7000
#define CCUHOST "192.168.0.140"
// #define CCUHOST "127.0.0.0"
#define MAX_UDP_PKT_SIZE 65535
#define SKEW_TOLERANCE 5
class PowerManger : public AppCastingMOOSApp
{
public:
PowerManger();
~PowerManger();
public:
bool FsmLoop();
protected: // Standard MOOSApp functions to overload
bool OnNewMail(MOOSMSG_LIST &NewMail);
bool Iterate();
bool OnConnectToServer();
bool OnStartUp();
bool buildReport();
void registerVariables();
public:
void updateTime(){m_current_time = MOOSTime();}
unsigned char getFaultLevel();
//数据库
SQLite* m_db;
//http
HttpServer* m_httpServer;
//系统数据
SystemData* m_systemData;
/************************************************************/
/* |<------------FEEDBACK--------<-- */
/* | | */
/* IN-->+-------[STATE CONTROL]---------+--->OUT */
/* */
/************************************************************/
public:
//===========================IN============================================
DriverTable m_MSDriverCmd;
MSControlCmd m_msCmd;
//===========================OUT===========================================
DriverTable m_subDisSysCmd;
ccuColCmd m_CcuColCmd;
disSysBreakerList m_targetDisBreakerState;
//===========================FEEDBACK======================================
pmState m_pmCurrentState; //能源系统的当前状态(由下位机线程修改)
disSysBreakerList m_currentDisBreakerState; //配电断路器状态
DriverTable m_currentDriverState;
DriverTable m_subDisSysState;
DriverTable m_subDisSysFault;
int m_depth;
MissionState m_missionState;
//===========================STATE=========================================
unsigned char workCondition;
unsigned char currentMod;
unsigned char powerState;
unsigned char faultLevel;
vector<unsigned int> faultCode;
unsigned int soc;
unsigned int sustainableTime;
unsigned int m_faultNum;
Driver m_driver; //设备表处理
unsigned char m_leakage;
std::set<unsigned int> m_faultCodes;
int m_thrustRpm;
int m_thrustEnable;
// std::set<unsigned int> m_disSysFaultCodes;
//============================Device Option================================
std::queue<DeviceSwitchCmd> m_deviceCmdQuenue;
mutable std::mutex m_deviceCmdMutex;
void pushDeviceCommand(DeviceSwitchCmd cmd);
bool popDeviceCommand(DeviceSwitchCmd &cmd);
bool isDeviceCmdEmpty() const;
//===========================CONFIG========================================
long m_lPort;
unsigned int m_nReceiveBufferSizeKB;
unsigned int m_nSendBufferSizeKB;
double m_current_time;
unsigned int m_ticker;
double m_skew;
int connectState;
//=============================过程函数======================================
void initControlCmd();
public:
CMOOSThread m_commThread;
LowerCommManager* m_lowerCommManager;
UpperCommManager* m_upperCommManager;
// PowerManagerFsm m_fsm;
public:
//===========================状态机接口=======================================
void printState(const std::string& stateName, const std::string& desp);
bool FSM_RUN_IS_OK = true;
//==================================================================================
bool commLoop();
int checkError();
bool sendNotification(const std::string& key, const std::string& value) {
return Notify(key, value);
}
bool sendNotification(const std::string& key, double value) {
return Notify(key, value);
}
public:
double GetPublicAppFreq() { return GetAppFreq(); }
unsigned int GetPublicCommsFreq() { return GetCommsFreq(); }
double GetPublicAppStartTime() { return GetAppStartTime(); }
double GetPublicCPULoad() { return GetCPULoad(); }
double GetPublicTimeSinceIterate() { return GetTimeSinceIterate(); }
double GetPublicLastIterateTime() { return GetLastIterateTime(); }
int GetPublicIterateCount() { return GetIterateCount(); }
string GetServerHost() { return m_sServerHost; }
int GetServerPort() { return m_lServerPort; }
public:
//故障注入
void injectFault(unsigned type,unsigned int faultCode);
};
#endif
-18
View File
@@ -1,18 +0,0 @@
/****************************************************************/
/* NAME: */
/* ORGN: MIT, Cambridge MA */
/* FILE: PowerManger_Info.h */
/* DATE: Dececmber 29th, 1963 */
/****************************************************************/
#ifndef PowerManger_INFO_HEADER
#define PowerManger_INFO_HEADER
void showSynopsis();
void showHelpAndExit();
void showExampleConfigAndExit();
void showInterfaceAndExit();
void showReleaseInfoAndExit();
#endif
-242
View File
@@ -1,242 +0,0 @@
#include "UpperCommManager.h"
#include "PowerManger.h"
#include "driver.h"
#include "fsm/PowerManagerFsm.hpp"
UpperCommManager::UpperCommManager(PowerManger* pm) :
m_pm(pm),
m_secdistStateMsg(),
m_setOpConditionMsg(),
m_depthMsg(),
m_taskStartMsg(),
m_equInsStateMsg(),
m_equStateMsg(),
m_setPMSysMsg(),
m_disSysStateMsg(),
m_currentStateMsg(),
m_pmStateMsg(),
m_fcsStateMsg(),
m_liBatStateMsg(),
m_setOpModeMsg(),
m_taskStopMsg(),
m_setDevSwitchMsg(),
m_sysStateMsg(),
m_msCmd(),
m_driver() {
}
DriverTable uDevice_secdistStateMsg::fault_state = {0};
bool UpperCommManager::processUpperMsg(const string& key, const string& strJson) {
if (key == m_sysStateMsg.key)
{
#ifdef _DEBUG
m_sysStateMsg_st_debug = std::make_pair(key, strJson);
#endif
m_pm->m_msCmd = m_sysStateMsg.getMasterControlCmd(key, strJson);
MasterCommandEvent event;
PowerManagerFsm::dispatch(event);
return true;
}
// 设备状态处理
else if(key == m_equStateMsg.key_cmd)
{
#ifdef _DEBUG
m_equStateMsg_cmd_debug = std::make_pair(key, strJson);
#endif
m_pm->m_MSDriverCmd = m_equStateMsg.getEquipmentCmd(key, strJson);
vector<DriverCommand> devCmdExt = m_pm->m_driver.loadDriverIdFromJson(strJson);
if (devCmdExt.empty())
{
LOG_F(WARNING, "控制指令为空");
}
else {
for (auto& cmd : devCmdExt) {
DevCmdExt event(cmd.id, cmd.cmd);
PowerManagerFsm::dispatch(event);
}
}
return true;
}
// 次级配电状态处理
else if(key == m_secdistStateMsg.key_fb)
{
#ifdef _DEBUG
m_secdistStateMsg_fb_debug = std::make_pair(key, strJson);
#endif
m_secdistStateMsg.parseMsg(strJson, m_pm->m_subDisSysState);
//TODO:自动更新到设备状态 调试
//m_pm->m_subDisSysState = m_pm->m_deviceState;
return true;
}
// 深度获取
else if(key == m_depthMsg.key)
{
#ifdef _DEBUG
m_depthMsg_st_debug = std::make_pair(key, strJson);
#endif
m_pm->m_depth = m_depthMsg.getDepth(key, strJson);
return true;
}
else if(key == m_taskStartMsg.key)
{
#ifdef _DEBUG
m_taskStartMsg_st_debug = std::make_pair(key, strJson);
#endif
m_pm->m_missionState = m_taskStartMsg.getMission(key, strJson);
TaskStartEvent event;
event.type = m_pm->m_missionState.type;
event.enableBowRudder = m_pm->m_missionState.enableBowRudder;
event.enableBuoyancyAdjustment = m_pm->m_missionState.enableBuoyancyAdjustment;
PowerManagerFsm::dispatch(event);
return true;
}
else if(key == m_setOpModeMsg.key)
{
#ifdef _DEBUG
m_setOpModeMsg_st_debug = std::make_pair(key, strJson);
#endif
m_pm->m_msCmd.modCMD = m_setOpModeMsg.getSetOpMode(key, strJson);
MasterCommandEvent event;
PowerManagerFsm::dispatch(event);
return true;
} else if(key == m_setOpConditionMsg.key)
{
#ifdef _DEBUG
m_setOpConditionMsg_st_debug = std::make_pair(key, strJson);
#endif
m_pm->m_msCmd.workCMD = m_setOpConditionMsg.getSetOpCondition(key, strJson);
MasterCommandEvent event;
PowerManagerFsm::dispatch(event);
return true;
}
else if(key == m_setPMSysMsg.key)
{
#ifdef _DEBUG
m_setPMSysMsg_st_debug = std::make_pair(key, strJson);
#endif
m_pm->m_msCmd.powerCMD = m_setPMSysMsg.getSetPMSys(key, strJson);
MasterCommandEvent event;
PowerManagerFsm::dispatch(event);
return true;
}
else if(key == m_setDevSwitchMsg.key)
{
#ifdef _DEBUG
m_setDevSwitchMsg_st_debug = std::make_pair(key, strJson);
#endif
DeviceSwitchCmd cmd;
cmd = m_setDevSwitchMsg.getSetDevSwitch(key, strJson);
if (cmd.id < 0) {
LOG_F(WARNING, "parse device switch error:%s", strJson.c_str());
}
else
{
DevCmdExt devCmdExt;
devCmdExt.id = cmd.id;
devCmdExt.cmd = cmd.cmd;
PowerManagerFsm::dispatch(devCmdExt);
LOG_F(INFO, "set dev switch success");
return true;
}
}
else if(key == m_taskStopMsg.key)
{
#ifdef _DEBUG
m_taskStopMsg_st_debug = std::make_pair(key, strJson);
#endif
bool stop = m_taskStopMsg.getTaskStop(key, strJson);
if (stop) {
LOG_F(INFO, "task stop success");
return true;
}
else {
LOG_F(WARNING, "task stop error:%s", strJson.c_str());
return false;
}
}
else if(key == uDetect_xcVoltage_cmd::key)
{
uDetect_xcVoltage_cmd xcCmd;
xcCmd.xcHighVoltageAdd_int32 = 0;
xcCmd.xcLowVoltageAdd_int32 = 0;
xcCmd.fromJsonString(strJson);
SideSonarCmd sideSonarCmd;
sideSonarCmd.xcHighVoltageAdd = xcCmd.xcHighVoltageAdd_int32;
sideSonarCmd.xcLowVoltageAdd = xcCmd.xcLowVoltageAdd_int32;
PowerManagerFsm::dispatch(sideSonarCmd);
return true;
}
else if(key == uDetect_zdpVoltage_cmd::key)
{
uDetect_zdpVoltage_cmd zdpCmd;
zdpCmd.fromJsonString(strJson);
DevCmdExt devCmdExt;
devCmdExt.id = DeviceId::lowFreqSonar;
devCmdExt.cmd = zdpCmd.zdpVoltageAdd_int32;
PowerManagerFsm::dispatch(devCmdExt);
LOG_F(INFO, "set dev switch success");
return true;
}
else if(key == uDevice_thruster_st::key)
{
uDevice_thruster_st thrusterSt;
thrusterSt.fromJsonString(strJson);
m_pm->m_thrustRpm = thrusterSt.rpm_int16;
if(thrusterSt.maxTorque_uint16 == 0)
m_pm->m_thrustEnable = 0;
else
m_pm->m_thrustEnable = 1;
return true;
};
LOG_F(WARNING, "Unknown key: %s", key.c_str());
return false;
}
void UpperCommManager::processPeriodicTasks() {
buildSystemReport();
std::string strJson;
//发送当前设备状态
strJson = m_equStateMsg.buildMsg(m_pm->m_currentDriverState);
m_pm->sendNotification(m_equStateMsg.key, strJson);
//发送次级配电指令 修改后的代码
strJson = m_secdistStateMsg.buildMsg(m_pm->m_subDisSysCmd,m_pm->m_subDisSysState, m_pm->m_subDisSysFault);
m_pm->sendNotification(m_secdistStateMsg.key_cmd, strJson);
//发送仪表电状态
m_pm->sendNotification(m_equInsStateMsg.key,m_equInsStateMsg.buildMsg(m_pm->m_subDisSysState));
#ifdef _DEBUG
m_equInsStateMsg_fb_degub = std::make_pair(m_equInsStateMsg.key,m_equInsStateMsg.buildMsg(m_pm->m_subDisSysState));
m_secdistStateMsg_cmd_debug = std::make_pair(m_secdistStateMsg.key_cmd,strJson);
m_equStateMsg_fb_debug = std::make_pair(m_equStateMsg.key_cmd,m_equStateMsg.buildMsg(m_pm->m_currentDriverState));
strJson = m_pm->m_driver.saveDriverTableToJson(&m_pm->m_subDisSysFault);
m_pm->sendNotification("uPower_subDisFault_st",strJson);
#endif
}
void UpperCommManager::buildSystemReport() {
m_pmStateMsg.state.workCondition = m_pm->workCondition;
m_pmStateMsg.state.currentMod = m_pm->currentMod;
m_pmStateMsg.state.powerState = m_pm->powerState;
m_pmStateMsg.state.faultLevel = m_pm->faultLevel;
m_pmStateMsg.state.faultCode = m_pm->faultCode;
// cout << "m_pmStateMsg.state.faultCode.size():" << m_pmStateMsg.state.faultCode.size() << endl;
m_pmStateMsg.state.soc = m_pm->soc;
m_pmStateMsg.state.sustainableTime = m_pm->sustainableTime;
// m_pmStateMsg.state.m_leakage = m_pm->m_leakage;
m_pmStateMsg.state.m_leakage = m_pm->m_systemData->generateLeakageCode();
m_pmStateMsg.state.m_faultNum = m_pm->m_faultNum;
m_pm->sendNotification(m_pmStateMsg.key, m_pmStateMsg.buildMsg(*(m_pm->m_systemData)));
m_pm->sendNotification(m_fcsStateMsg.key, m_fcsStateMsg.buildMsg(*(m_pm->m_systemData)));
m_pm->sendNotification(m_liBatStateMsg.key, m_liBatStateMsg.buildMsg(*(m_pm->m_systemData)));
m_pm->sendNotification(m_disSysStateMsg.key, m_disSysStateMsg.buildMsg(*(m_pm->m_systemData),m_pm->m_currentDisBreakerState));
m_pm->sendNotification(m_currentStateMsg.key, m_currentStateMsg.buildMsg(m_pm->m_pmCurrentState));
#ifdef _DEBUG
m_pmStateMsg_fb_debug = std::make_pair(m_pmStateMsg.key, m_pmStateMsg.buildMsg(*(m_pm->m_systemData)));
m_fcsStateMsg_st_debug = std::make_pair(m_fcsStateMsg.key, m_fcsStateMsg.buildMsg(*(m_pm->m_systemData)));
m_liBatStateMsg_st_debug = std::make_pair(m_liBatStateMsg.key, m_liBatStateMsg.buildMsg(*(m_pm->m_systemData)));
m_disSysStateMsg_st_debug = std::make_pair(m_disSysStateMsg.key, m_disSysStateMsg.buildMsg(*(m_pm->m_systemData),m_pm->m_currentDisBreakerState));
m_currentStateMsg_st_debug = std::make_pair(m_currentStateMsg.key, m_currentStateMsg.buildMsg(m_pm->m_pmCurrentState));
#endif
}
-118
View File
@@ -1,118 +0,0 @@
#ifndef UPPER_COMM_MANAGER_H
#define UPPER_COMM_MANAGER_H
#include "MOOS/libMOOS/Thirdparty/AppCasting/AppCastingMOOSApp.h"
#include "pmSysvariable.h"
#include "driver.h"
#include "json/json.h"
#include <string>
#include <vector>
#include <map>
#include "upmsg/uPower_equInsState.h"
#include "upmsg/uDevice_secdistState.h"
#include "upmsg/uPower_equState.h"
#include "upmsg/uPower_disSysState.h"
#include "upmsg/uPower_currentState_st.h"
#include "upmsg/uPower_pmState.h"
#include "upmsg/uPower_fcsState.h"
#include "upmsg/uPower_liBatState.h"
#include "upmsg/uDevice_depth.h"
#include "upmsg/uExternComm_setOpCondition.h"
#include "upmsg/uExternComm_setOpMode.h"
#include "upmsg/uPlan_taskStart.h"
#include "upmsg/uPlan_planStop.h"
#include "upmsg/uExternComm_setDeviceSwitch.h"
#include "upmsg/uExternComm_setPowerSystem.h"
#include "upmsg/uPower_equState.h"
#include "upmsg/uPower_sysState.h"
#include "upmsg/uDetect_zdpVoltage_cmd.h"
#include "upmsg/uDetect_xcVoltage_cmd.h"
#include "upmsg/uDevice_thruster_st.h"
#define _DEBUG
using namespace std;
#define VAR_TO_STR(var) #var
//控制消息
#define DB_TO_PM_MSCMD "uPower_sysState_cmd"
#define DB_TO_PM_EQUIPMENTCMD "uPower_equState_cmd"
//决策消息
#define DB_TO_PM_DEEPTH "uDevice_depth_st"
// #define DB_TO_PM_MISSION "uPlan_taskStart_cmd"
#define DB_TO_PM_TASKSTOP "uPlan_planStop_st"
//外部通信消息
#define DB_TO_PM_SETOPCONDITION "uExternComm_setOpCondition_cmd"
#define DB_TO_PM_SETOPMODE "uExternComm_setOpMode_cmd"
#define DB_TO_PM_SETPMSYS "uExternComm_setPowerSystem_cmd"
#define DB_TO_PM_SETDEVSWITCH "uExternComm_setDeviceSwitch_cmd"
class PowerManger; // 前向声明
class UpperCommManager {
public:
UpperCommManager(PowerManger* powerManager);
~UpperCommManager(){};
// 周期调用
void processPeriodicTasks();
// 处理上位机消息
bool processUpperMsg(const std::string& key, const std::string& msg);
// 生成系统报告
void buildSystemReport();
public:
//定义上位机消息
uPower_equInsStateMsg m_equInsStateMsg;
uDevice_secdistStateMsg m_secdistStateMsg;
uPower_equStateMsg m_equStateMsg;
uPower_disSysStateMsg m_disSysStateMsg;
uPower_currentStateMsg m_currentStateMsg;
uPower_pmStateMsg m_pmStateMsg;
uPower_fcsStateMsg m_fcsStateMsg;
uPower_liBatStateMsg m_liBatStateMsg;
//上位机订阅消息
uDevice_depthMsg m_depthMsg;
uExternComm_setOpConditionMsg m_setOpConditionMsg;
uExternComm_setOpModeMsg m_setOpModeMsg;
uPlan_taskStartMsg m_taskStartMsg;
uPlan_planStopMsg m_taskStopMsg;
uExternComm_setDeviceSwitchMsg m_setDevSwitchMsg;
uExternComm_setPowerSystemMsg m_setPMSysMsg;
uPower_sysStateMsg m_sysStateMsg;
private:
PowerManger* m_pm;
MSControlCmd m_msCmd;
Driver m_driver;
#ifdef _DEBUG
public:
std::pair<string, string> m_equInsStateMsg_fb_degub;
std::pair<string, string> m_secdistStateMsg_cmd_debug;
std::pair<string, string> m_secdistStateMsg_fb_debug;
std::pair<string, string> m_equStateMsg_cmd_debug;
std::pair<string, string> m_equStateMsg_fb_debug;
std::pair<string, string> m_disSysStateMsg_st_debug;
std::pair<string, string> m_currentStateMsg_st_debug;
std::pair<string, string> m_pmStateMsg_fb_debug;
std::pair<string, string> m_fcsStateMsg_st_debug;
std::pair<string, string> m_liBatStateMsg_st_debug;
std::pair<string, string> m_depthMsg_st_debug;
std::pair<string, string> m_setOpConditionMsg_st_debug;
std::pair<string, string> m_setOpModeMsg_st_debug;
std::pair<string, string> m_taskStartMsg_st_debug;
std::pair<string, string> m_taskStopMsg_st_debug;
std::pair<string, string> m_setDevSwitchMsg_st_debug;
std::pair<string, string> m_setPMSysMsg_st_debug;
std::pair<string, string> m_sysStateMsg_st_debug;
#endif
// bool parseControlCmdFromJson(MSControlCmd &cmd, const std::string &strJson);
};
#endif // UPPER_COMM_MANAGER_H
File diff suppressed because it is too large Load Diff
-138
View File
@@ -1,138 +0,0 @@
#ifndef DRIVER_H
#define DRIVER_H
#include <memory>
#include "json/json.h"
#include "iostream"
#include <sstream>
#include <vector>
#include "pmSysvariable.h"
#define DRIVER_POWER_OFF 0x00
#define DRIVER_FULL_POWER_ON 0x01
#define DRIVER_INSTRUMENT_POWER_ON 0x02
#define DRIVER_MOTIVE_POWER_ON 0x03
#define DRIVER_STANDBY 0x04
using namespace std;
struct DriverTable
{
unsigned char depth1;
unsigned char depth2;
unsigned char depth3;
unsigned char overDeep1;
unsigned char overDeep2;
unsigned char ctd1;
unsigned char ctd2;
unsigned char soundVeloc;
unsigned char commMast;
unsigned char ins;
unsigned char ntp;
unsigned char inputWaterSensor1;
unsigned char inputWaterSensor2;
unsigned char inputWaterSensor3;
//水声设备
unsigned char singleBeamSonar1;
unsigned char singleBeamSonar2;
unsigned char singleBeamSonar3;
unsigned char singleBeamSonar4;
unsigned char singleBeamSonar5;
unsigned char singleBeamSonar6;
unsigned char singleBeamSonar7;
unsigned char singleBeamSonar8;
unsigned char forwardSonar;
unsigned char DVL;
unsigned char lowFreqSonar;
unsigned char USBL;
//执行机构
unsigned char bowWaterInet1;
unsigned char bowWaterInet2;
unsigned char bowWaterInet3;
unsigned char bowWaterInet4;
unsigned char bowWaterInet5;
unsigned char bowWaterInet6;
unsigned char bowWaterInet7;
unsigned char bowWaterInet8;
unsigned char bowRudderDeploy;
unsigned char bowRudderServo1;
unsigned char bowRudderServo2;
unsigned char bowCover1;
unsigned char bowCover2;
unsigned char bowCover3;
unsigned char bowCover4;
unsigned char bowCover5;
unsigned char bowCover6;
unsigned char waterInject;
unsigned char mastLiftingServo;
unsigned char buoyancyAdjust1;
unsigned char buoyancyAdjust2;
unsigned char buoyancyAdjust3;
unsigned char buoyancyAdjust4;
unsigned char buoyancyAdjust5;
unsigned char sternRudderServo1;
unsigned char sternRudderServo2;
unsigned char sternRudderServo3;
unsigned char sternRudderServo4;
unsigned char thruster;
unsigned char bowThrow;
unsigned char sternThrow;
//负载
unsigned char photoSensor;
unsigned char electSensor;
unsigned char launchControler;
unsigned char sideSonar;
unsigned char buoyage;
};
struct DriverCommand
{
int id;
int cmd;
};
class Driver
{
public:
Driver();
~Driver();
void setDebug(bool debug);
public:
std::unique_ptr<DriverTable> currentDriverTable;
std::unique_ptr<DriverTable> nextDriverTable;
//操作函数,操作指定设备
void setDeviceState(DriverTable &driverTable, DeviceId::Type deviceId, unsigned char s);
unsigned char getDeviceState(const DriverTable &driverTable, DeviceId::Type deviceId);
void getSTANDBYTable(DriverTable* driverTable);
void getSHORE_BASED_READYable(DriverTable* driverTable);
void getWATER_BASED_READYable(DriverTable* driverTable);
void getCRUISE_MODEable(DriverTable* driverTable);
void getHIGH_SPEED_MODEable(DriverTable* driverTable);
void getASCEND_DESCENDable(DriverTable* driverTable);
void getBUOYANCY_ADJUSTable(DriverTable* driverTable);
void getUNDERWATER_RECONable(DriverTable* driverTable);
void getSURFACE_RECONable(DriverTable* driverTable);
void getDJ_MODEable(DriverTable* driverTable);
// void setDriverTable(DriverTable* driverTable, );
std::string getDriverTableJson();
std::string getSubDisCmdJson(const DriverTable* driverTable);
std::string getDriveStateJson(const DriverTable* driverTable);
std::string saveDriverTableToJson(const DriverTable* driverTable);
int loadDriverTableFromJson(const std::string &strJson, DriverTable& table);
vector<DriverCommand> loadDriverIdFromJson(const std::string &strJson);
int SafeReadUInt(const Json::Value& root, const std::string& key, unsigned char& value);
private:
bool debug;
};
#endif // DRIVER_H
-125
View File
@@ -1,125 +0,0 @@
#ifndef FAULTCODE_H
#define FAULTCODE_H
/**
* @file faultCode.h
* @brief 锂电池故障码定义及映射表
*/
// 故障码映射表
typedef struct {
int id; // 序号
int level; // 故障级别
const uint8_t code; // 16进制故障码
const char* desc; // 报警信息
} FaultMap;
static const FaultMap batfaultMap[] = {
//四级故障
{1, 4, 0xC1, "MBMS绝缘故障"},
{2, 4, 0xC2, "温度过高"},
{3, 4, 0xC3, "MBMS预充失败"},
{4, 4, 0xC4, "MBMS正主极继电器失效"},
{5, 4, 0xC5, "MBMS负主极继电器失效"},
{6, 4, 0xC6, "MBMS燃电充电正极继电器正极失效[仪器]"},
{7, 4, 0xC7, "母线高压回路异常"},
{8, 4, 0xC9, "MBMS电压测量异常"},
{9, 4, 0xCA, "MBMS电流测量异常"},
//三级故障
{10, 3, 0x81, "MBMS绝缘故障3级"},
{11, 3, 0x82, "温度过高3级"},
{12, 3, 0x83, "温度过低3级"},
{13, 3, 0x84, "MBMS总放电超功率3级"},
{14, 3, 0x85, "MBMS总充电电流过大3级"},
//二级故障
{15, 1, 0x31, "BMS均衡模块故障2级"},
{16, 2, 0x41, "MBMS绝缘故障2级"},
{17, 2, 0x42, "MBMS总放电超功率2级"},
{18, 2, 0x43, "电池包放电超功率2级"},
{19, 2, 0x44, "MBMS总放电过流2级"},
{20, 2, 0x45, "单包异常剔除2级"},
{21, 2, 0x46, "单包没有电流2级"},
{22, 2, 0x47, "自检有重复ID号2级"},
{23, 2, 0x48, "电池包接入失败2级"},
{24, 2, 0x49, "电池包充电电流过大2级"},
{25, 2, 0x51, "电池包绝缘故障2级"},
{26, 2, 0x52, "电池包温度过高2级"},
{27, 2, 0x53, "电池包温度过低2级"},
{28, 2, 0x54, "电池包单体电压不均衡2级"},
{29, 2, 0x55, "电池包单体温度不均衡2级"},
{30, 2, 0x56, "电池包过流2级"},
{31, 2, 0x57, "电池包充电电流过大2级"},
{32, 2, 0x58, "电池包单体电压过高2级"},
{33, 2, 0x59, "电池包单体电压过低2级"},
{34, 2, 0x5A, "电池包总电压过高2级"},
{35, 2, 0x5B, "电池包总电压过低2级"},
{36, 2, 0x5C, "电池包内部通讯故障2级"},
{37, 2, 0x5D, "电池包初始化故障2级"},
{38, 2, 0x62, "电池包高压回路连接异常2级"},
{39, 2, 0x65, "电池包烟雾报警2级"},
{40, 2, 0x72, "MBMS在线燃电充电超温2级"},
//一级故障
{41, 1, 0x01, "MBMS绝缘故障1级"},
{42, 1, 0x02, "MBMS总放电超功率1级"},
{43, 1, 0x11, "电池包绝缘故障1级"},
{44, 1, 0x12, "电池包最高温度过高1级"},
{45, 1, 0x13, "电池包最低温度过低1级"},
{46, 1, 0x14, "电池包单体电压不均衡1级"},
{47, 1, 0x15, "电池包单体温度不均衡1级"},
{48, 1, 0x16, "电池包放电过流1级"},
{49, 1, 0x17, "电池包充电电流过大1级"},
{50, 1, 0x18, "电池包单体电压过高1级"},
{51, 1, 0x19, "电池包单体电压过低1级"},
{52, 1, 0x1A, "电池包总电压过高1级"},
{53, 1, 0x1B, "电池包总电压过低1级"},
{54, 1, 0x1D, "电池包烟雾报警1级"},
};
static const FaultMap disfaultMap[] = {
{1, 3, 1, "燃料电池断路器故障脱扣"},
{2, 4, 2, "动力锂电池断路器故障脱扣"},
{3, 4, 3, "推进电机断路器故障脱扣"},
{4, 4, 4, "锂电池组仪表断路器故障脱扣"},
{5, 4, 5, "DC/DC5模块断路器故障脱扣"},
{6, 4, 6, "艏部高压配电箱断路器故障脱扣"},
{7, 4, 7, "艉部FT装置4/5断路器故障脱扣"},
{8, 4, 8, "艉舵开关1断路器故障脱扣"},
{9, 4, 9, "艉舵开关2断路器故障脱扣"},
{10, 4, 10, "FB预留断路器故障脱扣"},
{11, 4, 11, "TYZ预留断路器故障脱扣"},
{12, 4, 12, "预留断路器故障脱扣"},
{13, 4, 13, "艏部FT装置1-3 断路器故障脱扣"},
{14, 4, 14, "启闭机构断路器故障脱扣"},
{15, 4, 15, "桅杆舵机控制箱断路器故障脱扣"},
{16, 4, 16, "XCZ 断路器故障脱扣"},
{17, 4, 17, "艏舵控制箱断路器故障脱扣"},
{18, 4, 18, "敞水盖启动电缸断路器故障脱扣"},
{19, 4, 19, "锂电池组(仪表)断路器故障脱扣"},
{20, 4, 20, "艏部低压配电箱断路器故障脱扣"},
{21, 4, 21, "UNIT4仪表DC48V断路器故障脱扣"},
{22, 4, 22, "DC-DC1直流配电板断路器故障脱扣"},
{23, 4, 23, "预留断路器故障脱扣"},
{24, 4, 24, "预留断路器故障脱扣"},
{25, 4, 25, "应急锂电池组2断路器"},
{26, 4, 26, "UNIT1断路器故障脱扣"},
{27, 4, 27, "UNIT2断路器故障脱扣"},
{28, 4, 28, "UNIT3断路器故障脱扣"},
{29, 4, 29, "UNIT5断路器故障脱扣"},
{30, 4, 30, "艏部PZ装置断路器故障脱扣"},
{31, 4, 31, "预留断路器故障脱扣"},
{32, 4, 32, "预留断路器故障脱扣"},
{33, 4, 33, "应急锂电池组1断路器故障脱扣"},
{34, 4, 34, "动力母排绝缘低"},
{35, 4, 35, "A汇流排绝缘低"},
{36, 4, 36, "高压母线配电控制器仪表电源失电信号"},
{37, 4, 37, "高压母线配电控制器应急电源失电信号"},
{38, 4, 38, "高压汇流排A仪表电源失电信号"},
{39, 4, 39, "高压汇流排A应急电源失电信号"},
{40, 4, 40, "高压汇流排A仪表汇流排绝缘低"},
{41, 4, 41, "高压汇流排B仪表电源失电信号"},
{42, 4, 42, "高压汇流排B应急电源失电信号"},
{43, 4, 43, "高压汇流排B仪表汇流排绝缘低"},
{44, 4, 44, "仪表汇流排失电信号"},
{45, 4, 45, "仪表汇流排浸水报警"}
};
#endif // FAULTCODE_H
-59
View File
@@ -1,59 +0,0 @@
#include "PowerManagerFsm.hpp"
void CruiseMode::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
// pm->m_driver.getCRUISE_MODEable(&pm->m_subDisSysCmd);
handleCuriseWork();
//解除非必要动力电设备
//1 解除启闭机构
unsigned char cmd = 0;
if(pm->m_currentDriverState.bowCover1 != 0 ||
pm->m_currentDriverState.bowCover2 != 0 ||
pm->m_currentDriverState.bowCover3 != 0 ||
pm->m_currentDriverState.bowCover4 != 0 ||
pm->m_currentDriverState.bowCover5 != 0 ||
pm->m_currentDriverState.bowCover6 != 0)
{
pm->m_lowerCommManager->operate_HVA_actuatorCircuit_Breaker(0);
MOOSPause(3);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover6, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
}
//2 解除敞水机构
if(pm->m_currentDriverState.bowWaterInet1 != 0 ||
pm->m_currentDriverState.bowWaterInet2 != 0 ||
pm->m_currentDriverState.bowWaterInet3 != 0 ||
pm->m_currentDriverState.bowWaterInet4 != 0 ||
pm->m_currentDriverState.bowWaterInet5 != 0 ||
pm->m_currentDriverState.bowWaterInet6 != 0 ||
pm->m_currentDriverState.bowWaterInet7 != 0 ||
pm->m_currentDriverState.bowWaterInet8 != 0)
{
pm->m_lowerCommManager->operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(0);
MOOSPause(3);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet6, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet7, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet8, (unsigned char)cmd);
}
pm->workCondition = WorkCondition::CRUISE_MODE;
LOG_F(INFO, "进入巡航模式");
}
// CruiseMode实现
void CruiseMode::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
-71
View File
@@ -1,71 +0,0 @@
#include "PowerManagerFsm.hpp"
// DJMode实现
void DJMode::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void DJMode::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
handleCuriseWork();
//1 启动载荷装置
pm->m_subDisSysCmd.launchControler = 1;
//启动启闭机构和电缸
unsigned char cmd = 1;
//2.1 启闭机构
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover6, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(10);
pm->m_lowerCommManager->operate_HVA_actuatorCircuit_Breaker(1);
//2.2 敞水电缸
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet6, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet7, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet8, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(10);
pm->m_lowerCommManager->operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(1);
pm->workCondition = WorkCondition::DJ_MODE;
LOG_F(INFO, "进入DJ模式");
}
void DJMode::exit() {
PowerManagerFsm::exit();
//1 解除启闭机构
unsigned char cmd = 0;
pm->m_lowerCommManager->operate_HVA_actuatorCircuit_Breaker(0);
MOOSPause(3);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover6, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
//2 解除敞水机构
pm->m_lowerCommManager->operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(0);
MOOSPause(3);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet6, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet7, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet8, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
}
-20
View File
@@ -1,20 +0,0 @@
#include "PowerManagerFsm.hpp"
// FaultState实现
void FaultState::entry() {
PowerManagerFsm::entry();
if(pm->m_missionState.type == TaskType::THROW_LOAD)
{
LOG_F(INFO, "检测到抛载");
}
}
void FaultState::react(FaultEvent const & e) {
LOG_F(INFO, "FaultState 处理故障事件");
}
void FaultState::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
-16
View File
@@ -1,16 +0,0 @@
#include "PowerManagerFsm.hpp"
// FloatAdjust实现
void FloatAdjust::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void FloatAdjust::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
handleCuriseWork();
pm->workCondition = WorkCondition::BUOYANCY_ADJUST;
LOG_F(INFO, "进入浮调模式");
}
-16
View File
@@ -1,16 +0,0 @@
#include "PowerManagerFsm.hpp"
void FloatDown::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
handleCuriseWork();
pm->workCondition = WorkCondition::ASCEND_DESCEND;
LOG_F(INFO, "进入上浮下潜模式");
}
// FloatDown实现
void FloatDown::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
@@ -1,16 +0,0 @@
#include "PowerManagerFsm.hpp"
void HighSpeedMode::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
handleCuriseWork();
pm->workCondition = WorkCondition::HIGH_SPEED_MODE;
LOG_F(INFO, "进入高速模式");
}
// HighSpeedMode实现
void HighSpeedMode::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
-10
View File
@@ -1,10 +0,0 @@
#include "PowerManagerFsm.hpp"
// InitState实现
void InitState::entry() {
PowerManagerFsm::entry();
LOG_F(INFO, "进入初始化状态");
pm->initControlCmd();
pm->m_lowerCommManager->operate_hold();
transit<StandbyState>();
}
-90
View File
@@ -1,90 +0,0 @@
#include "PowerManagerFsm.hpp"
void Lifting::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
//1. 关闭所有水声设备
LOG_F(INFO,"前视声纳关机指令");
pm->sendNotification("uExternComm_frontSonarControl_cmd","0");
MOOSPause(10000);
for(auto iter : underWterDevices)
{
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, static_cast<DeviceId::Type>(iter), 0);
}
//2. 关闭动力电设备
//2.1 断开所有艏部高压断路器
pm->m_lowerCommManager->operate_HVA_actuatorCircuit_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HVA_bowFTDevice123Circuit_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HVA_bowRudderControlBoxCircuit_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HVA_mastSteeringGearControlBoxCircuit_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HVA_xczCircuit_Breaker(0);
MOOSPause(3);
//2.2 断开所有艉部高压设备断路器
pm->m_lowerCommManager->operate_HV_propulsionMotor_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_sternFt45_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_sternRudder1_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_sternRudder2_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_fbReserved_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_tyzReserved_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_reserved_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_dcDc5_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_coolingSystem_Breaker(0);
MOOSPause(3);
//2.5 断开动力电断路器
pm->m_lowerCommManager->operate_HV_bowHighVoltageBox_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_dcDc5_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_coolingSystem_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_dcDc5Module_Breaker(0);
MOOSPause(3);
pm->m_lowerCommManager->operate_HV_powerLithiumBattery_Breaker(0);
//3. 关闭燃料电池
//4. 关闭动力锂电池
MOOSPause(3);
{
int count=0;
while (!(pm->m_systemData->dyn_pos_relay_status == 0x02 && pm->m_systemData->dyn_neg_relay_status == 0x02))
{
LOG_F(INFO,"动力锂电池下电中...");
MOOSPause(3);
if(count>10)
{
LOG_F(INFO, "动力锂电池无下电反馈");
break;
}
count++;
}
}
LOG_F(INFO, "动力锂电池已下电");
pm->workCondition = WorkCondition::LIFT_MODE;
LOG_F(INFO, "WorkCondition %d", pm->workCondition);
}
void Lifting::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void Lifting::react(DevCmdExt const &e)
{
LOG_F(INFO, "DevCmdExt 执行");
PowerManagerFsm::react(e);
}
File diff suppressed because it is too large Load Diff
@@ -1,300 +0,0 @@
#ifndef POWER_MANAGER_FSM_HPP
#define POWER_MANAGER_FSM_HPP
#include "tinyfsm.hpp"
#include "../logc/loguru.hpp"
#include <iostream>
#include <chrono> // 新增头文件
#include <iomanip> // 新增头文件
#include <ctime> // 新增头文件,用于localtime函数
#include <cxxabi.h> // 新增头文件,用于demangle功能
#include "../pmSysvariable.h"
#include "../driver.h"
#include "../PowerManger.h"
#include "../upmsg/uExternComm_setThrustSpeed_cmd.h"
#define OVER_DEPTH 0.1
// 前向声明
class PowerManger;
enum class PowerSystemState {
INIT,
STANDBY,
FAULT,
};
//==================状态切换事件=============================
struct InitEvent : tinyfsm::Event { };
struct StandbyEvent : tinyfsm::Event { };
struct TaskStopEvent : tinyfsm::Event { };
struct TaskStartEvent : tinyfsm::Event {
int type;
bool enableBowRudder;
bool enableBuoyancyAdjustment;
TaskStartEvent() : type(0), enableBowRudder(false), enableBuoyancyAdjustment(false) {};
TaskStartEvent(int type, bool bowRudder, bool buoyancyAdjustment) : type(type), enableBowRudder(bowRudder), enableBuoyancyAdjustment(buoyancyAdjustment) {};
};
//==================状态流程事件============================
struct CycleTriggeredEvent : tinyfsm::Event {};
struct MasterCommandEvent : tinyfsm::Event {};
struct DeviceCmd : tinyfsm::Event {};
struct DevCmdExt : tinyfsm::Event {
int id;
int cmd;
DevCmdExt() : id(0), cmd(0) {};
DevCmdExt(int id, int cmd) : id(id), cmd(cmd) {};
};
struct SideSonarCmd : tinyfsm::Event {
int xcLowVoltageAdd;
int xcHighVoltageAdd;
SideSonarCmd() : xcLowVoltageAdd(0),xcHighVoltageAdd(0) {};
SideSonarCmd(int xcLowVoltageAdd,int xcHighVoltageAdd) : xcLowVoltageAdd(xcLowVoltageAdd),xcHighVoltageAdd(xcHighVoltageAdd) {};
};
//==================测试事件================================
struct TestEvent : tinyfsm::Event {
std::string eventName;
unsigned int type;
unsigned int eventId;
TestEvent() : eventName(""), eventId(0),type(0) {};
TestEvent(std::string eventName, unsigned int type,unsigned int eventId) : eventName(eventName), eventId(eventId),type(type) {};
};
struct FaultEvent : tinyfsm::Event {
unsigned int subDisSysFaultCode;
unsigned int disSysFaultCode;
unsigned int powerFaultCode;
unsigned int fcsFaultCode;
unsigned int ccsFaultCode;
unsigned int injectionFaultCode;
FaultEvent() : subDisSysFaultCode(0), disSysFaultCode(0), powerFaultCode(0), fcsFaultCode(0), ccsFaultCode(0),injectionFaultCode(0) {};
FaultEvent(unsigned int subDisSysFaultCode,
unsigned int disSysFaultCode,
unsigned int powerFaultCode,
unsigned int fcsFaultCode,
unsigned int ccsFaultCode,
unsigned int injectionFaultCode)
: subDisSysFaultCode(subDisSysFaultCode),
disSysFaultCode(disSysFaultCode),
powerFaultCode(powerFaultCode),
fcsFaultCode(fcsFaultCode),
ccsFaultCode(ccsFaultCode),
injectionFaultCode(injectionFaultCode) {};
};
class PowerManagerFsm : public tinyfsm::Fsm<PowerManagerFsm> {
protected:
static PowerManger* pm;
static DriverTable m_currentDriver;
public:
static void init(PowerManger* powerManager) {
pm = powerManager;
}
virtual void react(tinyfsm::Event const &) { std::cout << "[ Reseve Envt ] " << "No Idear" << std::endl; }
virtual void react(InitEvent const &) {};
virtual void react(StandbyEvent const &) {};
virtual void react(MasterCommandEvent const &) { LOG_F(INFO, "MasterCommandEvent");};
// virtual void react(DeviceCmd const &);
virtual void react(DevCmdExt const &e);
virtual void react(TaskStartEvent const &e);
virtual void react(FaultEvent const &e);
virtual void react(TaskStopEvent const &e);
virtual void react(CycleTriggeredEvent const &e);
virtual void react(SideSonarCmd const &e);
//测试事件
virtual void react(TestEvent const &);
//======================操作函数=============================
void handelPowerCmd();
void handleModCmd();
void handleWorkCmd();
void handleDeviceCmd();
void handleCuriseWork();
void handleNoPowerDeviceCmd(int id , int cmd); // 非水声仪表设备
void handleHydroacousticDeviceCmd(int id , int cmd); // 水声设备处理
void handleBowRudderSystemCmd(int id, int cmd); // 艏舵设备处理
void handleActuatorCmd(int id, int cmd); // 启闭机构处理
void handleOpenWaterCmd(int id, int cmd); // 敞水机构处理
void handleSternRudderCmd(int id, int cmd); // 艉舵设备处理
void handlePropulsionMotorCmd(int id, int cmd); // 推进电机处理
void handleBuoyancyAdjustmentCmd(int id, int cmd); // 浮调设备处理
void handleFBSystemCmd(int id, int cmd); // FB设备处理
void handleSideSonarCmd(int id, int cmd); // 舷侧设备处理
void handleMastFoldingSystemCmd(int id, int cmd); // 桅杆设备处理
//获取仅仪表设备状态
void getNoPowerDeviceStatus(DriverTable &s);
void getBowRudderSystemStatus(DriverTable &s);
void getActuatorStatus(DriverTable &s);
void getSternRudderStatus(DriverTable &s);
void getPropulsionMotorStatus(DriverTable &s);
void getBuoyancyAdjustmentStatus(DriverTable &s);
void getFBSystemCmdStatus(DriverTable &s);
void getSideSonarStatus(DriverTable &s);
void getMastFoldingSystem(DriverTable &s);
void getOpenWaterStatus(DriverTable &s);
//故障处理
void basicNavigationFault(FaultEvent const &e);
void extendedNavigationFault(FaultEvent const &e);
void obstacleAvoidanceFault(FaultEvent const &e);
void buoyancyControlFault(FaultEvent const &e);
void communicationSystemFault(FaultEvent const &e);
void payloadSystemFault(FaultEvent const &e);
void emergencySystemFault(FaultEvent const &e);
void sensorSystemFault(FaultEvent const &e);
void basicNavigationFault();
void extendedNavigationFault();
void obstacleAvoidanceFault();
void buoyancyControlFault();
void communicationSystemFault();
void payloadSystemFault();
void emergencySystemFault();
void sensorSystemFault();
// void handleFaultOfDisSys();
// 状态机生命周期方法
virtual void entry() {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
int status = 0;
char* demangled = abi::__cxa_demangle(typeid(*this).name(), nullptr, nullptr, &status);
std::string typeName = (status == 0) ? demangled : typeid(*this).name();
if (demangled) free(demangled);
std::cout << "\033[1;38;5;51m╭──────────────────────────────────────────────────────\n"
<< "│ \033[1;38;5;226m⮞⮞⮞ \033[0;38;5;51m[" << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S")
<< "] \033[1;38;5;255m进入状态: \033[1;38;5;213m" << typeName << "\n"
<< "│ \033[0m" << std::endl;
LOG_F(INFO, "进入状态: %s", typeName.c_str());
}
virtual void exit() {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
int status = 0;
char* demangled = abi::__cxa_demangle(typeid(*this).name(), nullptr, nullptr, &status);
std::string typeName = (status == 0) ? demangled : typeid(*this).name();
if (demangled) free(demangled);
std::cout << "\033[1;38;5;203m│ \n"
<< "│ \033[1;38;5;208m⮜⮜⮜ \033[0;38;5;203m[" << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S")
<< "] \033[1;38;5;255m离开状态: \033[1;38;5;213m" << typeName << "\n"
<< "╰──────────────────────────────────────────────────────\033[0m" << std::endl;
LOG_F(INFO, "离开状态: %s", typeName.c_str());
}
};
// 初始化
class InitState : public PowerManagerFsm {
public:
void entry() override;
};
//待机状态
class StandbyState : public PowerManagerFsm {
public:
void entry() override;
void react(StandbyEvent const &) override;
void react(DevCmdExt const &e) override;
void react(MasterCommandEvent const &) override;
};
// 岸基备航
class ShoreBasedReady : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// 水中备航
class WaterBasedReady : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
void react(DevCmdExt const &e) override;
private:
};
// 巡航模式
class CruiseMode : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// 上浮下潜
class FloatDown : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// 高速模式
class HighSpeedMode : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// 浮调模式
class FloatAdjust : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// 水下侦查
class UnderwaterSurvey : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// 水面侦查
class SurfaceSurvey : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
// DJ模式
class DJMode : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
void exit() override;
private:
};
// 遥控工况
class RemoteControl : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
private:
};
class FaultState : public PowerManagerFsm {
public:
void entry() override;
void react(FaultEvent const &);
void react(MasterCommandEvent const &) override;
};
class Lifting : public PowerManagerFsm {
public:
void entry() override;
void react(MasterCommandEvent const &) override;
};
#endif // POWER_MANAGER_FSM_HPP
@@ -1,16 +0,0 @@
#include "PowerManagerFsm.hpp"
// RemoteControl实现
void RemoteControl::react(MasterCommandEvent const &){
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void RemoteControl::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::REMOTE_CONTROL;
LOG_F(INFO, "进入遥控工况");
}
@@ -1,92 +0,0 @@
#include "PowerManagerFsm.hpp"
// ShoreBasedReady实现
void ShoreBasedReady::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void ShoreBasedReady::entry() {
PowerManagerFsm::entry();
pm->m_driver.getSHORE_BASED_READYable(&pm->m_subDisSysCmd);
pm->workCondition = WorkCondition::CHANING;
//依次接入执行机构动力电设备
//1. 开启动力电
//1.1 动力锂电池上电
pm->m_CcuColCmd.powerBatCmd = BatteryCommand::Start;
//1.2 闭合动力电断路器
pm->m_lowerCommManager->operate_HV_powerLithiumBattery_Breaker(1);
MOOSPause(100);
pm->m_lowerCommManager->operate_HV_dcDc5Module_Breaker(1);
MOOSPause(100);
pm->m_lowerCommManager->operate_HV_dcDc5_Breaker(1);
pm->m_lowerCommManager->operate_HV_coolingSystem_Breaker(1);
MOOSPause(100);
pm->m_lowerCommManager->operate_HV_bowHighVoltageBox_Breaker(1);
MOOSPause(100);
//2. 启动动力电设备
unsigned char cmd = 1;
//2.0 艉舵
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::sternRudderServo1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::sternRudderServo2, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
pm->m_lowerCommManager->operate_HV_sternRudder1_Breaker(1);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::sternRudderServo3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::sternRudderServo4, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
pm->m_lowerCommManager->operate_HV_sternRudder2_Breaker(1);
//2.1 启闭机构
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowCover6, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HVA_actuatorCircuit_Breaker(1);
//2.2 敞水电缸
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet3, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet5, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet6, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet7, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowWaterInet8, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HVA_openWaterCoverStartCylinderCircuit_Breaker(1);
//2.3 艏舵
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowRudderDeploy, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::bowRudderServo1, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HVA_bowRudderControlBoxCircuit_Breaker(1);
//2.4 桅杆舵机
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::mastLiftingServo, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HVA_mastSteeringGearControlBoxCircuit_Breaker(1);
//2.5 艏部浮调机构
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::buoyancyAdjust1, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::buoyancyAdjust2, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::buoyancyAdjust3, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HVA_bowFTDevice123Circuit_Breaker(1);
//2.6 艉部浮调机构
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::buoyancyAdjust4, (unsigned char)cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::buoyancyAdjust5, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HV_sternFt45_Breaker(1);
//2.7 推进电机
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::thruster, (unsigned char)cmd);
pm->m_upperCommManager->processPeriodicTasks();
MOOSPause(100);
pm->m_lowerCommManager->operate_HV_propulsionMotor_Breaker(1);
pm->workCondition = WorkCondition::SHORE_BASED_READY;
LOG_F(INFO, "进入岸基备航状态");
}
-24
View File
@@ -1,24 +0,0 @@
#include "PowerManagerFsm.hpp"
// StandbyState实现
void StandbyState::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::STANDBY;
pm->m_driver.getSTANDBYTable(&pm->m_subDisSysCmd);
pm->m_lowerCommManager->operate_hold();
LOG_F(INFO, "WorkCondition %d", pm->workCondition);
}
void StandbyState::react(StandbyEvent const &) {
transit<StandbyState>();
}
void StandbyState::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void StandbyState::react(DevCmdExt const &e)
{
LOG_F(INFO, "DevCmdExt 执行");
PowerManagerFsm::react(e);
}
@@ -1,16 +0,0 @@
#include "PowerManagerFsm.hpp"
// SurfaceSurvey实现
void SurfaceSurvey::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void SurfaceSurvey::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
handleCuriseWork();
pm->workCondition = WorkCondition::SURFACE_RECON;
LOG_F(INFO, "进入水面侦查模式");
}
@@ -1,17 +0,0 @@
#include "PowerManagerFsm.hpp"
void UnderwaterSurvey::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
handleCuriseWork();
pm->workCondition = WorkCondition::UNDERWATER_RECON;
LOG_F(INFO, "进入水下侦查模式");
}
// UnderwaterSurvey实现
void UnderwaterSurvey::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
@@ -1,62 +0,0 @@
#include "PowerManagerFsm.hpp"
void WaterBasedReady::entry() {
PowerManagerFsm::entry();
pm->workCondition = WorkCondition::CHANING;
PowerManagerFsm::handleCuriseWork();
//启动水声设备
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::DVL, (unsigned char)EquipmentCommand::ON);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, DeviceId::USBL, (unsigned char)EquipmentCommand::ON);
pm->m_upperCommManager->processPeriodicTasks();
pm->workCondition = WorkCondition::WATER_BASED_READY;
LOG_F(INFO, "进入水中备航状态");
}
// WaterBasedReady实现
void WaterBasedReady::react(MasterCommandEvent const &) {
PowerManagerFsm::handelPowerCmd();
PowerManagerFsm::handleModCmd();
PowerManagerFsm::handleWorkCmd();
}
void WaterBasedReady::react(DevCmdExt const &e)
{
int id = e.id;
int cmd = e.cmd;
if(underWterDevices.find(id) != underWterDevices.end())
{
if (pm->currentMod != PMMode::Debug)
{
if(pm->m_depth < OVER_DEPTH)
{
LOG_F(INFO, "水下设备操作上电保护 %d ", id);
return;
}
}
// 如果是前视声纳断电则先关机再断电
if(id == DeviceId::forwardSonar)
{
if(cmd == 0)
{
LOG_F(INFO,"前视声纳关机指令");
pm->sendNotification("uExternComm_frontSonarControl_cmd","0");
MOOSPause(10000);
}
}
LOG_F(INFO, "水声设备操作: %s,%d, %d", DeviceId::toString((DeviceId::Type)id), id, cmd);
pm->m_driver.setDeviceState(pm->m_subDisSysCmd, (DeviceId::Type)id, (unsigned char)cmd);
}
else
{
if(pm->currentMod == PMMode::Normal)
{
LOG_F(INFO,"正常模式:操作禁止");
return;
}
else
{
PowerManagerFsm::react(e);
}
}
}
@@ -1,476 +0,0 @@
#ifndef HTTP_SERVER_BATTERYOPERATION_H
#define HTTP_SERVER_BATTERYOPERATION_H
#include <string>
const std::string BATTERY_OPERATION_HTML = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>电池操作</title>
<link rel="stylesheet" href="/static/style.css">
<style>
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--bg-color: #f8fafc;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: #1a1a1a;
}
.container {
display: grid;
grid-template-areas:
"sidebar main-content";
grid-template-columns: 220px 1fr;
grid-template-rows: 1fr;
min-height: 100vh;
gap: 0;
}
.header {
grid-area: unset;
background: white;
padding: 0 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
right: 0;
left: 220px;
height: 36px;
z-index: 10;
}
.header h1 {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
}
.sidebar {
grid-area: sidebar;
background: white;
width: 220px;
height: 100vh;
position: fixed;
left: 0;
top: 0;
padding: 0.5rem 0.25rem;
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.1);
z-index: 20;
}
.sidebar-menu {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sidebar-button {
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
color: #4b5563;
text-decoration: none;
transition: all 0.2s ease;
}
.sidebar-button:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.sidebar-icon {
min-width: 1.5rem;
margin-right: 1rem;
font-size: 1.25rem;
}
.main-content {
grid-area: main-content;
padding: 1px;
margin-top: 36px;
}
.control-panel {
background: white;
border-radius: 4px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin: 20px;
}
.panel-title {
font-size: 1.2rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
.control-group {
margin-bottom: 20px;
}
.control-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.radio-group {
display: flex;
gap: 16px;
margin-bottom: 12px;
}
.radio-option {
display: flex;
align-items: center;
gap: 8px;
}
.select-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 4px;
background-color: white;
margin-bottom: 12px;
}
.slider-container {
display: flex;
align-items: center;
gap: 12px;
}
.slider {
flex-grow: 1;
height: 6px;
-webkit-appearance: none;
background: #d1d5db;
border-radius: 3px;
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
background: var(--primary-color);
border-radius: 50%;
cursor: pointer;
}
.slider-value {
min-width: 50px;
text-align: right;
}
.button-group {
display: flex;
gap: 12px;
margin-top: 8px;
}
.button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background-color: #e5e7eb;
color: #1a1a1a;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.button:hover {
background-color: #d1d5db;
}
.button-secondary {
background-color: #e5e7eb;
color: #1a1a1a;
}
.button-secondary:hover {
background-color: #d1d5db;
}
.button-active {
background-color: var(--primary-color);
color: white;
}
.button-active:hover {
background-color: var(--primary-hover);
}
.form-submit {
margin-top: 24px;
width: 100%;
padding: 12px;
font-size: 1rem;
}
.status-message {
margin-top: 16px;
padding: 12px;
border-radius: 4px;
background-color: #f0fdf4;
color: #166534;
display: none;
}
.status-message.error {
background-color: #fef2f2;
color: #991b1b;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="sidebar-menu">
<a href="/" class="sidebar-button">
<span class="sidebar-icon">🏠</span>
<span>系统主页</span>
</a>
<a href="/data" class="sidebar-button">
<span class="sidebar-icon">📊</span>
<span>系统数据</span>
</a>
<a href="/dashboard" class="sidebar-button">
<span class="sidebar-icon">⚡</span>
<span>配电操作</span>
</a>
<a href="/battery" class="sidebar-button">
<span class="sidebar-icon">🔋</span>
<span>电池操作</span>
</a>
<a href="/logs" class="sidebar-button">
<span class="sidebar-icon">📝</span>
<span>系统日志</span>
</a>
</div>
</div>
<div class="header">
<h1>电池操作系统</h1>
</div>
<div class="main-content">
<div class="control-panel">
<div class="panel-title">燃料电池控制</div>
<form id="batteryForm" method="POST" action="/battery/control">
<div class="control-group">
<label class="control-label">燃料电池模式设定</label>
<div class="radio-group">
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="00H" checked>
<span>无效</span>
</label>
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="01H">
<span>调试</span>
</label>
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="02H">
<span>自动</span>
</label>
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="03H">
<span>补给</span>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">燃料电池指令</label>
<select class="select-control" name="fuelCellCommand">
<option value="00H">无效</option>
<option value="01H">自检</option>
<option value="02H">启动</option>
<option value="03H">停机</option>
<option value="04H">复位</option>
<option value="05H">紧急停机</option>
<option value="06H">补给</option>
<option value="07H">制氢预热</option>
<option value="08H">停机维护</option>
</select>
</div>
<div class="control-group">
<label class="control-label">燃料电池动力通路功率配置 (kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="fuelCellPower" min="0" max="20" value="0" step="1">
<span class="slider-value">0</span>
</div>
</div>
<div class="panel-title">锂电池控制</div>
<div class="control-group">
<label class="control-label">仪表锂电池启停指令</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="instrumentBattery" data-value="00H">无效</button>
<button type="button" class="button button-secondary" data-command="instrumentBattery" data-value="10H">启动</button>
<button type="button" class="button button-secondary" data-command="instrumentBattery" data-value="20H">关闭</button>
</div>
<input type="hidden" name="instrumentBattery" value="00H">
</div>
<div class="control-group">
<label class="control-label">仪表锂电池允许充电</label>
<div class="button-group">
<button type="button" class="button button-secondary" data-command="instrumentBatteryCharge" data-value="0">允许</button>
<button type="button" class="button button-active" data-command="instrumentBatteryCharge" data-value="48">禁止</button>
</div>
<input type="hidden" name="instrumentBatteryCharge" value="1">
</div>
<div class="control-group">
<label class="control-label">动力锂电池启停指令</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="powerBattery" data-value="00H">无效</button>
<button type="button" class="button button-secondary" data-command="powerBattery" data-value="10H">启动</button>
<button type="button" class="button button-secondary" data-command="powerBattery" data-value="20H">关闭</button>
</div>
<input type="hidden" name="powerBattery" value="00H">
</div>
<div class="control-group">
<label class="control-label">动力锂电池电池功率配置值 (kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="powerBatteryPower" min="0" max="400" value="0" step="10">
<span class="slider-value">0</span>
</div>
</div>
<div class="panel-title">主机状态</div>
<div class="control-group">
<label class="control-label">主机状态指令</label>
<div class="radio-group">
<label class="radio-option">
<input type="radio" name="hostStatus" value="AAH" checked>
<span>运行</span>
</label>
<label class="radio-option">
<input type="radio" name="hostStatus" value="FFH">
<span>关机</span>
</label>
<label class="radio-option">
<input type="radio" name="hostStatus" value="55H">
<span>故障</span>
</label>
</div>
</div>
<button type="submit" class="button form-submit">提交配置</button>
<div id="statusMessage" class="status-message"></div>
</form>
</div>
</div>
</div>
<script>
// 更新滑块值显示
document.querySelectorAll('.slider').forEach(slider => {
const valueDisplay = slider.nextElementSibling;
valueDisplay.textContent = slider.value;
slider.addEventListener('input', () => {
valueDisplay.textContent = slider.value;
});
});
// 处理按钮组选择
document.querySelectorAll('.button-group button').forEach(button => {
button.addEventListener('click', () => {
const group = button.closest('.button-group');
const hiddenInput = group.nextElementSibling;
const command = button.dataset.command;
const value = button.dataset.value;
// 更新按钮样式
group.querySelectorAll('button').forEach(btn => {
btn.classList.remove('button-active');
btn.classList.add('button-secondary');
});
button.classList.remove('button-secondary');
button.classList.add('button-active');
// 更新隐藏输入值
hiddenInput.value = value;
});
});
// 处理表单提交
document.getElementById('batteryForm').addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const statusMessage = document.getElementById('statusMessage');
try {
// 将表单数据转换为URL编码格式
const params = new URLSearchParams();
for (const [key, value] of formData.entries()) {
params.append(key, value);
}
const response = await fetch(form.action, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
const result = await response.json();
if (result.success) {
statusMessage.textContent = '配置提交成功';
statusMessage.className = 'status-message';
statusMessage.style.display = 'block';
} else {
statusMessage.textContent = result.message || '配置提交失败';
statusMessage.className = 'status-message error';
statusMessage.style.display = 'block';
}
} catch (error) {
statusMessage.textContent = '网络错误: ' + error.message;
statusMessage.className = 'status-message error';
statusMessage.style.display = 'block';
}
setTimeout(() => {
statusMessage.style.display = 'none';
}, 5000);
});
</script>
</body>
</html>
)";
#endif // HTTP_SERVER_BATTERYOPERATION_H
-580
View File
@@ -1,580 +0,0 @@
#ifndef HTTP_SERVER_DASHBOARD_H
#define HTTP_SERVER_DASHBOARD_H
#include <string>
const std::string DASHBOARD_HTML = R"row(
<!DOCTYPE html>
<html>
<head>
<title>断路器控制面板</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
}
.container {
display: flex;
gap: 25px;
max-width: 1400px;
margin: 0 auto;
}
.column {
flex: 1;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: transform 0.2s;
}
.column:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.column-title {
font-weight: 600;
margin-bottom: 20px;
text-align: center;
font-size: 20px;
color: #2c3e50;
padding-bottom: 10px;
border-bottom: 2px solid #eee;
}
.breaker {
margin-bottom: 12px;
padding: 10px 15px;
background: #f8f9fa;
border-radius: 6px;
border-left: 4px solid #3498db;
display: flex;
flex-direction: column;
gap: 8px;
}
.breaker-name {
font-weight: 600;
font-size: 18px;
color: #2c3e50;
padding: 6px 10px;
background-color: rgba(46, 204, 113, 0.1);
border-radius: 4px;
display: inline-flex;
align-items: center;
width: fit-content;
}
.breaker-controls {
display: flex;
gap: 10px;
align-items: center;
}
button {
padding: 8px 16px;
min-width: 70px;
cursor: pointer;
border: none;
border-radius: 4px;
font-weight: 500;
font-size: 14px;
transition: all 0.2s;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
button:hover {
opacity: 0.9;
transform: translateY(-1px);
}
button:first-of-type {
background-color: #2ecc71;
color: white;
}
button:last-of-type {
background-color: #e74c3c;
color: white;
}
.status-message {
margin-top: 8px;
padding: 6px;
font-size: 13px;
border-radius: 3px;
}
.status-success {
background-color: rgba(46, 204, 113, 0.1);
color: #27ae60;
}
.status-error {
background-color: rgba(231, 76, 60, 0.1);
color: #c0392b;
}
.status-pending {
background-color: rgba(241, 196, 15, 0.1);
color: #f39c12;
}
.status-timer {
font-size: 12px;
color: #7f8c8d;
margin-top: 4px;
}
</style>
</head>
<body>
<div class="container">
<div class="column">
<div class="column-title">高压母线设备</div>
<!-- 高压母线断路器列表 -->
<div class="breaker">
<div class="breaker-name">燃料电池断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'fuel_cell', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'fuel_cell', 'open')">断开</button>
</div>
<div id="high_vol_bus-fuel_cell-status-message" class="status-message"></div>
<div id="high_vol_bus-fuel_cell-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">动力锂电池断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'power_battery', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'power_battery', 'open')">断开</button>
</div>
<div id="high_vol_bus-power_battery-status-message" class="status-message"></div>
<div id="high_vol_bus-power_battery-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">推进电机断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'propulsion_motor', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'propulsion_motor', 'open')">断开</button>
</div>
<div id="high_vol_bus-propulsion_motor-status-message" class="status-message"></div>
<div id="high_vol_bus-propulsion_motor-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">锂电池组仪表断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'battery_meter', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'battery_meter', 'open')">断开</button>
</div>
<div id="high_vol_bus-battery_meter-status-message" class="status-message"></div>
<div id="high_vol_bus-battery_meter-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">DC/DC5模块断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'dcdc5', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'dcdc5', 'open')">断开</button>
</div>
<div id="high_vol_bus-dcdc5-status-message" class="status-message"></div>
<div id="high_vol_bus-dcdc5-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艏部高压配电箱断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'bow_distribution', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'bow_distribution', 'open')">断开</button>
</div>
<div id="high_vol_bus-bow_distribution-status-message" class="status-message"></div>
<div id="high_vol_bus-bow_distribution-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艉部FT装置4/5断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'stern_ft45', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'stern_ft45', 'open')">断开</button>
</div>
<div id="high_vol_bus-stern_ft45-status-message" class="status-message"></div>
<div id="high_vol_bus-stern_ft45-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艉舵开关1断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'stern_rudder1', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'stern_rudder1', 'open')">断开</button>
</div>
<div id="high_vol_bus-stern_rudder1-status-message" class="status-message"></div>
<div id="high_vol_bus-stern_rudder1-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艉舵开关2断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'stern_rudder2', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'stern_rudder2', 'open')">断开</button>
</div>
<div id="high_vol_bus-stern_rudder2-status-message" class="status-message"></div>
<div id="high_vol_bus-stern_rudder2-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">FB预留断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'fb_reserve', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'fb_reserve', 'open')">断开</button>
</div>
<div id="high_vol_bus-fb_reserve-status-message" class="status-message"></div>
<div id="high_vol_bus-fb_reserve-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">TYZ预留断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'tyz_reserve', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'tyz_reserve', 'open')">断开</button>
</div>
<div id="high_vol_bus-tyz_reserve-status-message" class="status-message"></div>
<div id="high_vol_bus-tyz_reserve-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">预留断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'reserve', 'close')">闭合</button>
<button onclick="sendCommand('high_vol_bus', 'reserve', 'open')">断开</button>
</div>
<div id="high_vol_bus-reserve-status-message" class="status-message"></div>
<div id="high_vol_bus-reserve-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">DC/DC5模块启动</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'dcdc5_start', 'close')">启动</button>
<button onclick="sendCommand('high_vol_bus', 'dcdc5_start', 'open')">停止</button>
</div>
<div id="high_vol_bus-dcdc5_start-status-message" class="status-message"></div>
<div id="high_vol_bus-dcdc5_start-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">冷却系统启动</div>
<div class="breaker-controls">
<button onclick="sendCommand('high_vol_bus', 'cooling_system', 'close')">启动</button>
<button onclick="sendCommand('high_vol_bus', 'cooling_system', 'open')">停止</button>
</div>
<div id="high_vol_bus-cooling_system-status-message" class="status-message"></div>
<div id="high_vol_bus-cooling_system-timer" class="status-timer"></div>
</div>
</div>
<div class="column">
<div class="column-title">艏高压设备</div>
<!-- 艏高压断路器列表 -->
<div class="breaker">
<div class="breaker-name">艏部FT装置1-3断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_high_vol', 'bow_ft1_3', 'close')">闭合</button>
<button onclick="sendCommand('bow_high_vol', 'bow_ft1_3', 'open')">断开</button>
</div>
<div id="bow_high_vol-bow_ft1_3-status-message" class="status-message"></div>
<div id="bow_high_vol-bow_ft1_3-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">启闭机构断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_high_vol', 'opening_mechanism', 'close')">闭合</button>
<button onclick="sendCommand('bow_high_vol', 'opening_mechanism', 'open')">断开</button>
</div>
<div id="bow_high_vol-opening_mechanism-status-message" class="status-message"></div>
<div id="bow_high_vol-opening_mechanism-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">桅杆舵机控制箱断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_high_vol', 'mast_steering', 'close')">闭合</button>
<button onclick="sendCommand('bow_high_vol', 'mast_steering', 'open')">断开</button>
</div>
<div id="bow_high_vol-mast_steering-status-message" class="status-message"></div>
<div id="bow_high_vol-mast_steering-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">XCZ断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_high_vol', 'xcz', 'close')">闭合</button>
<button onclick="sendCommand('bow_high_vol', 'xcz', 'open')">断开</button>
</div>
<div id="bow_high_vol-xcz-status-message" class="status-message"></div>
<div id="bow_high_vol-xcz-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艏舵控制箱断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_high_vol', 'bow_rudder', 'close')">闭合</button>
<button onclick="sendCommand('bow_high_vol', 'bow_rudder', 'open')">断开</button>
</div>
<div id="bow_high_vol-bow_rudder-status-message" class="status-message"></div>
<div id="bow_high_vol-bow_rudder-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">敞水盖启动电缸断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_high_vol', 'water_cover', 'close')">闭合</button>
<button onclick="sendCommand('bow_high_vol', 'water_cover', 'open')">断开</button>
</div>
<div id="bow_high_vol-water_cover-status-message" class="status-message"></div>
<div id="bow_high_vol-water_cover-timer" class="status-timer"></div>
</div>
</div>
<div class="column">
<div class="column-title">仪表母线设备</div>
<!-- 仪表母线断路器列表 -->
<div class="breaker">
<div class="breaker-name">锂电池组(仪表)断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'battery_meter', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'battery_meter', 'open')">断开</button>
</div>
<div id="meter_bus-battery_meter-status-message" class="status-message"></div>
<div id="meter_bus-battery_meter-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艏部低压配电箱断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'bow_low_dist', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'bow_low_dist', 'open')">断开</button>
</div>
<div id="meter_bus-bow_low_dist-status-message" class="status-message"></div>
<div id="meter_bus-bow_low_dist-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">UNIT4仪表DC48V断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'unit4_dc48v', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'unit4_dc48v', 'open')">断开</button>
</div>
<div id="meter_bus-unit4_dc48v-status-message" class="status-message"></div>
<div id="meter_bus-unit4_dc48v-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">DC-C1直流配电板断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'dc_c1', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'dc_c1', 'open')">断开</button>
</div>
<div id="meter_bus-dc_c1-status-message" class="status-message"></div>
<div id="meter_bus-dc_c1-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">预留断路器1</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'reserve1', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'reserve1', 'open')">断开</button>
</div>
<div id="meter_bus-reserve1-status-message" class="status-message"></div>
<div id="meter_bus-reserve1-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">预留断路器2</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'reserve2', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'reserve2', 'open')">断开</button>
</div>
<div id="meter_bus-reserve2-status-message" class="status-message"></div>
<div id="meter_bus-reserve2-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">应急锂电池组2断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('meter_bus', 'emergency_battery2', 'close')">闭合</button>
<button onclick="sendCommand('meter_bus', 'emergency_battery2', 'open')">断开</button>
</div>
<div id="meter_bus-emergency_battery2-status-message" class="status-message"></div>
<div id="meter_bus-emergency_battery2-timer" class="status-timer"></div>
</div>
</div>
<div class="column">
<div class="column-title">艏低压设备</div>
<!-- 艏低压断路器列表 -->
<div class="breaker">
<div class="breaker-name">UNIT1断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'unit1', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'unit1', 'open')">断开</button>
</div>
<div id="bow_low_vol-unit1-status-message" class="status-message"></div>
<div id="bow_low_vol-unit1-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">UNIT2断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'unit2', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'unit2', 'open')">断开</button>
</div>
<div id="bow_low_vol-unit2-status-message" class="status-message"></div>
<div id="bow_low_vol-unit2-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">UNIT3断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'unit3', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'unit3', 'open')">断开</button>
</div>
<div id="bow_low_vol-unit3-status-message" class="status-message"></div>
<div id="bow_low_vol-unit3-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">UNIT5断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'unit5', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'unit5', 'open')">断开</button>
</div>
<div id="bow_low_vol-unit5-status-message" class="status-message"></div>
<div id="bow_low_vol-unit5-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">艏部PZ装置断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'bow_pz', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'bow_pz', 'open')">断开</button>
</div>
<div id="bow_low_vol-bow_pz-status-message" class="status-message"></div>
<div id="bow_low_vol-bow_pz-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">预留断路器1</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'reserve1', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'reserve1', 'open')">断开</button>
</div>
<div id="bow_low_vol-reserve1-status-message" class="status-message"></div>
<div id="bow_low_vol-reserve1-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">预留断路器2</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'reserve2', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'reserve2', 'open')">断开</button>
</div>
<div id="bow_low_vol-reserve2-status-message" class="status-message"></div>
<div id="bow_low_vol-reserve2-timer" class="status-timer"></div>
</div>
<div class="breaker">
<div class="breaker-name">应急锂电池组1断路器</div>
<div class="breaker-controls">
<button onclick="sendCommand('bow_low_vol', 'emergency_battery1', 'close')">闭合</button>
<button onclick="sendCommand('bow_low_vol', 'emergency_battery1', 'open')">断开</button>
</div>
<div id="bow_low_vol-emergency_battery1-status-message" class="status-message"></div>
<div id="bow_low_vol-emergency_battery1-timer" class="status-timer"></div>
</div>
</div>
</div>
<script>
// 全局存储所有设备的计时器状态
const deviceTimers = {};
function sendCommand(type, id, action, value) {
const statusEl = document.getElementById(`${type}-${id}-status-message`);
const timerEl = document.getElementById(`${type}-${id}-timer`);
const buttons = document.querySelectorAll(`button[onclick*="sendCommand('${type}', '${id}'"]`);
// 检查是否已有计时器运行
if (deviceTimers[`${type}-${id}`]) {
return;
}
// 禁用按钮
buttons.forEach(btn => {
btn.disabled = true;
btn.style.opacity = '0.6';
btn.style.cursor = 'not-allowed';
});
// 显示操作中状态
if (statusEl) {
statusEl.textContent = '操作中...';
statusEl.className = 'status-message status-pending';
}
// 启动5秒计时器
if (timerEl) {
let seconds = 8;
timerEl.textContent = `等待反馈: ${seconds}秒`;
timer = setInterval(() => {
seconds--;
if (seconds > 0) {
timerEl.textContent = `等待反馈: ${seconds}秒`;
} else {
clearInterval(timer);
timerEl.textContent = '';
if (statusEl && statusEl.textContent === '操作中...') {
statusEl.textContent = '操作超时: 未收到设备反馈';
statusEl.className = 'status-message status-error';
}
}
}, 1000);
}
// 设置10秒超时
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), 10000)
);
// 存储计时器
deviceTimers[`${type}-${id}`] = timer;
Promise.race([
fetch('/control', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ device_type: type, device_id: id, action: action, value: value })
}),
timeoutPromise
])
.then(res => {
// 立即清除计时器
const timerEl = document.getElementById(`${type}-${id}-timer`);
const buttons = document.querySelectorAll(`button[onclick*="sendCommand('${type}', '${id}'"]`);
if (timerEl) {
clearInterval(deviceTimers[`${type}-${id}`]);
delete deviceTimers[`${type}-${id}`];
timerEl.textContent = '';
}
// 恢复按钮状态
buttons.forEach(btn => {
btn.disabled = false;
btn.style.opacity = '1';
btn.style.cursor = 'pointer';
});
return res.json();
})
.then(data => {
const statusEl = document.getElementById(`${type}-${id}-status-message`);
if (statusEl) {
statusEl.textContent = data.message || (data.success ? '操作成功' : '操作失败');
statusEl.className = data.success ? 'status-message status-success' : 'status-message status-error';
}
})
.catch(error => {
const statusEl = document.getElementById(`${type}-${id}-status-message`);
const timerEl = document.getElementById(`${type}-${id}-timer`);
const buttons = document.querySelectorAll(`button[onclick*="sendCommand('${type}', '${id}'"]`);
if (timerEl) {
clearInterval(deviceTimers[`${type}-${id}`]);
delete deviceTimers[`${type}-${id}`];
timerEl.textContent = '';
}
// 恢复按钮状态
buttons.forEach(btn => {
btn.disabled = false;
btn.style.opacity = '1';
btn.style.cursor = 'pointer';
});
if (statusEl) {
if (error.message === '请求超时') {
statusEl.textContent = '操作超时: 未收到设备反馈';
} else {
statusEl.textContent = '通信错误: ' + error.message;
}
statusEl.className = 'status-message status-error';
}
});
}
</script>
</body>
</html>
)row";
#endif // HTTP_SERVER_DASHBOARD_H
File diff suppressed because it is too large Load Diff
-466
View File
@@ -1,466 +0,0 @@
#ifndef HTTP_SERVER_DEBUGPAGE_H
#define HTTP_SERVER_DEBUGPAGE_H
#include <string>
const std::string DEBUGPAGE_HTML = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Power Manager - 调试页面</title>
<link rel="stylesheet" href="/static/style.css">
<style>
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--bg-color: #f8fafc;
--sidebar-width: 60px;
--sidebar-expanded: 220px;
--header-height: 36px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: #1a1a1a;
}
.container {
display: grid;
grid-template-areas:
"sidebar main-content";
grid-template-columns: var(--sidebar-width) 1fr;
grid-template-rows: 1fr;
min-height: 100vh;
gap: 0;
}
.header {
grid-area: unset;
background: white;
padding: 0 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
right: 0;
left: var(--sidebar-width);
height: var(--header-height);
z-index: 10;
}
.header h1 {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
}
.sidebar {
grid-area: sidebar;
background: white;
width: var(--sidebar-width);
height: 100vh;
position: fixed;
left: 0;
top: 0;
padding: 0.5rem 0.25rem;
transition: width 0.3s ease;
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.1);
z-index: 20;
}
.sidebar:hover {
width: var(--sidebar-expanded);
}
.sidebar-menu {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sidebar-button {
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
color: #4b5563;
text-decoration: none;
transition: all 0.2s ease;
white-space: nowrap;
overflow: hidden;
}
.sidebar-button:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.sidebar-icon {
min-width: 1.5rem;
margin-right: 1rem;
font-size: 1.25rem;
}
.main-content {
grid-area: main-content;
padding: 1px;
margin-top: var(--header-height);
}
.status-bar {
position: fixed;
top: 4px;
right: 8px;
background: white;
padding: 4px 8px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
gap: 8px;
z-index: 15;
}
.status-info {
display: flex;
align-items: center;
gap: 8px;
color: #4b5563;
font-size: 0.875rem;
}
.debug-panel {
background: white;
border-radius: 4px;
padding: 16px;
margin: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.panel-title {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
.debug-section {
margin-bottom: 24px;
}
.section-title {
font-size: 1rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 8px;
}
.message-cards {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
margin-top: 16px;
max-width: 600px;
padding: 0 16px;
}
.message-card {
width: 100%;
box-sizing: border-box;
}
.message-card {
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.card-header {
padding: 12px 16px;
background: #f3f4f6;
border-bottom: 1px solid #e5e7eb;
font-weight: 600;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header:hover {
background: #e5e7eb;
}
.card-content {
padding: 16px;
}
.card-content pre {
margin: 0;
white-space: pre-wrap;
font-family: monospace;
max-height: 150px;
overflow: hidden;
transition: max-height 0.3s ease;
}
.card-content pre.expanded {
max-height: none;
}
.toggle-button {
background: none;
border: none;
color: #2563eb;
cursor: pointer;
font-size: 0.9em;
padding: 4px 8px;
border-radius: 4px;
}
.toggle-button:hover {
background: rgba(37, 99, 235, 0.1);
}
.raw-data {
font-family: monospace;
white-space: pre-wrap;
background-color: #f3f4f6;
padding: 12px;
border-radius: 4px;
max-height: 300px;
overflow: auto;
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main-content";
}
.sidebar {
transform: translateX(-100%);
}
.header {
left: 0;
}
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="sidebar-menu">
<a href="/" class="sidebar-button">
<span class="sidebar-icon">🏠</span>
<span>系统主页</span>
</a>
<a href="/debug" class="sidebar-button">
<span class="sidebar-icon">🐞</span>
<span>调试页面</span>
</a>
</div>
</div>
<div class="header">
<h1>Power Manager - 调试页面</h1>
<div class="status-bar">
<div class="status-info">
<span id="wsStatus">🔴 WebSocket: 未连接</span>
</div>
</div>
</div>
<div class="main-content">
<div class="debug-panel">
<div class="panel-title">系统调试信息</div>
<div class="debug-section">
<div class="section-title">订阅消息</div>
<div class="message-cards" id="subscribeDataTable">
<!-- 订阅消息将通过JavaScript动态填充 -->
</div>
</div>
<div class="debug-section">
<div class="section-title">发布消息</div>
<div class="message-cards" id="publishDataTable">
<!-- 发布消息将通过JavaScript动态填充 -->
</div>
</div>
<div class="debug-section">
<div class="section-title">原始数据</div>
<div class="raw-data" id="rawData">
等待数据...
</div>
</div>
</div>
</div>
</div>
<script>
let ws = null;
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.host + '/ws/debug');
ws.onopen = () => {
console.log('WebSocket调试连接已建立');
updateStatus('🟢 WebSocket: 已连接');
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
console.log('收到调试数据:', data);
document.getElementById('rawData').textContent = JSON.stringify(data, null, 2);
// 更新订阅消息表格
const subscribeTable = document.getElementById('subscribeDataTable');
const subscribeMessages = [
'm_secdistStateMsg_fb_debug',
'm_equStateMsg_cmd_debug',
'm_depthMsg_st_debug',
'm_setOpConditionMsg_st_debug',
'm_setOpModeMsg_st_debug',
'm_taskStartMsg_st_debug',
'm_taskStopMsg_st_debug',
'm_setDevSwitchMsg_st_debug',
'm_setPMSysMsg_st_debug',
'm_sysStateMsg_st_debug'
];
subscribeMessages.forEach(msg => {
if (data.debug && data.debug[msg] && data.debug[msg].name) {
const msgName = data.debug[msg].name;
const existingCard = Array.from(subscribeTable.children).find(card =>
card.querySelector('.card-header').textContent === msgName
);
if (existingCard) {
existingCard.querySelector('pre').textContent = formatJsonContent(data.debug[msg].content || '无内容');
} else if (msgName !== '未知消息') {
addDebugRow(subscribeTable, data.debug[msg]);
}
}
});
// 更新发布消息表格
const publishTable = document.getElementById('publishDataTable');
const publishMessages = [
'm_equInsStateMsg_fb_degub',
'm_secdistStateMsg_cmd_debug',
'm_equStateMsg_fb_debug',
'm_disSysStateMsg_st_debug',
'm_currentStateMsg_st_debug',
'm_pmStateMsg_fb_debug',
'm_fcsStateMsg_st_debug',
'm_liBatStateMsg_st_debug'
];
publishMessages.forEach(msg => {
if (data.debug && data.debug[msg] && data.debug[msg].name) {
const msgName = data.debug[msg].name;
const existingCard = Array.from(publishTable.children).find(card =>
card.querySelector('.card-header').textContent === msgName
);
if (existingCard) {
existingCard.querySelector('pre').textContent = formatJsonContent(data.debug[msg].content || '无内容');
} else if (msgName !== '未知消息') {
addDebugRow(publishTable, data.debug[msg]);
}
}
});
} catch (e) {
console.error('解析调试数据出错:', e);
document.getElementById('rawData').textContent = '解析错误: ' + e.message + '\n\n原始数据:\n' + event.data;
}
};
ws.onclose = () => {
console.log('WebSocket调试连接已关闭');
updateStatus('🔴 WebSocket: 已断开');
setTimeout(initWebSocket, 5000);
};
ws.onerror = (error) => {
console.error('WebSocket调试错误:', error);
updateStatus('⚠️ WebSocket: 错误');
};
}
function updateStatus(status) {
document.getElementById('wsStatus').textContent = status;
}
function formatJsonContent(content) {
try {
const parsed = JSON.parse(content);
return JSON.stringify(parsed, null, 2);
} catch (e) {
return content;
}
}
function addDebugRow(container, msgObj) {
const card = document.createElement('div');
card.className = 'message-card';
const header = document.createElement('div');
header.className = 'card-header';
header.textContent = msgObj.name || '未知消息';
const content = document.createElement('div');
content.className = 'card-content';
const pre = document.createElement('pre');
pre.textContent = formatJsonContent(msgObj.content || '无内容');
const toggleBtn = document.createElement('button');
toggleBtn.className = 'toggle-button';
toggleBtn.textContent = '展开';
toggleBtn.onclick = function(e) {
e.stopPropagation();
pre.classList.toggle('expanded');
this.textContent = pre.classList.contains('expanded') ? '收起' : '展开';
};
header.onclick = function(e) {
if(e.target !== toggleBtn) {
pre.classList.toggle('expanded');
toggleBtn.textContent = pre.classList.contains('expanded') ? '收起' : '展开';
}
};
content.appendChild(pre);
content.appendChild(toggleBtn);
card.appendChild(header);
card.appendChild(content);
container.appendChild(card);
}
window.addEventListener('load', initWebSocket);
</script>
</body>
</html>
)";
#endif // HTTP_SERVER_DEBUGPAGE_H
File diff suppressed because it is too large Load Diff
@@ -1,90 +0,0 @@
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
#include "MOOS/libMOOS/Utils/MOOSThread.h"
#include "mongoose.h"
#include "../logc/loguru.hpp"
#include "../systemData.h"
#include <vector>
#include <mutex>
#include <unordered_map>
#include <fstream>
#include <deque>
#include "../LowerCommManager.h"
#include <string>
#include <thread>
#include <atomic>
class HttpServer {
public:
// 构造函数,传入PowerManger指针和端口号
HttpServer(int port = 8000);
// 析构函数
~HttpServer();
// 启动HTTP服务器线程
void start();
// 停止HTTP服务器线程
void stop();
// 检查服务器是否正在运行
bool isRunning() const;
// 设置系统数据
void setSystemData(SystemData* systemData){
m_systemData = systemData;
}
void setLowerCommManager(LowerCommManager* lowerCommManager){
m_lowerCommManager = lowerCommManager;
}
void setPowerManger(PowerManger* powerManger){
m_powerManger = powerManger;
}
// 推送系统数据到所有WebSocket客户端
void pushSystemData();
// 监控日志文件变化并推送新内容
void monitorLogFile();
// 处理日志请求
void handleLogRequest(struct mg_connection* c, const std::string& msg);
// 读取日志文件
std::vector<std::string> readLogFile(int page, int pageSize, const std::string& level, const std::string& search);
// 测试页面请求处理函数
void handleTestPage(struct mg_connection* c, const std::string& msg);
private:
// WebSocket事件处理函数
void handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_data, void* fn_data);
// 线程回调函数
static bool serverThreadCB(void* pParam);
bool serverThreadFunc();
// 后台系统数据
SystemData *m_systemData = nullptr;
LowerCommManager *m_lowerCommManager = nullptr;
PowerManger *m_powerManger = nullptr;
struct mg_mgr mgr_; // Mongoose管理器
int port_; // 监听端口
bool running_; // 运行状态标志
CMOOSThread m_ServerThread; // 服务器线程
std::mutex ws_connections_mutex_; // WebSocket连接锁
std::vector<mg_connection*> ws_connections_; // WebSocket连接列表
std::unordered_map<mg_connection*, uint64_t> ws_last_io_time_; // 连接最后活动时间
// 日志文件相关
std::string m_logFilePath = "pPowerManger.log";
std::ifstream m_logFile;
std::streampos m_lastLogPosition = 0;
std::deque<std::string> m_logCache; // 缓存最近的日志
static const size_t MAX_CACHE_SIZE = 1000; // 最大缓存条数
};
#endif // HTTP_SERVER_H
File diff suppressed because it is too large Load Diff
-617
View File
@@ -1,617 +0,0 @@
#ifndef HTTP_SERVER_LOGPAGE_H
#define HTTP_SERVER_LOGPAGE_H
#include <string>
const std::string LOG_PAGE_HTML = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>系统日志 - Power Manager</title>
<style>
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--bg-color: #f8fafc;
--sidebar-width: 60px;
--sidebar-expanded: 220px;
--header-height: 36px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: #1a1a1a;
}
.container {
display: grid;
grid-template-areas: "sidebar main-content";
grid-template-columns: var(--sidebar-width) 1fr;
grid-template-rows: 1fr;
min-height: 100vh;
gap: 0;
}
.header {
grid-area: unset;
background: white;
padding: 0 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
right: 0;
left: var(--sidebar-width);
height: var(--header-height);
z-index: 10;
}
.header h1 {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
}
.sidebar {
grid-area: sidebar;
background: white;
width: var(--sidebar-width);
height: 100vh;
position: fixed;
left: 0;
top: 0;
padding: 0.5rem 0.25rem;
transition: width 0.3s ease;
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.1);
z-index: 20;
}
.sidebar:hover {
width: var(--sidebar-expanded);
}
.sidebar-menu {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sidebar-button {
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
color: #4b5563;
text-decoration: none;
transition: all 0.2s ease;
white-space: nowrap;
overflow: hidden;
}
.sidebar-button:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.sidebar-button.active {
background: var(--primary-color);
color: white;
}
.sidebar-icon {
min-width: 1.5rem;
margin-right: 1rem;
font-size: 1.25rem;
}
.main-content {
grid-area: main-content;
padding: 1px;
margin-top: var(--header-height);
}
.log-panel {
background: white;
border-radius: 4px;
padding: 6px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
height: calc(100vh - var(--header-height));
overflow: hidden;
display: flex;
flex-direction: column;
}
.log-controls {
display: flex;
gap: 8px;
padding: 4px;
border-bottom: 1px solid #e5e7eb;
flex-wrap: wrap;
}
.log-button {
padding: 4px 12px;
border: 1px solid #e5e7eb;
border-radius: 4px;
background: white;
color: #4b5563;
cursor: pointer;
font-size: 0.875rem;
transition: all 0.2s;
}
.log-button:hover {
background: var(--bg-color);
border-color: var(--primary-color);
color: var(--primary-color);
}
.log-filter {
display: flex;
gap: 8px;
align-items: center;
margin-left: auto;
}
.log-filter select, .log-filter input {
padding: 4px 8px;
border: 1px solid #e5e7eb;
border-radius: 4px;
font-size: 0.875rem;
color: #4b5563;
}
.log-filter input {
width: 150px;
}
.log-pagination {
display: flex;
gap: 4px;
align-items: center;
padding: 4px;
border-top: 1px solid #e5e7eb;
justify-content: space-between;
font-size: 0.875rem;
color: #4b5563;
}
.page-info {
display: flex;
align-items: center;
gap: 8px;
}
.page-size {
display: flex;
align-items: center;
gap: 4px;
}
.page-size select {
padding: 2px 4px;
border: 1px solid #e5e7eb;
border-radius: 4px;
}
.page-nav {
display: flex;
gap: 4px;
}
.page-nav button {
padding: 2px 8px;
border: 1px solid #e5e7eb;
border-radius: 4px;
background: white;
cursor: pointer;
}
.page-nav button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.log-content {
flex: 1;
overflow: auto;
padding: 8px;
font-family: monospace;
font-size: 0.875rem;
line-height: 1.5;
white-space: pre-wrap;
background: #1a1a1a;
color: #f8fafc;
margin: 4px;
border-radius: 4px;
}
.log-entry {
margin: 2px 0;
padding: 2px 4px;
border-radius: 2px;
}
.log-entry:hover {
background: rgba(255, 255, 255, 0.1);
}
.log-entry.error {
color: #f87171;
}
.log-entry.warning {
color: #fbbf24;
}
.log-entry.info {
color: #60a5fa;
}
.status-bar {
position: fixed;
top: 4px;
right: 8px;
background: white;
padding: 4px 8px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
gap: 8px;
z-index: 15;
}
.status-info {
display: flex;
align-items: center;
gap: 8px;
color: #4b5563;
font-size: 0.875rem;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="sidebar-menu">
<a href="/" class="sidebar-button">
<span class="sidebar-icon">🏠</span>
<span>系统主页</span>
</a>
<a href="/data" class="sidebar-button">
<span class="sidebar-icon">📊</span>
<span>系统数据</span>
</a>
<a href="/dashboard" class="sidebar-button">
<span class="sidebar-icon">⚡</span>
<span>配电操作</span>
</a>
<a href="/logs" class="sidebar-button active">
<span class="sidebar-icon">📝</span>
<span>系统日志</span>
</a>
<a href="/testpage" class="sidebar-button">
<span class="sidebar-icon">🧪</span>
<span>测试页面</span>
</a>
</div>
</div>
<div class="header">
<h1>系统日志</h1>
<div class="status-bar">
<div class="status-info">
<span>🟢 系统状态: <span id="systemStatus">运行中</span></span>
<span>⚡ 应用频率: <span id="appFreq">0</span> Hz</span>
<span>📡 通信频率: <span id="commFreq">0</span> Hz</span>
<span>💻 CPU负载: <span id="cpuLoad">0</span>%</span>
<span>⏱️ 迭代间隔: <span id="timeSinceIterate">0</span>ms</span>
<span>🕒 更新时间: <time id="updateTime">2025-05-18 15:52</time></span>
</div>
</div>
</div>
<div class="main-content">
<div class="log-panel">
<div class="log-controls">
<button class="log-button" id="clearBtn">清空显示</button>
<button class="log-button" id="scrollLockBtn">自动滚动</button>
<button class="log-button" id="downloadBtn">下载日志</button>
<div class="log-filter">
<select id="logLevel">
<option value="ALL">所有级别</option>
<option value="ERROR">错误</option>
<option value="WARNING">警告</option>
<option value="INFO">信息</option>
</select>
<input type="text" id="searchInput" placeholder="搜索日志...">
</div>
</div>
<div class="log-content" id="logContent"></div>
<div class="log-pagination">
<div class="page-size">
每页显示:
<select id="pageSize">
<option value="100">100</option>
<option value="200" selected>200</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select>
</div>
<div class="page-info">
<span id="pageInfo">第 0/0 页</span>
<div class="page-nav">
<button id="firstPage">首页</button>
<button id="prevPage">上一页</button>
<button id="nextPage">下一页</button>
<button id="lastPage">末页</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let ws = null;
let autoScroll = true;
let logContent = document.getElementById('logContent');
let currentPage = 1;
let totalPages = 1;
let pageSize = 200;
let allLogs = [];
let filteredLogs = [];
let searchTimer = null;
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.host + '/ws');
ws.onopen = () => {
console.log('WebSocket连接已建立');
updateStatus('运行中', true);
requestLogs();
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'log') {
// 初始加载的日志数据
allLogs = data.content;
filterAndDisplayLogs();
} else if (data.type === 'new_log') {
// 新的日志条目
if (Array.isArray(data.content)) {
let shouldUpdate = false;
data.content.forEach(log => {
allLogs.push(log);
if (shouldShowLog(log)) {
filteredLogs.push(log);
shouldUpdate = true;
// 如果在最后一页或自动滚动模式下,直接添加到显示
if (currentPage === totalPages || autoScroll) {
appendLogEntry(log);
}
}
});
if (shouldUpdate) {
totalPages = Math.ceil(filteredLogs.length / pageSize);
updatePagination();
// 如果开启了自动滚动,确保滚动到底部
if (autoScroll) {
logContent.scrollTop = logContent.scrollHeight;
}
}
}
} else if (data.moosdb) {
// 更新MOOSDB信息
document.getElementById('appFreq').textContent = data.moosdb.appFreq.toFixed(2);
document.getElementById('commFreq').textContent = data.moosdb.commFreq.toFixed(2);
document.getElementById('cpuLoad').textContent = data.moosdb.cpuLoad.toFixed(1);
document.getElementById('timeSinceIterate').textContent = data.moosdb.timeSinceIterate.toFixed(0);
} else if (data.type === 'system_status') {
updateStatus(data.status, true);
}
updateTime();
};
ws.onclose = () => {
console.log('WebSocket连接已关闭');
updateStatus('已断开', false);
// 增加重连延迟,避免快速重连
setTimeout(() => {
if (!ws || ws.readyState === WebSocket.CLOSED) {
initWebSocket();
}
}, 2000);
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
updateStatus('错误', false);
};
}
function requestLogs() {
ws.send(JSON.stringify({
type: 'get_logs',
page: currentPage,
pageSize: pageSize,
filter: {
level: document.getElementById('logLevel').value,
search: document.getElementById('searchInput').value
}
}));
}
function shouldShowLog(logEntry) {
const level = document.getElementById('logLevel').value;
const search = document.getElementById('searchInput').value.toLowerCase();
if (level !== 'ALL') {
if (!logEntry.includes(level)) return false;
}
if (search && !logEntry.toLowerCase().includes(search)) {
return false;
}
return true;
}
function filterAndDisplayLogs() {
filteredLogs = allLogs.filter(shouldShowLog);
totalPages = Math.ceil(filteredLogs.length / pageSize);
displayCurrentPage();
updatePagination();
}
function displayCurrentPage() {
logContent.innerHTML = '';
const start = (currentPage - 1) * pageSize;
const end = Math.min(start + pageSize, filteredLogs.length);
for (let i = start; i < end; i++) {
appendLogEntry(filteredLogs[i]);
}
}
function updatePagination() {
document.getElementById('pageInfo').textContent = `第 ${currentPage}/${totalPages} 页`;
document.getElementById('firstPage').disabled = currentPage === 1;
document.getElementById('prevPage').disabled = currentPage === 1;
document.getElementById('nextPage').disabled = currentPage === totalPages;
document.getElementById('lastPage').disabled = currentPage === totalPages;
}
// 事件监听器
document.getElementById('logLevel').addEventListener('change', () => {
currentPage = 1;
filterAndDisplayLogs();
});
document.getElementById('searchInput').addEventListener('input', (e) => {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
currentPage = 1;
filterAndDisplayLogs();
}, 300);
});
document.getElementById('pageSize').addEventListener('change', (e) => {
pageSize = parseInt(e.target.value);
currentPage = 1;
filterAndDisplayLogs();
});
document.getElementById('firstPage').addEventListener('click', () => {
currentPage = 1;
displayCurrentPage();
updatePagination();
});
document.getElementById('prevPage').addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
displayCurrentPage();
updatePagination();
}
});
document.getElementById('nextPage').addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage++;
displayCurrentPage();
updatePagination();
}
});
document.getElementById('lastPage').addEventListener('click', () => {
currentPage = totalPages;
displayCurrentPage();
updatePagination();
});
function appendLogEntry(content) {
const entry = document.createElement('div');
entry.className = 'log-entry';
// 根据日志内容设置不同的样式
if (content.includes('ERROR') || content.includes('ERR')) {
entry.classList.add('error');
} else if (content.includes('WARNING') || content.includes('WARN')) {
entry.classList.add('warning');
} else if (content.includes('INFO')) {
entry.classList.add('info');
}
entry.textContent = content;
logContent.appendChild(entry);
// 只在自动滚动模式下滚动到底部
if (autoScroll) {
logContent.scrollTop = logContent.scrollHeight;
}
}
function updateStatus(status, isConnected) {
const statusElement = document.querySelector('.status-info span:first-child');
statusElement.innerHTML = `${isConnected ? '🟢' : '🔴'} 系统状态: ${status}`;
}
function updateTime() {
const now = new Date();
const timeStr = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
document.getElementById('updateTime').textContent = timeStr;
}
// 按钮事件处理
document.getElementById('clearBtn').onclick = () => {
logContent.innerHTML = '';
};
document.getElementById('scrollLockBtn').onclick = function() {
autoScroll = !autoScroll;
this.textContent = autoScroll ? '自动滚动' : '已锁定';
this.style.backgroundColor = autoScroll ? 'white' : '#e5e7eb';
};
document.getElementById('downloadBtn').onclick = () => {
const text = logContent.innerText;
const blob = new Blob([text], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `system_log_${new Date().toISOString().slice(0,19).replace(/[:-]/g, '')}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
window.addEventListener('load', initWebSocket);
</script>
</body>
</html>
)";
#endif // HTTP_SERVER_LOGPAGE_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-458
View File
@@ -1,458 +0,0 @@
#ifndef HTTP_SERVER_TESTPAGE_H
#define HTTP_SERVER_TESTPAGE_H
#include <string>
const std::string TESTPAGE_HTML = R"row(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试页面</title>
<link rel="stylesheet" href="/static/style.css">
<style>
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--bg-color: #f8fafc;
--sidebar-width: 60px;
--sidebar-expanded: 220px;
--header-height: 36px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: #1a1a1a;
}
.container {
display: grid;
grid-template-areas:
"sidebar main-content";
grid-template-columns: var(--sidebar-width) 1fr;
grid-template-rows: 1fr;
min-height: 100vh;
gap: 0;
}
.header {
grid-area: unset;
background: white;
padding: 0 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
right: 0;
left: var(--sidebar-width);
height: var(--header-height);
z-index: 10;
}
.header h1 {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
}
.sidebar {
grid-area: sidebar;
background: white;
width: var(--sidebar-width);
height: 100vh;
position: fixed;
left: 0;
top: 0;
padding: 0.5rem 0.25rem;
transition: width 0.3s ease;
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.1);
z-index: 20;
}
.sidebar:hover {
width: var(--sidebar-expanded);
}
.sidebar-menu {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sidebar-button {
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
color: #4b5563;
text-decoration: none;
transition: all 0.2s ease;
white-space: nowrap;
overflow: hidden;
}
.sidebar-button:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.sidebar-icon {
min-width: 1.5rem;
margin-right: 1rem;
font-size: 1.25rem;
}
.main-content {
grid-area: main-content;
padding: 1px;
margin-top: var(--header-height);
}
.status-bar {
position: fixed;
top: 4px;
right: 8px;
background: white;
padding: 4px 8px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
gap: 8px;
z-index: 15;
}
.status-info {
display: flex;
align-items: center;
gap: 8px;
color: #4b5563;
font-size: 0.875rem;
}
.data-panel {
background: white;
border-radius: 4px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
height: calc(100vh - var(--header-height) + 3px);
overflow: auto;
}
.panel-title {
font-size: 1rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #374151;
}
.form-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);
}
.btn {
display: inline-block;
padding: 8px 16px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.btn:hover {
background-color: var(--primary-hover);
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main-content";
}
.sidebar {
transform: translateX(-100%);
}
.header {
left: 0;
}
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="sidebar-menu">
<a href="/" class="sidebar-button">
<span class="sidebar-icon">🏠</span>
<span>系统主页</span>
</a>
<a href="/data" class="sidebar-button">
<span class="sidebar-icon">📊</span>
<span>系统数据</span>
</a>
<a href="/dashboard" class="sidebar-button">
<span class="sidebar-icon">⚡</span>
<span>配电操作</span>
</a>
<a href="/logs" class="sidebar-button">
<span class="sidebar-icon">📝</span>
<span>系统日志</span>
</a>
<a href="/testpage" class="sidebar-button">
<span class="sidebar-icon">🧪</span>
<span>测试页面</span>
</a>
</div>
</div>
<div class="header">
<h1>测试页面</h1>
<div class="status-bar">
<div class="status-info">
<span>🟢 系统状态: <span id="systemStatus">运行中</span></span>
<span>⚡ 应用频率: <span id="appFreq">0</span> Hz</span>
<span>📡 通信频率: <span id="commFreq">0</span> Hz</span>
<span>💻 CPU负载: <span id="cpuLoad">0</span>%</span>
<span>⏱️ 迭代间隔: <span id="timeSinceIterate">0</span>ms</span>
<span>🕒 更新时间: <time id="updateTime">2025-05-19 11:54</time></span>
</div>
</div>
</div>
<div class="main-content">
<div class="data-panel">
<div class="panel-title">测试表单</div>
<form id="testForm">
<div class="form-section">
<div class="panel-subtitle">测试类型/ID设置</div>
<div class="form-group">
<label for="type" class="form-label">类型</label>
<input type="number" id="type" name="type" class="form-input" min="0" required>
</div>
<div class="form-group">
<label for="id" class="form-label">ID</label>
<input type="number" id="id" name="id" class="form-input" min="0" required>
</div>
<button type="button" class="btn" onclick="submitTestForm()">提交类型/ID</button>
</div>
<div class="form-section">
<div class="panel-subtitle">MOOSDB测试消息设置</div>
<div class="form-group">
<label for="key" class="form-label">键(Key)</label>
<input type="text" id="key" name="key" class="form-input" required>
</div>
<div class="form-group">
<label for="value" class="form-label">值(Value)</label>
<input type="text" id="value" name="value" class="form-input" required>
</div>
<button type="button" class="btn" onclick="submitKeyValueForm()" style="background-color: #4f46e5;">提交键值对</button>
</div>
</form>
<style>
.form-section {
margin-bottom: 24px;
padding: 16px;
background: #f9fafb;
border-radius: 8px;
}
.panel-subtitle {
font-size: 0.9rem;
font-weight: 600;
color: #4b5563;
margin-bottom: 12px;
padding-bottom: 4px;
border-bottom: 1px solid #e5e7eb;
}
</style>
</div>
</div>
</div>
<script>
let ws = null;
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.host + '/ws');
ws.onopen = () => {
console.log('WebSocket连接已建立');
updateStatus('运行中', true);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到系统数据:', data);
// 更新MOOSDB信息
if (data.moosdb) {
document.getElementById('appFreq').textContent = data.moosdb.appFreq.toFixed(2);
document.getElementById('commFreq').textContent = data.moosdb.commFreq.toFixed(2);
document.getElementById('cpuLoad').textContent = data.moosdb.cpuLoad.toFixed(1);
document.getElementById('timeSinceIterate').textContent = data.moosdb.timeSinceIterate.toFixed(0);
}
updateTime();
};
ws.onclose = () => {
console.log('WebSocket连接已关闭');
updateStatus('已断开', false);
setTimeout(initWebSocket, 5000);
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
updateStatus('错误', false);
};
}
function updateStatus(status, isConnected) {
const statusElement = document.querySelector('.status-info span:first-child');
statusElement.innerHTML = `${isConnected ? '🟢' : '🔴'} 系统状态: ${status}`;
}
function updateTime() {
const now = new Date();
const timeStr = now.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
document.getElementById('updateTime').textContent = timeStr;
}
function submitTestForm() {
const form = document.getElementById('testForm');
const typeValue = parseInt(form.type.value);
const idValue = parseInt(form.id.value);
if (typeValue < 0 || idValue < 0) {
alert('请输入非负整数');
return;
}
const formData = {
type: typeValue,
id: idValue
};
sendFormData(formData);
}
function submitKeyValueForm() {
const key = document.getElementById('key').value.trim();
const value = document.getElementById('value').value.trim();
if (!key || !value) {
alert('请输入键和值');
return;
}
// 直接构造JSON对象,不通过form
const payload = {
key: key,
value: value
};
// 专门发送到MOOSDB测试消息接口
fetch('/moosdb_test', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data.success) {
alert('MOOSDB测试消息提交成功: ' + data.message);
} else {
alert('MOOSDB测试消息提交失败: ' + data.error);
}
})
.catch(error => {
console.error('MOOSDB测试消息提交错误:', error);
alert('MOOSDB测试消息提交过程中发生错误: ' + error.message);
});
}
function sendFormData(formData) {
fetch('/testpage', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('提交成功: ' + data.message);
} else {
alert('提交失败: ' + data.error);
}
})
.catch(error => {
console.error('提交错误:', error);
alert('提交过程中发生错误');
});
}
window.addEventListener('load', initWebSocket);
</script>
</body>
</html>
)row";
#endif // HTTP_SERVER_TESTPAGE_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
//SERVERHOST = 192.168.0.223
//SERVERPORT = 9000
ProcessConfig = pPowerManger
{
AppTick = 4
CommsTick = 4
log_level = INFO
log_file = /path/to/your/logfile.log
}
-964
View File
@@ -1,964 +0,0 @@
#ifndef PMSYSVARIABLE_H
#define PMSYSVARIABLE_H
//宏定义
#include<string>
#include <map>
#include <unordered_set>
#define BREAK_OFF 0xAA
#define BREAK_ON 0x55
#define BREAK_FALSE 0x5A
//消息结构体变量定义
#pragma pack (1)
typedef struct ccuState
{
uint8_t fc_mode; // 燃料电池系统运行模式:00H-无效, 01H-调试, 02H-自动, 03H-补给
uint8_t fc_status; // 燃料电池状态:00H-无效, 01H-初始化, ..., 0DH-维护完成(详见协议)
uint8_t fc_fault_level; // 燃料电池故障等级:00H-无故障, 10H-一级, 20H-二级, 30H-三级, 40H-四级
uint16_t output_power_limit; // 系统输出功率限制值,单位:W,范围:0~30000W
uint16_t total_generation_time; // 系统累计发电时间,单位:0.1小时,最大值6553.5小时
uint16_t total_generation_power; // 系统累计发电总功率,单位:W
uint16_t fault_level_1; // 一级故障位,按位表示不同的故障项
uint16_t fault_level_2; // 二级故障位
uint16_t fault_level_3; // 三级故障位
uint16_t fault_level_4; // 四级故障位
uint8_t hydrogen_capacity; // 储氢罐剩余容量百分比,单位:%,范围:0~100
uint8_t liquid_oxygen_capacity; // 液氧罐剩余容量百分比,单位:%,范围:0~100
uint16_t palladium_temp; // 钯膜最高温度,单位:℃
uint16_t main_pipe_pressure; // 主水路压力,单位:0.01kPa
uint16_t aux_pipe_pressure; // 辅水路压力,单位:0.01kPa
uint16_t cabin_pressure1; // 舱室1压力,单位:0.01kPa
uint16_t cabin_pressure2; // 舱室2压力,单位:0.01kPa
uint16_t cabin_temp1; // 舱室1温度,单位:0.01℃
uint16_t cabin_temp2; // 舱室2温度,单位:0.01℃
uint16_t cabin_humidity1; // 舱室1湿度,单位:0.01%RH
uint16_t cabin_humidity2; // 舱室2湿度,单位:0.01%RH
uint8_t flame_detector1; // 火焰探测器1状态:00H-无效, 01H-报警, 02H-故障
uint8_t flame_detector2; // 火焰探测器2状态:00H-无效, 01H-报警, 02H-故障
uint16_t h2_concentration1; // 舱室H2浓度1,单位:0.01% LEL
uint16_t h2_concentration2; // 舱室H2浓度2,单位:0.01% LEL
uint16_t h2_concentration3; // 舱室H2浓度3,单位:0.01% LEL
uint16_t o2_concentration1; // 舱室O2浓度1,单位:0.01% LEL
uint16_t o2_concentration2; // 舱室O2浓度2,单位:0.01% LEL
uint16_t ch3oh_concentration1; // 舱室甲醇气体浓度1,单位:0.01% LEL
uint16_t ch3oh_concentration2; // 舱室甲醇气体浓度2,单位:0.01% LEL
uint16_t reserved1[2]; // 保留字段前2个元素
uint16_t emergency_battery1_voltage; // 应急电池1总电压 [V]
uint16_t emergency_battery1_current; // 应急电池1总电流 [A]
uint16_t emergency_battery1_max_temp; // 应急电池1最高温度 [°C]
uint16_t emergency_battery1_fault_word; // 应急电池1故障字
uint16_t emergency_battery2_voltage; // 应急电池2总电压 [V]
uint16_t emergency_battery2_current; // 应急电池2总电流 [A]
uint16_t emergency_battery2_max_temp; // 应急电池2最高温度 [°C]
uint16_t emergency_battery2_fault_word; // 应急电池2故障字
uint16_t cabin_ox_concentration; // 仪表舱内氧气浓度 [%]
uint16_t cabin_temperature; // 仪表舱内温度 [°C]
uint16_t cabin_humidity; // 仪表舱内湿度 [%RH]
uint16_t cabin_pressure; // 仪表舱内大气压 [kPa]
uint16_t power_cabin_ox_concentration; // 动力舱内氧气浓度 [%]
uint16_t power_cabin_temperature; // 动力舱内温度 [°C]
uint16_t power_cabin_humidity; // 动力舱内湿度 [%RH]
uint16_t power_cabin_pressure; // 动力舱内大气压 [kPa]
uint8_t reserved2[1]; // 保留字段,地址范围54~55,共2个uint8_t,协议预留
uint8_t dyn_alarm_flag[6]; // 动力锂电池报警标志字数组,共6字节,按位表示各类报警
uint8_t ins_alarm_flag[6]; // 仪表锂电池报警标志字数组,共6字节,按位表示各类报警
uint8_t ins_relay_status1; // 仪表电池继电器状态,高4位表示正极继电器,低4位表示充电继电器
uint8_t ins_relay_status2; // 仪表电池预充继电器和负极继电器状态
uint8_t dyn_relay_status1; // 动力电池正极与充电继电器状态
uint8_t dyn_relay_status2; // 动力电池预充与负极继电器状态
uint16_t ins_max_discharge_power; // 仪表电池最大允许放电功率,单位:kW,精度0.05kW
uint16_t dyn_max_discharge_power; // 动力电池最大允许放电功率,单位:kW,精度0.05kW
uint8_t ins_soc; // 仪表锂电池荷电状态SOC,范围:0~100
uint8_t dyn_soc; // 动力锂电池荷电状态SOC,范围:0~100
uint16_t ins_total_energy; // 仪表电池总能量,单位:kWh
uint16_t dyn_total_energy; // 动力电池总能量,单位:kWh
uint16_t ins_power_input; // 仪表电池输入功率,单位:kW
uint16_t dyn_power_input; // 动力电池输入功率,单位:kW
uint8_t ins_charge_status; // 仪表电池充放电状态:00H-无效,10H-充电,20H-放电
uint8_t dyn_charge_status; // 动力电池充放电状态:00H-无效,10H-充电,20H-放电
uint16_t ins_voltage_link; // 仪表电池LINK端电压,单位:0.01V
uint16_t ins_voltage_pack; // 仪表电池PACK端电压,单位:0.01V
uint16_t ins_current; // 仪表电池电流,单位:0.01A(正值放电)
uint16_t ins_resistance_pos; // 仪表电池正极绝缘电阻,单位:kΩ
uint16_t ins_resistance_neg; // 仪表电池负极绝缘电阻,单位:kΩ
uint16_t dyn_voltage_link; // 动力电池LINK端电压,单位:0.01V
uint16_t dyn_voltage_pack; // 动力电池PACK端电压,单位:0.01V
uint16_t dyn_current; // 动力电池电流,单位:0.01A(正值放电)
uint16_t dyn_resistance_pos; // 动力电池正极绝缘电阻,单位:kΩ
uint16_t dyn_resistance_neg; // 动力电池负极绝缘电阻,单位:kΩ
uint8_t ins_emergency_status; // 仪表电池紧急状态字,按位表示异常或告警状态
uint8_t dyn_emergency_status; // 动力电池紧急状态字,按位表示异常或告警状态
uint8_t reserved3[2]; // 保留字节,协议预留
uint8_t ins_soc_threshold1; // 仪表电池1 SOC门限,范围:0~100
uint8_t ins_soc_threshold2; // 仪表电池2 SOC门限,范围:0~100
uint8_t ins_soc_threshold3; // 仪表电池3 SOC门限,范围:0~100
uint8_t dyn_soc_threshold1; // 动力电池1 SOC门限,范围:0~100
uint8_t dyn_soc_threshold2; // 动力电池2 SOC门限,范围:0~100
uint8_t dyn_soc_threshold3; // 动力电池3 SOC门限,范围:0~100
uint8_t ins_power_limit1; // 仪表电池1功率限制值,单位:%
uint8_t ins_power_limit2; // 仪表电池2功率限制值,单位:%
uint8_t dyn_power_limit1; // 动力电池1功率限制值,单位:%
uint8_t dyn_power_limit2; // 动力电池2功率限制值,单位:%
uint32_t device_online_flag1; // 设备在线状态标志1,byte0:低压配电板在线标志位 byte1:复合管控主机在线标志位 byte2:复合管控备机在线标志位
uint32_t device_online_flag2; // 设备在线状态标志2,byte0:仪表锂电在线标志位 byte1:动力锂电在线标志位 byte2:燃电锂电在线标志位 byte3:高压配电板在线标志位
uint8_t dyn_charge_level; // 动力电池充电状态:01H-1级(不限), 02H-2级(限功率), 03H-3级(关闭)
uint8_t dyn_water_leak_status; // 动力漏水状态:0XH-正常, 1XH-异常漏水
uint8_t ins_water_leak_dcdc_status; // 仪表漏水状态与DCDC状态:低4位-漏水状态(0XH正常,1XH异常),高4位-DCDC状态(X0H正常,X1H故障)
uint8_t reserved4;
// uint32_t reserved4; // 保留字段,协议预留
uint32_t heartbeat; // 主机心跳计数器或标志值
} ccuState;
typedef struct {
unsigned char insSocHold1;
unsigned char insSocHold2;
unsigned char insSocHold3;
unsigned char fcuToInsLimit1;
unsigned char fcuToinsLimit2;
unsigned char powerSocHold1;
unsigned char powerSocHold2;
unsigned char powerSocHold3;
unsigned char fcuToPowerLimit1;
unsigned char fcuToPowerLimit2;
}ccuSetParmCmd;
typedef struct {
unsigned char fcmode;
unsigned char fcuCmd;
unsigned char fcuPowerEfficiency;
unsigned char insChargeEnable;
unsigned char insBatCmd;
unsigned char powerBatCmd;
unsigned short powerBatEfficiency;
unsigned char heartbeat;
unsigned char masterState;
unsigned int paceholder1;
unsigned int paceholder2;
}ccuColCmd;
typedef struct disHighVolBusState
{
//断路器状态
// 5: 燃料电池断路器
unsigned char fuelCellCircuitBreaker;
// 6: 动力锂电池断路器
unsigned char powerLithiumBatteryCircuitBreaker;
// 7: 推进电机断路器
unsigned char propulsionMotorCircuitBreaker;
// 8: 锂电池组仪表断路器
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker;
// 9: DC/DC5模块断路器
unsigned char dcDc5ModuleCircuitBreaker;
// 10: 艏部高压配电箱断路器
unsigned char bowHighVoltageDistributionBoxCircuitBreaker;
// 11: 艉部FT装置4/5断路器
unsigned char sternFTDevice45CircuitBreaker;
// 14: FB预留断路器
unsigned char fbReservedCircuitBreaker;
// 12: 艉舵开关1断路器
unsigned char sternRudderSwitch1CircuitBreaker;
// 13: 艉舵开关2断路器
unsigned char sternRudderSwitch2CircuitBreaker;
// 15: TYZ预留断路器
unsigned char tyzReservedCircuitBreaker;
// 16: 预留断路器
unsigned char reservedCircuitBreaker;
// unsigned char busInsulationLow;
// unsigned char busAInsulationHigh;
// 17: DC/DC5故障字低
// 0x00: 正常状态
// Bit0: DC/DC模块过温关机
// Bit1: 输出过流
// Bit2: 输出过压
// Bit3: 输出欠压
// Bit4: 输入过压
// Bit5: 输入欠压
// Bit6: 输出短路保护
// Bit7: 内部故障
// 18: DC/DC5故障字高
unsigned char dcDcFaultWord1;
// 0x00: 正常状态
// Bit0: 通讯故障报警
// Bit1: 工作状态 1:启动,0:关闭
// Bit2: 冷却泵1故障 冷却泵1故障 : 1故障, 0 正常
// Bit3: 冷却泵2故障 冷却泵2故障 :1故障,0 正常
// Bit4: 冷却水泄露
// Bit5:冷却泵1运行状态 :1 运行,0 停止
// Bit6:冷却泵2运行状态 :1 运行,0 停止
unsigned char dcDcFaultWord2;
// 19: 动力母排绝缘状态(0x55 正常,0xAA 绝缘低)
unsigned char powerBusInsulationStatus;
// 20: A汇流排绝缘状态(0x55 正常,0xAA 绝缘低)
unsigned char aBusInsulationStatus;
// 21: 仪表电源状态 (0x55 正常,0xAA 故障)
unsigned char meterPowerLossSignal;
// 22: 应急电源状态 (0x55 正常,0xAA 故障)
unsigned char emergencyPowerLossSignal;
// 22: dc/dc温度
unsigned char dcDcTemperature;
// 23: 动力汇流排电压
unsigned short powerBusVoltage;
// 24: DC/DC5模块电流
unsigned short dcDc5ModuleCurrent;
// 25: 动力汇流排电流
unsigned short powerBusCurrent;
// 26: A汇流排电压
unsigned short busbarAVoltage;
// 27: 推进电机控制箱电流
unsigned short propulsionMotorControlBoxCurrent;
// 28: A汇流排电流
unsigned short busbarACurrent;
// 29: 锂电池组(仪表)电流
unsigned short lithiumBatteryGroupMeterCurrent;
// 30: 艏部高压配电箱电流
unsigned short bowHighVoltageDistributionBoxCurrent;
// 31: 艉部FT装置4/5电流
unsigned short sternFTDevice45Current;
// 32: TYZ预留开关电流
unsigned short tyzReservedSwitchCurrent;
// 33: 艉舵开关1电流
unsigned short sternRudderSwitch1Current;
// 34: 预留电流
unsigned short reservedCurrent1; // 改为 reservedCurrent1 以避免重名
// 35: 艉舵开关2电流
unsigned short sternRudderSwitch2Current;
// 36: 预留电流2
unsigned short reservedCurrent2; // 改为 reservedCurrent2 以避免重名
// 37: FB预留电流
unsigned short fbReservedSwitchCurrent;
// 38: 预留电流3
unsigned short reservedCurrent3; // 改为 reservedCurrent3 以避免重名
// 39: 冷却水压力
unsigned short coolWaterPressure;
}disHighVolBusState;
typedef struct disHighAVolBusState
{
//5. 艏部FT装置1/2/3断路器
unsigned char bowFTDevice123CircuitBreaker;
//6. 启闭机构断路器
unsigned char actuatorCircuitBreaker;
//7. 桅杆舵机控制箱断路器
unsigned char mastSteeringGearControlBoxCircuitBreaker;
//8. XCZ断路器
unsigned char xczCircuitBreaker;
//9. 艏舵控制箱断路器
unsigned char bowRudderControlBoxCircuitBreaker;
//10. 敞水盖启动电缸断路器
unsigned char openWaterCoverStartCylinderCircuitBreaker;
//11. 仪表电源失电信号 (0x55 正常,0xAA 故障)
unsigned char instrumentPowerFailureSignal;
//12. 应急电源失电信号 (0x55 正常,0xAA 故障)
unsigned char emergencyPowerFailureSignal;
//13. 浸水报警
// unsigned char busbarBInsulationStatus;
unsigned char waterIngressionAlarm;
//14. 汇流排B电压
unsigned short busbarBVoltage;
//15. 汇流排B电流
unsigned short busbarBCurrent;
//16. 艏部FT装置1/2/3电流
unsigned short bowFTDevice123Current;
//17. 启闭机构电流
unsigned short actuatorCurrent;
//18. 桅杆舵机控制箱电流
unsigned short mastSteeringGearControlBoxCurrent;
//19. XCZ电流
unsigned short xczCurrent;
//20. 艏舵控制箱电流
unsigned short bowRudderControlBoxCurrent;
//21. 敞水盖启动电缸电流
unsigned short openWaterCoverStartCylinderCurrent;
}disHighAVolBusState;
typedef struct disHighBVolBusState
{
//5. 锂电池组(仪表)断路器
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker;
//6. 艏部低压配电箱断路器
unsigned char bowLowVoltageDistributionBoxCircuitBreaker;
//7. UNIT4仪表DC48V断路器
unsigned char unit4InstrumentDC48VCircuitBreaker;
//8. DC-C1直流配电板断路器
unsigned char dcC1DCDistributionPanelCircuitBreaker;
//9. 预留断路器1
unsigned char reservedCircuitBreaker1; // 为了区分,添加了数字后缀
//10. 预留断路器2
unsigned char reservedCircuitBreaker2; // 为了区分,添加了数字后缀
//11. 应急锂电池组2断路器
unsigned char emergencyLithiumBatteryGroup2CircuitBreaker;
//12. 仪表电源失电信号 (0x55 正常,0xAA 故障)
unsigned char instrumentPowerFailureSignal;
//13. 应急电源失电信号 (0x55 正常,0xAA 故障)
unsigned char emergencyPowerFailureSignal;
//14. 仪表汇流排绝缘低 (0x55 正常,0xAA 绝缘低)
unsigned char instrumentBusbarInsulationLow;
//15. 仪表母排电压
unsigned short instrumentBusbarVoltage;
//16. 艏部低压配电箱电流
unsigned short bowLowVoltageDistributionBoxCurrent;
//17. DC-C1(直流配电板1)仪表DC48V电流
unsigned short dcC1InstrumentDC48VCurrent;
//18. 预留电流
unsigned short reservedCurrent1; // 为了区分,添加了数字后缀
//19. 应急锂电池组2电流
unsigned short emergencyLithiumBatteryGroup2Current;
//20. 锂电池组(仪表)电流
unsigned short lithiumBatteryGroupInstrumentCurrent;
//21. UNIT4仪表DC48V电流
unsigned short unit4InstrumentDC48VCurrent;
//22. Ushort 预留电流
unsigned short reservedCurrent2; // 为了区分,添加了数字后缀
//23. 应急2汇流排电压
unsigned short emergency2BusbarVoltage;
//24. 复合能源管控系统应急DC48V电流
unsigned short compositeEnergyManagementSystemEmergencyDC48VCurrent;
//25. 燃料电池安全系统应急DC48V电流
unsigned short fuelCellSecuritySystemEmergencyDC48VCurrent;
//26. PLC控制电源电流
unsigned short plcControlPowerCurrent;
} disLowMainBusState;
typedef struct disLowBusState
{
//5. UNIT1断路器
unsigned char unit1CircuitBreaker;
//6. UNIT2断路器
unsigned char unit2CircuitBreaker;
//7. UNIT3断路器
unsigned char unit3CircuitBreaker;
//8. UNIT5断路器
unsigned char unit5CircuitBreaker;
//9. 艏部PZ装置断路器
unsigned char bowPZDeviceCircuitBreaker;
//10. 预留断路器1
unsigned char reservedCircuitBreaker1; // 为了区分,添加了数字后缀
//11. 预留断路器2
unsigned char reservedCircuitBreaker2; // 为了区分,添加了数字后缀
//12. 应急锂电池组1断路器
unsigned char emergencyLithiumBatteryGroup1CircuitBreaker;
//13. 仪表电源失电信号 (0x55 正常,0xAA 故障)
unsigned char powerFailureSignal;
//14. 应急电源失电信号 (0x55 正常,0xAA 故障)
unsigned char emergencyPowerFailureSignal;
//15. 浸水报警 (0x55 正常,0xAA 故障)
unsigned char waterIngressionAlarm;
//16. 仪表母排电压
unsigned short instrumentBusbarVoltage;
//17. 仪表母排电流
unsigned short instrumentBusbarCurrent;
//18. UNIT1电流
unsigned short unit1Current;
//19. UNIT2电流
unsigned short unit2Current;
//20. UNIT3电流
unsigned short unit3Current;
//21. UNIT5电流
unsigned short unit5Current;
//22. 艏部PZ装置电流
unsigned short bowPZDeviceCurrent;
//23. 应急锂电池组1电流
unsigned short emergencyLithiumBatteryGroup1Current;
//24. 应急1汇流排电压
unsigned short emergency1BusbarVoltage;
}disLowBusState;
typedef struct disHighVolBusCmd
{
// 5: 燃料电池断路器
unsigned char fuelCellCircuitBreaker;
// 6: 动力锂电池断路器
unsigned char powerLithiumBatteryCircuitBreaker;
// 7: 推进电机断路器
unsigned char propulsionMotorCircuitBreaker;
// 8: 锂电池组仪表断路器
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker;
// 9: DC/DC5模块断路器
unsigned char dcDc5ModuleCircuitBreaker;
// 10: 艏部高压配电箱断路器
unsigned char bowHighVoltageDistributionBoxCircuitBreaker;
// 11: 艉部FT装置4/5断路器
unsigned char sternFTDevice45CircuitBreaker;
// // 12: 艉舵开关1断路器
// unsigned char sternRudderSwitch1CircuitBreaker;
// // 13: 艉舵开关2断路器
// unsigned char sternRudderSwitch2CircuitBreaker;
// // 14: FB预留断路器
// unsigned char fbReservedCircuitBreaker;
// 12: FB预留断路器
unsigned char fbReservedCircuitBreaker;
// 13: 艉舵开关1断路器
unsigned char sternRudderSwitch1CircuitBreaker;
// 14: 艉舵开关2断路器
unsigned char sternRudderSwitch2CircuitBreaker;
// 15: TYZ预留断路器
unsigned char tyzReservedCircuitBreaker;
// 16: 预留断路器
unsigned char reservedCircuitBreaker;
// 17: DC/DC5模块启动
unsigned char dcdc5Module;
// 18: 冷却系统启动
unsigned char coolingSystem;
// unsigned char inputWater;
// unsigned char outputWater;
}disHighVolBusCmd;
typedef struct disHighAVolBusCmd
{
//5. 艏部FT装置1/2/3断路器
unsigned char bowFTDevice123CircuitBreaker;
//6. 启闭机构断路器
unsigned char actuatorCircuitBreaker;
//7. 桅杆舵机控制箱断路器
unsigned char mastSteeringGearControlBoxCircuitBreaker;
//8. XCZ断路器
unsigned char xczCircuitBreaker;
//9. 艏舵控制箱断路器
unsigned char bowRudderControlBoxCircuitBreaker;
//10. 敞水盖启动电缸断路器
unsigned char openWaterCoverStartCylinderCircuitBreaker;
}disHighAVolBusCmd;
typedef struct disHighBVolBusCmd
{
//5. 锂电池组(仪表)断路器
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker;
//6. 艏部低压配电箱断路器
unsigned char bowLowVoltageDistributionBoxCircuitBreaker;
//7. UNIT4仪表DC48V断路器
unsigned char unit4InstrumentDC48VCircuitBreaker;
//8. DC-C1直流配电板断路器
unsigned char dcC1DCDistributionPanelCircuitBreaker;
//9. 预留断路器1
unsigned char reservedCircuitBreaker1; // 为了区分,添加了数字后缀
//10. 预留断路器2
unsigned char reservedCircuitBreaker2; // 为了区分,添加了数字后缀
//11. 应急锂电池组2断路器
unsigned char emergencyLithiumBatteryGroup2CircuitBreaker;
}disLowMainBusCmd;
typedef struct disLowBusCmd
{
unsigned char unit1CircuitBreaker;
unsigned char unit2CircuitBreaker;
unsigned char unit3CircuitBreaker;
unsigned char unit5CircuitBreaker;
unsigned char bowPZDeviceCircuitBreaker;
unsigned char reservedCircuitBreaker1; // 为了区分,添加了数字后缀
unsigned char reservedCircuitBreaker2; // 为了区分,添加了数字后缀
unsigned char emergencyLithiumBatteryGroup1CircuitBreaker;
}disLowBusCmd;
#pragma pack (0)
//状态结构体定义
typedef struct pmState
{
ccuState ccustate;
disHighVolBusState bus1State;
disHighAVolBusState bus2State;
disLowMainBusState bus3State;
disLowBusState bus4State;
} PmState;
typedef struct disBus1Breaker
{
unsigned char fuelCellCircuitBreaker : 1;
unsigned char powerLithiumBatteryCircuitBreaker : 1;
unsigned char propulsionMotorCircuitBreaker : 1;
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker : 1;
unsigned char dcDc5ModuleCircuitBreaker : 1;
unsigned char bowHighVoltageDistributionBoxCircuitBreaker : 1;
unsigned char sternFTDevice45CircuitBreaker : 1;
unsigned char sternRudderSwitch1CircuitBreaker : 1;
unsigned char sternRudderSwitch2CircuitBreaker : 1;
unsigned char fbReservedCircuitBreaker : 1;
unsigned char tyzReservedCircuitBreaker : 1;
unsigned char reservedCircuitBreaker : 1;
unsigned char dcdcmodel : 1;
unsigned char coolingSystem : 1;
unsigned short paceholder : 4;
} disBus1Breaker;
typedef struct disBus2Breaker
{
unsigned char bowFTDevice123CircuitBreaker : 1;
unsigned char actuatorCircuitBreaker : 1;
unsigned char mastSteeringGearControlBoxCircuitBreaker : 1;
unsigned char xczCircuitBreaker : 1;
unsigned char bowRudderControlBoxCircuitBreaker : 1;
unsigned char openWaterCoverStartCylinderCircuitBreaker : 1;
unsigned short paceholder : 10;
/* data */
} disBus2Breaker;
typedef struct disBus3Breaker
{
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker : 1;
unsigned char bowLowVoltageDistributionBoxCircuitBreaker : 1;
unsigned char unit4InstrumentDC48VCircuitBreaker : 1;
unsigned char dcC1DCDistributionPanelCircuitBreaker : 1;
unsigned char reservedCircuitBreaker1 : 1;
unsigned char reservedCircuitBreaker2 : 1;
unsigned char emergencyLithiumBatteryGroup2CircuitBreaker : 1;
unsigned short paceholder : 9;
/* data */
} disBus3Breaker;
typedef struct disBus4Breaker
{
unsigned char unit1CircuitBreaker : 1;
unsigned char unit2CircuitBreaker : 1;
unsigned char unit3CircuitBreaker : 1;
unsigned char unit5CircuitBreaker : 1;
unsigned char bowPZDeviceCircuitBreaker : 1;
unsigned char reservedCircuitBreaker1 : 1; // 为了区分,添加了数字后缀
unsigned char reservedCircuitBreaker2 : 1; // 为了区分,添加了数字后缀
unsigned char emergencyLithiumBatteryGroup1CircuitBreaker : 1;
unsigned short paceholder : 8;
/* data */
} disBus4Breaker;
typedef struct disSysBreakerList
{
disBus1Breaker bus1Breaker;
disBus2Breaker bus2Breaker;
disBus3Breaker bus3Breaker;
disBus4Breaker bus4Breaker;
} disSysBreaker;
typedef struct MSControlCmd
{
unsigned int powerCMD;
unsigned int modCMD;
unsigned int workCMD;
}MSControlCmd;
namespace FcuCommand {
enum Type : unsigned char {
SelfCheck = 0x01,
Start = 0x02,
Stop = 0x03,
Reset = 0x04,
EmergencyStop = 0x05,
Supply = 0x06,
WarmUp = 0x07,
Maintenance = 0x08
};
}
enum class FcuState : unsigned char {
//初始化
Init = 0x01,
//待机
Standby = 0x02,
//自检异常
SelfCheckError = 0x03,
//就绪
Ready = 0x04,
//启动
Start = 0x05,
//运行
Running = 0x06,
//停机
Stop = 0x07,
//故障
Fault = 0x08,
//补给
Supply = 0x09,
//预热
WarmUp = 0x0A,
//预热完成
WarmUpComplete = 0x0B,
//维护
Maintenance = 0x0C,
//维护完成
MaintenanceComplete = 0x0D
};
namespace BatteryCommand {
enum Type : unsigned char {
Start = 0x10,
Stop = 0x20,
Hold = 0x00
};
};
namespace PMMode {
enum Type : unsigned char {
Normal = 0,
Test = 1,
Debug = 2
};
};
//工况定义
namespace WorkCondition {
enum Type : unsigned char {
CHANING = 0, // 工况切换中
STANDBY = 1, // 待机工况
SHORE_BASED_READY = 2, // 岸基备航
WATER_BASED_READY = 3, // 水中备航
REMOTE_CONTROL = 4, // 遥控工况
CRUISE_MODE = 5, // 巡航模式
HIGH_SPEED_MODE = 6, // 高速模式
ASCEND_DESCEND = 7, // 上浮下潜
BUOYANCY_ADJUST = 8, // 浮调模式
UNDERWATER_RECON = 9, // 水下侦查
SURFACE_RECON = 10, // 水面侦查
DJ_MODE = 11, // DJ模式
LIFT_MODE = 12 // 调试模式
};
};
namespace RunMode {
enum Type : unsigned char {
//正常模式
Normal = 0,
//测试模式
Test = 1,
//调试模式
Debug = 2
};
};
namespace TaskType {
enum Type : unsigned char {
THROW_LOAD = 1, // 抛载
FLOAT_UP = 2, // 运动上浮
RECYCLE = 3, // 回收
HOVER = 4, // 悬停
SAT_COMM = 5, // 上浮通信
SAT_CALIBRATE = 6, // 上浮校准
TOUR = 7, // 游曳
SAIL = 8, // 航行
EMER_SAT_COMM = 9, //紧急上浮通信
EMER_SAIL = 10 //紧急航行
};
inline const std::string toString(Type type) {
static const std::map<Type, std::string> names = {
{THROW_LOAD, "throwLoad"},
{FLOAT_UP, "floatUp"},
{RECYCLE, "recycle"},
{HOVER, "hover"},
{SAT_COMM, "satComm"},
{SAT_CALIBRATE, "satCalibrate"},
{TOUR, "tour"},
{SAIL, "sail"},
{EMER_SAT_COMM,"emerSatComm"},
{EMER_SAIL,"emerSail"}
};
auto it = names.find(type);
return it != names.end() ? it->second : "unknown";
}
inline Type fromString(const std::string& str) {
static const std::map<std::string, Type> stringToEnum = {
{"throwLoad", THROW_LOAD},
{"floatUp", FLOAT_UP},
{"recycle", RECYCLE},
{"hover", HOVER},
{"satComm", SAT_COMM},
{"emerSatComm",SAT_COMM},
{"satCalibrate",SAT_CALIBRATE},
{"tour", TOUR},
{"sail", SAIL},
{"emerSatComm",EMER_SAT_COMM},
{"emerSail",EMER_SAIL}
};
auto it = stringToEnum.find(str);
return it != stringToEnum.end() ? it->second : static_cast<Type>(0); // 返回0表示无效类型
}
}
namespace EquipmentCommand {
enum Type : unsigned char {
OFF = 0, //断电
ON = 1, //上电
//仪表电上电
METER_ON = 2,
//动力电上电
POWER_ON = 3,
//仪表电断电
METER_OFF = 4,
//动力电断电
POWER_OFF = 5,
};
};
struct MissionState
{
int type;
bool enableBowRudder;
bool enableBuoyancyAdjustment;
};
struct DeviceSwitchCmd
{
int id;
int cmd;
};
namespace DeviceId {
enum Type : unsigned char {
singleBeamSonar1 = 0,
singleBeamSonar2 = 1,
singleBeamSonar3 = 2,
singleBeamSonar4 = 3,
singleBeamSonar5 = 4,
singleBeamSonar6 = 5,
singleBeamSonar7 = 6,
singleBeamSonar8 = 7,
forwardSonar = 8,
bowCover1 = 9,
bowCover2 = 10,
bowCover3 = 11,
bowCover4 = 12,
bowCover5 = 13,
bowCover6 = 14,
bowRudderDeploy = 15,
bowRudderServo1 = 16,
bowRudderServo2 = 17,
depth1 = 18,
depth2 = 19,
depth3 = 20,
overDeep1 = 21,
overDeep2 = 22,
ctd1 = 23,
ctd2 = 24,
soundVeloc = 25,
waterInject = 26,
bowWaterInet1 = 27,
bowWaterInet2 = 28,
bowWaterInet3 = 29,
bowWaterInet4 = 30,
bowWaterInet5 = 31,
bowWaterInet6 = 32,
bowWaterInet7 = 33,
bowWaterInet8 = 34,
sideSonar = 35,
lowFreqSonar = 36,
launchControler = 37,
photoSensor = 38,
electSensor = 39,
commMast = 40,
mastLiftingServo = 41,
USBL = 42,
DVL = 43,
buoyancyAdjust1 = 44,
buoyancyAdjust2 = 45,
buoyancyAdjust3 = 46,
buoyancyAdjust4 = 47,
buoyancyAdjust5 = 48,
sternRudderServo1 = 49,
sternRudderServo2 = 50,
sternRudderServo3 = 51,
sternRudderServo4 = 52,
thruster = 53,
bowThrow = 54,
sternThrow = 55,
buoyage = 56,
ins = 57,
ntp = 58,
inputWaterSensor1 = 59,
inputWaterSensor2 = 60,
inputWaterSensor3 = 61,
};
inline const std::string toString(Type type) {
static const std::map<Type, std::string> names = {
{singleBeamSonar1, "singleBeamSonar1"},
{singleBeamSonar2, "singleBeamSonar2"},
{singleBeamSonar3, "singleBeamSonar3"},
{singleBeamSonar4, "singleBeamSonar4"},
{singleBeamSonar5, "singleBeamSonar5"},
{singleBeamSonar6, "singleBeamSonar6"},
{singleBeamSonar7, "singleBeamSonar7"},
{singleBeamSonar8, "singleBeamSonar8"},
{forwardSonar, "forwardSonar"},
{bowCover1, "bowCover1"},
{bowCover2, "bowCover2"},
{bowCover3, "bowCover3"},
{bowCover4, "bowCover4"},
{bowCover5, "bowCover5"},
{bowCover6, "bowCover6"},
{bowRudderDeploy, "bowRudderDeploy"},
{bowRudderServo1, "bowRudderServo1"},
{bowRudderServo2, "bowRudderServo2"},
{depth1, "depth1"},
{depth2, "depth2"},
{depth3, "depth3"},
{overDeep1, "overDeep1"},
{overDeep2, "overDeep2"},
{ctd1, "ctd1"},
{ctd2, "ctd2"},
{soundVeloc, "soundVeloc"},
{waterInject, "waterInject"},
{bowWaterInet1, "bowWaterInet1"},
{bowWaterInet2, "bowWaterInet2"},
{bowWaterInet3, "bowWaterInet3"},
{bowWaterInet4, "bowWaterInet4"},
{bowWaterInet5, "bowWaterInet5"},
{bowWaterInet6, "bowWaterInet6"},
{bowWaterInet7, "bowWaterInet7"},
{bowWaterInet8, "bowWaterInet8"},
{sideSonar, "sideSonar"},
{lowFreqSonar, "lowFreqSonar"},
{launchControler, "launchControler"},
{photoSensor, "photoSensor"},
{electSensor, "electSensor"},
{commMast, "commMast"},
{mastLiftingServo, "mastLiftingServo"},
{USBL, "USBL"},
{DVL, "DVL"},
{buoyancyAdjust1, "buoyancyAdjust1"},
{buoyancyAdjust2, "buoyancyAdjust2"},
{buoyancyAdjust3, "buoyancyAdjust3"},
{buoyancyAdjust4, "buoyancyAdjust4"},
{buoyancyAdjust5, "buoyancyAdjust5"},
{sternRudderServo1, "sternRudderServo1"},
{sternRudderServo2, "sternRudderServo2"},
{sternRudderServo3, "sternRudderServo3"},
{sternRudderServo4, "sternRudderServo4"},
{thruster, "thruster"},
{bowThrow, "bowThrow"},
{sternThrow, "sternThrow"},
{buoyage, "buoyage"},
{ins, "ins"},
{ntp, "ntp"},
{inputWaterSensor1, "inputWaterSensor1"},
{inputWaterSensor2, "inputWaterSensor2"},
{inputWaterSensor3, "inputWaterSensor3"}
};
auto it = names.find(type);
return it != names.end() ? it->second : "unknown";
}
}
// 设备类id存储
static const std::unordered_set<int> underWterDevices = {
DeviceId::singleBeamSonar1,
DeviceId::singleBeamSonar2,
DeviceId::singleBeamSonar3,
DeviceId::singleBeamSonar4,
DeviceId::singleBeamSonar5,
DeviceId::singleBeamSonar6,
DeviceId::singleBeamSonar7,
DeviceId::singleBeamSonar8,
DeviceId::forwardSonar,
DeviceId::USBL,
DeviceId::DVL,
DeviceId::lowFreqSonar
};
static const std::unordered_set<int> noPowerDevices = {
DeviceId::depth1,
DeviceId::depth2,
DeviceId::depth3,
DeviceId::overDeep1,
DeviceId::overDeep2,
DeviceId::ctd1,
DeviceId::ctd2,
DeviceId::soundVeloc,
DeviceId::waterInject,
DeviceId::launchControler,
DeviceId::photoSensor,
DeviceId::electSensor,
DeviceId::commMast,
DeviceId::ntp,
DeviceId::bowThrow,
DeviceId::sternThrow,
DeviceId::ins,
DeviceId::inputWaterSensor1,
DeviceId::inputWaterSensor2,
DeviceId::inputWaterSensor3
};
namespace FaultEnable {
enum Type : bool {
BasicNavigationFault_1 = false, // 使能故障
BasicNavigationFault_2 = false,
BasicNavigationFault_3 = false,
BasicNavigationFault_4 = false,
BasicNavigationFault_5 = false,
BasicNavigationFault_6 = false,
BasicNavigationFault_7 = false,
BasicNavigationFault_8 = false,
BasicNavigationFault_9 = false,
BasicNavigationFault_10 = false,
ExtendedNavigationFault_1 = false, // 使能故障
ExtendedNavigationFault_2 = false,
ExtendedNavigationFault_3 = false,
ExtendedNavigationFault_4 = false,
ObstacleAvoidanceFault_1 = false, // 使能故障
ObstacleAvoidanceFault_2 = false,
ObstacleAvoidanceFault_3 = false,
BuoyancyControlFault_1 = false, // 使能故障
BuoyancyControlFault_2 = false,
BuoyancyControlFault_3 = false,
BuoyancyControlFault_4 = false,
CommunicationSystemFault_1 = false, // 使能故障
CommunicationSystemFault_2 = false,
CommunicationSystemFault_3 = false,
PayloadSystemFault_1 = false, // 使能故障
PayloadSystemFault_2 = false,
PayloadSystemFault_3 = false,
PayloadSystemFault_4 = false,
PayloadSystemFault_5 = false,
PayloadSystemFault_6 = false,
PayloadSystemFault_7 = false,
EmergencySystemFault_1 = false, // 使能故障
EmergencySystemFault_2 = false,
EmergencySystemFault_3 = false,
EmergencySystemFault_4 = false,
EmergencySystemFault_5 = false,
EmergencySystemFault_6 = false,
SensorSystemFault_1 = false, // 使能故障
SensorSystemFault_2 = false,
SensorSystemFault_3 = false,
};
}
#endif
File diff suppressed because it is too large Load Diff
-36
View File
@@ -1,36 +0,0 @@
#ifndef SQLITE_H
#define SQLITE_H
#include "sqlite3.h"
#include <string>
#include "../pmSysvariable.h"
class SQLite
{
private:
sqlite3* m_db;
public:
std::string m_dbPath;
SQLite(const std::string& dbName);
~SQLite();
//创建表
bool createTable();
bool createTable(const std::string& tableName, const std::string& columns);
// 插入数据函数
bool insertData(ccuState data);
bool insertData(disHighVolBusState data);
bool insertData(disHighAVolBusState data);
bool insertData(disLowMainBusState data);
bool insertData(disLowBusState data);
//插入指令数据
bool insertData(ccuColCmd cmd);
bool insertData(disHighVolBusCmd cmd);
bool insertData(disHighAVolBusCmd cmd);
bool insertData(disLowMainBusCmd cmd);
bool insertData(disLowBusCmd cmd);
};
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-719
View File
@@ -1,719 +0,0 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*xsnprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*xvsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
/* Version 3.20.0 and later */
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
sqlite3_stmt**,const char**);
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
sqlite3_stmt**,const void**);
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
void *(*value_pointer)(sqlite3_value*,const char*);
int (*vtab_nochange)(sqlite3_context*);
int (*value_nochange)(sqlite3_value*);
const char *(*vtab_collation)(sqlite3_index_info*,int);
/* Version 3.24.0 and later */
int (*keyword_count)(void);
int (*keyword_name)(int,const char**,int*);
int (*keyword_check)(const char*,int);
sqlite3_str *(*str_new)(sqlite3*);
char *(*str_finish)(sqlite3_str*);
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
void (*str_append)(sqlite3_str*, const char *zIn, int N);
void (*str_appendall)(sqlite3_str*, const char *zIn);
void (*str_appendchar)(sqlite3_str*, int N, char C);
void (*str_reset)(sqlite3_str*);
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
/* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
/* Version 3.30.0 and later */
int (*drop_modules)(sqlite3*,const char**);
/* Version 3.31.0 and later */
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
const char *(*uri_key)(const char*,int);
const char *(*filename_database)(const char*);
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
const char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
/* Version 3.36.1 and later */
sqlite3_int64 (*changes64)(sqlite3*);
sqlite3_int64 (*total_changes64)(sqlite3*);
/* Version 3.37.0 and later */
int (*autovacuum_pages)(sqlite3*,
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
void*, void(*)(void*));
/* Version 3.38.0 and later */
int (*error_offset)(sqlite3*);
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
int (*vtab_distinct)(sqlite3_index_info*);
int (*vtab_in)(sqlite3_index_info*,int,int);
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
/* Version 3.39.0 and later */
int (*deserialize)(sqlite3*,const char*,unsigned char*,
sqlite3_int64,sqlite3_int64,unsigned);
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
/* Version 3.40.0 and later */
int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
/* Version 3.44.0 and later */
void *(*get_clientdata)(sqlite3*,const char*);
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->xsnprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
/* Version 3.20.0 and later */
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
#define sqlite3_result_pointer sqlite3_api->result_pointer
#define sqlite3_value_pointer sqlite3_api->value_pointer
/* Version 3.22.0 and later */
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
#define sqlite3_value_nochange sqlite3_api->value_nochange
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
/* Version 3.24.0 and later */
#define sqlite3_keyword_count sqlite3_api->keyword_count
#define sqlite3_keyword_name sqlite3_api->keyword_name
#define sqlite3_keyword_check sqlite3_api->keyword_check
#define sqlite3_str_new sqlite3_api->str_new
#define sqlite3_str_finish sqlite3_api->str_finish
#define sqlite3_str_appendf sqlite3_api->str_appendf
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
#define sqlite3_str_append sqlite3_api->str_append
#define sqlite3_str_appendall sqlite3_api->str_appendall
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
#define sqlite3_str_reset sqlite3_api->str_reset
#define sqlite3_str_errcode sqlite3_api->str_errcode
#define sqlite3_str_length sqlite3_api->str_length
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
#define sqlite3_value_frombind sqlite3_api->value_frombind
/* Version 3.30.0 and later */
#define sqlite3_drop_modules sqlite3_api->drop_modules
/* Version 3.31.0 and later */
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
#define sqlite3_uri_key sqlite3_api->uri_key
#define sqlite3_filename_database sqlite3_api->filename_database
#define sqlite3_filename_journal sqlite3_api->filename_journal
#define sqlite3_filename_wal sqlite3_api->filename_wal
/* Version 3.32.0 and later */
#define sqlite3_create_filename sqlite3_api->create_filename
#define sqlite3_free_filename sqlite3_api->free_filename
#define sqlite3_database_file_object sqlite3_api->database_file_object
/* Version 3.34.0 and later */
#define sqlite3_txn_state sqlite3_api->txn_state
/* Version 3.36.1 and later */
#define sqlite3_changes64 sqlite3_api->changes64
#define sqlite3_total_changes64 sqlite3_api->total_changes64
/* Version 3.37.0 and later */
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
/* Version 3.38.0 and later */
#define sqlite3_error_offset sqlite3_api->error_offset
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
#define sqlite3_vtab_in sqlite3_api->vtab_in
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
/* Version 3.39.0 and later */
#ifndef SQLITE_OMIT_DESERIALIZE
#define sqlite3_deserialize sqlite3_api->deserialize
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
/* Version 3.40.0 and later */
#define sqlite3_value_encoding sqlite3_api->value_encoding
/* Version 3.41.0 and later */
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
/* Version 3.44.0 and later */
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */
-953
View File
@@ -1,953 +0,0 @@
#ifndef SYSTEMDATA_H
#define SYSTEMDATA_H
#include "pmSysvariable.h"
#include "sqlit3/SQLite.h"
#include <set>
#include <mutex>
#define DEBUFG
class SystemData
{
private:
SQLite* m_db;
std::set<unsigned int> sysFaultCodes;
mutable std::mutex sysFaultCodesMutex;
public:
/**
* @brief 向系统故障码集合中插入一个故障码
* @param faultCode 要插入的故障码
*/
void insertFaultCode(unsigned int faultCode) {
std::lock_guard<std::mutex> lock(sysFaultCodesMutex);
sysFaultCodes.insert(faultCode);
}
/**
* @brief 从系统故障码集合中消除一个故障码
* @param faultCode 要消除的故障码
*/
void eraseFaultCode(unsigned int faultCode) {
std::lock_guard<std::mutex> lock(sysFaultCodesMutex);
sysFaultCodes.erase(faultCode);
}
/**
* @brief 获取系统当前的故障等级
* @return uint8_t 故障等级,00H-无故障, 10H-一级, 20H-二级, 30H-三级, 40H-四级
*/
uint8_t getFaultCodeLevel() const {
uint8_t maxLevel = 0;
// 遍历 sysFaultCodes 集合中的每个故障码
for (auto code : sysFaultCodes) {
// 提取0xabcd中的b位 (右移8位后取低4位)
uint8_t level = (code >> 8) & 0xF;
if (level > maxLevel) {
maxLevel = level;
}
}
return maxLevel;
}
/**
* @brief 获取系统当前的故障码集合
* @return std::set<unsigned int>& 系统当前的故障码集合
*/
const std::set<unsigned int>& getFaultCode() const {
return sysFaultCodes;
}
//燃料电池系统参数
uint8_t fc_mode; // 燃料电池系统运行模式:00H-无效, 01H-调试, 02H-自动, 03H-补给
uint8_t fc_status; // 燃料电池状态:00H-无效, 01H-初始化, ..., 0DH-维护完成
uint8_t fc_fault_level; // 燃料电池故障等级:00H-无故障, 10H-一级, 20H-二级, 30H-三级, 40H-四级
//功率和时间参数
float output_power_limit; // 系统输出功率限制值,单位:W
float total_generation_time; // 系统累计发电时间,单位:0.1小时
float total_generation_power; // 系统累计发电总功率,单位:W
//故障信息
uint16_t fc_fault_level_1; // 一级故障位
uint16_t fc_fault_level_2; // 二级故障位
uint16_t fc_fault_level_3; // 三级故障位
uint16_t fc_fault_level_4; // 四级故障位
//燃料容量
float hydrogen_capacity; // 储氢罐剩余容量百分比,单位:%
float liquid_oxygen_capacity; // 液氧罐剩余容量百分比,单位:%
//温度压力参数
float palladium_temp; // 钯膜最高温度,单位:℃
float main_pipe_pressure; // 主水路压力,单位:0.01kPa
float aux_pipe_pressure; // 辅水路压力,单位:0.01kPa
//舱室环境参数
float cabin_pressure1; // 舱室1压力,单位:0.01kPa
float cabin_pressure2; // 舱室2压力,单位:0.01kPa
float cabin_temp1; // 舱室1温度,单位:0.01℃
float cabin_temp2; // 舱室2温度,单位:0.01℃
float cabin_humidity1; // 舱室1湿度,单位:0.01%RH
float cabin_humidity2; // 舱室2湿度,单位:0.01%RH
//火焰探测器
uint8_t flame_detector1; // 火焰探测器1状态:00H-无效, 01H-报警, 02H-故障
uint8_t flame_detector2; // 火焰探测器2状态:00H-无效, 01H-报警, 02H-故障
//气体浓度
float h2_concentration1; // 舱室H2浓度1,单位:0.01% LEL
float h2_concentration2; // 舱室H2浓度2,单位:0.01% LEL
float h2_concentration3; // 舱室H2浓度3,单位:0.01% LEL
float o2_concentration1; // 舱室O2浓度1,单位:0.01% LEL
float o2_concentration2; // 舱室O2浓度2,单位:0.01% LEL
float ch3oh_concentration1; // 舱室甲醇气体浓度1,单位:0.01% LEL
float ch3oh_concentration2; // 舱室甲醇气体浓度2,单位:0.01% LEL
//保留字段
float reserved1[10]; // 保留字段
float cabin_ox_concentration; // 仪表舱内氧气浓度,单位:0.1%
float cabin_temperature; // 仪表舱内温度,单位:0.1℃
float cabin_humidity; // 仪表舱内湿度,单位:0.1%RH
float cabin_pressure; // 仪表舱内大气压,单位:0.2kPa
//动力舱参数
float power_cabin_ox_concentration; // 动力舱内氧气浓度,单位:0.1%
float power_cabin_temperature; // 动力舱内温度,单位:0.1℃
float power_cabin_humidity; // 动力舱内湿度,单位:0.1%RH
float power_cabin_pressure; // 动力舱内大气压,单位:0.2kPa
uint8_t reserved2[2]; // 保留字段
//电池报警标志
uint8_t dyn_alarm_flag[6]; // 动力锂电池报警标志
uint8_t ins_alarm_flag[6]; // 仪表锂电池报警标志
//继电器状态
uint8_t ins_relay_status1; // 仪表电池继电器状态
uint8_t ins_relay_status2; // 仪表电池预充继电器和负极继电器状态
uint8_t dyn_relay_status1; // 动力电池正极与充电继电器状态
uint8_t dyn_relay_status2; // 动力电池预充与负极继电器状态
//仪表电池继电器状态
uint8_t ins_pos_relay_status; // 仪表电池正极继电器状态 (X0H:无效 X1H:接通 X2H:断开)
uint8_t ins_charge_relay_status; // 仪表电池充电继电器状态 (0XH:无效 1XH:接通 2XH:断开)
uint8_t ins_precharge_relay_status; // 仪表电池预充继电器状态 (X0H:无效 X1H:接通 X2H:断开)
uint8_t ins_neg_relay_status; // 仪表电池负极继电器状态 (0XH:无效 1XH:接通 2XH:断开)
//动力电池继电器状态
uint8_t dyn_pos_relay_status; // 动力电池正极继电器状态 (X0H:无效 X1H:接通 X2H:断开)
uint8_t dyn_charge_relay_status; // 动力电池充电继电器状态 (0XH:无效 1XH:接通 2XH:断开)
uint8_t dyn_precharge_relay_status; // 动力电池预充继电器状态 (X0H:无效 X1H:接通 X2H:断开)
uint8_t dyn_neg_relay_status; // 动力电池负极继电器状态 (0XH:无效 1XH:接通 2XH:断开)
//电池功率参数
float ins_max_discharge_power; // 仪表电池最大允许放电功率,单位:0.05kW
float dyn_max_discharge_power; // 动力电池最大允许放电功率,单位:0.05kW
//电池SOC
int ins_soc; // 仪表锂电池荷电状态SOC,范围:0~100
int dyn_soc; // 动力锂电池荷电状态SOC,范围:0~100
//电池能量
float ins_total_energy; // 仪表电池总能量,单位:0.1kWh
float dyn_total_energy; // 动力电池总能量,单位:0.1kWh
//电池输入功率
float ins_power_input; // 仪表电池输入功率,单位:0.1kW
float dyn_power_input; // 动力电池输入功率,单位:0.1kW
//电池充放电状态
uint8_t ins_charge_status; // 仪表电池充放电状态:00H-无效,10H-充电,20H-放电
uint8_t dyn_charge_status; // 动力电池充放电状态:00H-无效,10H-充电,20H-放电
//电池电压电流
float ins_voltage_link; // 仪表电池LINK端电压,单位:0.01V
float ins_voltage_pack; // 仪表电池PACK端电压,单位:0.01V
float ins_current; // 仪表电池电流,单位:0.01A
float ins_resistance_pos; // 仪表电池正极绝缘电阻,单位:kΩ
float ins_resistance_neg; // 仪表电池负极绝缘电阻,单位:kΩ
float dyn_voltage_link; // 动力电池LINK端电压,单位:0.01V
float dyn_voltage_pack; // 动力电池PACK端电压,单位:0.01V
float dyn_current; // 动力电池电流,单位:0.05A
float dyn_resistance_pos; // 动力电池正极绝缘电阻,单位:kΩ
float dyn_resistance_neg; // 动力电池负极绝缘电阻,单位:kΩ
//紧急状态
uint8_t ins_emergency_status; // 仪表电池紧急状态
uint8_t dyn_emergency_status; // 动力电池紧急状态
//保留字段
uint8_t reserved3[2]; // 保留字段
float ins_soc_threshold1; // 仪表电池1 SOC门限
float ins_soc_threshold2; // 仪表电池2 SOC门限
float ins_soc_threshold3; // 仪表电池3 SOC门限
float dyn_soc_threshold1; // 动力电池1 SOC门限
float dyn_soc_threshold2; // 动力电池2 SOC门限
float dyn_soc_threshold3; // 动力电池3 SOC门限
float ins_power_limit1; // 仪表电池1功率限制值
float ins_power_limit2; // 仪表电池2功率限制值
float dyn_power_limit1; // 动力电池1功率限制值
float dyn_power_limit2; // 动力电池2功率限制值
//设备在线状态
uint32_t device_online_flag1; // 设备在线状态标志1
uint32_t device_online_flag2; // 设备在线状态标志2
//其他锂电池状态
uint8_t dyn_charge_level; // 动力电池充电状态:01H-1级(不限), 02H-2级(限功率), 03H-3级(关闭)
uint8_t dyn_water_leak_status; // 动力漏水状态:0XH-正常, 1XH-异常漏水
uint8_t ins_water_leak_dcdc_status; // 仪表漏水状态与DCDC状态:低4位-漏水状态(0XH正常,1XH异常),高4位-DCDC状态(X0H正常,X1H故障)
uint8_t reserved4;
unsigned char water_leak_status_from_insbat; // 仪表漏水状态
unsigned char water_leak_status_from_dynbat; // 动力漏水状态
unsigned char dcdc_status_from_insbat; // 仪表DCDC状态
//保留字段
// uint32_t reserved4; // 保留字段
uint32_t heartbeat; // 主机心跳计数器
//锂电池故障字段
unsigned char powfaultCode;
unsigned char insfaultCode;
//应急电池
double emergency_battery1_voltage; // 应急电池1总电压 [V]
double emergency_battery1_current; // 应急电池1总电流 [A]
double emergency_battery1_max_temp; // 应急电池1最高温度 [°C]
uint16_t emergency_battery1_fault_word; // 应急电池1故障字
double emergency_battery2_voltage; // 应急电池2总电压 [V]
double emergency_battery2_current; // 应急电池2总电流 [A]
double emergency_battery2_max_temp; // 应急电池2最高温度 [°C]
uint16_t emergency_battery2_fault_word; // 应急电池2故障字
// MOOSD 状态
bool moosd_status;
//配电系统状态
//DC/DC状态字
struct DcdcStatus
{
// DC/DC5故障字低
union {
unsigned char lowFaultWord;
struct {
bool overTemperatureShutdown : 1; // Bit0: DC/DC模块过温关机
bool outputOverCurrent : 1; // Bit1: 输出过流
bool outputOverVoltage : 1; // Bit2: 输出过压
bool outputUnderVoltage : 1; // Bit3: 输出欠压
bool inputOverVoltage : 1; // Bit4: 输入过压
bool inputUnderVoltage : 1; // Bit5: 输入欠压
bool outputShortCircuit : 1; // Bit6: 输出短路保护
bool internalFault : 1; // Bit7: 内部故障
};
};
// DC/DC5故障字高
union {
unsigned char highFaultWord;
struct {
bool communicationFault : 1; // Bit0: 通讯故障报警
bool workingStatus : 1; // Bit1: 工作状态(1:启动, 0:关闭)
bool coolingPump1Fault : 1; // Bit2: 冷却泵1故障
bool coolingPump2Fault : 1; // Bit3: 冷却泵2故障
bool coolingWaterLeak : 1; // Bit4: 冷却水泄露
bool coolingPump1Running : 1; // Bit5: 冷却泵1运行状态
bool coolingPump2Running : 1; // Bit6: 冷却泵2运行状态
bool reserved : 1; // Bit7: 保留位
};
};
// 构造函数,初始化所有状态为正常
DcdcStatus() : lowFaultWord(0x00), highFaultWord(0x00) {}
} m_dcdcStatus;
//动力母线状态
struct POWER_BUS_STATUS
{
// 5: 燃料电池断路器 0 断开 1 闭合 2 故障
unsigned char fuelCellCircuitBreaker;
// 6: 动力锂电池断路器 0 断开 1 闭合 2 故障
unsigned char powerLithiumBatteryCircuitBreaker;
// 7: 推进电机断路器 0 断开 1 闭合 2 故障
unsigned char propulsionMotorCircuitBreaker;
// 8: 锂电池组仪表断路器 0 断开 1 闭合 2 故障
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker;
// 9: DC/DC5模块断路器 0 断开 1 闭合 2 故障
unsigned char dcDc5ModuleCircuitBreaker;
// 10: 艏部高压配电箱断路器 0 断开 1 闭合 2 故障
unsigned char bowHighVoltageDistributionBoxCircuitBreaker;
// 11: 艉部FT装置4/5断路器 0 断开 1 闭合 2 故障
unsigned char sternFTDevice45CircuitBreaker;
// 12: 艉舵开关1断路器 0 断开 1 闭合 2 故障
unsigned char sternRudderSwitch1CircuitBreaker;
// 13: 艉舵开关2断路器 0 断开 1 闭合 2 故障
unsigned char sternRudderSwitch2CircuitBreaker;
// 14: FB预留断路器 0 断开 1 闭合 2 故障
unsigned char fbReservedCircuitBreaker;
// 15: TYZ预留断路器 0 断开 1 闭合 2 故障
unsigned char tyzReservedCircuitBreaker;
// 16: 预留断路器 0 断开 1 闭合 2 故障
unsigned char reservedCircuitBreaker;
// 17: DC/DC5模块状态 0 未运行;1 运行;2 DC/DC模块过温关机;3 输出过流;4 输出过压;5 输出欠压;6 输入过压;7 输入欠压;8 输出短路保护;9 内部故障
unsigned char dcDcModuleStatus;
// 18:冷却系统状态 0 未运行 1 运行 2 冷却泵1故障 3 冷却泵2故障 4 冷却水泄露
unsigned char coolingSystemStatus;
// 19: 动力母排绝缘状态 0 正常 1 绝缘低
unsigned char powerBusInsulationStatus;
// 20: A汇流排绝缘状态 0 正常 1 绝缘低
unsigned char aBusInsulationStatus;
// 21: 仪表电源状态 0 正常 1 故障
unsigned char meterPowerLossSignal;
// 22: 应急电源状态 0 正常 1 故障
unsigned char emergencyPowerLossSignal;
// 22: dc/dc温度
float dcDcTemperature;
// 23: 动力汇流排电压
float powerBusVoltage;
// 24: DC/DC5模块电流
float dcDc5ModuleCurrent;
// 25: 动力汇流排电流
float powerBusCurrent;
// 26: A汇流排电压
float busbarAVoltage;
// 27: 推进电机控制箱电流
float propulsionMotorControlBoxCurrent;
// 28: A汇流排电流
float busbarACurrent;
// 29: 锂电池组(仪表)电流
float lithiumBatteryGroupMeterCurrent;
// 30: 艏部高压配电箱电流
float bowHighVoltageDistributionBoxCurrent;
// 31: 艉部FT装置4/5电流
float sternFTDevice45Current;
// 32: TYZ预留开关电流
float tyzReservedSwitchCurrent;
// 33: 艉舵开关1电流
float sternRudderSwitch1Current;
// 34: 预留电流
float reservedCurrent1; // 改为 reservedCurrent1 以避免重名
// 35: 艉舵开关2电流
float sternRudderSwitch2Current;
// 36: 预留电流2
float reservedCurrent2; // 改为 reservedCurrent2 以避免重名
// 37: FB预留电流
float fbReservedSwitchCurrent;
// 38: 预留电流3
float reservedCurrent3; // 改为 reservedCurrent3 以避免重名
// 39: 冷却水压力
float coolWaterPressure;
}power_bus_status;
struct POWER_A_BUS_STATUS
{
//动力汇流排状态
//5. 艏部FT装置1/2/3断路器 0 断开 1 闭合 2 故障
unsigned char bowFTDevice123CircuitBreaker;
//6. 启闭机构断路器 0 断开 1 闭合 2 故障
unsigned char actuatorCircuitBreaker;
//7. 桅杆舵机控制箱断路器 0 断开 1 闭合 2 故障
unsigned char mastSteeringGearControlBoxCircuitBreaker;
//8. XCZ断路器 0 断开 1 闭合 2 故障
unsigned char xczCircuitBreaker;
//9. 艏舵控制箱断路器 0 断开 1 闭合 2 故障
unsigned char bowRudderControlBoxCircuitBreaker;
//10. 敞水盖启动电缸断路器 0 断开 1 闭合 2 故障
unsigned char openWaterCoverStartCylinderCircuitBreaker;
//11. 仪表电源失电信号 0 正常 1 故障
unsigned char instrumentPowerFailureSignal;
//12. 应急电源失电信号 0 正常 1 故障
unsigned char emergencyPowerFailureSignal;
//13. 浸水报警 0 无 1 有
unsigned char waterIngressionAlarm;
//14. 汇流排B电压
float busbarBVoltage;
//15. 汇流排B电流
float busbarBCurrent;
//16. 艏部FT装置1/2/3电流
float bowFTDevice123Current;
//17. 启闭机构电流
float actuatorCurrent;
//18. 桅杆舵机控制箱电流
float mastSteeringGearControlBoxCurrent;
//19. XCZ电流
float xczCurrent;
//20. 艏舵控制箱电流
float bowRudderControlBoxCurrent;
//21. 敞水盖启动电缸电流
float openWaterCoverStartCylinderCurrent;
} power_a_bus_status;
//仪表主母线状态
struct INSTRUMENT_MAIN_BUS_STATUS
{
//5. 锂电池组(仪表)断路器 0 断开 1 闭合 2 故障
unsigned char lithiumBatteryGroupInstrumentCircuitBreaker;
//6. 艏部低压配电箱断路器 0 断开 1 闭合 2 故障
unsigned char bowLowVoltageDistributionBoxCircuitBreaker;
//7. UNIT4仪表DC48V断路器 0 断开 1 闭合 2 故障
unsigned char unit4InstrumentDC48VCircuitBreaker;
//8. DC-C1直流配电板断路器 0 断开 1 闭合 2 故障
unsigned char dcC1DCDistributionPanelCircuitBreaker;
//9. 预留断路器1 0 断开 1 闭合 2 故障
unsigned char reservedCircuitBreaker1; // 为了区分,添加了数字后缀
//10. 预留断路器2 0 断开 1 闭合 2 故障
unsigned char reservedCircuitBreaker2; // 为了区分,添加了数字后缀
//11. 应急锂电池组2断路器 0 断开 1 闭合 2 故障
unsigned char emergencyLithiumBatteryGroup2CircuitBreaker;
//12. 仪表电源失电信号 0 正常 1 故障
unsigned char instrumentPowerFailureSignal;
//13. 应急电源失电信号 0 正常 1 故障
unsigned char emergencyPowerFailureSignal;
//14. 仪表汇流排绝缘低 0 正常 1 绝缘低
unsigned char instrumentBusbarInsulationLow;
//15. 仪表母排电压
float instrumentBusbarVoltage;
//16. 艏部低压配电箱电流
float bowLowVoltageDistributionBoxCurrent;
//17. DC-C1(直流配电板1)仪表DC48V电流
float dcC1InstrumentDC48VCurrent;
//18. 预留电流
float reservedCurrent1; // 为了区分,添加了数字后缀
//19. 应急锂电池组2电流
float emergencyLithiumBatteryGroup2Current;
//20. 锂电池组(仪表)电流
float lithiumBatteryGroupInstrumentCurrent;
//21. UNIT4仪表DC48V电流
float unit4InstrumentDC48VCurrent;
//22. Ushort 预留电流
float reservedCurrent2; // 为了区分,添加了数字后缀
//23. 应急2汇流排电压
float emergency2BusbarVoltage;
//24. 复合能源管控系统应急DC48V电流
float compositeEnergyManagementSystemEmergencyDC48VCurrent;
//25. 燃料电池安全系统应急DC48V电流
float fuelCellSecuritySystemEmergencyDC48VCurrent;
//26. PLC控制电源电流
float plcControlPowerCurrent;
} instrument_main_bus_status;
//仪表汇流排状态
struct INSTRUMENT_BUS_STATUS
{
//5. UNIT1断路器 0 断开 1 闭合 2 故障
unsigned char unit1CircuitBreaker;
//6. UNIT2断路器 0 断开 1 闭合 2 故障
unsigned char unit2CircuitBreaker;
//7. UNIT3断路器 0 断开 1 闭合 2 故障
unsigned char unit3CircuitBreaker;
//8. UNIT5断路器 0 断开 1 闭合 2 故障
unsigned char unit5CircuitBreaker;
//9. 艏部PZ装置断路器 0 断开 1 闭合 2 故障
unsigned char bowPZDeviceCircuitBreaker;
//10. 预留断路器1 0 断开 1 闭合 2 故障
unsigned char reservedCircuitBreaker1; // 为了区分,添加了数字后缀
//11. 预留断路器2 0 断开 1 闭合 2 故障
unsigned char reservedCircuitBreaker2; // 为了区分,添加了数字后缀
//12. 应急锂电池组1断路器 0 断开 1 闭合 2 故障
unsigned char emergencyLithiumBatteryGroup1CircuitBreaker;
//13. 仪表电源失电信号 0 正常 1 故障
unsigned char powerFailureSignal;
//14. 应急电源失电信号 0 正常 1 故障
unsigned char emergencyPowerFailureSignal;
//15. 浸水报警 0 无 1 有
unsigned char waterIngressionAlarm;
//16. 仪表母排电压
float instrumentBusbarVoltage;
//17. 仪表母排电流
float instrumentBusbarCurrent;
//18. UNIT1电流
float unit1Current;
//19. UNIT2电流
float unit2Current;
//20. UNIT3电流
float unit3Current;
//21. UNIT5电流
float unit5Current;
//22. 艏部PZ装置电流
float bowPZDeviceCurrent;
//23. 应急锂电池组1电流
float emergencyLithiumBatteryGroup1Current;
//24. 应急1汇流排电压
float emergency1BusbarVoltage;
} instrument_bus_status;
struct COMMON_STATUS
{
//超时状态
bool isTimeout;
//最后更新时间
double lastUpdateTime;
};
struct DeviceCommonStatus
{
COMMON_STATUS ccu_status;
COMMON_STATUS dis_hv_c1;
COMMON_STATUS dis_hv_c2;
COMMON_STATUS dis_lv_c1;
COMMON_STATUS dis_lv_c2;
} device_common_status;
struct DeviceOnlineStatus
{
//在线状态
//0x00: 未上线
//0x01: 在线
//0x02: 上线后掉线
uint8_t ins_online_state; //仪表锂电在线标志位
uint8_t dyn_online_state; //动力锂电在线标志位
uint8_t fuel_online_state; //燃电锂电在线标志位
uint8_t high_voltage_online_state; //高压配电板在线标志位
uint8_t low_voltage_online_state; //低压配电板在线标志位
uint8_t composite_online_state; //复合管控主机在线标志位
uint8_t composite_backup_online_state; //复合管控备机在线标志位
} device_online_status;
enum LeakageStatus {
LEAKAGE_BOW_EMERGENCY = 0, // 艏部应急漏水 (0x01)
LEAKAGE_BOW_HIGH_VOLTAGE, // 艏高压漏水 (0x02)
LEAKAGE_BOW_LOW_VOLTAGE, // 艏低压漏水 (0x04)
LEAKAGE_FUEL_CELL, // 燃电舱漏水 (0x08)
LEAKAGE_LI_BATTERY, // 锂电池舱漏水 (0x10)
LEAKAGE_STERN_EMERGENCY, // 艉应急漏水 (0x20)
LEAKAGE_FUEL_CELL_CABIN, // 燃料电池舱漏水 (0x40)
LEAKAGE_BOW_EMERGENCY2 // 艏部应急漏水2 (0x80)
};
unsigned char leakageCode = 0;
bool leakStatus[8] = {false};
unsigned char generateLeakageCode() {
unsigned char code = 0;
if(leakStatus[LEAKAGE_BOW_EMERGENCY]) code |= 0x01;
if(leakStatus[LEAKAGE_BOW_HIGH_VOLTAGE]) code |= 0x02;
if(leakStatus[LEAKAGE_BOW_LOW_VOLTAGE]) code |= 0x04;
if(leakStatus[LEAKAGE_FUEL_CELL]) code |= 0x08;
if(leakStatus[LEAKAGE_LI_BATTERY]) code |= 0x10;
if(leakStatus[LEAKAGE_STERN_EMERGENCY]) code |= 0x20;
if(leakStatus[LEAKAGE_FUEL_CELL_CABIN]) code |= 0x40;
if(leakStatus[LEAKAGE_BOW_EMERGENCY2]) code |= 0x80;
return code;
}
public:
SystemData(){
//初始化变量全部为0
memset(this, 0, sizeof(SystemData));
disSysFaultCode.clear();
sysFaultCodes.clear();
};
~SystemData(){};
void updateCommonStatus(double time_out=10){
//更新时间
double currentTime = MOOSTime();
//判断是否超时
device_common_status.ccu_status.isTimeout = abs(currentTime - device_common_status.ccu_status.lastUpdateTime) > time_out;
device_common_status.dis_hv_c1.isTimeout = abs(currentTime - device_common_status.dis_hv_c1.lastUpdateTime) > time_out;
device_common_status.dis_hv_c2.isTimeout = abs(currentTime - device_common_status.dis_hv_c2.lastUpdateTime) > time_out;
device_common_status.dis_lv_c1.isTimeout = abs(currentTime - device_common_status.dis_lv_c1.lastUpdateTime) > time_out;
device_common_status.dis_lv_c2.isTimeout = abs(currentTime - device_common_status.dis_lv_c2.lastUpdateTime) > time_out;
};
void updateSystemData(ccuState data){
fc_mode = data.fc_mode;
fc_status = data.fc_status;
fc_fault_level = data.fc_fault_level;
//功率和时间参数
output_power_limit = data.output_power_limit;
total_generation_time = data.total_generation_time * 0.1;
total_generation_power = data.total_generation_power;
//故障信息
#ifdef DEBUG
fc_fault_level_1 = data.fault_level_1;
fc_fault_level_2 = data.fault_level_2;
fc_fault_level_3 = data.fault_level_3;
fc_fault_level_4 = data.fault_level_4;
#endif
//燃料容量
hydrogen_capacity = static_cast<int>(data.hydrogen_capacity);
liquid_oxygen_capacity = static_cast<int>(data.liquid_oxygen_capacity);
//温度压力参数
palladium_temp = data.palladium_temp;
main_pipe_pressure = data.main_pipe_pressure * 0.01;
aux_pipe_pressure = data.aux_pipe_pressure * 0.01;
//舱室环境参数
cabin_pressure1 = data.cabin_pressure1 * 0.01;
cabin_pressure2 = data.cabin_pressure2 * 0.01;
cabin_temp1 = data.cabin_temp1 * 0.01;
cabin_temp2 = data.cabin_temp2 * 0.01;
cabin_humidity1 = data.cabin_humidity1 * 0.01;
cabin_humidity2 = data.cabin_humidity2 * 0.01;
//火焰探测器
flame_detector1 = data.flame_detector1;
flame_detector2 = data.flame_detector2;
//气体浓度
h2_concentration1 = data.h2_concentration1 * 0.01;
h2_concentration2 = data.h2_concentration2 * 0.01;
h2_concentration3 = data.h2_concentration3 * 0.01;
o2_concentration1 = data.o2_concentration1 * 0.01;
o2_concentration2 = data.o2_concentration2 * 0.01;
ch3oh_concentration1 = data.ch3oh_concentration1 * 0.01;
ch3oh_concentration2 = data.ch3oh_concentration2 * 0.01;
//保留字段
for(int i=0; i<10; i++) {
reserved1[i] = data.reserved1[i];
}
//仪表舱参数
cabin_ox_concentration = data.cabin_ox_concentration * 0.1;
cabin_temperature = static_cast<int16_t>(data.cabin_temperature) * 0.1;
cabin_humidity = data.cabin_humidity * 0.1;
cabin_pressure = data.cabin_pressure * 0.2;
//动力舱参数
power_cabin_ox_concentration = data.power_cabin_ox_concentration * 0.1;
power_cabin_temperature = static_cast<int16_t>(data.power_cabin_temperature) * 0.1;
power_cabin_humidity = data.power_cabin_humidity * 0.1;
power_cabin_pressure = data.power_cabin_pressure * 0.2;
//电池报警标志
for(int i=0; i<6; i++) {
dyn_alarm_flag[i] = data.dyn_alarm_flag[i];
ins_alarm_flag[i] = data.ins_alarm_flag[i];
}
//继电器状态
ins_relay_status1 = data.ins_relay_status1;
ins_relay_status2 = data.ins_relay_status2;
dyn_relay_status1 = data.dyn_relay_status1;
dyn_relay_status2 = data.dyn_relay_status2;
//继电器状态
ins_pos_relay_status = (data.ins_relay_status1 ) & 0x0F; // 取高4位作为正极继电器状态
ins_charge_relay_status = ( data.ins_relay_status1 >> 4)& 0x0F; // 取低4位作为充电继电器状态
ins_precharge_relay_status = (data.ins_relay_status2 ) & 0x0F; // 取高4位作为预充继电器状态
ins_neg_relay_status = (data.ins_relay_status2 >> 4) & 0x0F; // 取低4位作为负极继电器状态
dyn_pos_relay_status = (data.dyn_relay_status1 ) & 0x0F; // 取高4位作为正极继电器状态
dyn_charge_relay_status = (data.dyn_relay_status1 >> 4) & 0x0F; // 取低4位作为充电继电器状态
dyn_precharge_relay_status = (data.dyn_relay_status2) & 0x0F; // 取高4位作为预充继电器状态
dyn_neg_relay_status = (data.dyn_relay_status2 >> 4) & 0x0F; // 取低4位作为负极继电器状态
//电池功率参数
ins_max_discharge_power = data.ins_max_discharge_power * 0.05;
dyn_max_discharge_power = data.dyn_max_discharge_power * 0.05;
//电池SOC
ins_soc = static_cast<int>(data.ins_soc);
dyn_soc = static_cast<int>(data.dyn_soc);
//电池能量
ins_total_energy = data.ins_total_energy * 0.1;
dyn_total_energy = data.dyn_total_energy * 0.1;
//电池输入功率
ins_power_input = data.ins_power_input ;
dyn_power_input = data.dyn_power_input ;
//电池充放电状态
ins_charge_status = data.ins_charge_status;
dyn_charge_status = data.dyn_charge_status;
//电池电压电流
ins_voltage_link = data.ins_voltage_link * 0.1;
ins_voltage_pack = data.ins_voltage_pack * 0.1;
ins_current = static_cast<int16_t>(data.ins_current) * 0.05;
ins_resistance_pos = data.ins_resistance_pos;
ins_resistance_neg = data.ins_resistance_neg;
dyn_voltage_link = data.dyn_voltage_link * 0.1;
dyn_voltage_pack = data.dyn_voltage_pack * 0.1;
dyn_current = static_cast<int16_t>(data.dyn_current) * 0.05;
dyn_resistance_pos = data.dyn_resistance_pos;
dyn_resistance_neg = data.dyn_resistance_neg;
//紧急状态
ins_emergency_status = data.ins_emergency_status;
dyn_emergency_status = data.dyn_emergency_status;
//保留字段
for(int i=0; i<2; i++) {
reserved2[i] = data.reserved2[i];
}
//SOC门限值
ins_soc_threshold1 = data.ins_soc_threshold1;
ins_soc_threshold2 = data.ins_soc_threshold2;
ins_soc_threshold3 = data.ins_soc_threshold3;
//功率限制值
ins_power_limit1 = data.ins_power_limit1;
ins_power_limit2 = data.ins_power_limit2;
//设备在线状态
device_online_flag1 = data.device_online_flag1;
device_online_flag2 = data.device_online_flag2;
//更新设备在线状态
device_online_status.ins_online_state = (data.device_online_flag1 >> 0) & 0xFF; // byte0:仪表锂电在线标志位
device_online_status.dyn_online_state = (data.device_online_flag1 >> 8) & 0xFF; // byte1:动力锂电在线标志位
device_online_status.fuel_online_state = (data.device_online_flag1 >> 16) & 0xFF; // byte2:燃电锂电在线标志位
device_online_status.high_voltage_online_state = (data.device_online_flag1 >> 24) & 0xFF; // byte3:高压配电板在线标志位
device_online_status.low_voltage_online_state = (data.device_online_flag2 >> 0) & 0xFF; // byte0:低压配电板在线标志位
device_online_status.composite_online_state = (data.device_online_flag2 >> 8) & 0xFF; // byte1:复合管控主机在线标志位
device_online_status.composite_backup_online_state = (data.device_online_flag2 >> 16) & 0xFF; // byte2:复合管控备机在线标志位
// 更新锂电池其他状态
dyn_water_leak_status = data.dyn_water_leak_status;
ins_water_leak_dcdc_status = data.ins_water_leak_dcdc_status;
dyn_charge_level = data.dyn_charge_level; //01H:1级(不限)02H:2级(限功率)03H:3级(关闭)
water_leak_status_from_insbat = ( data.ins_water_leak_dcdc_status >> 4) & 0x0F; // 00 正常,01 异常漏水
water_leak_status_from_dynbat = (data.dyn_water_leak_status >> 4) & 0x0F; // 00 正常,01 异常漏水
dcdc_status_from_insbat = (data.ins_water_leak_dcdc_status) & 0x0F; // 00 正常,01 故障
//更新漏水状态
if(water_leak_status_from_insbat == 0x01)
{
leakStatus[LEAKAGE_LI_BATTERY] = true;
}
else
{
leakStatus[LEAKAGE_LI_BATTERY] = false;
}
if(water_leak_status_from_dynbat == 0x01)
{
leakStatus[LEAKAGE_LI_BATTERY] = true;
}
else
{
leakStatus[LEAKAGE_LI_BATTERY] = false;
}
//应急电池
emergency_battery1_voltage = data.emergency_battery1_voltage * 0.1;
emergency_battery1_current = data.emergency_battery1_current * 0.05;
emergency_battery1_max_temp = data.emergency_battery1_max_temp * 0.1;
emergency_battery1_fault_word = data.emergency_battery1_fault_word;
emergency_battery2_voltage = data.emergency_battery2_voltage * 0.1;
emergency_battery2_current = data.emergency_battery2_current * 0.05;
emergency_battery2_max_temp = data.emergency_battery2_max_temp * 0.1;
emergency_battery2_fault_word = data.emergency_battery2_fault_word;
//心跳计数器
heartbeat = data.heartbeat;
}
std::set<unsigned int> disSysFaultCode;
mutable std::mutex faultCodeMutex;
void updateBreakerFault(unsigned char& breakerStatus, unsigned char rawStatus, unsigned int faultCode)
{
std::lock_guard<std::mutex> lock(faultCodeMutex);
unsigned char newStatus = rawStatus == 0x55 ? 1 : (rawStatus == 0xAA ? 0 : 2);
breakerStatus = newStatus; // 保证状态更新也是原子的
if(newStatus == 2) {
disSysFaultCode.insert(faultCode);
} else {
disSysFaultCode.erase(faultCode);
}
}
void updateBreakerFault(unsigned char& breakerStatus, unsigned char rawStatus)
{
std::lock_guard<std::mutex> lock(faultCodeMutex);
unsigned char newStatus = rawStatus == 0x55 ? 1 : (rawStatus == 0xAA ? 0 : 2);
breakerStatus = newStatus; // 保证状态更新也是原子的
}
void updateSystemData(disHighVolBusState data){
updateBreakerFault(power_bus_status.fuelCellCircuitBreaker, data.fuelCellCircuitBreaker, 1);
updateBreakerFault(power_bus_status.powerLithiumBatteryCircuitBreaker, data.powerLithiumBatteryCircuitBreaker, 2);
updateBreakerFault(power_bus_status.propulsionMotorCircuitBreaker, data.propulsionMotorCircuitBreaker, 3);
updateBreakerFault(power_bus_status.lithiumBatteryGroupInstrumentCircuitBreaker, data.lithiumBatteryGroupInstrumentCircuitBreaker, 4);
updateBreakerFault(power_bus_status.dcDc5ModuleCircuitBreaker, data.dcDc5ModuleCircuitBreaker, 5);
updateBreakerFault(power_bus_status.bowHighVoltageDistributionBoxCircuitBreaker, data.bowHighVoltageDistributionBoxCircuitBreaker, 6);
updateBreakerFault(power_bus_status.sternFTDevice45CircuitBreaker, data.sternFTDevice45CircuitBreaker, 7);
updateBreakerFault(power_bus_status.sternRudderSwitch1CircuitBreaker, data.sternRudderSwitch1CircuitBreaker, 8);
updateBreakerFault(power_bus_status.sternRudderSwitch2CircuitBreaker, data.sternRudderSwitch2CircuitBreaker, 9);
updateBreakerFault(power_bus_status.fbReservedCircuitBreaker, data.fbReservedCircuitBreaker);
updateBreakerFault(power_bus_status.tyzReservedCircuitBreaker, data.tyzReservedCircuitBreaker);
updateBreakerFault(power_bus_status.reservedCircuitBreaker, data.reservedCircuitBreaker);
//模块状态
power_bus_status.dcDcModuleStatus = data.dcDcFaultWord1;
power_bus_status.coolingSystemStatus = data.dcDcFaultWord2;
m_dcdcStatus.lowFaultWord = data.dcDcFaultWord1;
m_dcdcStatus.highFaultWord = data.dcDcFaultWord2;
//绝缘状态
power_bus_status.powerBusInsulationStatus = data.powerBusInsulationStatus == 0x55 ? 0 : 1;
// if(power_bus_status.powerBusInsulationStatus == 1) disSysFaultCode.insert(34); else disSysFaultCode.erase(34);
power_bus_status.aBusInsulationStatus = data.aBusInsulationStatus == 0x55 ? 0 : 1;
// if(power_bus_status.aBusInsulationStatus == 1) disSysFaultCode.insert(35); else disSysFaultCode.erase(35);
//电源状态
power_bus_status.meterPowerLossSignal = data.meterPowerLossSignal == 0x55 ? 0 : 1;
// if(power_bus_status.meterPowerLossSignal == 1) disSysFaultCode.insert(36); else disSysFaultCode.erase(36);
power_bus_status.emergencyPowerLossSignal = data.emergencyPowerLossSignal == 0x55 ? 0 : 1;
// if(power_bus_status.emergencyPowerLossSignal == 1) disSysFaultCode.insert(37); else disSysFaultCode.erase(37);
{
std::lock_guard<std::mutex> lock(faultCodeMutex);
if(power_bus_status.emergencyPowerLossSignal == 1) disSysFaultCode.insert(37); else disSysFaultCode.erase(37);
if(power_bus_status.meterPowerLossSignal == 1) disSysFaultCode.insert(36); else disSysFaultCode.erase(36);
if(power_bus_status.aBusInsulationStatus == 1) disSysFaultCode.insert(35); else disSysFaultCode.erase(35);
if(power_bus_status.powerBusInsulationStatus == 1) disSysFaultCode.insert(34); else disSysFaultCode.erase(34);
}
//温度
power_bus_status.dcDcTemperature = data.dcDcTemperature-40;
//电压电流参数
power_bus_status.powerBusVoltage = data.powerBusVoltage * 0.1;
power_bus_status.dcDc5ModuleCurrent = data.dcDc5ModuleCurrent * 0.1;
power_bus_status.powerBusCurrent = data.powerBusCurrent * 0.1;
power_bus_status.busbarAVoltage = data.busbarAVoltage * 0.1;
power_bus_status.propulsionMotorControlBoxCurrent = data.propulsionMotorControlBoxCurrent * 0.1;
power_bus_status.busbarACurrent = data.busbarACurrent * 0.1;
power_bus_status.lithiumBatteryGroupMeterCurrent = data.lithiumBatteryGroupMeterCurrent * 0.1;
power_bus_status.bowHighVoltageDistributionBoxCurrent = data.bowHighVoltageDistributionBoxCurrent * 0.1;
power_bus_status.sternFTDevice45Current = data.sternFTDevice45Current * 0.1;
power_bus_status.tyzReservedSwitchCurrent = data.tyzReservedSwitchCurrent * 0.1;
power_bus_status.sternRudderSwitch1Current = data.sternRudderSwitch1Current * 0.1;
power_bus_status.reservedCurrent1 = data.reservedCurrent1 * 0.1;
power_bus_status.sternRudderSwitch2Current = data.sternRudderSwitch2Current * 0.1;
power_bus_status.reservedCurrent2 = data.reservedCurrent2 * 0.1;
power_bus_status.fbReservedSwitchCurrent = data.fbReservedSwitchCurrent * 0.1;
power_bus_status.reservedCurrent3 = data.reservedCurrent3 * 0.1;
//冷却水压力
power_bus_status.coolWaterPressure = data.coolWaterPressure * 0.1;
};
void updateSystemData(disHighAVolBusState data){
//断路器状态 (0x55 1 0xAA 0 0x5A 2)
updateBreakerFault(power_a_bus_status.bowFTDevice123CircuitBreaker, data.bowFTDevice123CircuitBreaker, 13);
updateBreakerFault(power_a_bus_status.actuatorCircuitBreaker, data.actuatorCircuitBreaker, 14);
updateBreakerFault(power_a_bus_status.mastSteeringGearControlBoxCircuitBreaker, data.mastSteeringGearControlBoxCircuitBreaker, 15);
updateBreakerFault(power_a_bus_status.xczCircuitBreaker, data.xczCircuitBreaker, 16);
updateBreakerFault(power_a_bus_status.bowRudderControlBoxCircuitBreaker, data.bowRudderControlBoxCircuitBreaker, 17);
updateBreakerFault(power_a_bus_status.openWaterCoverStartCylinderCircuitBreaker, data.openWaterCoverStartCylinderCircuitBreaker, 18);
//电源状态
power_a_bus_status.instrumentPowerFailureSignal = data.instrumentPowerFailureSignal == 0x55 ? 0 : 1;
// if(power_a_bus_status.instrumentPowerFailureSignal == 1) disSysFaultCode.insert(38); else disSysFaultCode.erase(38);
power_a_bus_status.emergencyPowerFailureSignal = data.emergencyPowerFailureSignal == 0x55 ? 0 : 1;
// if(power_a_bus_status.emergencyPowerFailureSignal == 1) disSysFaultCode.insert(39); else disSysFaultCode.erase(39);
{
std::lock_guard<std::mutex> lock(faultCodeMutex);
if(power_a_bus_status.emergencyPowerFailureSignal == 1) disSysFaultCode.insert(39); else disSysFaultCode.erase(39);
if(power_a_bus_status.instrumentPowerFailureSignal == 1) disSysFaultCode.insert(38); else disSysFaultCode.erase(38);
}
//浸水报警
power_a_bus_status.waterIngressionAlarm = data.waterIngressionAlarm == 0x55 ? 0 : 1;
//更新漏水状态
if(power_a_bus_status.waterIngressionAlarm == 1)
{
leakStatus[LEAKAGE_BOW_HIGH_VOLTAGE] = true;
}
else
{
leakStatus[LEAKAGE_BOW_HIGH_VOLTAGE] = false;
}
//电压电流参数
power_a_bus_status.busbarBVoltage = data.busbarBVoltage * 0.1;
power_a_bus_status.busbarBCurrent = data.busbarBCurrent * 0.1;
power_a_bus_status.bowFTDevice123Current = data.bowFTDevice123Current * 0.1;
power_a_bus_status.actuatorCurrent = data.actuatorCurrent * 0.1;
power_a_bus_status.mastSteeringGearControlBoxCurrent = data.mastSteeringGearControlBoxCurrent * 0.1;
power_a_bus_status.xczCurrent = data.xczCurrent * 0.1;
power_a_bus_status.bowRudderControlBoxCurrent = data.bowRudderControlBoxCurrent * 0.1;
power_a_bus_status.openWaterCoverStartCylinderCurrent = data.openWaterCoverStartCylinderCurrent * 0.1;
};
void updateSystemData(disLowMainBusState data){
//断路器状态 (0x55 1 0xAA 0 0x5A 2)
updateBreakerFault(instrument_main_bus_status.lithiumBatteryGroupInstrumentCircuitBreaker, data.lithiumBatteryGroupInstrumentCircuitBreaker, 19);
updateBreakerFault(instrument_main_bus_status.bowLowVoltageDistributionBoxCircuitBreaker,data.bowLowVoltageDistributionBoxCircuitBreaker, 20);
updateBreakerFault(instrument_main_bus_status.unit4InstrumentDC48VCircuitBreaker,data.unit4InstrumentDC48VCircuitBreaker, 21);
updateBreakerFault(instrument_main_bus_status.dcC1DCDistributionPanelCircuitBreaker, data.dcC1DCDistributionPanelCircuitBreaker, 22);
updateBreakerFault(instrument_main_bus_status.reservedCircuitBreaker1, data.reservedCircuitBreaker1);
updateBreakerFault(instrument_main_bus_status.reservedCircuitBreaker2, data.reservedCircuitBreaker2);
updateBreakerFault(instrument_main_bus_status.emergencyLithiumBatteryGroup2CircuitBreaker, data.emergencyLithiumBatteryGroup2CircuitBreaker, 25);
//电源状态
instrument_main_bus_status.instrumentPowerFailureSignal = data.instrumentPowerFailureSignal == 0x55 ? 0 : 1;
// if(instrument_main_bus_status.instrumentPowerFailureSignal == 1) disSysFaultCode.insert(40); else disSysFaultCode.erase(41);
instrument_main_bus_status.emergencyPowerFailureSignal = data.emergencyPowerFailureSignal == 0x55 ? 0 : 1;
// if(instrument_main_bus_status.emergencyPowerFailureSignal == 1) disSysFaultCode.insert(41); else disSysFaultCode.erase(42);
//绝缘状态
instrument_main_bus_status.instrumentBusbarInsulationLow = data.instrumentBusbarInsulationLow == 0x55 ? 0 : 1;
// if(instrument_main_bus_status.instrumentBusbarInsulationLow == 1) disSysFaultCode.insert(42); else disSysFaultCode.erase(43);
{
std::lock_guard<std::mutex> lock(faultCodeMutex);
if(instrument_main_bus_status.instrumentPowerFailureSignal == 1) disSysFaultCode.insert(41); else disSysFaultCode.erase(41);
if(instrument_main_bus_status.emergencyPowerFailureSignal == 1) disSysFaultCode.insert(42); else disSysFaultCode.erase(42);
if(instrument_main_bus_status.instrumentBusbarInsulationLow == 1) disSysFaultCode.insert(43); else disSysFaultCode.erase(43);
}
//电压电流参数
instrument_main_bus_status.instrumentBusbarVoltage = data.instrumentBusbarVoltage * 0.1;
instrument_main_bus_status.bowLowVoltageDistributionBoxCurrent = data.bowLowVoltageDistributionBoxCurrent * 0.1;
instrument_main_bus_status.dcC1InstrumentDC48VCurrent = data.dcC1InstrumentDC48VCurrent * 0.1;
instrument_main_bus_status.reservedCurrent1 = data.reservedCurrent1 * 0.1;
instrument_main_bus_status.emergencyLithiumBatteryGroup2Current = data.emergencyLithiumBatteryGroup2Current * 0.1;
instrument_main_bus_status.lithiumBatteryGroupInstrumentCurrent = data.lithiumBatteryGroupInstrumentCurrent * 0.1;
instrument_main_bus_status.unit4InstrumentDC48VCurrent = data.unit4InstrumentDC48VCurrent * 0.1;
instrument_main_bus_status.reservedCurrent2 = data.reservedCurrent2 * 0.1;
instrument_main_bus_status.emergency2BusbarVoltage = data.emergency2BusbarVoltage * 0.1;
instrument_main_bus_status.compositeEnergyManagementSystemEmergencyDC48VCurrent = data.compositeEnergyManagementSystemEmergencyDC48VCurrent * 0.1;
instrument_main_bus_status.fuelCellSecuritySystemEmergencyDC48VCurrent = data.fuelCellSecuritySystemEmergencyDC48VCurrent * 0.1;
instrument_main_bus_status.plcControlPowerCurrent = data.plcControlPowerCurrent * 0.1;
}
void updateSystemData(disLowBusState data){
//断路器状态 (0x55 1 0xAA 0 0x5A 2)
updateBreakerFault(instrument_bus_status.unit1CircuitBreaker, data.unit1CircuitBreaker, 26);
updateBreakerFault(instrument_bus_status.unit2CircuitBreaker, data.unit2CircuitBreaker, 27);
updateBreakerFault(instrument_bus_status.unit3CircuitBreaker, data.unit3CircuitBreaker, 28);
updateBreakerFault(instrument_bus_status.unit5CircuitBreaker, data.unit5CircuitBreaker, 29);
updateBreakerFault(instrument_bus_status.bowPZDeviceCircuitBreaker, data.bowPZDeviceCircuitBreaker);
updateBreakerFault(instrument_bus_status.reservedCircuitBreaker1, data.reservedCircuitBreaker1);
updateBreakerFault(instrument_bus_status.reservedCircuitBreaker2, data.reservedCircuitBreaker2);
updateBreakerFault(instrument_bus_status.emergencyLithiumBatteryGroup1CircuitBreaker, data.emergencyLithiumBatteryGroup1CircuitBreaker, 33);
//电源状态
instrument_bus_status.powerFailureSignal = data.powerFailureSignal == 0x55 ? 0 : 1;
// if(instrument_bus_status.powerFailureSignal == 1) disSysFaultCode.insert(44); else disSysFaultCode.erase(44);
instrument_bus_status.emergencyPowerFailureSignal = data.emergencyPowerFailureSignal == 0x55 ? 0 : 1;
// if(instrument_bus_status.emergencyPowerFailureSignal == 1) disSysFaultCode.insert(45); else disSysFaultCode.erase(45);
{
std::lock_guard<std::mutex> lock(faultCodeMutex);
if(instrument_bus_status.emergencyPowerFailureSignal == 1) disSysFaultCode.insert(45); else disSysFaultCode.erase(45);
if(instrument_bus_status.powerFailureSignal == 1) disSysFaultCode.insert(44); else disSysFaultCode.erase(44);
}
//浸水报警
instrument_bus_status.waterIngressionAlarm = data.waterIngressionAlarm == 0x55 ? 0 : 1;
//更新漏水状态
if(instrument_bus_status.waterIngressionAlarm == 1)
{
leakStatus[LEAKAGE_BOW_LOW_VOLTAGE] = true;
}
else
{
leakStatus[LEAKAGE_BOW_LOW_VOLTAGE] = false;
}
//电压电流参数
instrument_bus_status.instrumentBusbarVoltage = data.instrumentBusbarVoltage * 0.1;
instrument_bus_status.instrumentBusbarCurrent = data.instrumentBusbarCurrent * 0.1;
instrument_bus_status.unit1Current = data.unit1Current * 0.1;
instrument_bus_status.unit2Current = data.unit2Current * 0.1;
instrument_bus_status.unit3Current = data.unit3Current * 0.1;
instrument_bus_status.unit5Current = data.unit5Current * 0.1;
instrument_bus_status.bowPZDeviceCurrent = data.bowPZDeviceCurrent * 0.1;
instrument_bus_status.emergencyLithiumBatteryGroup1Current = data.emergencyLithiumBatteryGroup1Current * 0.1;
instrument_bus_status.emergency1BusbarVoltage = data.emergency1BusbarVoltage * 0.1;
}
};
#endif
-139
View File
@@ -1,139 +0,0 @@
#ifndef CCUUDPMSG__H
#define CCUUDPMSG__H
#include "../pmSysvariable.h"
#define CCU_UDPMSG_START1 0x20
#define CCU_UDPMSG_START2 0x20
#define DIS_MSG_HEAD1 0x40
#define DIS_MSG_HEAD2 0x40
#define CCU_CMD_ID 0x0040
#define CCU_SET_PARM_ID 0x0041
#define CCU_STATE_FB_ID 0x0051
#define CCU_SET_PARM_FB_ID 0x0040
#define DIS_HVBUS_FB_ID 0x0005
#define DIS_HVBUSA_FB_ID 0x0006
#define DIS_HVBUSB_FB_ID 0x0007
#define DIS_LVBUS_FB_ID 0x0008
#define DIS_HVBUS_CMD_ID 0x0001
#define DIS_HVBUSA_CMD_ID 0x0002
#define DIS_HVBUSB_CMD_ID 0x0003
#define DIS_LVBUS_CMD_ID 0x0004
#define DIS_BREAKER_ON 0x55
#define DIS_BREAKER_OFF 0xAA
#define DIS_FAULTOFF 0x5A
typedef unsigned int Checksum;
typedef unsigned char cChecksum;
#pragma pack (1)
typedef struct {
unsigned char start1;
unsigned char start2;
unsigned short id;
unsigned short length;
} m_header;
typedef struct {
unsigned short year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char minute;
unsigned char second;
unsigned char milliseconds; //每10毫秒
} m_date;
// 复合管控器操作指令
typedef struct {
m_header header;
m_date date;
ccuColCmd cmd;
Checksum checkCode;
} msg_CcuColCmdMsg;
// 复合管控器参数设定指令
typedef struct {
m_header header;
ccuSetParmCmd cmd;
Checksum checkCode;
} msg_CcuSetParmMsg;
// 配电指令
typedef struct
{
m_header header;
disHighVolBusCmd data;
cChecksum checkCode;
}msg_disHVBCmd;
typedef struct
{
m_header header;
disHighAVolBusCmd data;
cChecksum checkCode;
}msg_disHVBACmd;
typedef struct msg_disHVBBCmd
{
m_header header;
disHighBVolBusCmd data;
cChecksum checkCode;
}msg_disLVMBCmd;
typedef struct
{
m_header header;
disLowBusCmd data;
cChecksum checkCode;
}msg_disLVBCmd;
// 复合管控参数设定反馈指令
typedef struct {
m_header header;
unsigned char flag;
unsigned char failureCode;
Checksum checkCode;
} msg_CcuSetParmFbMsg;
// 复合管控状态反馈
typedef struct {
m_header header;
ccuState data;
Checksum checkCode;
} msg_CcuStateFbMsg;
//高压母线配电反馈
typedef struct {
m_header header;
disHighVolBusState data;
cChecksum checkCode;
} msg_disHighVolBusFbMsg;
// 高压母线A配电反馈
typedef struct {
m_header header;
disHighAVolBusState data;
cChecksum checkCode;
} msg_disHighAVolBusFbMsg;
// 仪表母线配电反馈
typedef struct {
m_header header;
disLowMainBusState data;
cChecksum checkCode;
} msg_disLowMainBusFbMsg;
//仪表汇流排配电反馈
typedef struct {
m_header header;
disLowBusState data;
cChecksum checkCode;
} msg_disLowBusFbMsg;
#pragma pack ()
#endif
-627
View File
@@ -1,627 +0,0 @@
#include "udpComm.h"
#include <iostream>
#include <memory>
udpComm::udpComm()
{
udpScoket = nullptr;
ccuHost = "";
ccuPort = 0;
}
udpComm::~udpComm() {
if (udpScoket) {
delete udpScoket;
udpScoket = nullptr;
}
}
Checksum udpComm::calculateChecksum(const unsigned char* data, int size)
{
Checksum checksum = 0;
int size_data = size - sizeof(Checksum);
for (int i = 0; i < size_data; i++) {
checksum += data[i];
}
return checksum;
}
Checksum udpComm::calculateChecksum(const unsigned char* data, int dataSize, int checkSize)
{
Checksum checksum = 0;
int size_data = dataSize - checkSize;
for (int i = 0; i < size_data; i++) {
checksum += data[i];
}
return checksum;
}
bool udpComm::getMsgId(char *m,unsigned short &id)
{
m_header h;
h = *reinterpret_cast<m_header *>(m);
// 判断消息是否有效
if (h.start1== DIS_MSG_HEAD1 && h.start2==DIS_MSG_HEAD2)
{
id = h.id;
return true;
}
else if (h.start1==CCU_UDPMSG_START1 && h.start2==CCU_UDPMSG_START2)
{
id = h.id;
return true;
}
else
// sError.push_back("Msg Header errar");
// LOG_F(ERROR, "Msg Header errar start1: %d, start2: %d", h.start1, h.start2);
return false;
}
bool udpComm::getMsgFromBuff(char *m, msg_CcuStateFbMsg &s) {
Checksum sum;
msg_CcuStateFbMsg *p;
p = reinterpret_cast<msg_CcuStateFbMsg *>(m);
sum = calculateChecksum((unsigned char*)m, sizeof(msg_CcuStateFbMsg));
if (sum != p->checkCode) {
sError.push_back("CcuStat Msg Check error");
return false;
}
s = *p;
return true;
}
bool udpComm::getMsgFromBuff(char *m, msg_CcuSetParmFbMsg &s)
{
Checksum sum;
msg_CcuSetParmFbMsg *p;
p = reinterpret_cast<msg_CcuSetParmFbMsg *>(m);
sum = calculateChecksum((unsigned char*)m, sizeof(msg_CcuSetParmFbMsg));
if (sum!= p->checkCode) {
sError.push_back("CcuSetParm Msg Check error");
return false;
}
s = *p;
return true;
}
bool udpComm::getMsgFromBuff(char *m, msg_disHighVolBusFbMsg &s)
{
cChecksum sum;
msg_disHighVolBusFbMsg *p;
p = reinterpret_cast<msg_disHighVolBusFbMsg *>(m);
sum = calculateChecksum((unsigned char*)m, sizeof(msg_disHighVolBusFbMsg), sizeof(cChecksum)) & 0xFF;
if (sum != p->checkCode) {
sError.push_back("DisSys HVBus Msg Check error");
LOG_F(ERROR, "DisSys HVBus Msg Check error sum: %d, source checkCode: %d", sum, p->checkCode);
return false;
}
s = *p;
// cout << s.data.busbarAVoltage << endl;
return true;
}
bool udpComm::getMsgFromBuff(char *m, msg_disHighAVolBusFbMsg &s)
{
cChecksum sum;
msg_disHighAVolBusFbMsg *p;
p = reinterpret_cast<msg_disHighAVolBusFbMsg *>(m);
sum = calculateChecksum((unsigned char*)m, sizeof(msg_disHighAVolBusFbMsg), sizeof(cChecksum)) & 0xFF;
if (sum!= p->checkCode) {
sError.push_back("DisSys HVABus Msg Check error");
LOG_F(ERROR, "DisSys HVABus Msg Check error sum: %d, source checkCode: %d", sum, p->checkCode);
return false;
}
s = *p;
return true;
}
bool udpComm::getMsgFromBuff(char *m, msg_disLowMainBusFbMsg &s)
{
cChecksum sum;
msg_disLowMainBusFbMsg *p;
p = reinterpret_cast<msg_disLowMainBusFbMsg *>(m);
sum = calculateChecksum((unsigned char*)m, sizeof(msg_disLowMainBusFbMsg), sizeof(cChecksum)) & 0xFF;
if (sum!= p->checkCode) {
sError.push_back("DisSys HVBBus Msg Check error");
LOG_F(ERROR, "DisSys HVBBus Msg Check error sum: %d, source checkCode: %d", sum, p->checkCode);
return false;
}
s = *p;
return true;
}
bool udpComm::getMsgFromBuff(char *m, msg_disLowBusFbMsg &s)
{
cChecksum sum;
msg_disLowBusFbMsg *p;
p = reinterpret_cast<msg_disLowBusFbMsg *>(m);
sum = calculateChecksum((unsigned char*)m, sizeof(msg_disLowBusFbMsg), sizeof(cChecksum)) & 0xFF;
if (sum!= p->checkCode) {
sError.push_back("DisSys LVBus Msg Check error");
LOG_F(ERROR, "DisSys LVBus Msg Check error sum: %d, source checkCode: %d", sum, p->checkCode);
return false;
}
s = *p;
return true;
}
bool udpComm::pushMsgToQueue(unsigned char *m)
{
if(m==NULL)
{
// sError.push_back("pushMsgToQueue faile,Msg is NULL");
LOG_F(ERROR, "pushMsgToQueue faile,Msg is NULL m: %p", m);
return false;
}
unsigned char* Buff = m;
//消息头鉴定
char *header = new char[sizeof(m_header)];
unsigned short id;
memcpy(header,Buff,sizeof(m_header));
if(!getMsgId(header,id))
{
// sError.push_back("pushMsgToQueue faile,Msg ID error");
// LOG_F(INFO, "pushMsgToQueue faile,Msg ID error header: %p", header);
delete []header;
return false;
}
delete []header;
//输入消息
switch (id)
{
case CCU_STATE_FB_ID: //CCU_STATE_FB 0x0051
{
msg_CcuStateFbMsg s;
if(getMsgFromBuff((char *)Buff,s))
{
if(m_qReceiveCcuStateBuffer.size()<QUENUE_SIZE)
{
double time = MOOSTime();
m_qReceiveCcuStateBuffer.push(std::make_pair(time, s));
}
else
{
// sError.push_back("pushMsgToQueue faile,CcuState buffer is full");
LOG_F(INFO, "pushMsgToQueue faile,CcuState buffer is full");
return false;
}
}
else
{
return false;
}
break;
}
case CCU_SET_PARM_FB_ID: //CCU_SET_PARM_FB 0x0040
{
msg_CcuSetParmFbMsg s;
if(getMsgFromBuff((char *)Buff,s))
{
if(m_qReceiveCcuSetParmBuffer.size()<QUENUE_SIZE)
{
double time = MOOSTime();
m_qReceiveCcuSetParmBuffer.push(std::make_pair(time, s));
}
else
{
// sError.push_back("pushMsgToQueue faile,CcuSetParm buffer is full");
LOG_F(INFO, "pushMsgToQueue faile,CcuSetParm buffer is full");
return false;
}
}
else
{
return false;
}
break;
}
case DIS_HVBUS_FB_ID: //DIS_HVBUS_FB 0x0020
{
msg_disHighVolBusFbMsg s;
if(getMsgFromBuff((char *)Buff,s))
{
if(m_qReceiveDisHighVolBusBuffer.size()<QUENUE_SIZE)
{
double time = MOOSTime();
m_qReceiveDisHighVolBusBuffer.push(std::make_pair(time, s));
}
else
{
// sError.push_back("pushMsgToQueue faile,DisSysHVBus buffer is full");
LOG_F(INFO, "pushMsgToQueue faile,DisSysHVBus buffer is full");
return false;
}
}
else
{
return false;
}
break;
}
case DIS_HVBUSA_FB_ID: //DIS_HVBUSA_FB 0x0006
{
msg_disHighAVolBusFbMsg s;
if(getMsgFromBuff((char *)Buff,s))
{
if(m_qReceiveDisHighAVolBusBuffer.size()<QUENUE_SIZE)
{
double time = MOOSTime();
m_qReceiveDisHighAVolBusBuffer.push(std::make_pair(time, s));
}
else
{
// sError.push_back("pushMsgToQueue faile,DisSysHVABus buffer is full");
LOG_F(INFO, "pushMsgToQueue faile,DisSysHVABus buffer is full");
return false;
}
}
else
{
return false;
}
break;
}
case DIS_HVBUSB_FB_ID: //DIS_HVBUSB_FB 0x0007
{
msg_disLowMainBusFbMsg s;
if(getMsgFromBuff((char *)Buff,s))
{
if(m_qReceiveDisHighBVolBusBuffer.size()<QUENUE_SIZE)
{
double time = MOOSTime();
m_qReceiveDisHighBVolBusBuffer.push(std::make_pair(time, s));
}
else
{
// sError.push_back("pushMsgToQueue faile,DisSysHVBBus buffer is full");
LOG_F(INFO, "pushMsgToQueue faile,DisSysHVBBus buffer is full");
return false;
}
}
else
{
return false;
}
break;
}
case DIS_LVBUS_FB_ID: //DIS_LVBUS_FB 0x0008
{
msg_disLowBusFbMsg s;
if(getMsgFromBuff((char *)Buff,s))
{
if(m_qReceiveDisLowBusBuffer.size()<QUENUE_SIZE)
{
double time = MOOSTime();
m_qReceiveDisLowBusBuffer.push(std::make_pair(time, s));
}
else
{
// sError.push_back("pushMsgToQueue faile,DisSysLVBus buffer is full");
LOG_F(INFO, "pushMsgToQueue faile,DisSysLVBus buffer is full");
return false;
}
}
else
{
return false;
}
break;
}
default:
break;
}
}
bool udpComm::clearMsgQueue()
{
{
std::lock_guard<std::mutex> lock(m_mutexCcuState);
while(!m_qReceiveCcuStateBuffer.empty())
m_qReceiveCcuStateBuffer.pop();
}
{
std::lock_guard<std::mutex> lock(m_mutexCcuSetParm);
while(!m_qReceiveCcuSetParmBuffer.empty())
m_qReceiveCcuSetParmBuffer.pop();
}
{
std::lock_guard<std::mutex> lock(m_mutexDisHighVolBus);
while(!m_qReceiveDisHighVolBusBuffer.empty())
m_qReceiveDisHighVolBusBuffer.pop();
}
{
std::lock_guard<std::mutex> lock(m_mutexDisHighAVolBus);
while(!m_qReceiveDisHighAVolBusBuffer.empty())
m_qReceiveDisHighAVolBusBuffer.pop();
}
{
std::lock_guard<std::mutex> lock(m_mutexDisHighBVolBus);
while(!m_qReceiveDisHighBVolBusBuffer.empty())
m_qReceiveDisHighBVolBusBuffer.pop();
}
{
std::lock_guard<std::mutex> lock(m_mutexDisLowBus);
while(!m_qReceiveDisLowBusBuffer.empty())
m_qReceiveDisLowBusBuffer.pop();
}
return true;
}
bool udpComm::popMsgFormQueue(double &time,msg_CcuStateFbMsg &m)
{
std::lock_guard<std::mutex> lock(m_mutexCcuState);
if(m_qReceiveCcuStateBuffer.empty())
return false;
time = m_qReceiveCcuStateBuffer.front().first;
m = m_qReceiveCcuStateBuffer.front().second;
m_qReceiveCcuStateBuffer.pop();
return true;
}
bool udpComm::popMsgFormQueue(double &time,msg_CcuSetParmFbMsg &m)
{
std::lock_guard<std::mutex> lock(m_mutexCcuSetParm);
if(m_qReceiveCcuSetParmBuffer.empty())
return false;
time = m_qReceiveCcuSetParmBuffer.front().first;
m = m_qReceiveCcuSetParmBuffer.front().second;
m_qReceiveCcuSetParmBuffer.pop();
return true;
}
bool udpComm::popMsgFormQueue(double &time,msg_disHighVolBusFbMsg &m)
{
std::lock_guard<std::mutex> lock(m_mutexDisHighVolBus);
if(m_qReceiveDisHighVolBusBuffer.empty())
return false;
time = m_qReceiveDisHighVolBusBuffer.front().first;
m = m_qReceiveDisHighVolBusBuffer.front().second;
m_qReceiveDisHighVolBusBuffer.pop();
return true;
}
bool udpComm::popMsgFormQueue(double &time,msg_disHighAVolBusFbMsg &m)
{
std::lock_guard<std::mutex> lock(m_mutexDisHighAVolBus);
if(m_qReceiveDisHighAVolBusBuffer.empty())
return false;
time = m_qReceiveDisHighAVolBusBuffer.front().first;
m = m_qReceiveDisHighAVolBusBuffer.front().second;
m_qReceiveDisHighAVolBusBuffer.pop();
return true;
}
bool udpComm::popMsgFormQueue(double &time,msg_disLowMainBusFbMsg &m)
{
std::lock_guard<std::mutex> lock(m_mutexDisHighBVolBus);
if(m_qReceiveDisHighBVolBusBuffer.empty())
return false;
time = m_qReceiveDisHighBVolBusBuffer.front().first;
m = m_qReceiveDisHighBVolBusBuffer.front().second;
m_qReceiveDisHighBVolBusBuffer.pop();
return true;
}
bool udpComm::popMsgFormQueue(double &time,msg_disLowBusFbMsg &m)
{
std::lock_guard<std::mutex> lock(m_mutexDisLowBus);
if(m_qReceiveDisLowBusBuffer.empty())
return false;
time = m_qReceiveDisLowBusBuffer.front().first;
m = m_qReceiveDisLowBusBuffer.front().second;
m_qReceiveDisLowBusBuffer.pop();
return true;
}
bool udpComm::sendDisSysCmd(const disHighVolBusCmd cmd)
{
unsigned char* msgBuf;
cChecksum cc;
msg_disHVBCmd* msg = new msg_disHVBCmd;
msg->header.id = DIS_HVBUS_CMD_ID;
msg->header.start1 = DIS_MSG_HEAD1;
msg->header.start2 = DIS_MSG_HEAD2;
//TODO : 阈字节长度固定了,后续会修改
msg->header.length = 18;
// msg->header.length = sizeof(disHighVolBusCmd);
msg->data = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_disHVBCmd),sizeof(cChecksum));
msg->checkCode = cc;
try
{
sendMsg(msgBuf,sizeof(msg_disHVBCmd));
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
delete msg;
return true;
}
bool udpComm::sendDisSysCmd(const disHighAVolBusCmd cmd)
{
unsigned char* msgBuf;
cChecksum cc;
msg_disHVBACmd* msg = new msg_disHVBACmd;
msg->header.id = DIS_HVBUSA_CMD_ID;
msg->header.start1 = DIS_MSG_HEAD1;
msg->header.start2 = DIS_MSG_HEAD2;
//TODO : 阈字节长度固定了,后续会修改
msg->header.length = 6;
// msg->header.length = sizeof(disHighAVolBusCmd);
msg->data = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_disHVBACmd),sizeof(cChecksum));
msg->checkCode = cc;
try
{
sendMsg(msgBuf,sizeof(msg_disHVBACmd));
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
delete msg;
return true;
}
bool udpComm::sendDisSysCmd(const disHighBVolBusCmd cmd)
{
unsigned char* msgBuf;
cChecksum cc;
msg_disHVBBCmd* msg = new msg_disHVBBCmd;
msg->header.id = DIS_HVBUSB_CMD_ID;
msg->header.start1 = DIS_MSG_HEAD1;
msg->header.start2 = DIS_MSG_HEAD2;
//TODO : 阈字节长度固定了,后续会修改
msg->header.length = 8;
// msg->header.length = sizeof(disHighBVolBusCmd);
msg->data = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_disHVBBCmd),sizeof(cChecksum));
msg->checkCode = cc;
try
{
// cout << "=========" <<(int)sizeof(msg_disHVBBCmd) << endl;
sendMsg(msgBuf,sizeof(msg_disHVBBCmd));
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
delete msg;
return true;
}
bool udpComm::sendDisSysCmd(const disLowBusCmd cmd)
{
unsigned char* msgBuf;
cChecksum cc;
msg_disLVBCmd* msg = new msg_disLVBCmd;
msg->header.id = DIS_LVBUS_CMD_ID;
msg->header.start1 = DIS_MSG_HEAD1;
msg->header.start2 = DIS_MSG_HEAD2;
//TODO : 阈字节长度固定了,后续会修改
msg->header.length = 8;
// msg->header.length = sizeof(disLowBusCmd);
msg->data = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_disLVBCmd),sizeof(cChecksum));
// msg->checkCode = static_cast<cChecksum>(cc & 0xFF);
msg->checkCode = cc;
cout << "cc======disLowBusCmd=============================" << (int) msg->checkCode << endl;
try
{
sendMsg(msgBuf,sizeof(msg_disLVBCmd));
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
delete msg;
return true;
}
bool udpComm::sendCcuColCmd(const ccuColCmd cmd)
{
unsigned char* msgBuf;
Checksum cc;
msg_CcuColCmdMsg* msg = new msg_CcuColCmdMsg();
memset(msg, 0, sizeof(msg_CcuColCmdMsg)); // 初始化所有字段为0
msg->header.id = CCU_CMD_ID;
msg->header.start1 = CCU_UDPMSG_START1;
msg->header.start2 = CCU_UDPMSG_START2;
msg->header.length = sizeof(ccuColCmd);
// 确保日期获取成功
if(!getDate(msg->date.year,msg->date.month,msg->date.day,
msg->date.hour,msg->date.minute,msg->date.second,msg->date.milliseconds)) {
LOG_F(ERROR, "Failed to get current date/time");
delete msg;
return false;
}
msg->cmd = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_CcuColCmdMsg));
msg->checkCode = static_cast<cChecksum>(cc & 0xFF);
bool sendResult = false;
try {
sendResult = sendMsg(msgBuf,sizeof(msg_CcuColCmdMsg));
if (!sendResult) {
LOG_F(ERROR, "Failed to send CCU command");
}
} catch (const std::exception& e) {
LOG_F(ERROR, "Exception in sendCcuColCmd: %s", e.what());
}
delete msg;
return sendResult;
}
bool udpComm::sendCcuSetParmCmd(const ccuSetParmCmd cmd)
{
unsigned char* msgBuf;
Checksum cc;
msg_CcuSetParmMsg* msg = new msg_CcuSetParmMsg;
msg->header.id = CCU_SET_PARM_ID;
msg->header.start1 = CCU_UDPMSG_START1;
msg->header.start2 = CCU_UDPMSG_START2;
msg->header.length = sizeof(ccuSetParmCmd);
msg->cmd = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_CcuSetParmMsg));
msg->checkCode = static_cast<cChecksum>(cc & 0xFF);
try
{
sendMsg(msgBuf,sizeof(msg_CcuSetParmMsg));
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
delete msg;
return true;
}
bool udpComm::getDate(unsigned short &year, unsigned char &month, unsigned char &day, unsigned char &hour, unsigned char &minute, unsigned char &second, unsigned char &milliseconds) {
// 获取当前时间
std::time_t now = std::time(nullptr);
if (now == -1) {
return false; // 获取时间失败
}
// 转换为本地时间
std::tm *localTime = std::localtime(&now);
if (!localTime) {
return false; // 转换失败
}
// 获取年、月、日、时、分、秒
year = localTime->tm_year + 1900; // tm_year 是从 1900 年开始的
month = localTime->tm_mon + 1; // tm_mon 是从 0 开始的,0 对应 1 月
day = localTime->tm_mday;
hour = localTime->tm_hour;
minute = localTime->tm_min;
second = localTime->tm_sec;
// 获取毫秒,使用 std::chrono
auto now_ms = std::chrono::system_clock::now();
auto duration = now_ms.time_since_epoch();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; // 取毫秒部分
ms = ms/10;
milliseconds = static_cast<unsigned char>(ms);
return true;
}
-108
View File
@@ -1,108 +0,0 @@
#ifndef UDPCOMM__H
#define UDPCOMM__H
#define UNIX
#include "ccuUdpMsg.h"
#include <string>
#include <vector>
#include <queue>
#include <string.h>
#include "MOOS/libMOOS/Utils/MOOSUtilityFunctions.h"
#include "MOOS/libMOOS/Comms/XPCUdpSocket.h"
#include <ctime>
#include <mutex>
#include <chrono>
#include <iostream>
#include <utility>
#include "../logc/loguru.hpp"
using namespace std;
#define QUENUE_SIZE 100
class udpComm {
public:
udpComm();
~udpComm();
XPCUdpSocket *udpScoket;
string ccuHost;
long int ccuPort;
bool pushMsgToQueue(unsigned char *m);
bool clearMsgQueue();
bool getMsgFromBuff(char *m, msg_CcuStateFbMsg &s);
bool getMsgFromBuff(char *m, msg_CcuSetParmFbMsg &s);
bool getMsgFromBuff(char *m, msg_disHighVolBusFbMsg &s);
bool getMsgFromBuff(char *m, msg_disHighAVolBusFbMsg &s);
bool getMsgFromBuff(char *m, msg_disLowMainBusFbMsg &s);
bool getMsgFromBuff(char *m, msg_disLowBusFbMsg &s);
bool popMsgFormQueue(double &time,msg_CcuStateFbMsg &m);
bool popMsgFormQueue(double &time,msg_CcuSetParmFbMsg &m);
bool popMsgFormQueue(double &time,msg_disHighVolBusFbMsg &m);
bool popMsgFormQueue(double &time,msg_disHighAVolBusFbMsg &m);
bool popMsgFormQueue(double &time,msg_disLowMainBusFbMsg &m);
bool popMsgFormQueue(double &time,msg_disLowBusFbMsg &m);
bool sendCcuColCmd(const ccuColCmd cmd);
bool sendCcuSetParmCmd(const ccuSetParmCmd cmd);
bool sendDisSysCmd(const disHighVolBusCmd cmd);
bool sendDisSysCmd(const disHighAVolBusCmd cmd);
bool sendDisSysCmd(const disHighBVolBusCmd cmd);
bool sendDisSysCmd(const disLowBusCmd cmd);
bool sendMsg(const unsigned char* msg, unsigned int size) {
if (!udpScoket) {
LOG_F(ERROR, "UDP socket is null in sendMsg");
return false;
}
if (!msg || size == 0) {
LOG_F(ERROR, "Invalid message parameters in sendMsg");
return false;
}
if (ccuHost.empty() || ccuPort <= 0) {
LOG_F(ERROR, "Invalid CCU address: %s:%d", ccuHost.c_str(), ccuPort);
return false;
}
try {
udpScoket->iSendMessageTo((void*)msg, size, ccuPort, ccuHost);
return true;
} catch (const XPCException& e) {
LOG_F(ERROR, "XPCException in sendMsg");
} catch (const std::exception& e) {
LOG_F(ERROR, "Exception in sendMsg: %s", e.what());
} catch (...) {
LOG_F(ERROR, "Unknown exception in sendMsg");
}
return false;
}
bool getMsgId(char *m,unsigned short &id);
Checksum calculateChecksum(const unsigned char* data, int size);
Checksum calculateChecksum(const unsigned char* data, int dataSize, int checkSize);
bool getDate(unsigned short &year, unsigned char &month, unsigned char &day, unsigned char &hour, unsigned char &minute, unsigned char &second, unsigned char &milliseconds);
std::vector<std::string> sError;
std::mutex m_mutexCcuState;
std::mutex m_mutexCcuSetParm;
std::mutex m_mutexDisHighVolBus;
std::mutex m_mutexDisHighAVolBus;
std::mutex m_mutexDisHighBVolBus;
std::mutex m_mutexDisLowBus;
// std::queue<msgWithTime> m_qReceiveCcuStateBuffer;
std::queue<std::pair<double,msg_CcuStateFbMsg>> m_qReceiveCcuStateBuffer;
std::queue<std::pair<double,msg_CcuSetParmFbMsg>> m_qReceiveCcuSetParmBuffer;
std::queue<std::pair<double,msg_disHighVolBusFbMsg>> m_qReceiveDisHighVolBusBuffer;
std::queue<std::pair<double,msg_disHighAVolBusFbMsg>> m_qReceiveDisHighAVolBusBuffer;
std::queue<std::pair<double,msg_disLowMainBusFbMsg>> m_qReceiveDisHighBVolBusBuffer;
std::queue<std::pair<double,msg_disLowBusFbMsg>> m_qReceiveDisLowBusBuffer;
private:
msg_CcuColCmdMsg m_ccuColCmd;
msg_CcuSetParmMsg m_ccuSetParmCmd;
};
#endif
@@ -1,60 +0,0 @@
/* json 格式 XC阵控制软件→复合能源管控系统
{
"xcLowVoltageAdd_int32 " : XX //命令类型:“无命令”0,“申请仪表上电”1,“申请仪表断电”2;类型:int
"xcHighVoltageAdd_int32 " : XX, //命令类型:“无命令”0,“申请高压上电”1,“申请高压断电”2;类型:int
}
*/
#ifndef UDETECT_XCVOLTAGE_CMD_H
#define UDETECT_XCVOLTAGE_CMD_H
#include "../logc/loguru.hpp"
#include "json/json.h"
#include <string>
#include <memory>
class uDetect_xcVoltage_cmd
{
private:
/* data */
public:
uDetect_xcVoltage_cmd(){};
~uDetect_xcVoltage_cmd(){};
int xcLowVoltageAdd_int32;
int xcHighVoltageAdd_int32;
static std::string key;
std::string toJsonString()
{
Json::Value j;
j["xcLowVoltageAdd_int32"] = xcLowVoltageAdd_int32;
j["xcHighVoltageAdd_int32"] = xcHighVoltageAdd_int32;
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = "";
return Json::writeString(builder, j);
}
bool fromJsonString(std::string jsonString)
{
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> reader(readerBuilder.newCharReader());
Json::Value j;
std::string errors;
LOG_F(INFO, "jsonString: %s", jsonString.c_str());
bool parsingSuccessful = reader->parse(jsonString.c_str(), jsonString.c_str() + jsonString.length(), &j, &errors);
if (!parsingSuccessful) {
LOG_F(ERROR, "Failed to parse JSON: %s", errors.c_str());
return false;
}
if (j.isMember("xcLowVoltageAdd_int32") && j.isMember("xcHighVoltageAdd_int32")) {
xcLowVoltageAdd_int32 = j["xcLowVoltageAdd_int32"].asInt();
xcHighVoltageAdd_int32 = j["xcHighVoltageAdd_int32"].asInt();
return true;
}
LOG_F(ERROR, "JSON does not contain required fields");
return false;
}
};
#endif
@@ -1,56 +0,0 @@
/* json 格式 XC阵控制软件→复合能源管控系统
{
"zdpVoltageAdd_int32 " : XX //命令类型:“无命令”0,“申请仪表上电”1,“申请仪表断电”2;类型:int
}
*/
#ifndef UDETECT_ZDPVOLTAGE_CMD_H
#define UDETECT_ZDPVOLTAGE_CMD_H
#include "../logc/loguru.hpp"
#include "json/json.h"
#include <string>
#include <memory>
class uDetect_zdpVoltage_cmd
{
private:
/* data */
public:
uDetect_zdpVoltage_cmd(){};
~uDetect_zdpVoltage_cmd(){};
int zdpVoltageAdd_int32;
static std::string key;
std::string toJsonString()
{
Json::Value j;
j["zdpVoltageAdd_int32"] = zdpVoltageAdd_int32;
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = "";
return Json::writeString(builder, j);
}
bool fromJsonString(std::string jsonString)
{
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> reader(readerBuilder.newCharReader());
Json::Value j;
std::string errors;
LOG_F(INFO, "jsonString: %s", jsonString.c_str());
bool parsingSuccessful = reader->parse(jsonString.c_str(), jsonString.c_str() + jsonString.length(), &j, &errors);
if (!parsingSuccessful) {
LOG_F(ERROR, "Failed to parse JSON: %s", errors.c_str());
return false;
}
if (j.isMember("zdpVoltageAdd_int32")) {
zdpVoltageAdd_int32 = j["zdpVoltageAdd_int32"].asInt();
return true;
}
LOG_F(ERROR, "JSON does not contain required fields");
return false;
}
};
#endif
@@ -1,52 +0,0 @@
//决策消息
#ifndef UDEVICE_DEPTH_ST_H
#define UDEVICE_DEPTH_ST_H
#include <string>
#include "../logc/loguru.hpp"
#define DB_TO_PM_DEEPTH "uDevice_depth_st"
class uDevice_depthMsg
{
private:
public:
std::string key = "uDevice_depth_st";
uDevice_depthMsg(/* args */){};
~uDevice_depthMsg(){};
int getDepth(const std::string& msgkey, const std::string& msg){
if (msgkey != DB_TO_PM_DEEPTH)
{
LOG_F(WARNING, "消息类型错误 %s", msgkey.c_str());
return 0;
}
int deepth;
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
std::cerr << "JSON解析失败: " << errs << std::endl;
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return -1;
}
// 检查并读取深度数组
if (root.isMember("value_array_int32") && root["value_array_int32"].isArray()) {
const Json::Value& array = root["value_array_int32"];
if (array.size() >= 3) {
// 计算三个深度值的平均值
deepth = (array[0].asInt() + array[1].asInt() + array[2].asInt()) / 3;
} else {
LOG_F(WARNING, "深度数组元素不足3个");
return -1;
}
} else {
LOG_F(WARNING, "缺少或类型不匹配: value_array_int32");
return -1;
}
return deepth;
}
};
#endif
@@ -1,531 +0,0 @@
#ifndef UDEVICE_SECDISTSTATE_H
#define UDEVICE_SECDISTSTATE_H
#include <string>
#include <json/json.h>
#include "../driver.h"
#include "../logc/loguru.hpp"
#include <cstring>
class uDevice_secdistStateMsg
{
private:
/* data */
public:
uDevice_secdistStateMsg(){};
~uDevice_secdistStateMsg(){};
std::string key_fb = "uDevice_secdistState_st";
std::string key_cmd = "uPower_secdistState_cmd";
struct DeviceSecdistState
{
// UNIT1
u_int8_t bowCover1;
u_int8_t bowCover2;
u_int8_t bowCover3;
u_int8_t bowCover4;
u_int8_t bowCover5;
u_int8_t bowCover6;
u_int8_t singleBeamSonar1;
u_int8_t singleBeamSonar2;
u_int8_t singleBeamSonar3;
u_int8_t singleBeamSonar4;
u_int8_t singleBeamSonar5;
u_int8_t singleBeamSonar6;
u_int8_t forwardSonar;
u_int8_t depth3;
u_int8_t soundVeloc;
u_int8_t bowRudderServo;
u_int8_t bowRudderDeploy;
u_int8_t ctd1;
u_int8_t waterInject;
// UNIT2
u_int8_t sideSonar;
u_int8_t bowWaterInet1;
u_int8_t bowWaterInet2;
u_int8_t bowWaterInet3;
u_int8_t bowWaterInet4;
u_int8_t bowWaterInet5;
u_int8_t bowWaterInet6;
u_int8_t bowWaterInet7;
u_int8_t bowWaterInet8;
u_int8_t launchControler;
u_int8_t electSensor;
u_int8_t lowFreqSonar;
// UNIT3
u_int8_t buoyancyAdjust1;
u_int8_t buoyancyAdjust2;
u_int8_t buoyancyAdjust3;
u_int8_t USBL;
u_int8_t depth1;
u_int8_t commDev;
u_int8_t mastLiftingServo;
u_int8_t DVL;
u_int8_t ADCP;
u_int8_t inOutLetWater1;
u_int8_t inOutLetWater2;
// UNIT4
u_int8_t singleBeamSonar7;
u_int8_t singleBeamSonar8;
u_int8_t buoyage;
u_int8_t bowThrow;
u_int8_t depth2;
u_int8_t thruster;
u_int8_t dragSonar;
u_int8_t haulingMachine;
u_int8_t buoyancyAdjust4;
u_int8_t buoyancyAdjust5;
u_int8_t sternRudderServo1;
u_int8_t sternRudderServo2;
u_int8_t sternRudderServo3;
u_int8_t sternRudderServo4;
u_int8_t ctd2;
u_int8_t commDevStern;
u_int8_t inOutLetWater3;
// UNIT5
u_int8_t secINS;
u_int8_t photoSensor;
u_int8_t sternThrow;
u_int8_t mainINS;
} deviceSecdistState;
bool parseSecdistStateFromJson(DeviceSecdistState &state, const std::string &strJson){
Json::Value root;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> reader(readerBuilder.newCharReader());
std::string errors;
if(!reader->parse(strJson.c_str(), strJson.c_str() + strJson.length(), &root, &errors)){
LOG_F(ERROR, "Failed to parse JSON: %s", errors.c_str());
return false;
}
auto checkField = [&root](const std::string& field) -> bool {
if(!root.isMember(field) || !root[field].isUInt()) {
// std::cerr << "Missing or invalid field: " << field << std::endl;
// LOG_F(ERROR, "Missing or invalid field: %s", field.c_str());
return false;
}
return true;
};
bool ok = true;
// UNIT1
if(!checkField("bowCover1_uint8")) ok=false; state.bowCover1 = root["bowCover1_uint8"].asUInt();
if(!checkField("bowCover2_uint8")) ok=false; state.bowCover2 = root["bowCover2_uint8"].asUInt();
if(!checkField("bowCover3_uint8")) ok=false; state.bowCover3 = root["bowCover3_uint8"].asUInt();
if(!checkField("bowCover4_uint8")) ok=false; state.bowCover4 = root["bowCover4_uint8"].asUInt();
if(!checkField("bowCover5_uint8")) ok=false; state.bowCover5 = root["bowCover5_uint8"].asUInt();
if(!checkField("bowCover6_uint8")) ok=false; state.bowCover6 = root["bowCover6_uint8"].asUInt();
if(!checkField("singleBeamSonar1_uint8")) ok=false; state.singleBeamSonar1 = root["singleBeamSonar1_uint8"].asUInt();
if(!checkField("singleBeamSonar2_uint8")) ok=false; state.singleBeamSonar2 = root["singleBeamSonar2_uint8"].asUInt();
if(!checkField("singleBeamSonar3_uint8")) ok=false; state.singleBeamSonar3 = root["singleBeamSonar3_uint8"].asUInt();
if(!checkField("singleBeamSonar4_uint8")) ok=false; state.singleBeamSonar4 = root["singleBeamSonar4_uint8"].asUInt();
if(!checkField("singleBeamSonar5_uint8")) ok=false; state.singleBeamSonar5 = root["singleBeamSonar5_uint8"].asUInt();
if(!checkField("singleBeamSonar6_uint8")) ok=false; state.singleBeamSonar6 = root["singleBeamSonar6_uint8"].asUInt();
if(!checkField("forwardSonar_uint8")) ok=false; state.forwardSonar = root["forwardSonar_uint8"].asUInt();
if(!checkField("depth3_uint8")) ok=false; state.depth3 = root["depth3_uint8"].asUInt();
if(!checkField("soundVeloc_uint8")) ok=false; state.soundVeloc = root["soundVeloc_uint8"].asUInt();
if(!checkField("bowRudderServo_uint8")) ok=false; state.bowRudderServo = root["bowRudderServo_uint8"].asUInt();
if(!checkField("bowRudderDeploy_uint8")) ok=false; state.bowRudderDeploy = root["bowRudderDeploy_uint8"].asUInt();
if(!checkField("ctd1_uint8")) ok=false; state.ctd1 = root["ctd1_uint8"].asUInt();
if(!checkField("waterInject_uint8")) ok=false; state.waterInject = root["waterInject_uint8"].asUInt();
// UNIT2
if(!checkField("sideSonar_uint8")) ok=false; state.sideSonar = root["sideSonar_uint8"].asUInt();
if(!checkField("bowWaterInet1_uint8")) ok=false; state.bowWaterInet1 = root["bowWaterInet1_uint8"].asUInt();
if(!checkField("bowWaterInet2_uint8")) ok=false; state.bowWaterInet2 = root["bowWaterInet2_uint8"].asUInt();
if(!checkField("bowWaterInet3_uint8")) ok=false; state.bowWaterInet3 = root["bowWaterInet3_uint8"].asUInt();
if(!checkField("bowWaterInet4_uint8")) ok=false; state.bowWaterInet4 = root["bowWaterInet4_uint8"].asUInt();
if(!checkField("bowWaterInet5_uint8")) ok=false; state.bowWaterInet5 = root["bowWaterInet5_uint8"].asUInt();
if(!checkField("bowWaterInet6_uint8")) ok=false; state.bowWaterInet6 = root["bowWaterInet6_uint8"].asUInt();
if(!checkField("bowWaterInet7_uint8")) ok=false; state.bowWaterInet7 = root["bowWaterInet7_uint8"].asUInt();
if(!checkField("bowWaterInet8_uint8")) ok=false; state.bowWaterInet8 = root["bowWaterInet8_uint8"].asUInt();
if(!checkField("launchControler_uint8")) ok=false; state.launchControler = root["launchControler_uint8"].asUInt();
if(!checkField("electSensor_uint8")) ok=false; state.electSensor = root["electSensor_uint8"].asUInt();
if(!checkField("lowFreqSonar_uint8")) ok=false; state.lowFreqSonar = root["lowFreqSonar_uint8"].asUInt();
// UNIT3
if(!checkField("buoyancyAdjust1_uint8")) ok=false; state.buoyancyAdjust1 = root["buoyancyAdjust1_uint8"].asUInt();
if(!checkField("buoyancyAdjust2_uint8")) ok=false; state.buoyancyAdjust2 = root["buoyancyAdjust2_uint8"].asUInt();
if(!checkField("buoyancyAdjust3_uint8")) ok=false; state.buoyancyAdjust3 = root["buoyancyAdjust3_uint8"].asUInt();
if(!checkField("USBL_uint8")) ok=false; state.USBL = root["USBL_uint8"].asUInt();
if(!checkField("depth1_uint8")) ok=false; state.depth1 = root["depth1_uint8"].asUInt();
if(!checkField("commDev_uint8")) ok=false; state.commDev = root["commDev_uint8"].asUInt();
if(!checkField("mastLiftingServo_uint8")) ok=false; state.mastLiftingServo = root["mastLiftingServo_uint8"].asUInt();
if(!checkField("DVL_uint8")) ok=false; state.DVL = root["DVL_uint8"].asUInt();
if(!checkField("ADCP_uint8")) ok=false; state.ADCP = root["ADCP_uint8"].asUInt();
if(!checkField("inOutLetWater1_uint8")) ok=false; state.inOutLetWater1 = root["inOutLetWater1_uint8"].asUInt();
if(!checkField("inOutLetWater2_uint8")) ok=false; state.inOutLetWater2 = root["inOutLetWater2_uint8"].asUInt();
// UNIT4
if(!checkField("singleBeamSonar7_uint8")) ok=false; state.singleBeamSonar7 = root["singleBeamSonar7_uint8"].asUInt();
if(!checkField("singleBeamSonar8_uint8")) ok=false; state.singleBeamSonar8 = root["singleBeamSonar8_uint8"].asUInt();
if(!checkField("buoyage_uint8")) ok=false; state.buoyage = root["buoyage_uint8"].asUInt();
if(!checkField("bowThrow_uint8")) ok=false; state.bowThrow = root["bowThrow_uint8"].asUInt();
if(!checkField("depth2_uint8")) ok=false; state.depth2 = root["depth2_uint8"].asUInt();
if(!checkField("thruster_uint8")) ok=false; state.thruster = root["thruster_uint8"].asUInt();
if(!checkField("dragSonar_uint8")) ok=false; state.dragSonar = root["dragSonar_uint8"].asUInt();
if(!checkField("haulingMachine_uint8")) ok=false; state.haulingMachine = root["haulingMachine_uint8"].asUInt();
if(!checkField("buoyancyAdjust4_uint8")) ok=false; state.buoyancyAdjust4 = root["buoyancyAdjust4_uint8"].asUInt();
if(!checkField("buoyancyAdjust5_uint8")) ok=false; state.buoyancyAdjust5 = root["buoyancyAdjust5_uint8"].asUInt();
if(!checkField("sternRudderServo1_uint8")) ok=false; state.sternRudderServo1 = root["sternRudderServo1_uint8"].asUInt();
if(!checkField("sternRudderServo2_uint8")) ok=false; state.sternRudderServo2 = root["sternRudderServo2_uint8"].asUInt();
if(!checkField("sternRudderServo3_uint8")) ok=false; state.sternRudderServo3 = root["sternRudderServo3_uint8"].asUInt();
if(!checkField("sternRudderServo4_uint8")) ok=false; state.sternRudderServo4 = root["sternRudderServo4_uint8"].asUInt();
if(!checkField("ctd2_uint8")) ok=false; state.ctd2 = root["ctd2_uint8"].asUInt();
if(!checkField("commDevStern_uint8")) ok=false; state.commDevStern = root["commDevStern_uint8"].asUInt();
if(!checkField("inOutLetWater3_uint8")) ok=false; state.inOutLetWater3 = root["inOutLetWater3_uint8"].asUInt();
// UNIT5
if(!checkField("secINS_uint8")) ok=false; state.secINS = root["secINS_uint8"].asUInt();
if(!checkField("photoSensor_uint8")) ok=false; state.photoSensor = root["photoSensor_uint8"].asUInt();
if(!checkField("sternThrow_uint8")) ok=false; state.sternThrow = root["sternThrow_uint8"].asUInt();
if(!checkField("mainINS_uint8")) ok=false; state.mainINS = root["mainINS_uint8"].asUInt();
//不检查字段缺失
ok = true;
return ok;
};
std::string buildSecdistStateToJson(DeviceSecdistState &state){
Json::Value root;
// UNIT1
root["bowCover1_uint8"] = state.bowCover1;
root["bowCover2_uint8"] = state.bowCover2;
root["bowCover3_uint8"] = state.bowCover3;
root["bowCover4_uint8"] = state.bowCover4;
root["bowCover5_uint8"] = state.bowCover5;
root["bowCover6_uint8"] = state.bowCover6;
root["singleBeamSonar1_uint8"] = state.singleBeamSonar1;
root["singleBeamSonar2_uint8"] = state.singleBeamSonar2;
root["singleBeamSonar3_uint8"] = state.singleBeamSonar3;
root["singleBeamSonar4_uint8"] = state.singleBeamSonar4;
root["singleBeamSonar5_uint8"] = state.singleBeamSonar5;
root["singleBeamSonar6_uint8"] = state.singleBeamSonar6;
root["forwardSonar_uint8"] = state.forwardSonar;
root["depth3_uint8"] = state.depth3;
root["soundVeloc_uint8"] = state.soundVeloc;
root["bowRudderServo_uint8"] = state.bowRudderServo;
root["bowRudderDeploy_uint8"] = state.bowRudderDeploy;
root["ctd1_uint8"] = state.ctd1;
root["waterInject_uint8"] = state.waterInject;
// UNIT2
root["sideSonar_uint8"] = state.sideSonar;
root["bowWaterInet1_uint8"] = state.bowWaterInet1;
root["bowWaterInet2_uint8"] = state.bowWaterInet2;
root["bowWaterInet3_uint8"] = state.bowWaterInet3;
root["bowWaterInet4_uint8"] = state.bowWaterInet4;
root["bowWaterInet5_uint8"] = state.bowWaterInet5;
root["bowWaterInet6_uint8"] = state.bowWaterInet6;
root["bowWaterInet7_uint8"] = state.bowWaterInet7;
root["bowWaterInet8_uint8"] = state.bowWaterInet8;
root["launchControler_uint8"] = state.launchControler;
root["electSensor_uint8"] = state.electSensor;
root["lowFreqSonar_uint8"] = state.lowFreqSonar;
// UNIT3
root["buoyancyAdjust1_uint8"] = state.buoyancyAdjust1;
root["buoyancyAdjust2_uint8"] = state.buoyancyAdjust2;
root["buoyancyAdjust3_uint8"] = state.buoyancyAdjust3;
root["USBL_uint8"] = state.USBL;
root["depth1_uint8"] = state.depth1;
root["commDev_uint8"] = state.commDev;
root["mastLiftingServo_uint8"] = state.mastLiftingServo;
root["DVL_uint8"] = state.DVL;
root["ADCP_uint8"] = state.ADCP;
root["inOutLetWater1_uint8"] = state.inOutLetWater1;
root["inOutLetWater2_uint8"] = state.inOutLetWater2;
// UNIT4
root["singleBeamSonar7_uint8"] = state.singleBeamSonar7;
root["singleBeamSonar8_uint8"] = state.singleBeamSonar8;
root["buoyage_uint8"] = state.buoyage;
root["bowThrow_uint8"] = state.bowThrow;
root["depth2_uint8"] = state.depth2;
root["thruster_uint8"] = state.thruster;
root["dragSonar_uint8"] = state.dragSonar;
root["haulingMachine_uint8"] = state.haulingMachine;
root["buoyancyAdjust4_uint8"] = state.buoyancyAdjust4;
root["buoyancyAdjust5_uint8"] = state.buoyancyAdjust5;
root["sternRudderServo1_uint8"] = state.sternRudderServo1;
root["sternRudderServo2_uint8"] = state.sternRudderServo2;
root["sternRudderServo3_uint8"] = state.sternRudderServo3;
root["sternRudderServo4_uint8"] = state.sternRudderServo4;
root["ctd2_uint8"] = state.ctd2;
root["commDevStern_uint8"] = state.commDevStern;
root["inOutLetWater3_uint8"] = state.inOutLetWater3;
// UNIT5
root["secINS_uint8"] = state.secINS;
root["photoSensor_uint8"] = state.photoSensor;
root["sternThrow_uint8"] = state.sternThrow;
root["mainINS_uint8"] = state.mainINS;
Json::StreamWriterBuilder writerBuilder;
writerBuilder.settings_["indentation"] = ""; // 紧凑格式,无缩进
return Json::writeString(writerBuilder, root);
};
bool converFromDriverTable(DeviceSecdistState &state,const DriverTable &driver){
// UNIT1
state.bowCover1 = driver.bowCover1;
state.bowCover2 = driver.bowCover2;
state.bowCover3 = driver.bowCover3;
state.bowCover4 = driver.bowCover4;
state.bowCover5 = driver.bowCover5;
state.bowCover6 = driver.bowCover6;
state.singleBeamSonar1 = driver.singleBeamSonar1;
state.singleBeamSonar2 = driver.singleBeamSonar2;
state.singleBeamSonar3 = driver.singleBeamSonar3;
state.singleBeamSonar4 = driver.singleBeamSonar4;
state.singleBeamSonar5 = driver.singleBeamSonar5;
state.singleBeamSonar6 = driver.singleBeamSonar6;
state.forwardSonar = driver.forwardSonar;
state.depth3 = driver.depth3;
state.soundVeloc = driver.soundVeloc;
state.bowRudderServo = driver.bowRudderServo1;
state.bowRudderDeploy = driver.bowRudderDeploy;
state.ctd1 = driver.ctd1;
state.waterInject = driver.waterInject;
// UNIT2
state.sideSonar = driver.sideSonar;
state.bowWaterInet1 = driver.bowWaterInet1;
state.bowWaterInet2 = driver.bowWaterInet2;
state.bowWaterInet3 = driver.bowWaterInet3;
state.bowWaterInet4 = driver.bowWaterInet4;
state.bowWaterInet5 = driver.bowWaterInet5;
state.bowWaterInet6 = driver.bowWaterInet6;
state.bowWaterInet7 = driver.bowWaterInet7;
state.bowWaterInet8 = driver.bowWaterInet8;
state.launchControler = driver.launchControler;
state.electSensor = driver.electSensor;
state.lowFreqSonar = driver.lowFreqSonar;
// UNIT3
state.buoyancyAdjust1 = driver.buoyancyAdjust1;
state.buoyancyAdjust2 = driver.buoyancyAdjust2;
state.buoyancyAdjust3 = driver.buoyancyAdjust3;
state.USBL = driver.USBL;
state.depth1 = driver.depth1;
state.commDev = driver.commMast;
state.mastLiftingServo = driver.mastLiftingServo;
state.DVL = driver.DVL;
state.ADCP = driver.DVL; // Assuming ADCP is same as DVL
//出入水传感器1、2共用一个继电器
state.inOutLetWater1 = driver.inputWaterSensor1;
state.inOutLetWater2 = driver.inputWaterSensor1;
// state.inOutLetWater2 = driver.inputWaterSensor2;
// UNIT4
state.singleBeamSonar7 = driver.singleBeamSonar7;
state.singleBeamSonar8 = driver.singleBeamSonar8;
state.buoyage = driver.buoyage;
state.bowThrow = driver.bowThrow;
state.depth2 = driver.depth2;
state.thruster = driver.thruster;
state.haulingMachine = 2; // No corresponding field in DriverTable
state.buoyancyAdjust4 = driver.buoyancyAdjust4;
state.buoyancyAdjust5 = driver.buoyancyAdjust5;
state.sternRudderServo1 = driver.sternRudderServo1;
state.sternRudderServo2 = driver.sternRudderServo2;
state.sternRudderServo3 = driver.sternRudderServo3;
state.sternRudderServo4 = driver.sternRudderServo4;
state.ctd2 = driver.ctd2;
state.commDevStern = 2; // No corresponding field in DriverTable
state.inOutLetWater3 = driver.inputWaterSensor3;
// UNIT5
state.secINS = driver.ins;
state.photoSensor = driver.photoSensor;
state.sternThrow = driver.sternThrow;
state.mainINS = driver.ins;
// 未映射字段
// driver.overDeep1
// driver.overDeep2
// driver.ntp
state.dragSonar = 2; // Assuming dragSonar is same as lowFreqSonar
return true;
};
bool converToDriverTable(const DeviceSecdistState &state, DriverTable &driver){
// UNIT1
driver.bowCover1 = state.bowCover1;
driver.bowCover2 = state.bowCover2;
driver.bowCover3 = state.bowCover3;
driver.bowCover4 = state.bowCover4;
driver.bowCover5 = state.bowCover5;
driver.bowCover6 = state.bowCover6;
driver.singleBeamSonar1 = state.singleBeamSonar1;
driver.singleBeamSonar2 = state.singleBeamSonar2;
driver.singleBeamSonar3 = state.singleBeamSonar3;
driver.singleBeamSonar4 = state.singleBeamSonar4;
driver.singleBeamSonar5 = state.singleBeamSonar5;
driver.singleBeamSonar6 = state.singleBeamSonar6;
driver.forwardSonar = state.forwardSonar;
driver.depth3 = state.depth3;
driver.soundVeloc = state.soundVeloc;
driver.bowRudderServo1 = state.bowRudderServo;
driver.bowRudderServo2 = state.bowRudderServo;
driver.bowRudderDeploy = state.bowRudderDeploy;
driver.ctd1 = state.ctd1;
driver.waterInject = state.waterInject;
// UNIT2
driver.sideSonar = state.sideSonar;
driver.bowWaterInet1 = state.bowWaterInet1;
driver.bowWaterInet2 = state.bowWaterInet2;
driver.bowWaterInet3 = state.bowWaterInet3;
driver.bowWaterInet4 = state.bowWaterInet4;
driver.bowWaterInet5 = state.bowWaterInet5;
driver.bowWaterInet6 = state.bowWaterInet6;
driver.bowWaterInet7 = state.bowWaterInet7;
driver.bowWaterInet8 = state.bowWaterInet8;
driver.launchControler = state.launchControler;
driver.electSensor = state.electSensor;
driver.lowFreqSonar = state.lowFreqSonar;
// UNIT3
driver.buoyancyAdjust1 = state.buoyancyAdjust1;
driver.buoyancyAdjust2 = state.buoyancyAdjust2;
driver.buoyancyAdjust3 = state.buoyancyAdjust3;
driver.USBL = state.USBL;
driver.depth1 = state.depth1;
driver.commMast = state.commDev;
driver.ntp = 1;
driver.mastLiftingServo = state.mastLiftingServo;
driver.DVL = state.DVL;
driver.inputWaterSensor1 = state.inOutLetWater1;
driver.inputWaterSensor2 = state.inOutLetWater2;
driver.overDeep1 = (state.depth1 == 1) ? 1 : 0;
driver.overDeep2 = (state.depth2 == 1 || state.depth3 == 1) ? 1 : 0;
// UNIT4
driver.singleBeamSonar7 = state.singleBeamSonar7;
driver.singleBeamSonar8 = state.singleBeamSonar8;
driver.buoyage = state.buoyage;
driver.bowThrow = state.bowThrow;
driver.depth2 = state.depth2;
driver.thruster = state.thruster;
driver.buoyancyAdjust4 = state.buoyancyAdjust4;
driver.buoyancyAdjust5 = state.buoyancyAdjust5;
driver.sternRudderServo1 = state.sternRudderServo1;
driver.sternRudderServo2 = state.sternRudderServo2;
driver.sternRudderServo3 = state.sternRudderServo3;
driver.sternRudderServo4 = state.sternRudderServo4;
driver.ctd2 = state.ctd2;
driver.inputWaterSensor3 = state.inOutLetWater3;
// UNIT5
driver.ins = state.secINS | state.mainINS;
driver.photoSensor = state.photoSensor;
driver.sternThrow = state.sternThrow;
return true;
}
std::string buildMsg(DriverTable &driver){
DeviceSecdistState s;
converFromDriverTable(s,driver);
// std::cout << (int)s.depth1 << std::endl;
// std::cout << buildSecdistStateToJson(s) << std::endl;
return buildSecdistStateToJson(s);
};
bool parseMsg(const std::string &strJson,DriverTable &driver){
DeviceSecdistState s;
if(!parseSecdistStateFromJson(s,strJson)){
LOG_F(WARNING, "parseSecdistStateFromJson failed");
return false;
}
converToDriverTable(s,driver);
return true;
};
std::string buildMsg(DriverTable driver, DriverTable driver_state){
DeviceSecdistState s;
DriverTable driverCopy = driver;
for (size_t i = 0; i < sizeof(DriverTable); ++i) {
unsigned char* driverCopyPtr = reinterpret_cast<unsigned char*>(&driverCopy) + i;
unsigned char* statePtr = reinterpret_cast<unsigned char*>(&driver_state) + i;
if (*driverCopyPtr == *statePtr) {
*driverCopyPtr = 2;
}
}
converFromDriverTable(s, driverCopy);
// std::cout << (int)s.depth1 << std::endl;
// std::cout << buildSecdistStateToJson(s) << std::endl;
return buildSecdistStateToJson(s);
};
std::string buildMsg(DriverTable driver, DriverTable driver_state, unsigned int nCheck){
static size_t callCount = 0; // 静态变量,记录函数调用次数
DeviceSecdistState s;
DriverTable driverCopy = driver;
if (nCheck > 0 && callCount % nCheck == 0)
{
for (size_t i = 0; i < sizeof(DriverTable); ++i) {
unsigned char* driverCopyPtr = reinterpret_cast<unsigned char*>(&driverCopy) + i;
unsigned char* statePtr = reinterpret_cast<unsigned char*>(&driver_state) + i;
if (*driverCopyPtr == *statePtr) {
*driverCopyPtr = 2;
}
}
}
else {
memset(&driverCopy, 2, sizeof(DriverTable));
}
callCount ++;
converFromDriverTable(s, driverCopy);
// std::cout << (int)s.depth1 << std::endl;
// std::cout << buildSecdistStateToJson(s) << std::endl;
return buildSecdistStateToJson(s);
};
static DriverTable fault_state;
std::string buildMsg(DriverTable driver, DriverTable driver_state, DriverTable &driver_fault_state){
static size_t f_callCount = 0; // 静态变量,记录函数调用次数
unsigned int nCheck = 10;
DeviceSecdistState s;
DriverTable driverCopy = driver;
for (size_t i = 0; i < sizeof(DriverTable); ++i)
{
unsigned char* driverCopyPtr = reinterpret_cast<unsigned char*>(&driverCopy) + i;
unsigned char* statePtr = reinterpret_cast<unsigned char*>(&driver_state) + i;
unsigned char* faultStatePtr = reinterpret_cast<unsigned char*>(&driver_fault_state) + i;
unsigned char* faultState = reinterpret_cast<unsigned char*>(&fault_state) + i;
if (*driverCopyPtr == *statePtr)
{
*driverCopyPtr = 2;
*faultStatePtr = 0;
*faultState = 0;
}
else
{
*faultState = *faultState + 1;
if(*faultState >= 5)
{
//只有开启失败才计入故障
if(*driverCopyPtr == 1)
*faultStatePtr = 1;
*faultState = 5;
}
}
}
f_callCount ++;
converFromDriverTable(s, driverCopy);
return buildSecdistStateToJson(s);
};
};
#endif
@@ -1,124 +0,0 @@
/*
{
"rpm int16": "xxx", // 实际转速,类型:int16_t
"voltage_uint16": "xxx", // 电压,类型:uint16_t,单位 V
"current_uint16": "xxx", // 电流,类型:uint16_t,单位 A
"ctrlTemp_uint16": "xxx", // 控制器温度,类型:uint16_t,单位 °C
"motorTemp_uint16": "xxx", // 电机温度,类型:uint16_t,单位 °C
"motorTorque_uint16": "xxx", // 电机扭矩,类型:uint16_t,单位 N・m
"maxTorque_uint16": "xxx", // 电机最大扭矩,类型:uint16_t,单位 N・m,电机使能时该字段非零,无使能地址报警,含使能整标标识字,00 无故障,按位常量零
"error_int64": "xxx" // 见表 [1],类型:int64_t
}
*/
#ifndef UDEVICE_THRUSTER_ST_H
#define UDEVICE_THRUSTER_ST_H
#include "../logc/loguru.hpp"
#include "json/json.h"
#include <string>
#include <memory>
#include <sstream>
class uDevice_thruster_st
{
private:
/* data */
public:
uDevice_thruster_st(){};
~uDevice_thruster_st(){};
int16_t rpm_int16;
uint16_t voltage_uint16;
uint16_t current_uint16;
uint16_t ctrlTemp_uint16;
uint16_t motorTemp_uint16;
uint16_t motorTorque_uint16;
uint16_t maxTorque_uint16;
int64_t error_int64;
static std::string key;
std::string toJsonString()
{
Json::Value j;
j["rpm_int16"] = rpm_int16;
j["voltage_uint16"] = voltage_uint16;
j["current_uint16"] = current_uint16;
j["ctrlTemp_uint16"] = ctrlTemp_uint16;
j["motorTemp_uint16"] = motorTemp_uint16;
j["motorTorque_uint16"] = motorTorque_uint16;
j["maxTorque_uint16"] = maxTorque_uint16;
j["error_int64"] = static_cast<Json::Int64>(error_int64);
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = "";
return Json::writeString(builder, j);
}
bool fromJsonString(std::string jsonString)
{
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(jsonString);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return false;
}
// 提取rpm_int16
if (root.isMember("rpm_int16") && root["rpm_int16"].isInt()) {
rpm_int16 = root["rpm_int16"].asInt();
// LOG_F(INFO, "rpm_int16: %d", rpm_int16);
}
// 提取voltage_uint16
if (root.isMember("voltage_uint16") && root["voltage_uint16"].isUInt()) {
voltage_uint16 = root["voltage_uint16"].asUInt();
// LOG_F(INFO, "voltage_uint16: %u", voltage_uint16);
}
// 提取current_uint16
if (root.isMember("current_uint16") && root["current_uint16"].isUInt()) {
current_uint16 = root["current_uint16"].asUInt();
// LOG_F(INFO, "current_uint16: %u", current_uint16);
}
// 提取ctrlTemp_uint16
if (root.isMember("ctrlTemp_uint16") && root["ctrlTemp_uint16"].isUInt()) {
ctrlTemp_uint16 = root["ctrlTemp_uint16"].asUInt();
// LOG_F(INFO, "ctrlTemp_uint16: %u", ctrlTemp_uint16);
}
// 提取motorTemp_uint16
if (root.isMember("motorTemp_uint16") && root["motorTemp_uint16"].isUInt()) {
motorTemp_uint16 = root["motorTemp_uint16"].asUInt();
// LOG_F(INFO, "motorTemp_uint16: %u", motorTemp_uint16);
}
// 提取motorTorque_uint16
if (root.isMember("motorTorque_uint16") && root["motorTorque_uint16"].isUInt()) {
motorTorque_uint16 = root["motorTorque_uint16"].asUInt();
// LOG_F(INFO, "motorTorque_uint16: %u", motorTorque_uint16);
}
// 提取maxTorque_uint16
if (root.isMember("maxTorque_uint16") && root["maxTorque_uint16"].isUInt()) {
maxTorque_uint16 = root["maxTorque_uint16"].asUInt();
// LOG_F(INFO, "maxTorque_uint16: %u", maxTorque_uint16);
}
// 提取error_int64
if (root.isMember("error_int64") && root["error_int64"].isInt64()) {
error_int64 = root["error_int64"].asInt64();
// LOG_F(INFO, "error_int64: %lld", error_int64);
}
// LOG_F(INFO, "jsonString: %s", jsonString.c_str());
return true;
}
};
#endif
@@ -1,43 +0,0 @@
#ifndef UEXTERNCOMM_SETDEVICESWITCH_CMD_H
#define UEXTERNCOMM_SETDEVICESWITCH_CMD_H
#include <string>
class uExternComm_setDeviceSwitchMsg
{
private:
/* data */
public:
std::string key = "uExternComm_setDeviceSwitch_cmd";
uExternComm_setDeviceSwitchMsg(/* args */){};
~uExternComm_setDeviceSwitchMsg(){};
DeviceSwitchCmd getSetDevSwitch(const std::string& key, const std::string& msg)
{
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
DeviceSwitchCmd cmd{};
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
cmd.id = -1;
}
// 检查并读取设备开关状态
if (root.isMember("id_uint8") && root["id_uint8"].isUInt()) {
cmd.id = root["id_uint8"].asUInt();
} else {
cmd.id = -1;
LOG_F(WARNING, "缺少或类型不匹配: id_uint8");
}
if (root.isMember("cmd_uint8") && root["cmd_uint8"].isUInt()) {
cmd.cmd = root["cmd_uint8"].asUInt();
} else {
LOG_F(WARNING, "缺少或类型不匹配: state_bool");
}
return cmd;
};
};
#endif
@@ -1,43 +0,0 @@
#ifndef UEXTERCOMM_SETOPCONDITION_CMD_H
#define UEXTERCOMM_SETOPCONDITION_CMD_H
#include <string>
#include "../logc/loguru.hpp"
#include "json/json.h"
//外部通信消息
#define DB_TO_PM_SETOPCONDITION "uExternComm_setOpCondition_cmd"
class uExternComm_setOpConditionMsg
{
private:
/* data */
public:
std::string key = "uExternComm_setOpCondition_cmd";
uExternComm_setOpConditionMsg(/* args */){};
~uExternComm_setOpConditionMsg(){};
int getSetOpCondition(const std::string& key, const std::string& msg)
{
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return -1;
}
// 检查并读取操作条件
if (root.isMember("state_uint8") && root["state_uint8"].isUInt()) {
return root["state_uint8"].asUInt();
LOG_F(INFO, "上位机指令:opCondition: %d", root["state_uint8"].asUInt());
} else {
LOG_F(WARNING, "缺少或类型不匹配: opCondition");
return -1;
}
}
};
#endif
@@ -1,46 +0,0 @@
#ifndef UEXTERCOMM_SETOPMODE_CMD_H
#define UEXTERCOMM_SETOPMODE_CMD_H
#include <string>
#include "../logc/loguru.hpp"
//外部通信消息
#define DB_TO_PM_SETOPMODE "uExternComm_setOpMode_cmd"
class uExternComm_setOpModeMsg
{
private:
/* data */
public:
std::string key = "uExternComm_setOpMode_cmd";
uExternComm_setOpModeMsg(/* args */){};
~uExternComm_setOpModeMsg(){};
int getSetOpMode(const std::string& msgkey, const std::string& msg){
if (msgkey!= key)
{
LOG_F(WARNING, "消息类型错误 %s", msgkey.c_str());
return 0;
}
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return -1;
}
// 检查并读取操作模式
if (root.isMember("state_uint8") && root["state_uint8"].isUInt()) {
return root["state_uint8"].asUInt();
} else {
LOG_F(WARNING, "缺少或类型不匹配: opMode");
return -1;
}
};
};
#endif
@@ -1,39 +0,0 @@
#ifndef UEXTERNCONM_SETPMSYS_H
#define UEXTERNCONM_SETPMSYS_H
#include <string>
#include "../logc/loguru.hpp"
#define DB_TO_PM_SETPMSYS "uExternComm_setPowerSystem_cmd"
class uExternComm_setPowerSystemMsg
{
private:
public:
std::string key = "uExternComm_setPowerSystem_cmd";
uExternComm_setPowerSystemMsg(/* args */){};
~uExternComm_setPowerSystemMsg(){};
int getSetPMSys(const std::string& key, const std::string& msg)
{
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
std::cerr << "JSON解析失败: " << errs << std::endl;
return -1;
}
// 检查并读取电源系统设置
if (root.isMember("powerCmd_uint8") && root["powerCmd_uint8"].isUInt()) {
return root["powerCmd_uint8"].asUInt();
} else {
LOG_F(WARNING, "缺少或类型不匹配: powerCmd_uint8");
return -1;
}
}
};
#endif
@@ -1,70 +0,0 @@
/*
{
"rpm_int16": "xx", // 期望转速;单位:rpm;类型 int16_t
"enable_int16": "xx" // 使能指令,1 - 使能,0 - 去使能。类型 int16_t
}
*/
#ifndef UEXTERNCOMM_SETTHRUSTSPEED_CMD_H
#define UEXTERNCOMM_SETTHRUSTSPEED_CMD_H
#include "../logc/loguru.hpp"
#include "json/json.h"
#include <string>
#include <memory>
#include <sstream>
class uExternComm_setThrustSpeed_cmd
{
private:
/* data */
public:
uExternComm_setThrustSpeed_cmd(){};
~uExternComm_setThrustSpeed_cmd(){};
int16_t rpm_int16;
int16_t enable_int16;
static std::string key;
std::string toJsonString()
{
Json::Value j;
j["rpm_int16"] = rpm_int16;
j["enable_int16"] = enable_int16;
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = "";
return Json::writeString(builder, j);
}
bool fromJsonString(std::string jsonString)
{
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(jsonString);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return false;
}
// 提取rpm_int16
if (root.isMember("rpm_int16") && root["rpm_int16"].isInt()) {
rpm_int16 = root["rpm_int16"].asInt();
// LOG_F(INFO, "rpm_int16: %d", rpm_int16);
}
// 提取enable_int16
if (root.isMember("enable_int16") && root["enable_int16"].isInt()) {
enable_int16 = root["enable_int16"].asInt();
// LOG_F(INFO, "enable_int16: %d", enable_int16);
}
// LOG_F(INFO, "jsonString: %s", jsonString.c_str());
return true;
}
};
#endif
@@ -1,35 +0,0 @@
#ifndef UPLAN_PLANSTOP_CMD_H
#define UPLAN_PLANSTOP_CMD_H
#include <string>
#include "../logc/loguru.hpp"
#define DB_TO_PM_TASKSTOP "uPlan_planStop_st"
class uPlan_planStopMsg
{
private:
/* data */
public:
std::string key = "uPlan_planStop_st";
uPlan_planStopMsg(/* args */){};
~uPlan_planStopMsg(){};
bool getTaskStop(const std::string& key, const std::string& msg) {
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return false;
}
// 检查op_int32字段
if (root.isMember("op_int32") && root["op_int32"].isInt()) {
return root["op_int32"].asInt() == 1;
}
return false;
};
};
#endif
@@ -1,55 +0,0 @@
#ifndef UPPERCOMMMANAGER_H
#define UPPERCOMMMANAGER_H
#include <string>
#include "../pmSysvariable.h"
#include "../logc/loguru.hpp"
//决策消息
#define DB_TO_PM_MISSION "uPlan_taskStart_st"
class uPlan_taskStartMsg
{
private:
/* data */
public:
std::string key = "uPlan_taskStart_st";
uPlan_taskStartMsg(/* args */){};
~uPlan_taskStartMsg(){};
MissionState getMission(const std::string& key, const std::string& msg) {
MissionState mission{};
Json::Value root;
Json::CharReaderBuilder builder;
JSONCPP_STRING errs;
// 解析JSON字符串
std::istringstream stream(msg);
if (!Json::parseFromStream(builder, stream, &root, &errs)) {
std::cerr << "JSON解析失败: " << errs << std::endl;
LOG_F(WARNING, "JSON解析失败: %s", errs.c_str());
return mission;
}
// 提取taskType_str
if (root.isMember("taskType_str") && root["taskType_str"].isString()) {
mission.type = TaskType::fromString(root["taskType_str"].asString());
LOG_F(INFO, "taskType: %d", mission.type, root["taskType_str"].asString());
}
// 提取balanceEnabled_bool
if (root.isMember("balanceEnabled_bool") && root["balanceEnabled_bool"].isBool()) {
mission.enableBuoyancyAdjustment = root["balanceEnabled_bool"].asBool();
LOG_F(INFO, "balanceEnabled_bool: %d", mission.enableBuoyancyAdjustment);
}
// 提取bowRudderUnfold_bool
if (root.isMember("bowRudderUnfold_bool") && root["bowRudderUnfold_bool"].isBool()) {
mission.enableBowRudder = root["bowRudderUnfold_bool"].asBool();
LOG_F(INFO, "bowRudderUnfold_bool: %d", mission.enableBowRudder);
}
return mission;
};
};
#endif
@@ -1,81 +0,0 @@
#ifndef U_POWER_CURRENTSTATE_ST_H
#define U_POWER_CURRENTSTATE_ST_H
#include "../driver.h"
#include "string"
#include "json/json.h"
#include <bitset>
class uPower_currentStateMsg
{
private:
/* data */
public:
uPower_currentStateMsg() {
memset(&state, 0, sizeof(state));
};
~uPower_currentStateMsg() {};
std::string key = "uPower_currentState_st";
pmState state;
std::string buildMsg(pmState state)
{
// 状态赋值
Json::Value currentBusState;
currentBusState["HVBpowerBusVol_uint16"] = state.bus1State.powerBusVoltage;
currentBusState["HVBdcDc5Cur_uint16"] = state.bus1State.dcDc5ModuleCurrent;
currentBusState["HVBpowerBusCur_uint16"] = state.bus1State.powerBusCurrent;
currentBusState["HVBbusAVol_uint16"] = state.bus1State.busbarAVoltage;
currentBusState["HVBmotorCur_uint16"] = state.bus1State.propulsionMotorControlBoxCurrent;
currentBusState["HVBbusACur_uint16"] = state.bus1State.busbarACurrent;
currentBusState["HVBbatGroupCur_uint16"] = state.bus1State.lithiumBatteryGroupMeterCurrent;
currentBusState["HVBbowHVolDisBoxCur_uint16"] = state.bus1State.bowHighVoltageDistributionBoxCurrent;
currentBusState["HVBFTDev45Cur_uint16"] = state.bus1State.sternFTDevice45Current;
currentBusState["HVBtyzCur_uint16"] = state.bus1State.tyzReservedSwitchCurrent;
currentBusState["HVBsternRud1Cur_uint16"] = state.bus1State.sternRudderSwitch1Current;
currentBusState["HVBreservedCur1_uint16"] = state.bus1State.reservedCurrent1;
currentBusState["HVBsternRud2Cur_uint16"] = state.bus1State.sternRudderSwitch2Current;
currentBusState["HVBreservedCur2_uint16"] = state.bus1State.reservedCurrent2;
currentBusState["HVBfbCur_uint16"] = state.bus1State.fbReservedSwitchCurrent;
currentBusState["HVBreservedCur3_uint16"] = state.bus1State.reservedCurrent3;
currentBusState["HVABbusBVol_uint16"] = state.bus2State.busbarBVoltage;
currentBusState["HVABbusBCur_uint16"] = state.bus2State.busbarBCurrent;
currentBusState["HVABFTDev123Cur_uint16"] = state.bus2State.bowFTDevice123Current;
currentBusState["HVABactCur_uint16"] = state.bus2State.actuatorCurrent;
currentBusState["HVABmastSteGeaCur_uint16"] = state.bus2State.mastSteeringGearControlBoxCurrent;
currentBusState["HVABxczCur_uint16"] = state.bus2State.xczCurrent;
currentBusState["HVABbowRudCur_uint16"] = state.bus2State.bowRudderControlBoxCurrent;
currentBusState["HVABopenWaterCur_uint16"] = state.bus2State.openWaterCoverStartCylinderCurrent;
currentBusState["HVBBinsBusVol_uint16"] = state.bus3State.instrumentBusbarVoltage;
currentBusState["HVBBbowLowVolDisBoxCur_uint16"] = state.bus3State.bowLowVoltageDistributionBoxCurrent;
currentBusState["HVBBdcC1InsCur_uint16"] = state.bus3State.dcC1InstrumentDC48VCurrent;
currentBusState["HVBBreservedCur1_uint16"] = state.bus3State.reservedCurrent1;
currentBusState["HVBBemerBatCur_uint16"] = state.bus3State.emergencyLithiumBatteryGroup2Current;
currentBusState["HVBBbatInsCur_uint16"] = state.bus3State.lithiumBatteryGroupInstrumentCurrent;
currentBusState["HVBBunit4Cur_uint16"] = state.bus3State.unit4InstrumentDC48VCurrent;
currentBusState["HVBBreservedCur2_uint16"] = state.bus3State.reservedCurrent2;
currentBusState["HVBBemer2BusVol_uint16"] = state.bus3State.emergency2BusbarVoltage;
currentBusState["HVBBCCUCur_uint16"] = state.bus3State.compositeEnergyManagementSystemEmergencyDC48VCurrent;
currentBusState["HVBBFCSSecurSysCur_uint16"] = state.bus3State.fuelCellSecuritySystemEmergencyDC48VCurrent;
currentBusState["HVBBplcCur_uint16"] = state.bus3State.plcControlPowerCurrent;
currentBusState["LVBinsBusVol_uint16"] = state.bus4State.instrumentBusbarVoltage;
currentBusState["LVBBinsBusCur_uint16"] = state.bus4State.instrumentBusbarCurrent;
currentBusState["LVBBunit1Cur_uint16"] = state.bus4State.unit1Current;
currentBusState["LVBBunit2Cur_uint16"] = state.bus4State.unit2Current;
currentBusState["LVBBunit3Cur_uint16"] = state.bus4State.unit3Current;
currentBusState["LVBBunit5Cur_uint16"] = state.bus4State.unit5Current;
currentBusState["LVBBbowPZDevCur_uint16"] = state.bus4State.bowPZDeviceCurrent;
currentBusState["LVBBemerBatCur_uint16"] = state.bus4State.emergencyLithiumBatteryGroup1Current;
currentBusState["LVBBemerBusVol_uint16"] = state.bus4State.emergency1BusbarVoltage;
// 转化为json格式
Json::StreamWriterBuilder writerBuilder;
writerBuilder.settings_["indentation"] = "";
return Json::writeString(writerBuilder, currentBusState);
}
};
#endif
@@ -1,203 +0,0 @@
#ifndef U_POWER_DISSYSSTATE_ST_H
#define U_POWER_DISSYSSTATE_ST_H
#include "../driver.h"
#include "string"
#include "json/json.h"
#include <bitset>
#include "../pmSysvariable.h"
#include "../systemData.h"
class uPower_disSysStateMsg
{
private:
/* data */
public:
uPower_disSysStateMsg() {
// state_of_bus_hv = {};
// state_of_bus_hv_a = {};
// state_of_bus_lv = {};
// state_of_bus_lv_a = {};
};
~uPower_disSysStateMsg() {};
std::string key = "uPower_disSysState_st";
struct state_of_bus_hv
{
// 使用 std::bitset 存储 16 位继电器状态
std::bitset<16> relay_states;
// 燃料电池断路器
void set_fuel_cell_breaker(bool state) { relay_states[0] = state; }
// 动力锂电池断路器
void set_power_lithium_battery_breaker(bool state) { relay_states[1] = state; }
// 推进电机断路器
void set_propulsion_motor_breaker(bool state) { relay_states[2] = state; }
// 锂电池组仪表断路器
void set_lithium_battery_meter_breaker(bool state) { relay_states[3] = state; }
// DC/DC5模块断路器
void set_dc_dc5_module_breaker(bool state) { relay_states[4] = state; }
// 艏部高压配电箱断路器
void set_bow_high_voltage_box_breaker(bool state) { relay_states[5] = state; }
// 艉部FT装置4/5断路器
void set_stern_ft_device_breaker(bool state) { relay_states[6] = state; }
// 艉舵开关1断路器
void set_stern_rudder_switch1_breaker(bool state) { relay_states[7] = state; }
// 艉舵开关2断路器
void set_stern_rudder_switch2_breaker(bool state) { relay_states[8] = state; }
// FB预留断路器
void set_fb_reserved_breaker(bool state) { relay_states[9] = state; }
// TYZ预留断路器
void set_tyz_reserved_breaker(bool state) { relay_states[10] = state; }
// 预留断路器
void set_reserved_breaker(bool state) { relay_states[11] = state; }
// 初始化后 16 - 12 位为 0
state_of_bus_hv() {
relay_states.reset(12);
relay_states.reset(13);
relay_states.reset(14);
relay_states.reset(15);
}
} m_state_of_bus_hv;
struct state_of_bus_hv_a
{
std::bitset<16> relay_states;
// 艏部FT装置1 - 3 断路器
void set_bow_ft_device_1_3_breaker(bool state) { relay_states[0] = state; }
// 启闭机构断路器
void set_opening_closing_mechanism_breaker(bool state) { relay_states[1] = state; }
// 桅杆舵机控制箱断路器
void set_mast_servo_control_box_breaker(bool state) { relay_states[2] = state; }
// XCZ 断路器
void set_xcz_breaker(bool state) { relay_states[3] = state; }
// 艏舵控制箱断路器
void set_bow_rudder_control_box_breaker(bool state) { relay_states[4] = state; }
// 敞水盖启动电缸断路器
void set_open_water_cover_actuator_breaker(bool state) { relay_states[5] = state; }
// 初始化后 6 - 15 位为 0
state_of_bus_hv_a() {
for (int i = 6; i < 16; ++i) {
relay_states.reset(i);
}
}
}m_state_of_bus_hv_a;
struct state_of_bus_lv
{
std::bitset<16> relay_states;
// 锂电池组(仪表)断路器
void set_lithium_battery_group_meter_breaker(bool state) { relay_states[0] = state; }
// 艏部低压配电箱断路器
void set_bow_low_voltage_box_breaker(bool state) { relay_states[1] = state; }
// UNIT4仪表DC48V断路器
void set_unit4_meter_dc48v_breaker(bool state) { relay_states[2] = state; }
// DC - C1直流配电板断路器
void set_dc_c1_dc_distribution_board_breaker(bool state) { relay_states[3] = state; }
// 预留断路器
void set_reserved_breaker_1(bool state) { relay_states[4] = state; }
// 预留断路器
void set_reserved_breaker_2(bool state) { relay_states[5] = state; }
// 应急锂电池组2断路器
void set_emergency_lithium_battery_group_2_breaker(bool state) { relay_states[6] = state; }
// 初始化后 7 - 15 位为 0
state_of_bus_lv() {
for (int i = 7; i < 16; ++i) {
relay_states.reset(i);
}
}
}m_state_of_bus_lv;
struct state_of_bus_lv_a
{
// 使用 std::bitset 存储 16 位继电器状态
std::bitset<16> relay_states;
// UNIT1断路器
void set_unit1_breaker(bool state) { relay_states[0] = state; }
// UNIT2断路器
void set_unit2_breaker(bool state) { relay_states[1] = state; }
// UNIT3断路器
void set_unit3_breaker(bool state) { relay_states[2] = state; }
// UNIT5断路器
void set_unit5_breaker(bool state) { relay_states[3] = state; }
// 艏部PZ装置断路器
void set_bow_pz_device_breaker(bool state) { relay_states[4] = state; }
// 预留断路器
void set_reserved_breaker_1(bool state) { relay_states[5] = state; }
// 预留断路器
void set_reserved_breaker_2(bool state) { relay_states[6] = state; }
// 应急锂电池组1断路器
void set_emergency_lithium_battery_group_1_breaker(bool state) { relay_states[7] = state; }
// 初始化后 8 - 15 位为 0
state_of_bus_lv_a() {
for (int i = 8; i < 16; ++i) {
relay_states.reset(i);
}
}
}m_state_of_bus_lv_a;
// std::string buildMsg(SystemData data,disSysBreakerList currentDisBreakerState)
std::string buildMsg(const SystemData& data,disSysBreakerList currentDisBreakerState)
{
//构建配电系统状态报告
Json::Value dis;
// disBus1Breaker dis1 = currentDisBreakerState.bus1Breaker;
m_state_of_bus_hv.set_fuel_cell_breaker((data.power_bus_status.fuelCellCircuitBreaker == 1));
m_state_of_bus_hv.set_power_lithium_battery_breaker((data.power_bus_status.powerLithiumBatteryCircuitBreaker == 1));
m_state_of_bus_hv.set_propulsion_motor_breaker((data.power_bus_status.propulsionMotorCircuitBreaker == 1));
m_state_of_bus_hv.set_lithium_battery_meter_breaker((data.power_bus_status.lithiumBatteryGroupInstrumentCircuitBreaker == 1));
m_state_of_bus_hv.set_dc_dc5_module_breaker((data.power_bus_status.dcDc5ModuleCircuitBreaker == 1));
m_state_of_bus_hv.set_bow_high_voltage_box_breaker((data.power_bus_status.bowHighVoltageDistributionBoxCircuitBreaker == 1));
m_state_of_bus_hv.set_stern_ft_device_breaker((data.power_bus_status.sternFTDevice45CircuitBreaker == 1));
m_state_of_bus_hv.set_stern_rudder_switch1_breaker((data.power_bus_status.sternRudderSwitch1CircuitBreaker == 1));
m_state_of_bus_hv.set_stern_rudder_switch2_breaker((data.power_bus_status.sternRudderSwitch2CircuitBreaker == 1));
m_state_of_bus_hv.set_fb_reserved_breaker((data.power_bus_status.fbReservedCircuitBreaker == 1));
m_state_of_bus_hv.set_tyz_reserved_breaker((data.power_bus_status.tyzReservedCircuitBreaker == 1));
m_state_of_bus_hv.set_reserved_breaker((data.power_bus_status.reservedCircuitBreaker == 1));
dis["HVolABusBreaker_uint16"] = m_state_of_bus_hv.relay_states.to_ulong();
m_state_of_bus_hv_a.set_bow_ft_device_1_3_breaker((data.power_a_bus_status.bowFTDevice123CircuitBreaker == 1));
m_state_of_bus_hv_a.set_opening_closing_mechanism_breaker((data.power_a_bus_status.actuatorCircuitBreaker == 1));
m_state_of_bus_hv_a.set_mast_servo_control_box_breaker((data.power_a_bus_status.mastSteeringGearControlBoxCircuitBreaker == 1));
m_state_of_bus_hv_a.set_xcz_breaker((data.power_a_bus_status.xczCircuitBreaker == 1));
m_state_of_bus_hv_a.set_bow_rudder_control_box_breaker((data.power_a_bus_status.bowRudderControlBoxCircuitBreaker == 1));
m_state_of_bus_hv_a.set_open_water_cover_actuator_breaker((data.power_a_bus_status.openWaterCoverStartCylinderCircuitBreaker == 1));
dis["HVolBBusBreaker_uint16"] = m_state_of_bus_hv_a.relay_states.to_ulong();
m_state_of_bus_lv.set_bow_low_voltage_box_breaker((data.instrument_main_bus_status.bowLowVoltageDistributionBoxCircuitBreaker == 1));
m_state_of_bus_lv.set_dc_c1_dc_distribution_board_breaker((data.instrument_main_bus_status.dcC1DCDistributionPanelCircuitBreaker == 1));
m_state_of_bus_lv.set_unit4_meter_dc48v_breaker((data.instrument_main_bus_status.unit4InstrumentDC48VCircuitBreaker == 1));
m_state_of_bus_lv.set_lithium_battery_group_meter_breaker((data.instrument_main_bus_status.lithiumBatteryGroupInstrumentCircuitBreaker == 1));
m_state_of_bus_lv.set_emergency_lithium_battery_group_2_breaker((data.instrument_main_bus_status.emergencyLithiumBatteryGroup2CircuitBreaker == 1));
m_state_of_bus_lv.set_reserved_breaker_1((data.instrument_main_bus_status.reservedCircuitBreaker1 == 1));
m_state_of_bus_lv.set_reserved_breaker_2((data.instrument_main_bus_status.reservedCircuitBreaker2 == 1));
dis["HVolBusBreaker_uint16"] = m_state_of_bus_lv.relay_states.to_ulong();
m_state_of_bus_lv_a.set_unit1_breaker((data.instrument_bus_status.unit1CircuitBreaker == 1));
m_state_of_bus_lv_a.set_unit2_breaker((data.instrument_bus_status.unit2CircuitBreaker == 1));
m_state_of_bus_lv_a.set_unit3_breaker((data.instrument_bus_status.unit3CircuitBreaker == 1));
m_state_of_bus_lv_a.set_unit5_breaker((data.instrument_bus_status.unit5CircuitBreaker == 1));
m_state_of_bus_lv_a.set_bow_pz_device_breaker((data.instrument_bus_status.bowPZDeviceCircuitBreaker == 1));
m_state_of_bus_lv_a.set_reserved_breaker_1((data.instrument_bus_status.reservedCircuitBreaker1 == 1));
m_state_of_bus_lv_a.set_reserved_breaker_2((data.instrument_bus_status.reservedCircuitBreaker2 == 1));
m_state_of_bus_lv_a.set_emergency_lithium_battery_group_1_breaker((data.instrument_bus_status.emergencyLithiumBatteryGroup1CircuitBreaker == 1));
dis["LVolBusBreaker_uint16"] = m_state_of_bus_lv_a.relay_states.to_ulong();
//构建配电系统故障码
Json::Value faultCodeDis_array = Json::Value(Json::arrayValue);
{
std::lock_guard<std::mutex> lock(data.faultCodeMutex);
for(auto val : data.disSysFaultCode)
{
faultCodeDis_array.append(val);
}
}
dis["faultCode_array"] = faultCodeDis_array;
Json::StreamWriterBuilder writerBuilder;
writerBuilder.settings_["indentation"] = "";
return Json::writeString(writerBuilder, dis);
}
};
#endif
@@ -1,220 +0,0 @@
#ifndef U_POWER_EQUINSSTATE_H
#define U_POWER_EQUINSSTATE_H
#include "../driver.h"
#include "string"
#include "json/json.h"
#include <bitset>
class uPower_equInsStateMsg
{
private:
/* data */
public:
uPower_equInsStateMsg() {
// 初始化成员变量
//state全部初始化为0
memset(&state, 0, sizeof(state));
}; // 添加默认构造函数实现
~uPower_equInsStateMsg() {}; // 添加析构函数实现
std::string key = "uPower_equInsState_fb";
struct SensorState
{
unsigned int singleBeamSonar1 : 1; // 单波束声呐1
unsigned int singleBeamSonar2 : 1; // 单波束声呐2
unsigned int singleBeamSonar3 : 1; // 单波束声呐3
unsigned int singleBeamSonar4 : 1; // 单波束声呐4
unsigned int singleBeamSonar5 : 1; // 单波束声呐5
unsigned int singleBeamSonar6 : 1; // 单波束声呐6
unsigned int singleBeamSonar7 : 1; // 单波束声呐7
unsigned int singleBeamSonar8 : 1; // 单波束声呐8
unsigned int forwardSonar : 1; // 前视声呐
unsigned int ctd1 : 1; // CTD1
unsigned int ctd2 : 1; // CTD2
unsigned int soundVelocityMeter : 1; // 声速仪
unsigned int ntp : 1; // 深度计
unsigned int depthMeter1 : 1; // 深度计1
unsigned int depthMeter2 : 1; // 深度计2
unsigned int depthMeter3 : 1; // 深度计3
};
struct ActuatorState
{
unsigned int openCloseMechanism1 : 1; // bit 0 启闭机构1
unsigned int openCloseMechanism2 : 1; // bit 1 启闭机构2
unsigned int openCloseMechanism3 : 1; // bit 2 启闭机构3
unsigned int openCloseMechanism4 : 1; // bit 3 启闭机构4
unsigned int openCloseMechanism5 : 1; // bit 4 启闭机构5
unsigned int openCloseMechanism6 : 1; // bit 5 启闭机构6
unsigned int reserved1 : 2; // bit 6-7 保留
unsigned int openWaterMechanism1 : 1; // bit 8 敞水机构1
unsigned int openWaterMechanism2 : 1; // bit 9 敞水机构2
unsigned int openWaterMechanism3 : 1; // bit 10 敞水机构3
unsigned int openWaterMechanism4 : 1; // bit 11 敞水机构4
unsigned int openWaterMechanism5 : 1; // bit 12 敞水机构5
unsigned int openWaterMechanism6 : 1; // bit 13 敞水机构6
unsigned int openWaterMechanism7 : 1; // bit 14 敞水机构7
unsigned int openWaterMechanism8 : 1; // bit 15 敞水机构8
};
struct MotionState
{
unsigned int sternRudder1 : 1; // bit 0 艉部舵机1
unsigned int sternRudder2 : 1; // bit 1 艉部舵机2
unsigned int sternRudder3 : 1; // bit 2 艉部舵机3
unsigned int sternRudder4 : 1; // bit 3 艉部舵机4
unsigned int bowRudder : 1; // bit 4 艏舵电缸
unsigned int waterInjection : 1; // bit 5 注水机构
unsigned int propulsionMotor : 1; // bit 6 推进电机
unsigned int buoyancyAdjust1 : 1; // bit 7 浮调机构(艏部)1
unsigned int buoyancyAdjust2 : 1; // bit 8 浮调机构(艏部)2
unsigned int buoyancyAdjust3 : 1; // bit 9 浮调机构(艏部)3
unsigned int buoyancyAdjust4 : 1; // bit 10 浮调机构(艉部)4
unsigned int buoyancyAdjust5 : 1; // bit 11 浮调机构(艉部)5
unsigned int DVL : 1; // bit 12 DVL
unsigned int INS : 1; // bit 13 惯导系统
unsigned int bowRudderLeft : 1; // bit 14 艏舵左舵机
unsigned int bowRudderRight : 1; // bit 15 艏右舵舵机
};
struct LoadcommState
{
unsigned int payloadControl : 1; // bit 0 载荷发控
unsigned int optMast : 1; // bit 1 光电桅杆
unsigned int emMast : 1; // bit 2 电磁桅杆
unsigned int reserved1 : 1; // bit 3 保留
unsigned int sideSonar : 1; // bit 4 舷侧声纳
unsigned int towedSonar : 1; // bit 5 拖曳声纳
unsigned int reserved2 : 2; // bit 6-7 保留
unsigned int commMast : 1; // bit 8 通信桅杆
unsigned int midLowFreqSonar : 1; // bit 9 中低频声呐
unsigned int highFreqComm : 1; // bit 10 高频声通机
unsigned int buoySystem : 1; // bit 11 浮标系统
unsigned int commMastRudder : 1; // bit 12 通信桅杆舵机
unsigned int optMastRudder : 1; // bit 13 光电桅杆舵机
unsigned int emMastRudder : 1; // bit 14 电磁桅杆舵机
unsigned int reserved3 : 1; // bit 15 保留
};
struct ThrowPowerState
{
unsigned int bowThrow : 1; // bit 0 艏部抛载
unsigned int reserved1 : 7; // bit 1-7 保留
unsigned int fuelCellController : 1; // bit 8 燃料电池控制器
unsigned int instrumentPanel : 1; // bit 9 仪表配电板
unsigned int powerPanel : 1; // bit 10 动力配电板
unsigned int instrumentBattery : 1; // bit 11 仪表锂电池
unsigned int powerBattery : 1; // bit 12 动力锂电
unsigned int sternThrow : 1; // bit 13 艉部抛载
unsigned int reserved2 : 2; // bit 14-15 保留
};
struct uPower_equInsState
{
SensorState sensorState;
ActuatorState actuatorState;
MotionState motionState;
LoadcommState loadcommState;
ThrowPowerState throwPowerState;
} state;
std::string buildMsg(DriverTable s)
{
// 按照协议格式填充数据
// 传感器状态赋值
state.sensorState.singleBeamSonar1 = s.singleBeamSonar1;
state.sensorState.singleBeamSonar2 = s.singleBeamSonar2;
state.sensorState.singleBeamSonar3 = s.singleBeamSonar3;
state.sensorState.singleBeamSonar4 = s.singleBeamSonar4;
state.sensorState.singleBeamSonar5 = s.singleBeamSonar5;
state.sensorState.singleBeamSonar6 = s.singleBeamSonar6;
state.sensorState.singleBeamSonar7 = s.singleBeamSonar7;
state.sensorState.singleBeamSonar8 = s.singleBeamSonar8;
state.sensorState.forwardSonar = s.forwardSonar;
state.sensorState.ctd1 = s.ctd1;
state.sensorState.ctd2 = s.ctd2;
state.sensorState.soundVelocityMeter = s.soundVeloc;
state.sensorState.ntp = s.ntp;
state.sensorState.depthMeter1 = s.depth1;
state.sensorState.depthMeter2 = s.depth2;
state.sensorState.depthMeter3 = s.depth3;
// 执行机构状态赋值
state.actuatorState.openCloseMechanism1 = s.bowCover1;
state.actuatorState.openCloseMechanism2 = s.bowCover2;
state.actuatorState.openCloseMechanism3 = s.bowCover3;
state.actuatorState.openCloseMechanism4 = s.bowCover4;
state.actuatorState.openCloseMechanism5 = s.bowCover5;
state.actuatorState.openCloseMechanism6 = s.bowCover6;
state.actuatorState.openWaterMechanism1 = s.bowWaterInet1;
state.actuatorState.openWaterMechanism2 = s.bowWaterInet2;
state.actuatorState.openWaterMechanism3 = s.bowWaterInet3;
state.actuatorState.openWaterMechanism4 = s.bowWaterInet4;
state.actuatorState.openWaterMechanism5 = s.bowWaterInet5;
state.actuatorState.openWaterMechanism6 = s.bowWaterInet6;
state.actuatorState.openWaterMechanism7 = s.bowWaterInet7;
state.actuatorState.openWaterMechanism8 = s.bowWaterInet8;
// 运动状态赋值
state.motionState.sternRudder1 = s.sternRudderServo1;
state.motionState.sternRudder2 = s.sternRudderServo2;
state.motionState.sternRudder3 = s.sternRudderServo3;
state.motionState.sternRudder4 = s.sternRudderServo4;
state.motionState.bowRudder = s.bowRudderDeploy;
state.motionState.waterInjection = s.waterInject;
state.motionState.propulsionMotor = s.thruster;
state.motionState.buoyancyAdjust1 = s.buoyancyAdjust1;
state.motionState.buoyancyAdjust2 = s.buoyancyAdjust2;
state.motionState.buoyancyAdjust3 = s.buoyancyAdjust3;
state.motionState.buoyancyAdjust4 = s.buoyancyAdjust4;
state.motionState.buoyancyAdjust5 = s.buoyancyAdjust5;
state.motionState.DVL = s.DVL;
state.motionState.INS = s.ins;
state.motionState.bowRudderLeft = s.bowRudderServo1;
state.motionState.bowRudderRight = s.bowRudderServo2;
// 负载通信状态赋值
state.loadcommState.payloadControl = s.launchControler;
state.loadcommState.sideSonar = s.sideSonar;
state.loadcommState.optMast = s.photoSensor;
state.loadcommState.emMast = s.electSensor;
state.loadcommState.buoySystem = s.buoyage;
state.loadcommState.commMastRudder = s.mastLiftingServo;
state.loadcommState.optMastRudder = s.mastLiftingServo;
state.loadcommState.emMastRudder = s.mastLiftingServo;
state.loadcommState.highFreqComm = s.USBL;
state.loadcommState.midLowFreqSonar = s.lowFreqSonar;
state.loadcommState.commMast = s.commMast;
state.loadcommState.towedSonar = 0; //拖曳声纳未安装,默认为0
// 抛载和电源状态赋值
state.throwPowerState.bowThrow = s.bowThrow; // 艏部抛载
state.throwPowerState.sternThrow = s.sternThrow; // 艉部抛载
state.throwPowerState.fuelCellController = 1; // 燃料电池控制器 默认开启
state.throwPowerState.instrumentPanel = 1; // 仪表配电板 默认开启
state.throwPowerState.powerPanel = 1; // 动力配电板 默认开启
state.throwPowerState.instrumentBattery = 1; // 仪表锂电池 默认开启
state.throwPowerState.powerBattery = 1; // 动力锂电 默认开启
// 转化为json格式
Json::Value root;
root["sensor_int16"] = *reinterpret_cast<uint16_t*>(&state.sensorState);
root["actuator_int16"] = *reinterpret_cast<uint16_t*>(&state.actuatorState);
root["motion_int16"] = *reinterpret_cast<uint16_t*>(&state.motionState);
root["loadcomm_int16"] = *reinterpret_cast<uint16_t*>(&state.loadcommState);
root["throwpower_int16"] = *reinterpret_cast<uint16_t*>(&state.throwPowerState);
//以二进制形式打印
// std::cout << "sensor_int16: " << std::bitset<16>(*reinterpret_cast<uint16_t*>(&state.sensorState)) << std::endl;
// std::cout << "actuator_int16: " << std::bitset<16>(*reinterpret_cast<uint16_t*>(&state.actuatorState)) << std::endl;
// std::cout << "motion_int16: " << std::bitset<16>(*reinterpret_cast<uint16_t*>(&state.motionState)) << std::endl;
// std::cout << "loadcomm_int16: " << std::bitset<16>(*reinterpret_cast<uint16_t*>(&state.loadcommState)) << std::endl;
// std::cout << "throwpower_int16: " << std::bitset<16>(*reinterpret_cast<uint16_t*>(&state.throwPowerState)) << std::endl;
// 返回json字符串
Json::StreamWriterBuilder writerBuilder;
writerBuilder.settings_["indentation"] = ""; // 紧凑格式,无缩进
return Json::writeString(writerBuilder, root);
}
};
#endif

Some files were not shown because too many files have changed in this diff Show More