diff --git a/docs/信息中心-复合管控双锂电阶段协议20231006.docx b/docs/信息中心-复合管控双锂电阶段协议20231006.docx deleted file mode 100644 index 2ffdcaa..0000000 Binary files a/docs/信息中心-复合管控双锂电阶段协议20231006.docx and /dev/null differ diff --git a/docs/控制主机与复合管控器通信协议- 0824.xlsx b/docs/控制主机与复合管控器通信协议- 0824.xlsx index 1b8801c..28cf83e 100644 Binary files a/docs/控制主机与复合管控器通信协议- 0824.xlsx and b/docs/控制主机与复合管控器通信协议- 0824.xlsx differ diff --git a/docs/超滑FC和复合管控器通讯协议 - 0821.docx b/docs/超滑FC和复合管控器通讯协议 - 0821.docx deleted file mode 100644 index e1dda24..0000000 Binary files a/docs/超滑FC和复合管控器通讯协议 - 0821.docx and /dev/null differ diff --git a/docs/超滑FC和复合管控器通讯协议 - 0827.docx b/docs/超滑FC和复合管控器通讯协议 - 0827.docx new file mode 100644 index 0000000..523db56 Binary files /dev/null and b/docs/超滑FC和复合管控器通讯协议 - 0827.docx differ diff --git a/docs/锂电池协议20230324(1).docx b/docs/锂电池协议20230324(1).docx deleted file mode 100644 index 75d089c..0000000 Binary files a/docs/锂电池协议20230324(1).docx and /dev/null differ diff --git a/missions/h100.moos b/missions/h100.moos index 3420367..091dc9b 100644 --- a/missions/h100.moos +++ b/missions/h100.moos @@ -16,6 +16,9 @@ ProcessConfig = pPowerManger logpath = /root/work/h100/data/pPowerManger.log // 数据库存储路径(目录需已存在,由 deploy.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 } ProcessConfig = pPowerMangerHost diff --git a/missions/pPowerManger.moos b/missions/pPowerManger.moos index 3fe4061..60c307b 100644 --- a/missions/pPowerManger.moos +++ b/missions/pPowerManger.moos @@ -2,4 +2,7 @@ ProcessConfig = pPowerManger { AppTick = 4 CommsTick = 4 + // CCU 地址;不配置则默认 127.0.0.1:7000 + // ccuhost = 127.0.0.1 + // ccuport = 7000 } \ No newline at end of file diff --git a/scripts/build-board.sh b/scripts/build-board.sh index 5974d2a..c1b4b82 100755 --- a/scripts/build-board.sh +++ b/scripts/build-board.sh @@ -19,6 +19,8 @@ # --jobs 编译并行度(默认板卡 nproc) # --start 编译部署后重启服务 # --clean 先重新 cmake(配置变更时用) +# --ccu-host 覆盖 mission 中 pPowerManger 的 CCU 地址 (ccuhost) +# --ccu-port 覆盖 mission 中 pPowerManger 的 CCU 端口 (ccuport) # -h, --help 显示帮助 # # 前置: 板卡已配免密 SSH,且有编译环境(cmake/g++/MOOS/jsoncpp), @@ -39,7 +41,8 @@ # 关联端口: # FC 状态: FC:7000 -> 板卡:6000 (pCCU 监听) # FC 控制: 板卡:6000 -> FC:7000 (pCCU 发送) -# PM 指令: pPowerManger:5001 -> pCCU:7000 (127.0.0.1,见 PowerManger.h CCUHOST) +# 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) # # 排障提示: @@ -62,6 +65,8 @@ BOARD_BIN="/root/work/h100/bin" JOBS="" DO_START=0 DO_CLEAN=0 +CCU_HOST="" +CCU_PORT="" SERVICES=(moosdb pPowerManger pPowerMangerHost pCCU) @@ -71,7 +76,7 @@ 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,29p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' + sed -n '2,31p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' exit 0 } @@ -87,6 +92,8 @@ while [ $# -gt 0 ]; do --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 @@ -95,6 +102,10 @@ 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. 前置检查 #------------------------------------------------------------------- @@ -105,6 +116,21 @@ if ! ${SSH} "${SSH_TARGET}" 'true' 2>/dev/null; then 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 / 数据) #------------------------------------------------------------------- @@ -129,8 +155,19 @@ ok "源码已同步" if [ -f "${PROJECT_ROOT}/missions/h100.moos" ]; then info "同步 mission h100.moos ..." - rsync -az "${PROJECT_ROOT}/missions/h100.moos" \ - "${SSH_TARGET}:/root/work/h100/missions/" || { err "rsync mission 失败"; exit 1; } + 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 diff --git a/scripts/deploy.sh b/scripts/deploy.sh index f70f974..2962b8b 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -20,6 +20,8 @@ # --user 登录用户(默认 root) # --board-dir 板卡部署根目录(默认 /root/work/h100) # --start 部署后立即启动服务 +# --ccu-host 覆盖 mission 中 pPowerManger 的 CCU 地址 (ccuhost) +# --ccu-port 覆盖 mission 中 pPowerManger 的 CCU 端口 (ccuport) # # 板卡布局: # /root/work/h100/bin/ aarch64 二进制 @@ -37,6 +39,8 @@ HOST="192.168.0.223" USER="root" BOARD_DIR="/root/work/h100" DO_START=0 +CCU_HOST="" +CCU_PORT="" SERVICES=(moosdb pPowerManger pPowerMangerHost pCCU) @@ -46,7 +50,7 @@ warn() { printf '\033[1;33m[deploy]\033[0m %s\n' "$*"; } err() { printf '\033[1;31m[deploy]\033[0m %s\n' "$*"; } usage() { - sed -n '2,29p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' + sed -n '2,31p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' exit 0 } @@ -60,6 +64,8 @@ while [ $# -gt 0 ]; do --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 ;; + --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 ;; setup-ssh|status|stop|help|-h|--help) ACTION="$1"; shift ;; "") shift ;; *) err "未知参数: $1(见 help)"; exit 1 ;; @@ -67,6 +73,10 @@ while [ $# -gt 0 ]; do done [ -n "${ACTION}" ] || ACTION="deploy" +case "${CCU_PORT}" in + ""|*[!0-9]*) [ -z "${CCU_PORT}" ] || { err "--ccu-port 必须是数字端口: ${CCU_PORT}"; exit 1; } ;; +esac + SSH_TARGET="${USER}@${HOST}" SSH="ssh -o BatchMode=yes -o ConnectTimeout=5" SCP="scp -o BatchMode=yes -o ConnectTimeout=5" @@ -164,6 +174,21 @@ WantedBy=multi-user.target EOF } +#------------------------------------------------------------------- +# 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 +} + #------------------------------------------------------------------- # 命令: deploy #------------------------------------------------------------------- @@ -182,8 +207,19 @@ cmd_deploy() { "${SSH_TARGET}:${BOARD_DIR}/bin/" || { err "推送二进制失败"; return 1; } info "推送 mission h100.moos ..." - ${SCP} -q "${PROJECT_ROOT}/missions/h100.moos" \ - "${SSH_TARGET}:${BOARD_DIR}/missions/" || { err "推送 mission 失败"; return 1; } + local mission_src="${PROJECT_ROOT}/missions/h100.moos" + local mission_push="${mission_src}" tmp_moos="" + if [ -n "${CCU_HOST}" ] || [ -n "${CCU_PORT}" ]; then + tmp_moos="$(mktemp)" + cp "${mission_src}" "${tmp_moos}" + [ -n "${CCU_HOST}" ] && upsert_moos_param "${tmp_moos}" ccuhost "${CCU_HOST}" + [ -n "${CCU_PORT}" ] && upsert_moos_param "${tmp_moos}" ccuport "${CCU_PORT}" + mission_push="${tmp_moos}" + info "CCU 地址覆盖: ccuhost=${CCU_HOST:-<保持原值>} ccuport=${CCU_PORT:-<保持原值>}" + fi + ${SCP} -q "${mission_push}" \ + "${SSH_TARGET}:${BOARD_DIR}/missions/" || { err "推送 mission 失败"; [ -n "${tmp_moos}" ] && rm -f "${tmp_moos}"; return 1; } + [ -n "${tmp_moos}" ] && rm -f "${tmp_moos}" ${SSH} "${SSH_TARGET}" "chmod +x ${BOARD_DIR}/bin/pPowerManger ${BOARD_DIR}/bin/pPowerMangerHost ${BOARD_DIR}/bin/pCCU" ok "二进制与 mission 已推送" diff --git a/src/pCCU/CCU.cpp b/src/pCCU/CCU.cpp index 092adfb..fa2f596 100644 --- a/src/pCCU/CCU.cpp +++ b/src/pCCU/CCU.cpp @@ -218,15 +218,13 @@ void CCU::handlePmMessage(Message* msg, const std::vector& frame) { LOG_F(INFO, "[PM] control: mode=%d cmd=%d power=%d", c.mode, c.cmd, c.outputPower); - // 映射并转发到 FC(1.1.1:姿态为双通道 int16,PM 单通道填数据1) + // 映射并转发到 FC(0827协议:姿态仅数据1有效,数据2字节预留填0) FcControlValue f; f.mode = c.mode; f.cmd = c.cmd; f.outputPower = c.outputPower; f.pitch1 = c.pitch; - f.pitch2 = 0; f.roll1 = c.roll; - f.roll2 = 0; f.emergencyAllow = c.emergencyAllow; f.depth = c.depth; f.supplyCmd = c.supplyCmd; diff --git a/src/pCCU/core/SnapshotBuilder.cpp b/src/pCCU/core/SnapshotBuilder.cpp index a4b7f71..c0063b9 100644 --- a/src/pCCU/core/SnapshotBuilder.cpp +++ b/src/pCCU/core/SnapshotBuilder.cpp @@ -133,6 +133,7 @@ JsonVal fcStatusJson(const FcStatusValue& v) { put(j, "alloy_h2_flow", v.alloy_h2_flow); put(j, "fc_h2_flow", v.fc_h2_flow); put(j, "fc_o2_flow", v.fc_o2_flow); + put(j, "remaining_generation", v.remaining_generation); put(j, "heartbeat", v.heartbeat); put(j, "emergency_cmd", v.emergency_cmd); return j; @@ -173,9 +174,7 @@ JsonVal fcControlJson(const FcControlValue& v) { put(j, "cmd", v.cmd); put(j, "outputPower", v.outputPower); j["pitch1"] = JsonVal(static_cast(v.pitch1)); // 有符号 int16,×0.1° - j["pitch2"] = JsonVal(static_cast(v.pitch2)); j["roll1"] = JsonVal(static_cast(v.roll1)); - j["roll2"] = JsonVal(static_cast(v.roll2)); put(j, "emergencyAllow", v.emergencyAllow); put(j, "depth", v.depth); put(j, "supplyCmd", v.supplyCmd); diff --git a/src/pCCU/protocol/FcProtocol.cpp b/src/pCCU/protocol/FcProtocol.cpp index 3a7d95f..8081944 100644 --- a/src/pCCU/protocol/FcProtocol.cpp +++ b/src/pCCU/protocol/FcProtocol.cpp @@ -22,9 +22,9 @@ enum : size_t { CO_CMD = 1, // 操控指令 (字节8) CO_OUT_POWER = 2, // 输出功率指令 (字节9) CO_PITCH1 = 3, // 纵倾姿态数据1 int16 (字节10,11) - CO_PITCH2 = 5, // 纵倾姿态数据2 int16 (字节12,13) + CO_RESERVED1 = 5, // 预留 (字节12,13),0827协议由纵倾姿态数据2改为预留 CO_ROLL1 = 7, // 横倾姿态数据1 int16 (字节14,15) - CO_ROLL2 = 9, // 横倾姿态数据2 int16 (字节16,17) + CO_RESERVED2 = 9, // 预留 (字节16,17),0827协议由横倾姿态数据2改为预留 CO_EMER_ALLOW = 11, // 应急允许 (字节18) CO_DEPTH = 12, // 潜深深度 u16 (字节19,20) CO_SUPPLY_CMD = 14, // 补给/排放指令 (字节21) @@ -42,9 +42,9 @@ std::vector FcControlMessage::encode(const void* obj) const { FieldCodec::putU8(payload, CO_CMD, v->cmd); FieldCodec::putU8(payload, CO_OUT_POWER, v->outputPower); FieldCodec::putU16(payload, CO_PITCH1, static_cast(v->pitch1)); - FieldCodec::putU16(payload, CO_PITCH2, static_cast(v->pitch2)); + FieldCodec::putU16(payload, CO_RESERVED1, 0); // 预留恒填0 FieldCodec::putU16(payload, CO_ROLL1, static_cast(v->roll1)); - FieldCodec::putU16(payload, CO_ROLL2, static_cast(v->roll2)); + FieldCodec::putU16(payload, CO_RESERVED2, 0); // 预留恒填0 FieldCodec::putU8(payload, CO_EMER_ALLOW, v->emergencyAllow); FieldCodec::putU16(payload, CO_DEPTH, v->depth); FieldCodec::putU8(payload, CO_SUPPLY_CMD, v->supplyCmd); @@ -64,9 +64,9 @@ bool FcControlMessage::decode(const std::vector& frame, void* obj) cons v->cmd = FieldCodec::getU8(frame, o + CO_CMD); v->outputPower = FieldCodec::getU8(frame, o + CO_OUT_POWER); v->pitch1 = static_cast(FieldCodec::getU16(frame, o + CO_PITCH1)); - v->pitch2 = static_cast(FieldCodec::getU16(frame, o + CO_PITCH2)); + // 字节12,13 预留,忽略 v->roll1 = static_cast(FieldCodec::getU16(frame, o + CO_ROLL1)); - v->roll2 = static_cast(FieldCodec::getU16(frame, o + CO_ROLL2)); + // 字节16,17 预留,忽略 v->emergencyAllow = FieldCodec::getU8(frame, o + CO_EMER_ALLOW); v->depth = FieldCodec::getU16(frame, o + CO_DEPTH); v->supplyCmd = FieldCodec::getU8(frame, o + CO_SUPPLY_CMD); @@ -153,7 +153,8 @@ enum : size_t { OFF_ALLOY_H2_FLOW = 112, // 合金供氢流量 (字节119-120) OFF_FC_H2_FLOW = 114, // FC供氢流量 (字节121-122) OFF_FC_O2_FLOW = 116, // FC供氧流量 (字节123-124) - OFF_RESERVED = 118, // 预留1~12 u16*12 -> 118..141 (字节125-148) + OFF_RESERVED = 118, // 预留1~11 u16*11 -> 118..138 (字节125-146) + OFF_REMAIN_GEN = 140, // 剩余发电量 u16 (字节147,148),0827协议由预留12改为 OFF_HEARTBEAT = 142, // 通信心跳 (字节149) OFF_EMERGENCY_CMD = 143, // 应急指令 (字节150) }; @@ -244,7 +245,8 @@ std::vector FcStatusMessage::encode(const void* obj) const { FieldCodec::putU16(p, OFF_FC_H2_FLOW, v->fc_h2_flow); FieldCodec::putU16(p, OFF_FC_O2_FLOW, v->fc_o2_flow); - putU16Arr(p, OFF_RESERVED, v->reserved, 12); + putU16Arr(p, OFF_RESERVED, v->reserved, 11); + FieldCodec::putU16(p, OFF_REMAIN_GEN, v->remaining_generation); FieldCodec::putU8(p, OFF_HEARTBEAT, v->heartbeat); FieldCodec::putU8(p, OFF_EMERGENCY_CMD, v->emergency_cmd); @@ -331,7 +333,8 @@ bool FcStatusMessage::decode(const std::vector& frame, void* obj) const v->fc_h2_flow = FieldCodec::getU16(frame, o + OFF_FC_H2_FLOW); v->fc_o2_flow = FieldCodec::getU16(frame, o + OFF_FC_O2_FLOW); - getU16Arr(frame, o + OFF_RESERVED, v->reserved, 12); + getU16Arr(frame, o + OFF_RESERVED, v->reserved, 11); + v->remaining_generation = FieldCodec::getU16(frame, o + OFF_REMAIN_GEN); v->heartbeat = FieldCodec::getU8(frame, o + OFF_HEARTBEAT); v->emergency_cmd = FieldCodec::getU8(frame, o + OFF_EMERGENCY_CMD); diff --git a/src/pCCU/protocol/FcProtocol.h b/src/pCCU/protocol/FcProtocol.h index 1a755b1..7b4b927 100644 --- a/src/pCCU/protocol/FcProtocol.h +++ b/src/pCCU/protocol/FcProtocol.h @@ -10,28 +10,28 @@ namespace ccu { //============================================================================ // FC 协议(燃料电池系统 <-> 复合管控器) -// 依据:docs/超滑FC和复合管控器通讯协议 - 0821.docx +// 依据:docs/超滑FC和复合管控器通讯协议 - 0827.docx // // 帧格式统一:0x40 0x40 + 域标识符(2B) + 域字节数(2B) + 数据域 + [校验和] // 注意:FC 协议文档未列出校验和行,故默认采用 None(可插拔,联调可切换)。 // // 两条消息: -// 0x0001 复合管控器->燃料电池控制器(控制指令域) payload 14B,总长 20B +// 0x0001 复合管控器->燃料电池控制器(控制指令域) payload 18B,总长 24B // 0x0002 燃料电池控制器->复合管控器(状态反馈域) payload 144B,总长 150B //============================================================================ //-------------------------------------------------------------------------- // 0x0001 控制指令域(复合管控器 -> 燃料电池) -// payload 14 字节,对应文档字节 7~20 +// payload 18 字节,对应文档字节 7~24 //-------------------------------------------------------------------------- struct FcControlValue { uint8_t mode = 0; // 模式设定 00~03 uint8_t cmd = 0; // 操控指令 00~0B uint8_t outputPower = 0; // 输出功率指令 0~250,分辨率0.1kW int16_t pitch1 = 0; // 纵倾姿态数据1 int16,分辨率0.1°(字节10,11) - int16_t pitch2 = 0; // 纵倾姿态数据2 int16,分辨率0.1°(字节12,13) + uint16_t reserved1 = 0; // 预留(字节12,13),0827协议由纵倾姿态数据2改为预留 int16_t roll1 = 0; // 横倾姿态数据1 int16,分辨率0.1°(字节14,15) - int16_t roll2 = 0; // 横倾姿态数据2 int16,分辨率0.1°(字节16,17) + uint16_t reserved2 = 0; // 预留(字节16,17),0827协议由横倾姿态数据2改为预留 uint8_t emergencyAllow = 0; // 应急允许:Bit0 允许降载 / Bit1 允许排气(字节18) uint16_t depth = 0; // 潜深深度,单位 m(字节19,20) uint8_t supplyCmd = 0; // 补给/排放指令 00~0B(字节21) @@ -119,7 +119,8 @@ struct FcStatusValue { uint16_t fc_h2_flow = 0; // FC供氢流量,0.01L/min uint16_t fc_o2_flow = 0; // FC供氧流量,0.01L/min - uint16_t reserved[12] = {0}; // 预留1~12 + uint16_t reserved[11] = {0}; // 预留1~11(字节125~146) + uint16_t remaining_generation = 0; // 剩余发电量,MWh(字节147,148),0827协议由预留12改为 uint8_t heartbeat = 0; // 通信心跳,每次+1 uint8_t emergency_cmd = 0; // 应急指令:Bit0 降载/Bit1 上浮/... diff --git a/src/pCCU/protocol/PmProtocol.cpp b/src/pCCU/protocol/PmProtocol.cpp index 667b6f7..f23c6d4 100644 --- a/src/pCCU/protocol/PmProtocol.cpp +++ b/src/pCCU/protocol/PmProtocol.cpp @@ -29,8 +29,8 @@ enum : size_t { PO_MODE = 8, PO_CMD = 9, PO_OUTPOWER = 10, - PO_PITCH = 11, // 纵倾姿态 数据1=低8位/数据2=高8位 (字节18-19) - PO_ROLL = 13, // 横倾姿态 数据1=低8位/数据2=高8位 (字节20-21) + PO_PITCH = 11, // 纵倾姿态数据1 int16 (字节18-19) + PO_ROLL = 13, // 横倾姿态数据1 int16 (字节20-21) PO_EMER_ALLOW = 15, PO_DEPTH = 16, // u16 PO_SUPPLY_CMD = 18, @@ -60,7 +60,7 @@ std::vector PmControlMessage::encode(const void* obj) const { FieldCodec::putU8(p, PO_MODE, v->mode); FieldCodec::putU8(p, PO_CMD, v->cmd); FieldCodec::putU8(p, PO_OUTPOWER, v->outputPower); - FieldCodec::putU16(p, PO_PITCH, static_cast(v->pitch)); // 数据1=低位/数据2=高位 + FieldCodec::putU16(p, PO_PITCH, static_cast(v->pitch)); // 姿态数据1,数据2字节预留 FieldCodec::putU16(p, PO_ROLL, static_cast(v->roll)); FieldCodec::putU8(p, PO_EMER_ALLOW, v->emergencyAllow); FieldCodec::putU16(p, PO_DEPTH, v->depth); diff --git a/src/pCCU/protocol/PmProtocol.h b/src/pCCU/protocol/PmProtocol.h index 74c6adb..ed3d693 100644 --- a/src/pCCU/protocol/PmProtocol.h +++ b/src/pCCU/protocol/PmProtocol.h @@ -38,8 +38,8 @@ struct PmControlValue { uint8_t mode = 0; // 模式设定 00~03 uint8_t cmd = 0; // 操控指令 00~0B uint8_t outputPower = 0; // 输出功率指令 0~250,0.1kW - int16_t pitch = 0; // 纵倾姿态(数据1=低8位 / 数据2=高8位,int16),0.1° - int16_t roll = 0; // 横倾姿态(数据1=低8位 / 数据2=高8位,int16),0.1° + int16_t pitch = 0; // 纵倾姿态数据1 int16,分辨率0.1°(字节18,19) + int16_t roll = 0; // 横倾姿态数据1 int16,分辨率0.1°(字节20,21) uint8_t emergencyAllow = 0; // 应急允许 uint16_t depth = 0; // 潜深深度 m uint8_t supplyCmd = 0; // 补给/排放指令 diff --git a/src/pCCU/web/pages/index.h b/src/pCCU/web/pages/index.h index 4c205ef..3068343 100644 --- a/src/pCCU/web/pages/index.h +++ b/src/pCCU/web/pages/index.h @@ -154,6 +154,7 @@ function fcCard(d){ h+=row('四级故障码', faultHex(d.fault_level_4)); h+=row('累积发电时间', (d.total_generation_time*0.1).toFixed(1)+' h'); h+=row('系统发电功率', (d.generation_power*0.01).toFixed(2)+' kW'); + h+=row('剩余发电量', d.remaining_generation+' MWh'); h+=row('储氢剩余容量', d.hydrogen_capacity+'%'); h+=row('液氧剩余容量', d.liquid_oxygen_capacity+'%'); h+=row('通信心跳', d.heartbeat); @@ -325,9 +326,7 @@ function fcCmdCard(d){ h+=row('操控指令', parsed(d.cmd, cmdText(d.cmd))); h+=row('输出功率指令', d.outputPower+' (×0.1kW)'); h+=row('纵倾姿态数据1', (d.pitch1*0.1).toFixed(1)+'°'); - h+=row('纵倾姿态数据2', (d.pitch2*0.1).toFixed(1)+'°'); h+=row('横倾姿态数据1', (d.roll1*0.1).toFixed(1)+'°'); - h+=row('横倾姿态数据2', (d.roll2*0.1).toFixed(1)+'°'); h+=row('应急允许', parsed(d.emergencyAllow, emerAllowText(d.emergencyAllow))); h+=row('潜深深度', d.depth+' m'); h+=row('补给/排放指令', parsed(d.supplyCmd, supplyCmdText(d.supplyCmd))); diff --git a/src/pPowerManger/LowerCommManager.cpp b/src/pPowerManger/LowerCommManager.cpp index 658a241..f6f4ea7 100644 --- a/src/pPowerManger/LowerCommManager.cpp +++ b/src/pPowerManger/LowerCommManager.cpp @@ -75,6 +75,15 @@ bool LowerCommManager::initUdpComm(){ return true; } +void LowerCommManager::setCcuAddress(const std::string& host, long port){ + m_ccuHost = host; + m_ccuPort = port; + if (m_udpComm) { + m_udpComm->ccuHost = host; + m_udpComm->ccuPort = port; + } +} + void LowerCommManager::setCommLogger(ICommLogger* logger) { if (m_udpComm) m_udpComm->setCommLogger(logger); diff --git a/src/pPowerManger/LowerCommManager.h b/src/pPowerManger/LowerCommManager.h index 6a8a406..eb24c4c 100644 --- a/src/pPowerManger/LowerCommManager.h +++ b/src/pPowerManger/LowerCommManager.h @@ -32,6 +32,9 @@ public: // 初始化UDP通信 bool initUdpComm(); + // 设置 CCU 地址(moos 配置读取后调用;须在监听/发送线程启动前调用) + void setCcuAddress(const std::string& host, long port); + // 设置原始帧日志回调(转发给 udpComm,可为空) void setCommLogger(ICommLogger* logger); diff --git a/src/pPowerManger/PowerManger.cpp b/src/pPowerManger/PowerManger.cpp index c75a949..5a5e031 100644 --- a/src/pPowerManger/PowerManger.cpp +++ b/src/pPowerManger/PowerManger.cpp @@ -48,6 +48,10 @@ PowerManger::PowerManger() m_skew = 10; + // CCU 地址默认值,可在 moos 文件中用 ccuhost/ccuport 覆盖 + m_ccuHost = CCUHOST; + m_ccuPort = CCUPORT; + m_pmCurrentState = {}; m_currentDisBreakerState = {}; m_targetDisBreakerState = {}; @@ -64,7 +68,7 @@ PowerManger::PowerManger() initControlCmd(); //初始化下位机通信 - m_lowerCommManager = new LowerCommManager(this,IPORT,CCUPORT,CCUHOST); + m_lowerCommManager = new LowerCommManager(this,IPORT,m_ccuPort,m_ccuHost); m_lowerCommManager->initUdpComm(); //初始化上位机通信 @@ -189,6 +193,8 @@ bool PowerManger::OnStartUp() std::string dbPath; // 数据库路径,不配置则默认当前目录 power_data.db std::string logPath; // 日志文件路径,不配置则默认当前目录 pPowerManger.log + std::string ccuHost; // CCU 地址,不配置则沿用默认值 + long ccuPort = 0; // CCU 端口,不配置则沿用默认值 STRING_LIST::iterator p; for(p=sParams.begin(); p!=sParams.end(); p++) { @@ -212,6 +218,14 @@ bool PowerManger::OnStartUp() logPath = value; handled = true; } + else if(param == "ccuhost") { + ccuHost = value; + handled = true; + } + else if(param == "ccuport") { + ccuPort = atoi(value.c_str()); + handled = true; + } if(!handled) reportUnhandledConfigWarning(orig); @@ -230,6 +244,13 @@ bool PowerManger::OnStartUp() LOG_F(INFO, "数据库存储路径: %s", dbPath.c_str()); m_db = new SQLite(dbPath); m_db->createTable(); + + // 应用 moos 文件中的 CCU 地址配置(在监听/通信线程启动前生效) + if (!ccuHost.empty()) m_ccuHost = ccuHost; + if (ccuPort > 0) m_ccuPort = ccuPort; + m_lowerCommManager->setCcuAddress(m_ccuHost, m_ccuPort); + LOG_F(INFO, "CCU 地址: %s:%ld", m_ccuHost.c_str(), m_ccuPort); + // 注入原始帧日志回调(RX/TX 双向落库) m_lowerCommManager->setCommLogger(m_db); // 数据库就绪后再启动下位机监听线程 @@ -365,10 +386,8 @@ void PowerManger::initControlCmd() m_CcuColCmd.fcmode = 0x00; //默认无效 m_CcuColCmd.fcuCmd = 0x00; //无效指令 m_CcuColCmd.fcuPowerEfficiency = 0; //输出功率指令,0~250,分辨率0.1kW - m_CcuColCmd.pitchData1 = 0; //纵倾姿态数据1 - m_CcuColCmd.pitchData2 = 0; //纵倾姿态数据2 + m_CcuColCmd.pitchData1 = 0; //纵倾姿态数据1(int16,分辨率0.1°;0824协议:姿态数据2已删除) m_CcuColCmd.rollData1 = 0; //横倾姿态数据1 - m_CcuColCmd.rollData2 = 0; //横倾姿态数据2 m_CcuColCmd.emergencyAllow = 0; //应急允许 m_CcuColCmd.depth = 0; //潜深深度 m_CcuColCmd.supplyExhaustCmd = 0x00; //补给/排放指令 diff --git a/src/pPowerManger/PowerManger.h b/src/pPowerManger/PowerManger.h index 626b761..aa45bf4 100644 --- a/src/pPowerManger/PowerManger.h +++ b/src/pPowerManger/PowerManger.h @@ -42,6 +42,7 @@ #define CCUPORT 7000 // #define CCUHOST "192.168.0.140" // pCCU 与本程序同机部署时用回环地址;127.0.0.0 是网络号,内核不路由 +// 默认值,可在 moos 文件中用 ccuhost/ccuport 覆盖 #define CCUHOST "127.0.0.1" #define MAX_UDP_PKT_SIZE 65535 #define SKEW_TOLERANCE 5 @@ -124,6 +125,8 @@ class PowerManger : public AppCastingMOOSApp bool isDeviceCmdEmpty() const; //===========================CONFIG======================================== long m_lPort; + std::string m_ccuHost; // CCU 地址,moos 配置项 ccuhost + long m_ccuPort; // CCU 端口,moos 配置项 ccuport unsigned int m_nReceiveBufferSizeKB; unsigned int m_nSendBufferSizeKB; double m_current_time; diff --git a/src/pPowerManger/httpserver/fuelcell.h b/src/pPowerManger/httpserver/fuelcell.h index 11afcec..1b12503 100644 --- a/src/pPowerManger/httpserver/fuelcell.h +++ b/src/pPowerManger/httpserver/fuelcell.h @@ -541,19 +541,15 @@ body {
- +
- - - - + +
- - - - + +
diff --git a/src/pPowerManger/httpserver/httpserver.cpp b/src/pPowerManger/httpserver/httpserver.cpp index 854c255..afb3852 100644 --- a/src/pPowerManger/httpserver/httpserver.cpp +++ b/src/pPowerManger/httpserver/httpserver.cpp @@ -107,10 +107,8 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_ unsigned char fcMode = 0; unsigned char fcCommand = 0; unsigned char fcPower = 0; - unsigned char pitch1 = 0; - unsigned char pitch2 = 0; - unsigned char roll1 = 0; - unsigned char roll2 = 0; + short pitch = 0; + short roll = 0; unsigned char emergencyAllow = 0; unsigned short depth = 0; unsigned char supplyExhaustCmd = 0; @@ -125,14 +123,10 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_ fcCommand = (unsigned char)strtoul(tmp, NULL, 16); mg_http_get_var(&body, "fcPower", tmp, sizeof(tmp)); fcPower = (unsigned char)atoi(tmp); - mg_http_get_var(&body, "pitch1", tmp, sizeof(tmp)); - pitch1 = (unsigned char)atoi(tmp); - mg_http_get_var(&body, "pitch2", tmp, sizeof(tmp)); - pitch2 = (unsigned char)atoi(tmp); - mg_http_get_var(&body, "roll1", tmp, sizeof(tmp)); - roll1 = (unsigned char)atoi(tmp); - mg_http_get_var(&body, "roll2", tmp, sizeof(tmp)); - roll2 = (unsigned char)atoi(tmp); + mg_http_get_var(&body, "pitch", tmp, sizeof(tmp)); + pitch = (short)atoi(tmp); + mg_http_get_var(&body, "roll", tmp, sizeof(tmp)); + roll = (short)atoi(tmp); mg_http_get_var(&body, "emergencyAllow", tmp, sizeof(tmp)); emergencyAllow = (unsigned char)strtoul(tmp, NULL, 16); mg_http_get_var(&body, "depth", tmp, sizeof(tmp)); @@ -148,8 +142,8 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_ mg_http_get_var(&body, "hostStatus", tmp, sizeof(tmp)); hostStatus = (unsigned char)strtoul(tmp, NULL, 16); - LOG_F(INFO, "Received fuel cell control config: fcMode=%02XH fcCommand=%02XH fcPower=%d pitch1=%d pitch2=%d roll1=%d roll2=%d emergencyAllow=%02XH depth=%d supplyExhaustCmd=%02XH insBatCmd=%02XH powerBatCmd=%02XH powerBatPower=%d hostStatus=%02XH", - fcMode, fcCommand, fcPower, pitch1, pitch2, roll1, roll2, + LOG_F(INFO, "Received fuel cell control config: fcMode=%02XH fcCommand=%02XH fcPower=%d pitch=%d roll=%d emergencyAllow=%02XH depth=%d supplyExhaustCmd=%02XH insBatCmd=%02XH powerBatCmd=%02XH powerBatPower=%d hostStatus=%02XH", + fcMode, fcCommand, fcPower, pitch, roll, emergencyAllow, depth, supplyExhaustCmd, insBatCmd, powerBatCmd, powerBatPower, hostStatus); { @@ -157,10 +151,8 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_ m_powerManger->m_CcuColCmd.fcmode = fcMode; m_powerManger->m_CcuColCmd.fcuCmd = fcCommand; m_powerManger->m_CcuColCmd.fcuPowerEfficiency = fcPower; - m_powerManger->m_CcuColCmd.pitchData1 = pitch1; - m_powerManger->m_CcuColCmd.pitchData2 = pitch2; - m_powerManger->m_CcuColCmd.rollData1 = roll1; - m_powerManger->m_CcuColCmd.rollData2 = roll2; + m_powerManger->m_CcuColCmd.pitchData1 = pitch; + m_powerManger->m_CcuColCmd.rollData1 = roll; m_powerManger->m_CcuColCmd.emergencyAllow = emergencyAllow; m_powerManger->m_CcuColCmd.depth = depth; m_powerManger->m_CcuColCmd.supplyExhaustCmd = supplyExhaustCmd; @@ -936,10 +928,8 @@ void HttpServer::pushSystemData() { j["ccuColCmd"]["fcmode"] = Json::Value(ccuCmd.fcmode); j["ccuColCmd"]["fcuCmd"] = Json::Value(ccuCmd.fcuCmd); j["ccuColCmd"]["fcuPowerEfficiency"] = Json::Value(ccuCmd.fcuPowerEfficiency); - j["ccuColCmd"]["pitchData1"] = Json::Value(ccuCmd.pitchData1); - j["ccuColCmd"]["pitchData2"] = Json::Value(ccuCmd.pitchData2); - j["ccuColCmd"]["rollData1"] = Json::Value(ccuCmd.rollData1); - j["ccuColCmd"]["rollData2"] = Json::Value(ccuCmd.rollData2); + j["ccuColCmd"]["pitchData1"] = Json::Value(static_cast(ccuCmd.pitchData1)); + j["ccuColCmd"]["rollData1"] = Json::Value(static_cast(ccuCmd.rollData1)); j["ccuColCmd"]["emergencyAllow"] = Json::Value(ccuCmd.emergencyAllow); j["ccuColCmd"]["depth"] = Json::Value(ccuCmd.depth); j["ccuColCmd"]["supplyExhaustCmd"] = Json::Value(ccuCmd.supplyExhaustCmd); diff --git a/src/pPowerManger/pPowerManger.moos b/src/pPowerManger/pPowerManger.moos index cb4522b..8a8f717 100644 --- a/src/pPowerManger/pPowerManger.moos +++ b/src/pPowerManger/pPowerManger.moos @@ -8,4 +8,7 @@ ProcessConfig = pPowerManger // logpath = /data/pPowerManger.log // 数据库存储路径;不配置则默认当前目录 power_data.db // dbpath = /data/power_data.db + // CCU 地址;不配置则默认 127.0.0.1:7000 + // ccuhost = 127.0.0.1 + // ccuport = 7000 } \ No newline at end of file diff --git a/src/pPowerManger/pmSysvariable.h b/src/pPowerManger/pmSysvariable.h index 57e9680..9c18377 100644 --- a/src/pPowerManger/pmSysvariable.h +++ b/src/pPowerManger/pmSysvariable.h @@ -212,10 +212,8 @@ typedef struct { unsigned char fcmode; // 模式设定:00H-无效, 01H-调试, 02H-自动, 03H-补给/排放 unsigned char fcuCmd; // 操控指令:00H-无效, 01H-自检, 02H-启动, 03H-停机, 04H-复位, 05H-紧急停机, 06H-补给, 07H-制氢预热, 08H-停机维护, 0AH-开机, 0BH-关机 unsigned char fcuPowerEfficiency; // 输出功率指令:0~250,分辨率0.1,单位kW,非净输出 - unsigned char pitchData1; // 纵倾姿态数据1:分辨率0.1,单位° - unsigned char pitchData2; // 纵倾姿态数据2:分辨率0.1,单位° - unsigned char rollData1; // 横倾姿态数据1:分辨率0.1,单位° - unsigned char rollData2; // 横倾姿态数据2:分辨率0.1,单位° + short pitchData1; // 纵倾姿态数据1:int16,分辨率0.1,单位°(0824协议:姿态数据2已删除) + short rollData1; // 横倾姿态数据1:int16,分辨率0.1,单位° unsigned char emergencyAllow; // 应急允许:Bit0允许降载, Bit1允许排气, Bit2~Bit7预留 unsigned short depth; // 潜深深度,单位:m unsigned char supplyExhaustCmd; // 补给/排放指令:00H-无效, 01H-液氧加注, 02H-甲醇加注, 03H-液氧排空, 04H-甲醇排空, 05H-CO2排空, 06H-/, 07H-合金加注, 08H-氮气加注, 09H-纯水加注, 0AH-备用, 0BH-备用 diff --git a/src/pPowerManger/sqlit3/SQLite.cpp b/src/pPowerManger/sqlit3/SQLite.cpp index b9b1796..f262f3b 100644 --- a/src/pPowerManger/sqlit3/SQLite.cpp +++ b/src/pPowerManger/sqlit3/SQLite.cpp @@ -286,9 +286,7 @@ const std::vector& SQLite::allTables() COL(ccuColCmd, fcuCmd), COL(ccuColCmd, fcuPowerEfficiency), COL(ccuColCmd, pitchData1), - COL(ccuColCmd, pitchData2), COL(ccuColCmd, rollData1), - COL(ccuColCmd, rollData2), COL(ccuColCmd, emergencyAllow), COL(ccuColCmd, depth), COL(ccuColCmd, supplyExhaustCmd), diff --git a/test/pccu/pccuTest.cpp b/test/pccu/pccuTest.cpp index 83fa833..e6e474f 100644 --- a/test/pccu/pccuTest.cpp +++ b/test/pccu/pccuTest.cpp @@ -52,8 +52,8 @@ static void testFrameLengths() { // 2. FC 控制指令编解码往返 static void testFcControlRoundTrip() { FcControlValue v; - v.mode = 2; v.cmd = 2; v.outputPower = 100; v.pitch1 = 35; v.pitch2 = -21; - v.roll1 = -12; v.roll2 = 48; + v.mode = 2; v.cmd = 2; v.outputPower = 100; v.pitch1 = 35; + v.roll1 = -12; v.emergencyAllow = 0x03; v.depth = 150; v.supplyCmd = 1; v.reservedCmd5 = 5; v.reservedCmd6 = 6; v.heartbeat = 7; @@ -64,11 +64,11 @@ static void testFcControlRoundTrip() { CHECK(frame[0] == 0x40 && frame[1] == 0x40); CHECK(frame[4] == 0x18 && frame[5] == 0x00); // 域字节数 0x0018 小端 - // 字节位置校验(数据域起点=帧头6):姿态各 int16 小端 + // 字节位置校验(数据域起点=帧头6):姿态数据1 int16 小端,数据2字节预留恒0 CHECK(frame[6 + 3] == 0x23 && frame[6 + 4] == 0x00); // 纵倾数据1 = 35 - CHECK(frame[6 + 5] == 0xEB && frame[6 + 6] == 0xFF); // 纵倾数据2 = -21 (0xFFEB) + CHECK(frame[6 + 5] == 0x00 && frame[6 + 6] == 0x00); // 预留(0827协议,原纵倾数据2) CHECK(frame[6 + 7] == 0xF4 && frame[6 + 8] == 0xFF); // 横倾数据1 = -12 (0xFFF4) - CHECK(frame[6 + 9] == 0x30 && frame[6 + 10] == 0x00); // 横倾数据2 = 48 + CHECK(frame[6 + 9] == 0x00 && frame[6 + 10] == 0x00); // 预留(0827协议,原横倾数据2) FcControlValue out; CHECK(msg.decode(frame, static_cast(&out))); @@ -76,9 +76,7 @@ static void testFcControlRoundTrip() { CHECK(out.cmd == 2); CHECK(out.outputPower == 100); CHECK(out.pitch1 == 35); - CHECK(out.pitch2 == -21); CHECK(out.roll1 == -12); - CHECK(out.roll2 == 48); CHECK(out.emergencyAllow == 0x03); CHECK(out.depth == 150); CHECK(out.supplyCmd == 1); @@ -101,7 +99,7 @@ static void testFcStatusRoundTrip() { v.methanol_total_use = 100; v.cabin_temp1 = 25; v.h2_concentration1 = 500; v.heartbeat = 9; v.emergency_cmd = 0x01; - v.reserved[0] = 0xAAAA; v.reserved[11] = 0xBBBB; + v.reserved[0] = 0xAAAA; v.remaining_generation = 0xBBBB; FcStatusMessage msg; std::vector frame = msg.encode(v); @@ -125,7 +123,7 @@ static void testFcStatusRoundTrip() { CHECK(out.methanol_total_use == 100); CHECK(out.cabin_temp1 == 25 && out.h2_concentration1 == 500); CHECK(out.heartbeat == 9 && out.emergency_cmd == 0x01); - CHECK(out.reserved[0] == 0xAAAA && out.reserved[11] == 0xBBBB); + CHECK(out.reserved[0] == 0xAAAA && out.remaining_generation == 0xBBBB); } //-------------------------------------------------------------------------- diff --git a/test/pccu/pccu_integration_test.py b/test/pccu/pccu_integration_test.py index e1cc45d..b6010a7 100644 --- a/test/pccu/pccu_integration_test.py +++ b/test/pccu/pccu_integration_test.py @@ -9,7 +9,7 @@ 校验: 1. FC 状态收到并整合,pCCU 周期发送 PM 状态(0x0004, 248B)到 16003 - 2. PM 操控(0x0001)转发为 FC 控制(0x0001, 20B)到 16001 + 2. PM 操控(0x0001)转发为 FC 控制(0x0001, 24B)到 16001 3. PM 参数设定(0x0002)触发参数反馈(0x0003, 12B)到 16003 4. SQLite 记录了收/发原始帧 5. Web 页面可访问 @@ -62,6 +62,7 @@ def fc_status_frame(): p[17] = 70 # 液氧 struct.pack_into("=1, 每帧20B)") + # 校验 2:PM 操控转发为 FC 控制(24B,0827协议:姿态数据1转发,数据2字节预留恒0) + fc_ctl = [d for d in fc_ctl_results if len(d) == 24] + print(f"[FC 控制转发] 收到 {len(fc_ctl)} 帧 (期望>=1, 每帧24B)") if fc_ctl: d = fc_ctl[0] - cmd = d[7]; power = d[8]; hb = d[19] - print(f" -> cmd={cmd}, outputPower={power}, heartbeat={hb}") + cmd = d[7]; power = d[8]; hb = d[23] + pitch = struct.unpack_from(" cmd={cmd}, outputPower={power}, pitch={pitch}, roll={roll}, " + f"resv1={resv1}, resv2={resv2}, heartbeat={hb}") ok = ok and (cmd == 2) and (power == 120) and (hb == 7) + ok = ok and (pitch == 123) and (roll == -45) + ok = ok and (resv1 == 0) and (resv2 == 0) else: ok = False diff --git a/test/pccu/pccu_sim.py b/test/pccu/pccu_sim.py index d1e7ebd..89b62d5 100644 --- a/test/pccu/pccu_sim.py +++ b/test/pccu/pccu_sim.py @@ -120,6 +120,8 @@ class FcSim: struct.pack_into("