协议:按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/测试:更新建表列、解析打印与单元/集成测试适配新协议
This commit is contained in:
zjk
2026-08-26 20:56:39 +08:00
parent 12f0dee86e
commit e0488d513b
15 changed files with 1565 additions and 245 deletions
+11 -3
View File
@@ -362,11 +362,19 @@ void PowerManger::initControlCmd()
//================初始化复合管控=============================
m_CcuColCmd.fcmode = 0x00; //默认无效
m_CcuColCmd.fcuCmd = 0x00; //无效指令
m_CcuColCmd.fcuPowerEfficiency = 8; //默认为8kw
m_CcuColCmd.insChargeEnable = 0x30; //不允许充电
m_CcuColCmd.fcuPowerEfficiency = 0; //输出功率指令,0~250,分辨率0.1kW
m_CcuColCmd.pitchData1 = 0; //纵倾姿态数据1
m_CcuColCmd.pitchData2 = 0; //纵倾姿态数据2
m_CcuColCmd.rollData1 = 0; //横倾姿态数据1
m_CcuColCmd.rollData2 = 0; //横倾姿态数据2
m_CcuColCmd.emergencyAllow = 0; //应急允许
m_CcuColCmd.depth = 0; //潜深深度
m_CcuColCmd.supplyExhaustCmd = 0x00; //补给/排放指令
m_CcuColCmd.reservedCmd5 = 0; //预留指令5
m_CcuColCmd.reservedCmd6 = 0; //预留指令6
m_CcuColCmd.insBatCmd = 0x00; //无效指令
m_CcuColCmd.powerBatCmd = 0x00; //无效指令
m_CcuColCmd.powerBatEfficiency = 00; //默认为最大
m_CcuColCmd.powerBatEfficiency = 0; //动力锂电池功率配置值
m_CcuColCmd.heartbeat = 0;
m_CcuColCmd.masterState = 0xAA;//主站状态,运行中
//================初始化上位机指令=========================
+6 -11
View File
@@ -265,6 +265,10 @@ body {
<span class="sidebar-icon">🔋</span>
<span>电池操作</span>
</a>
<a href="/fcs" class="sidebar-button">
<span class="sidebar-icon">🔥</span>
<span>燃料电池</span>
</a>
<a href="/logs" class="sidebar-button">
<span class="sidebar-icon">📝</span>
<span>系统日志</span>
@@ -319,9 +323,9 @@ body {
</div>
<div class="control-group">
<label class="control-label">燃料电池动力通路功率配置 (kW)</label>
<label class="control-label">燃料电池输出功率指令 (0~250,分辨率0.1kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="fuelCellPower" min="0" max="20" value="0" step="1">
<input type="range" class="slider" name="fuelCellPower" min="0" max="250" value="0" step="1">
<span class="slider-value">0</span>
</div>
</div>
@@ -338,15 +342,6 @@ body {
<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">
+2 -2
View File
@@ -71,7 +71,7 @@ const std::string DATA_HTML = R"(
<span class="data-value" id="total-generation-time">正在加载...</span>
</div>
<div class="data-item">
<span class="data-label">累计发电功率:</span>
<span class="data-label">系统发电功率:</span>
<span class="data-value" id="total-generation-power">正在加载...</span>
</div>
<div class="data-item">
@@ -989,7 +989,7 @@ function updateSystemData(data) {
document.getElementById('total-generation-time').textContent = (data.total_generation_time).toFixed(1) + ' 小时';
}
if (data.total_generation_power !== undefined) {
document.getElementById('total-generation-power').textContent = data.total_generation_power + ' W';
document.getElementById('total-generation-power').textContent = data.total_generation_power + ' kW';
}
if (data.fc_fault_level_1 !== undefined) {
document.getElementById('fc-fault-level-1').textContent = data.fc_fault_level_1;
+894
View File
@@ -0,0 +1,894 @@
#ifndef HTTP_SERVER_FUELCELL_H
#define HTTP_SERVER_FUELCELL_H
#include <string>
const std::string FUELCELL_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: 1rem;
margin-top: 36px;
}
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
gap: 16px;
margin-bottom: 16px;
}
.data-card {
background: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border: 1px solid #e5e7eb;
}
.data-card h3 {
font-size: 1rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 0.75rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #e5e7eb;
}
.data-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 4px;
border-bottom: 1px solid #f3f4f6;
min-height: 28px;
}
.data-row:last-child {
border-bottom: none;
}
.data-row span:first-child {
color: #4b5563;
font-weight: 500;
white-space: nowrap;
font-size: 0.85rem;
}
.data-row span:last-child {
color: #1a1a1a;
font-weight: 600;
font-family: 'SF Mono', 'Consolas', monospace;
text-align: right;
font-size: 0.85rem;
}
.badge {
padding: 2px 8px;
border-radius: 4px;
font-weight: 600;
}
.badge.on {
background-color: #dcfce7;
color: #166534;
}
.badge.off {
background-color: #fee2e2;
color: #991b1b;
}
.badge.warn {
background-color: #fef3c7;
color: #92400e;
}
.control-panel {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-top: 16px;
}
.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;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 12px;
}
.radio-option {
display: flex;
align-items: center;
gap: 6px;
}
.button-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.button {
padding: 8px 14px;
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);
}
.button-danger {
background-color: #dc2626;
color: white;
}
.button-danger:hover {
background-color: #b91c1c;
}
.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: 60px;
text-align: right;
font-family: 'SF Mono', 'Consolas', monospace;
}
.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;
}
.two-col {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0 20px;
}
@media (max-width: 768px) {
.two-col {
grid-template-columns: 1fr;
}
}
</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="/fcs" 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>
<span id="commState" class="badge off">通讯中断</span>
</div>
<div class="main-content">
<div class="cards-grid">
<!-- 系统总体 -->
<div class="data-card">
<h3>系统总体</h3>
<div class="data-row"><span>运行模式:</span><span id="fc-mode">--</span></div>
<div class="data-row"><span>运行状态:</span><span id="fc-status">--</span></div>
<div class="data-row"><span>系统故障等级:</span><span id="fc-fault-level">--</span></div>
<div class="data-row"><span>一级故障码:</span><span id="fault-level-1">--</span></div>
<div class="data-row"><span>二级故障码:</span><span id="fault-level-2">--</span></div>
<div class="data-row"><span>三级故障码:</span><span id="fault-level-3">--</span></div>
<div class="data-row"><span>四级故障码:</span><span id="fault-level-4">--</span></div>
<div class="data-row"><span>累积发电时间:</span><span id="total-gen-time">--</span></div>
<div class="data-row"><span>输出功率限定:</span><span id="output-power-limit">--</span></div>
<div class="data-row"><span>系统发电功率:</span><span id="total-gen-power">--</span></div>
<div class="data-row"><span>储氢剩余容量:</span><span id="hydrogen-capacity">--</span></div>
<div class="data-row"><span>液氧剩余容量:</span><span id="liquid-oxygen-capacity">--</span></div>
<div class="data-row"><span>通信心跳:</span><span id="heartbeat">--</span></div>
<div class="data-row"><span>应急指令:</span><span id="emergency-command">--</span></div>
</div>
<!-- FC单片电压 -->
<div class="data-card">
<h3>FC单片电压</h3>
<div class="data-row"><span>I#FC最低单片电压:</span><span id="fc1-min-cell-v">--</span></div>
<div class="data-row"><span>I#FC最低单片电压位置:</span><span id="fc1-min-cell-pos">--</span></div>
<div class="data-row"><span>I#FC平均单片电压:</span><span id="fc1-avg-cell-v">--</span></div>
<div class="data-row"><span>2#FC最低单片电压:</span><span id="fc2-min-cell-v">--</span></div>
<div class="data-row"><span>2#FC最低单片电压位置:</span><span id="fc2-min-cell-pos">--</span></div>
<div class="data-row"><span>2#FC平均单片电压:</span><span id="fc2-avg-cell-v">--</span></div>
</div>
<!-- 甲醇制氢装置 -->
<div class="data-card">
<h3>甲醇制氢装置</h3>
<div class="data-row"><span>钯膜最高温度:</span><span id="palladium-temp">--</span></div>
<div class="data-row"><span>缓冲罐压力:</span><span id="buffer-tank-pressure">--</span></div>
<div class="data-row"><span>烟气累计排放量:</span><span id="flue-total-emission">--</span></div>
<div class="data-row"><span>烟气压力:</span><span id="flue-pressure">--</span></div>
<div class="data-row"><span>反应器压力:</span><span id="reactor-pressure">--</span></div>
<div class="data-row"><span>电动阀开度:</span><span id="electric-valve-open">--</span></div>
</div>
<!-- DC/DC -->
<div class="data-card">
<h3>DC/DC</h3>
<div class="data-row"><span>通道1输入电压:</span><span id="dcdc1-in-voltage">--</span></div>
<div class="data-row"><span>通道1输入电流:</span><span id="dcdc1-in-current">--</span></div>
<div class="data-row"><span>通道2输入电压:</span><span id="dcdc2-in-voltage">--</span></div>
<div class="data-row"><span>通道2输入电流:</span><span id="dcdc2-in-current">--</span></div>
<div class="data-row"><span>输出电压:</span><span id="dcdc-out-voltage">--</span></div>
<div class="data-row"><span>输出电流:</span><span id="dcdc-out-current">--</span></div>
<div class="data-row"><span>控制电源电压:</span><span id="dcdc-control-voltage">--</span></div>
<div class="data-row"><span>辅电输出电压:</span><span id="dcdc-aux-out-voltage">--</span></div>
</div>
<!-- 甲醇/水系统 -->
<div class="data-card">
<h3>甲醇/水系统</h3>
<div class="data-row"><span>甲醇累计使用量:</span><span id="methanol-total-usage">--</span></div>
<div class="data-row"><span>甲醇溶液进料量:</span><span id="methanol-solution-feed">--</span></div>
<div class="data-row"><span>氧侧生成水箱液位:</span><span id="oxygen-water-tank-level">--</span></div>
<div class="data-row"><span>氢侧生成水箱液位:</span><span id="hydrogen-water-tank-level">--</span></div>
<div class="data-row"><span>配重水箱液位:</span><span id="ballast-water-tank-level">--</span></div>
</div>
<!-- 尾气装置 -->
<div class="data-card">
<h3>尾气装置</h3>
<div class="data-row"><span>进气压力:</span><span id="exhaust-in-pressure">--</span></div>
<div class="data-row"><span>排气压力:</span><span id="exhaust-out-pressure">--</span></div>
<div class="data-row"><span>运行频率:</span><span id="exhaust-run-freq">--</span></div>
<div class="data-row"><span>进气温度:</span><span id="exhaust-in-temp">--</span></div>
<div class="data-row"><span>排气温度:</span><span id="exhaust-out-temp">--</span></div>
<div class="data-row"><span>进水压力:</span><span id="exhaust-in-water-pressure">--</span></div>
<div class="data-row"><span>排水压力:</span><span id="exhaust-out-water-pressure">--</span></div>
</div>
<!-- 一体化罐装置 -->
<div class="data-card">
<h3>一体化罐装置</h3>
<div class="data-row"><span>液氧罐压力:</span><span id="tank-lo2-pressure">--</span></div>
<div class="data-row"><span>二氧化碳压力:</span><span id="tank-co2-pressure">--</span></div>
<div class="data-row"><span>液氧罐液位:</span><span id="tank-lo2-level">--</span></div>
</div>
<!-- 供氢/供氧流量 -->
<div class="data-card">
<h3>供氢/供氧流量</h3>
<div class="data-row"><span>合金供氢流量:</span><span id="alloy-h2-flow">--</span></div>
<div class="data-row"><span>FC供氢流量:</span><span id="fc-h2-flow">--</span></div>
<div class="data-row"><span>FC供氧流量:</span><span id="fc-o2-flow">--</span></div>
</div>
<!-- 应急上浮 -->
<div class="data-card">
<h3>应急上浮</h3>
<div class="data-row"><span>应急上浮深度:</span><span id="emergency-float-depth">--</span></div>
<div class="data-row"><span>应急上浮时间:</span><span id="emergency-float-time">--</span></div>
</div>
<!-- 舱室环境 -->
<div class="data-card">
<h3>舱室环境</h3>
<div class="data-row"><span>舱室压力1:</span><span id="cabin-pressure1">--</span></div>
<div class="data-row"><span>舱室压力2:</span><span id="cabin-pressure2">--</span></div>
<div class="data-row"><span>舱室温度1:</span><span id="cabin-temp1">--</span></div>
<div class="data-row"><span>舱室温度2:</span><span id="cabin-temp2">--</span></div>
<div class="data-row"><span>舱室湿度1:</span><span id="cabin-humidity1">--</span></div>
<div class="data-row"><span>舱室湿度2:</span><span id="cabin-humidity2">--</span></div>
<div class="data-row"><span>H2浓度1:</span><span id="h2-concentration1">--</span></div>
<div class="data-row"><span>H2浓度2:</span><span id="h2-concentration2">--</span></div>
<div class="data-row"><span>H2浓度3:</span><span id="h2-concentration3">--</span></div>
<div class="data-row"><span>O2浓度1:</span><span id="o2-concentration1">--</span></div>
<div class="data-row"><span>O2浓度2:</span><span id="o2-concentration2">--</span></div>
<div class="data-row"><span>甲醇浓度1:</span><span id="ch3oh-concentration1">--</span></div>
<div class="data-row"><span>甲醇浓度2:</span><span id="ch3oh-concentration2">--</span></div>
<div class="data-row"><span>火焰探测器1:</span><span id="flame-detector1">--</span></div>
<div class="data-row"><span>火焰探测器2:</span><span id="flame-detector2">--</span></div>
</div>
</div>
<!-- 指令交互 -->
<div class="control-panel">
<div class="panel-title">燃料电池操控指令</div>
<form id="fcsForm" method="POST" action="/fcs/control">
<div class="control-group">
<label class="control-label">模式设定</label>
<div class="radio-group">
<label class="radio-option"><input type="radio" name="fcMode" value="00H" checked><span>无效</span></label>
<label class="radio-option"><input type="radio" name="fcMode" value="01H"><span>调试</span></label>
<label class="radio-option"><input type="radio" name="fcMode" value="02H"><span>自动</span></label>
<label class="radio-option"><input type="radio" name="fcMode" value="03H"><span>补给/排放</span></label>
</div>
</div>
<div class="control-group">
<label class="control-label">操控指令</label>
<select class="select-control" name="fcCommand">
<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>
<option value="0AH">开机</option>
<option value="0BH">关机</option>
</select>
</div>
<div class="control-group">
<label class="control-label">输出功率指令 (0~250,分辨率0.1kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="fcPower" min="0" max="250" value="0" step="1">
<span class="slider-value">0 (0.0 kW)</span>
</div>
</div>
<div class="control-group">
<label class="control-label">姿态数据(分辨率0.1°)</label>
<div class="two-col">
<div class="control-group">
<label class="control-label">纵倾姿态1</label>
<input type="number" name="pitch1" min="0" max="255" value="0" class="select-control">
<label class="control-label">纵倾姿态2</label>
<input type="number" name="pitch2" min="0" max="255" value="0" class="select-control">
</div>
<div class="control-group">
<label class="control-label">横倾姿态1</label>
<input type="number" name="roll1" min="0" max="255" value="0" class="select-control">
<label class="control-label">横倾姿态2</label>
<input type="number" name="roll2" min="0" max="255" value="0" class="select-control">
</div>
</div>
</div>
<div class="control-group">
<label class="control-label">应急允许</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="emergencyAllow" data-value="00H">无</button>
<button type="button" class="button button-secondary" data-command="emergencyAllow" data-value="01H">允许降载</button>
<button type="button" class="button button-secondary" data-command="emergencyAllow" data-value="02H">允许排气</button>
<button type="button" class="button button-secondary" data-command="emergencyAllow" data-value="03H">降载+排气</button>
</div>
<input type="hidden" name="emergencyAllow" value="00H">
</div>
<div class="control-group">
<label class="control-label">潜深深度 (m)</label>
<div class="slider-container">
<input type="range" class="slider" name="depth" min="0" max="1000" value="0" step="1">
<span class="slider-value">0 m</span>
</div>
</div>
<div class="control-group">
<label class="control-label">补给/排放指令</label>
<select class="select-control" name="supplyExhaustCmd">
<option value="00H">无效</option>
<option value="01H">液氧加注</option>
<option value="02H">甲醇加注</option>
<option value="03H">液氧排空</option>
<option value="04H">甲醇排空</option>
<option value="05H">CO2排空</option>
<option value="07H">合金加注</option>
<option value="08H">氮气加注</option>
<option value="09H">纯水加注</option>
<option value="0AH">备用</option>
<option value="0BH">备用</option>
</select>
</div>
<div class="control-group">
<label class="control-label">仪表锂电池启停指令</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="insBatCmd" data-value="00H">无效</button>
<button type="button" class="button button-secondary" data-command="insBatCmd" data-value="10H">启动</button>
<button type="button" class="button button-secondary" data-command="insBatCmd" data-value="20H">关闭</button>
</div>
<input type="hidden" name="insBatCmd" value="00H">
</div>
<div class="control-group">
<label class="control-label">动力锂电池启停指令</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="powerBatCmd" data-value="00H">无效</button>
<button type="button" class="button button-secondary" data-command="powerBatCmd" data-value="10H">启动</button>
<button type="button" class="button button-secondary" data-command="powerBatCmd" data-value="20H">关闭</button>
</div>
<input type="hidden" name="powerBatCmd" value="00H">
</div>
<div class="control-group">
<label class="control-label">动力锂电池功率配置值 (0~400 kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="powerBatPower" min="0" max="400" value="0" step="10">
<span class="slider-value">0 kW</span>
</div>
</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>
let ws = null;
function getFCModeText(mode) {
const modes = { 0: '无效', 1: '调试', 2: '自动', 3: '补给' };
return modes[mode] || '未知';
}
function getFCStatusText(status) {
const statuses = {
0: '无效', 1: '初始化', 2: '待机', 3: '自检异常', 4: '就绪',
5: '启动', 6: '运行', 7: '停机', 8: '故障', 9: '补给',
10: '制氢预热', 11: '制氢预热完成', 12: '停机维护', 13: '维护完成'
};
return statuses[status] || '未知';
}
function getFaultLevelText(level) {
const levels = { 0: '无故障', 1: '一级故障', 2: '二级故障', 3: '三级故障', 4: '四级故障' };
return levels[level] || '未知';
}
function getFlameText(state) {
const states = { 0: '无效', 1: '火焰报警', 2: '探测器故障' };
return states[state] || '未知';
}
function fmt(v, digits) {
if (v === undefined || v === null || isNaN(v)) return '--';
return Number(v).toFixed(digits === undefined ? 2 : digits);
}
function updateText(id, text) {
const el = document.getElementById(id);
if (el) el.textContent = text;
}
function updateData(data) {
updateText('fc-mode', getFCModeText(data.fc_mode));
updateText('fc-status', getFCStatusText(data.fc_status));
updateText('fc-fault-level', getFaultLevelText(data.fc_fault_level));
updateText('fault-level-1', '0x' + (data.fc_fault_level_1 === undefined ? 0 : data.fc_fault_level_1).toString(16).toUpperCase().padStart(4, '0'));
updateText('fault-level-2', '0x' + (data.fc_fault_level_2 === undefined ? 0 : data.fc_fault_level_2).toString(16).toUpperCase().padStart(4, '0'));
updateText('fault-level-3', '0x' + (data.fc_fault_level_3 === undefined ? 0 : data.fc_fault_level_3).toString(16).toUpperCase().padStart(4, '0'));
updateText('fault-level-4', '0x' + (data.fc_fault_level_4 === undefined ? 0 : data.fc_fault_level_4).toString(16).toUpperCase().padStart(4, '0'));
updateText('total-gen-time', fmt(data.total_generation_time, 1) + ' h');
updateText('output-power-limit', fmt(data.output_power_limit, 0) + ' W');
updateText('total-gen-power', fmt(data.total_generation_power, 2) + ' kW');
updateText('hydrogen-capacity', fmt(data.hydrogen_capacity, 0) + ' %');
updateText('liquid-oxygen-capacity', fmt(data.liquid_oxygen_capacity, 0) + ' %');
updateText('heartbeat', data.heartbeat === undefined ? '--' : data.heartbeat);
updateText('emergency-command', data.emergency_command === undefined ? '--' : '0x' + data.emergency_command.toString(16).toUpperCase().padStart(2, '0'));
// FC单片电压
updateText('fc1-min-cell-v', fmt(data.fc1_min_cell_voltage, 0) + ' mV');
updateText('fc1-min-cell-pos', data.fc1_min_cell_pos === undefined ? '--' : data.fc1_min_cell_pos);
updateText('fc1-avg-cell-v', fmt(data.fc1_avg_cell_voltage, 0) + ' mV');
updateText('fc2-min-cell-v', fmt(data.fc2_min_cell_voltage, 0) + ' mV');
updateText('fc2-min-cell-pos', data.fc2_min_cell_pos === undefined ? '--' : data.fc2_min_cell_pos);
updateText('fc2-avg-cell-v', fmt(data.fc2_avg_cell_voltage, 0) + ' mV');
// 甲醇制氢装置
updateText('palladium-temp', fmt(data.palladium_temp, 1) + ' ℃');
updateText('buffer-tank-pressure', fmt(data.buffer_tank_pressure, 1) + ' kPa');
updateText('flue-total-emission', fmt(data.flue_total_emission, 1) + ' kg');
updateText('flue-pressure', fmt(data.flue_pressure, 3) + ' MPa');
updateText('reactor-pressure', fmt(data.reactor_pressure, 3) + ' MPa');
updateText('electric-valve-open', fmt(data.electric_valve_open, 1) + ' %');
// DC/DC
updateText('dcdc1-in-voltage', fmt(data.dcdc1_in_voltage, 1) + ' V');
updateText('dcdc1-in-current', fmt(data.dcdc1_in_current, 1) + ' A');
updateText('dcdc2-in-voltage', fmt(data.dcdc2_in_voltage, 1) + ' V');
updateText('dcdc2-in-current', fmt(data.dcdc2_in_current, 1) + ' A');
updateText('dcdc-out-voltage', fmt(data.dcdc_out_voltage, 1) + ' V');
updateText('dcdc-out-current', fmt(data.dcdc_out_current, 1) + ' A');
updateText('dcdc-control-voltage', fmt(data.dcdc_control_voltage, 2) + ' V');
updateText('dcdc-aux-out-voltage', fmt(data.dcdc_aux_out_voltage, 3) + ' V');
// 甲醇/水系统
updateText('methanol-total-usage', fmt(data.methanol_total_usage, 1) + ' kg');
updateText('methanol-solution-feed', fmt(data.methanol_solution_feed, 0) + ' mL/min');
updateText('oxygen-water-tank-level', fmt(data.oxygen_water_tank_level, 1) + ' mm');
updateText('hydrogen-water-tank-level', fmt(data.hydrogen_water_tank_level, 1) + ' mm');
updateText('ballast-water-tank-level', fmt(data.ballast_water_tank_level, 1) + ' mm');
// 尾气装置
updateText('exhaust-in-pressure', fmt(data.exhaust_in_pressure, 2) + ' MPa');
updateText('exhaust-out-pressure', fmt(data.exhaust_out_pressure, 2) + ' MPa');
updateText('exhaust-run-freq', fmt(data.exhaust_run_freq, 2) + ' Hz');
updateText('exhaust-in-temp', fmt(data.exhaust_in_temp, 2) + ' ℃');
updateText('exhaust-out-temp', fmt(data.exhaust_out_temp, 2) + ' ℃');
updateText('exhaust-in-water-pressure', fmt(data.exhaust_in_water_pressure, 2) + ' kPa');
updateText('exhaust-out-water-pressure', fmt(data.exhaust_out_water_pressure, 2) + ' kPa');
// 一体化罐装置
updateText('tank-lo2-pressure', fmt(data.tank_lo2_pressure, 2) + ' MPa');
updateText('tank-co2-pressure', fmt(data.tank_co2_pressure, 2) + ' MPa');
updateText('tank-lo2-level', fmt(data.tank_lo2_level, 1) + ' mm');
// 供氢/供氧流量
updateText('alloy-h2-flow', fmt(data.alloy_h2_flow, 2) + ' L/min');
updateText('fc-h2-flow', fmt(data.fc_h2_flow, 2) + ' L/min');
updateText('fc-o2-flow', fmt(data.fc_o2_flow, 2) + ' L/min');
// 应急上浮
updateText('emergency-float-depth', fmt(data.emergency_float_depth, 0) + ' m');
updateText('emergency-float-time', fmt(data.emergency_float_time, 0) + ' min');
// 舱室环境
updateText('cabin-pressure1', fmt(data.cabin_pressure1, 2) + ' kPa');
updateText('cabin-pressure2', fmt(data.cabin_pressure2, 2) + ' kPa');
updateText('cabin-temp1', fmt(data.cabin_temp1, 2) + ' ℃');
updateText('cabin-temp2', fmt(data.cabin_temp2, 2) + ' ℃');
updateText('cabin-humidity1', fmt(data.cabin_humidity1, 2) + ' %RH');
updateText('cabin-humidity2', fmt(data.cabin_humidity2, 2) + ' %RH');
updateText('h2-concentration1', fmt(data.h2_concentration1, 2) + ' %LEL');
updateText('h2-concentration2', fmt(data.h2_concentration2, 2) + ' %LEL');
updateText('h2-concentration3', fmt(data.h2_concentration3, 2) + ' %LEL');
updateText('o2-concentration1', fmt(data.o2_concentration1, 2) + ' %Vol');
updateText('o2-concentration2', fmt(data.o2_concentration2, 2) + ' %Vol');
updateText('ch3oh-concentration1', fmt(data.ch3oh_concentration1, 2) + ' %LEL');
updateText('ch3oh-concentration2', fmt(data.ch3oh_concentration2, 2) + ' %LEL');
updateText('flame-detector1', getFlameText(data.flame_detector1));
updateText('flame-detector2', getFlameText(data.flame_detector2));
// 通信状态
const timeout = data.comm_state && data.comm_state.ccu_status ? data.comm_state.ccu_status.isTimeout : true;
const commEl = document.getElementById('commState');
if (timeout) {
commEl.textContent = '通讯中断';
commEl.className = 'badge off';
} else {
commEl.textContent = '通讯正常';
commEl.className = 'badge on';
}
}
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.host + '/ws');
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 忽略日志推送消息(type=new_log/log),只处理系统数据推送
if (data.type === 'new_log' || data.type === 'log') return;
if (data.fc_mode === undefined) return;
updateData(data);
} catch (e) {
console.error('JSON parse error', e);
}
};
ws.onclose = () => {
setTimeout(initWebSocket, 5000);
};
ws.onerror = (err) => {
console.error('WebSocket error', err);
};
}
// 更新滑块值显示
document.querySelectorAll('.slider').forEach(slider => {
const valueDisplay = slider.nextElementSibling;
if (slider.name === 'fcPower') {
valueDisplay.textContent = slider.value + ' (0.0 kW)';
} else if (slider.name === 'depth') {
valueDisplay.textContent = slider.value + ' m';
} else if (slider.name === 'powerBatPower') {
valueDisplay.textContent = slider.value + ' kW';
} else {
valueDisplay.textContent = slider.value;
}
slider.addEventListener('input', () => {
if (slider.name === 'fcPower') {
valueDisplay.textContent = slider.value + ' (' + (slider.value * 0.1).toFixed(1) + ' kW)';
} else if (slider.name === 'depth') {
valueDisplay.textContent = slider.value + ' m';
} else if (slider.name === 'powerBatPower') {
valueDisplay.textContent = slider.value + ' kW';
} else {
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('fcsForm').addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const statusMessage = document.getElementById('statusMessage');
try {
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);
});
window.addEventListener('load', initWebSocket);
</script>
</body>
</html>
)";
#endif // HTTP_SERVER_FUELCELL_H
+151 -7
View File
@@ -11,6 +11,7 @@
#include "testpage.h"
#include "degubpage.h"
#include "batteryoperation.h"
#include "fuelcell.h"
using json = Json::Value;
@@ -94,6 +95,86 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_
} else if (mg_match(hm->uri, mg_str("/battery"), NULL)) {
// 返回电池操作页面HTML
mg_http_reply(c, 200, "Content-Type: text/html; charset=utf-8\r\n", BATTERY_OPERATION_HTML.c_str());
} else if (mg_match(hm->uri, mg_str("/fcs"), NULL)) {
// 返回燃料电池页面HTML
mg_http_reply(c, 200, "Content-Type: text/html; charset=utf-8\r\n", FUELCELL_HTML.c_str());
} else if (mg_match(hm->uri, mg_str("/fcs/control"), NULL)) {
// 处理燃料电池控制POST请求
if (mg_strcmp(hm->method, mg_str("POST")) == 0) {
struct mg_str body = hm->body;
char tmp[16] = {0};
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;
unsigned char emergencyAllow = 0;
unsigned short depth = 0;
unsigned char supplyExhaustCmd = 0;
unsigned char insBatCmd = 0;
unsigned char powerBatCmd = 0;
unsigned short powerBatPower = 0;
unsigned char hostStatus = 0;
mg_http_get_var(&body, "fcMode", tmp, sizeof(tmp));
fcMode = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "fcCommand", tmp, sizeof(tmp));
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, "emergencyAllow", tmp, sizeof(tmp));
emergencyAllow = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "depth", tmp, sizeof(tmp));
depth = (unsigned short)atoi(tmp);
mg_http_get_var(&body, "supplyExhaustCmd", tmp, sizeof(tmp));
supplyExhaustCmd = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "insBatCmd", tmp, sizeof(tmp));
insBatCmd = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "powerBatCmd", tmp, sizeof(tmp));
powerBatCmd = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "powerBatPower", tmp, sizeof(tmp));
powerBatPower = (unsigned short)atoi(tmp);
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,
emergencyAllow, depth, supplyExhaustCmd, insBatCmd, powerBatCmd, powerBatPower, hostStatus);
{
std::lock_guard<std::mutex> lock(m_powerManger->m_stateMutex);
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.emergencyAllow = emergencyAllow;
m_powerManger->m_CcuColCmd.depth = depth;
m_powerManger->m_CcuColCmd.supplyExhaustCmd = supplyExhaustCmd;
m_powerManger->m_CcuColCmd.insBatCmd = insBatCmd;
m_powerManger->m_CcuColCmd.powerBatCmd = powerBatCmd;
m_powerManger->m_CcuColCmd.powerBatEfficiency = powerBatPower;
m_powerManger->m_CcuColCmd.masterState = hostStatus;
}
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
"{\"success\": true, \"message\": \"燃料电池配置提交成功\"}");
} else {
mg_http_reply(c, 405, "Content-Type: text/plain\r\n", "Method Not Allowed");
}
} else if (mg_match(hm->uri, mg_str("/battery/control"), NULL)) {
// 处理电池控制POST请求
if (mg_strcmp(hm->method, mg_str("POST")) == 0) {
@@ -116,8 +197,6 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_
fuelCellPower = (unsigned short)atoi(tmp);
mg_http_get_var(&body, "instrumentBattery", tmp, sizeof(tmp));
instrumentBattery = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "instrumentBatteryCharge", tmp, sizeof(tmp));
unsigned char instrumentBatteryCharge = (unsigned char)atoi(tmp);
mg_http_get_var(&body, "powerBattery", tmp, sizeof(tmp));
powerBattery = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "powerBatteryPower", tmp, sizeof(tmp));
@@ -128,10 +207,10 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_
// 记录接收到的配置
LOG_F(INFO, "Received battery control config: "
"fuelCellMode=%02XH, fuelCellCommand=%02XH, fuelCellPower=%d, "
"instrumentBattery=%02XH, instrumentBatteryCharge=%d, powerBattery=%02XH, powerBatteryPower=%d, "
"instrumentBattery=%02XH, powerBattery=%02XH, powerBatteryPower=%d, "
"hostStatus=%02XH",
fuelCellMode, fuelCellCommand, fuelCellPower,
instrumentBattery, instrumentBatteryCharge, powerBattery, powerBatteryPower, hostStatus);
instrumentBattery, powerBattery, powerBatteryPower, hostStatus);
{
std::lock_guard<std::mutex> lock(m_powerManger->m_stateMutex);
@@ -142,7 +221,6 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_
m_powerManger->m_CcuColCmd.powerBatCmd = powerBattery;
m_powerManger->m_CcuColCmd.powerBatEfficiency = powerBatteryPower;
m_powerManger->m_CcuColCmd.masterState = hostStatus;
m_powerManger->m_CcuColCmd.insChargeEnable = instrumentBatteryCharge;
}
cout << "powerBatteryPower:" << (int)instrumentBattery << endl;
cout << "m_powerManger->m_CcuColCmd.masterState:" << (int)hostStatus << endl;
@@ -376,7 +454,7 @@ void HttpServer::pushSystemData() {
//功率和时间参数
j["output_power_limit"] = Json::Value(m_systemData->output_power_limit); // 系统输出功率限制值,单位:W
j["total_generation_time"] = Json::Value(m_systemData->total_generation_time); // 系统累计发电时间,单位:0.1小时
j["total_generation_power"] = Json::Value(m_systemData->total_generation_power); // 系统累计发电功率,单位:W
j["total_generation_power"] = Json::Value(m_systemData->total_generation_power); // 系统发电功率,单位:kW
//燃料电池故障信息
j["fc_fault_level_1"] = Json::Value(m_systemData->fc_fault_level_1);
@@ -388,6 +466,64 @@ void HttpServer::pushSystemData() {
j["hydrogen_capacity"] = Json::Value(m_systemData->hydrogen_capacity); // 储氢罐剩余容量百分比,单位:%
j["liquid_oxygen_capacity"] = Json::Value(m_systemData->liquid_oxygen_capacity); // 液氧罐剩余容量百分比,单位:%
//FC单片电压
j["fc1_min_cell_voltage"] = Json::Value(m_systemData->fc1_min_cell_voltage); // I#FC最低单片电压 mV
j["fc1_min_cell_pos"] = Json::Value(m_systemData->fc1_min_cell_pos);
j["fc1_avg_cell_voltage"] = Json::Value(m_systemData->fc1_avg_cell_voltage); // I#FC平均单片电压 mV
j["fc2_min_cell_voltage"] = Json::Value(m_systemData->fc2_min_cell_voltage);
j["fc2_min_cell_pos"] = Json::Value(m_systemData->fc2_min_cell_pos);
j["fc2_avg_cell_voltage"] = Json::Value(m_systemData->fc2_avg_cell_voltage);
//甲醇制氢装置
j["buffer_tank_pressure"] = Json::Value(m_systemData->buffer_tank_pressure); // 缓冲罐压力 kPa
j["flue_total_emission"] = Json::Value(m_systemData->flue_total_emission); // 烟气累计排放量 kg
j["flue_pressure"] = Json::Value(m_systemData->flue_pressure); // 烟气压力 MPa
j["reactor_pressure"] = Json::Value(m_systemData->reactor_pressure); // 反应器压力 MPa
j["electric_valve_open"] = Json::Value(m_systemData->electric_valve_open); // 电动阀开度 %
//DC/DC
j["dcdc1_in_voltage"] = Json::Value(m_systemData->dcdc1_in_voltage);
j["dcdc1_in_current"] = Json::Value(m_systemData->dcdc1_in_current);
j["dcdc2_in_voltage"] = Json::Value(m_systemData->dcdc2_in_voltage);
j["dcdc2_in_current"] = Json::Value(m_systemData->dcdc2_in_current);
j["dcdc_out_voltage"] = Json::Value(m_systemData->dcdc_out_voltage);
j["dcdc_out_current"] = Json::Value(m_systemData->dcdc_out_current);
j["dcdc_control_voltage"] = Json::Value(m_systemData->dcdc_control_voltage);
j["dcdc_aux_out_voltage"] = Json::Value(m_systemData->dcdc_aux_out_voltage);
//甲醇/水系统
j["methanol_total_usage"] = Json::Value(m_systemData->methanol_total_usage); // 甲醇累计使用量 kg
j["methanol_solution_feed"] = Json::Value(m_systemData->methanol_solution_feed); // 甲醇溶液进料量 mL/min
j["oxygen_water_tank_level"] = Json::Value(m_systemData->oxygen_water_tank_level); // 氧侧水箱液位 mm
j["hydrogen_water_tank_level"] = Json::Value(m_systemData->hydrogen_water_tank_level); // 氢侧水箱液位 mm
j["ballast_water_tank_level"] = Json::Value(m_systemData->ballast_water_tank_level); // 配重水箱液位 mm
//尾气装置
j["exhaust_in_pressure"] = Json::Value(m_systemData->exhaust_in_pressure);
j["exhaust_out_pressure"] = Json::Value(m_systemData->exhaust_out_pressure);
j["exhaust_run_freq"] = Json::Value(m_systemData->exhaust_run_freq);
j["exhaust_in_temp"] = Json::Value(m_systemData->exhaust_in_temp);
j["exhaust_out_temp"] = Json::Value(m_systemData->exhaust_out_temp);
j["exhaust_in_water_pressure"] = Json::Value(m_systemData->exhaust_in_water_pressure);
j["exhaust_out_water_pressure"] = Json::Value(m_systemData->exhaust_out_water_pressure);
//一体化罐装置
j["tank_lo2_pressure"] = Json::Value(m_systemData->tank_lo2_pressure);
j["tank_co2_pressure"] = Json::Value(m_systemData->tank_co2_pressure);
j["tank_lo2_level"] = Json::Value(m_systemData->tank_lo2_level);
//供氢/供氧流量
j["alloy_h2_flow"] = Json::Value(m_systemData->alloy_h2_flow);
j["fc_h2_flow"] = Json::Value(m_systemData->fc_h2_flow);
j["fc_o2_flow"] = Json::Value(m_systemData->fc_o2_flow);
//应急上浮
j["emergency_float_depth"] = Json::Value(m_systemData->emergency_float_depth);
j["emergency_float_time"] = Json::Value(m_systemData->emergency_float_time);
//帧尾
j["emergency_command"] = Json::Value(m_systemData->emergency_command);
//温度压力参数
j["palladium_temp"] = Json::Value(m_systemData->palladium_temp); // 钯膜最高温度,单位:℃
j["main_pipe_pressure"] = Json::Value(m_systemData->main_pipe_pressure); // 主水路压力,单位:0.01kPa
@@ -785,7 +921,15 @@ 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"]["insChargeEnable"] = Json::Value(ccuCmd.insChargeEnable);
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"]["emergencyAllow"] = Json::Value(ccuCmd.emergencyAllow);
j["ccuColCmd"]["depth"] = Json::Value(ccuCmd.depth);
j["ccuColCmd"]["supplyExhaustCmd"] = Json::Value(ccuCmd.supplyExhaustCmd);
j["ccuColCmd"]["reservedCmd5"] = Json::Value(ccuCmd.reservedCmd5);
j["ccuColCmd"]["reservedCmd6"] = Json::Value(ccuCmd.reservedCmd6);
j["ccuColCmd"]["insBatCmd"] = Json::Value(ccuCmd.insBatCmd);
j["ccuColCmd"]["powerBatCmd"] = Json::Value(ccuCmd.powerBatCmd);
j["ccuColCmd"]["powerBatEfficiency"] = Json::Value(ccuCmd.powerBatEfficiency);
+11 -11
View File
@@ -585,6 +585,10 @@ body {
<span class="sidebar-icon">🔋</span>
<span>电池操作</span>
</a>
<a href="/fcs" class="sidebar-button">
<span class="sidebar-icon">🔥</span>
<span>燃料电池</span>
</a>
<a href="/testpage" class="sidebar-button">
<span class="sidebar-icon">🧪</span>
<span>测试页面</span>
@@ -702,8 +706,7 @@ body {
<div class="battery-data">
<div class="data-row"><span>燃料电池模式设定:</span><span id="fcmode">--</span></div>
<div class="data-row"><span>燃料电池指令:</span><span id="fcuCmd">--</span></div>
<div class="data-row"><span>燃料电池动力通路功率配置:</span><span><span id="fcuPowerEfficiency">--</span> kW</span></div>
<div class="data-row"><span>仪表锂电池允许充电标志位:</span><span id="insChargeEnable">--</span></div>
<div class="data-row"><span>燃料电池输出功率指令:</span><span><span id="fcuPowerEfficiency">--</span> (0.1kW)</span></div>
<div class="data-row"><span>仪表锂电池启停指令:</span><span id="insBatCmd">--</span></div>
<div class="data-row"><span>动力锂电池启停指令:</span><span id="powerBatCmd">--</span></div>
<div class="data-row"><span>动力锂电池电池功率配置值:</span><span><span id="powerBatEfficiency">--</span> kW</span></div>
@@ -1488,7 +1491,7 @@ body {
}
function getFCStatusText(status) {
const statuses = { 0: '无效', 1: '初始化', 2: '待机', 3: '启动中', 4: '运行', 5: '关机中', 6: '故障', 7: '维护', 8: '校准', 9: '测试', 10: '紧急停止', 11: '重启', 12: '升级', 13: '维护完成' };
const statuses = { 0: '无效', 1: '初始化', 2: '待机', 3: '自检异常', 4: '就绪', 5: '启动', 6: '运行', 7: '停机', 8: '故障', 9: '补给', 10: '制氢预热', 11: '制氢预热完成', 12: '停机维护', 13: '维护完成' };
return statuses[status] || '未知';
}
@@ -1523,11 +1526,7 @@ body {
}
function getInsChargeEnableText(state) {
const states = {
0: '允许充电',
48: '禁止充电'
};
return states[state] || '未知状态';
return state === undefined || state === null ? '--' : 'N/A';
}
function getFcuCmdText(cmd) {
@@ -1540,7 +1539,9 @@ body {
5: '紧急停机',
6: '补给',
7: '制氢预热',
8: '停机维护(关机)'
8: '停机维护(关机)',
10: '开机',
11: '关机'
};
return commands[cmd] || '未知指令';
}
@@ -1555,8 +1556,7 @@ body {
const ccuData = data.ccuColCmd;
updateStatusWithDebounce('fcmode', ccuData.fcmode, getFCModeText);
updateStatusWithDebounce('fcuCmd', ccuData.fcuCmd, getFcuCmdText);
updateWithDebounce('fcuPowerEfficiency', ccuData.fcuPowerEfficiency, 2);
updateStatusWithDebounce('insChargeEnable', ccuData.insChargeEnable, getInsChargeEnableText);
updateWithDebounce('fcuPowerEfficiency', ccuData.fcuPowerEfficiency, 0);
updateStatusWithDebounce('insBatCmd', ccuData.insBatCmd, getBatCmdText);
updateStatusWithDebounce('powerBatCmd', ccuData.powerBatCmd, getBatCmdText);
updateWithDebounce('powerBatEfficiency', ccuData.powerBatEfficiency, 2);
+188 -119
View File
@@ -15,152 +15,219 @@
#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-四级
uint8_t fc_status; // 燃料电池系统运行状态:00H-无效, 01H-初始化, 02H-待机, 03H-自检异常, 04H-就绪, 05H-启动, 06H-运行, 07H-停机, 08H-故障, 09H-补给, 0AH-制氢预热, 0BH-制氢预热完成, 0CH-停机维护, 0DH-维护完成
uint16_t fault_level_1; // 一级故障码:0x0000~0xFFFF
uint16_t fault_level_2; // 二级故障码:0x0000~0xFFFF
uint16_t fault_level_3; // 三级故障码:0x0000~0xFFFF
uint16_t fault_level_4; // 四级故障码:0x0000~0xFFFF
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; // 四级故障位
uint16_t total_generation_time; // 累积发电时间,单位:0.1h
uint8_t fc_fault_level; // 系统故障等级:00H-无故障, 01H-一级故障, 02H-二级故障, 03H-三级故障, 04H-四级故障
uint16_t output_power_limit; // 系统输出功率限定功率,单位:W
uint16_t total_generation_power; // 系统发电功率,单位:0.01kW
uint8_t hydrogen_capacity; // 储氢罐剩余容量百分比,单位:%,范围:0~100
uint8_t liquid_oxygen_capacity; // 液氧罐剩余容量百分比,单位:%,范围:0~100
uint8_t hydrogen_capacity; // 储氢剩余容量,单位:%
uint8_t liquid_oxygen_capacity; // 液氧剩余容量,单位:%
uint16_t palladium_temp; // 钯膜最高温度,单位:℃
uint16_t main_pipe_pressure; // 主水路压力,单位:0.01kPa
uint16_t aux_pipe_pressure; // 辅水路压力,单位:0.01kPa
// ==================== FC单片电压 ====================
uint8_t fc1_min_cell_voltage; // I#FC最低单片电压,单位:10mV
uint8_t fc1_min_cell_pos; // I#FC最低单片电压位置,单位:节
uint8_t fc1_avg_cell_voltage; // I#FC平均单片电压,单位:10mV
uint8_t fc2_min_cell_voltage; // 2#FC最低单片电压,单位:10mV
uint8_t fc2_min_cell_pos; // 2#FC最低单片电压位置,单位:节
uint8_t fc2_avg_cell_voltage; // 2#FC平均单片电压,单位:10mV
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
// ==================== 甲醇制氢装置 ====================
uint16_t palladium_temp; // 甲醇制氢装置钯膜最高温度,单位:0.1℃
uint16_t buffer_tank_pressure; // 甲醇制氢装置缓冲罐压力,单位:0.1kPa
uint16_t flue_total_emission; // 甲醇制氢装置烟气累计排放量,单位:0.1kg
uint16_t flue_pressure; // 甲醇制氢装置烟气压力,单位:0.001MPa
uint16_t reactor_pressure; // 甲醇制氢装置反应器压力,单位:0.001MPa
uint16_t electric_valve_open; // 甲醇制氢装置电动阀开度,单位:0.1%
uint8_t flame_detector1; // 火焰探测器1状态:00H-无效, 01H-报警, 02H-故障
uint8_t flame_detector2; // 火焰探测器2状态:00H-无效, 01H-报警, 02H-故障
// ==================== 水路/盘管 ====================
uint16_t main_pipe_pressure; // 主水路盘管侧压力,单位:0.01kPa
uint16_t aux_pipe_pressure; // 辅水路盘管侧压力,单位:0.01kPa
// ==================== DC/DC ====================
uint16_t dcdc1_in_voltage; // DC/DC通道1输入电压,单位:0.1V
uint16_t dcdc1_in_current; // DC/DC通道1输入电流,单位:0.1A
uint16_t dcdc2_in_voltage; // DC/DC通道2输入电压,单位:0.1V
uint16_t dcdc2_in_current; // DC/DC通道2输入电流,单位:0.1A
uint16_t dcdc_out_voltage; // DC/DC输出电压,单位:0.1V
uint16_t dcdc_out_current; // DC/DC输出电流,单位:0.1A
uint8_t dcdc_control_voltage; // DC/DC控制电源电压,单位:0.25V
uint8_t dcdc_aux_out_voltage; // DC/DC辅电输出电压,单位:0.125V
// ==================== 甲醇/水系统 ====================
uint16_t methanol_total_usage; // 甲醇累计使用量,单位:0.1kg
uint16_t methanol_solution_feed; // 甲醇溶液进料量,单位:1mL/min
uint16_t oxygen_water_tank_level; // 氧侧生成水箱液位,单位:0.01mm
uint16_t hydrogen_water_tank_level; // 氢侧生成水箱液位,单位:0.01mm
uint16_t ballast_water_tank_level; // 配重水箱液位,单位:0.01mm
// ==================== 尾气装置 ====================
uint16_t exhaust_in_pressure; // 尾气装置进气压力,单位:0.01MPa
uint16_t exhaust_out_pressure; // 尾气装置排气压力,单位:0.01MPa
uint16_t exhaust_run_freq; // 尾气装置运行频率,单位:0.01Hz
uint16_t exhaust_in_temp; // 尾气装置进气温度,单位:0.01℃
uint16_t exhaust_out_temp; // 尾气装置排气温度,单位:0.01℃
uint16_t exhaust_in_water_pressure; // 尾气装置进水压力,单位:0.01kPa
uint16_t exhaust_out_water_pressure; // 尾气装置排水压力,单位:0.01kPa
// ==================== 一体化罐装置 ====================
uint16_t tank_lo2_pressure; // 一体化罐装置液氧罐压力,单位:0.01MPa
uint16_t tank_co2_pressure; // 一体化罐装置二氧化碳压力,单位:0.01MPa
uint16_t tank_lo2_level; // 一体化罐装置液氧罐液位,单位:0.01mm
// ==================== 供氢/供氧流量 ====================
uint16_t alloy_h2_flow; // 合金供氢流量,单位:0.01L/min
uint16_t fc_h2_flow; // FC供氢流量,单位:0.01L/min
uint16_t fc_o2_flow; // FC供氧流量,单位:0.01L/min
// ==================== 应急 ====================
uint16_t emergency_float_depth; // 应急上浮深度,单位:1m
uint16_t emergency_float_time; // 应急上浮时间,单位:1min
// ==================== 舱室环境 ====================
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
// ==================== 舱室气体浓度 ====================
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 o2_concentration1; // 舱室O2浓度1,单位:0.01% Vol
uint16_t o2_concentration2; // 舱室O2浓度2,单位:0.01% Vol
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]
uint8_t flame_detector1; // 火焰探测器1状态:00H-无效, 01H-火焰报警, 02H-火焰探测器故障
uint8_t flame_detector2; // 火焰探测器2状态:00H-无效, 01H-火焰报警, 02H-火焰探测器故障
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,协议预留
uint16_t reserved1[2]; // 保留字段
uint8_t dyn_alarm_flag[6]; // 动力锂电池报警标志字数组,共6字节,按位表示各类报警
uint8_t ins_alarm_flag[6]; // 仪表锂电池报警标志字数组,共6字节,按位表示各类报警
// ==================== 应急电池 ====================
uint16_t emergency_battery1_voltage; // 应急电池1总电压
uint16_t emergency_battery1_current; // 应急电池1总电流
uint16_t emergency_battery1_max_temp; // 应急电池1最高温度
uint16_t emergency_battery1_fault_word; // 应急电池1故障字(H=故障字1+L=故障字2)
uint16_t emergency_battery2_voltage; // 应急电池2总电压
uint16_t emergency_battery2_current; // 应急电池2总电流
uint16_t emergency_battery2_max_temp; // 应急电池2最高温度
uint16_t emergency_battery2_fault_word; // 应急电池2故障字(H=故障字1+L=故障字2)
uint8_t ins_relay_status1; // 仪表电池继电器状态,高4位表示正极继电器,低4位表示充电继电器
uint8_t ins_relay_status2; // 仪表电池预充继电器和负极继电器状态
uint8_t dyn_relay_status1; // 动力电池正极与充电继电器状态
uint8_t dyn_relay_status2; // 动力电池预充与负极继电器状态
// ==================== 仪表舱/动力舱 ====================
uint16_t cabin_ox_concentration; // 仪表舱内氧气浓度,0~30%
uint16_t cabin_temperature; // 仪表舱内温度,-40~80℃
uint16_t cabin_humidity; // 仪表舱内湿度,0~100%RH
uint16_t cabin_pressure; // 仪表舱内大气压,10~450kPa
uint16_t power_cabin_ox_concentration; // 动力舱内氧气浓度,0~30%
uint16_t power_cabin_temperature; // 动力舱内温度,-40~80℃
uint16_t power_cabin_humidity; // 动力舱内湿度,0~100%RH
uint16_t power_cabin_pressure; // 动力舱内大气压,10~450kPa
uint8_t reserved2[1]; // 保留字段
uint16_t ins_max_discharge_power; // 仪表电池最大允许放电功率,单位:kW,精度0.05kW
uint16_t dyn_max_discharge_power; // 动力电池最大允许放电功率,单位:kW,精度0.05kW
// ==================== 电池报警标识 ====================
uint8_t dyn_alarm_flag[6]; // 动力锂电池报警标识字1~6
uint8_t ins_alarm_flag[6]; // 仪表锂电池报警标识字1~6
uint8_t ins_soc; // 仪表锂电池荷电状态SOC,范围:0~100
uint8_t dyn_soc; // 动力锂电池荷电状态SOC,范围:0~100
// ==================== 继电器状态 ====================
uint8_t ins_relay_status1; // 仪表电池总控正极继电器与充电继电器状态
uint8_t ins_relay_status2; // 仪表电池总控预充继电器与负极继电器状态
uint8_t dyn_relay_status1; // 动力电池总控正极继电器与充电继电器状态
uint8_t dyn_relay_status2; // 动力电池总控预充继电器与负极继电器状态
uint16_t ins_total_energy; // 仪表电池总能量,单位:kWh
uint16_t dyn_total_energy; // 动力电池总能量,单位:kWh
// ==================== 电池功率/SOC ====================
uint16_t ins_max_discharge_power; // 仪表电池允许最高放电功率,单位:0.05kW
uint16_t dyn_max_discharge_power; // 动力电池允许最高放电功率,单位:0.05kW
uint8_t ins_soc; // 仪表锂电池SOC,单位:%
uint8_t dyn_soc; // 动力锂电池SOC,单位:%
uint16_t ins_total_energy; // 仪表锂电池总能量,单位:0.1kWh
uint16_t dyn_total_energy; // 动力锂电池总电量,单位:0.1kWh
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_power_input; // 仪表电池输入功率,单位:kW
uint16_t dyn_power_input; // 动力电池输入功率,单位:kW
// ==================== 电池总控电压/电流/绝缘 ====================
uint16_t ins_voltage_link; // 仪表电池总控电池电压LINK端,单位:0.01V
uint16_t ins_voltage_pack; // 仪表电池总控电池电压PACK端,单位:0.01V
uint16_t ins_current; // 仪表电池总控电池放电电流,单位:0.05A
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.05A
uint16_t dyn_resistance_pos; // 动力电池总控电池正端绝缘电阻,单位:kΩ
uint16_t dyn_resistance_neg; // 动力电池总控电池负端绝缘电阻,单位:kΩ
uint8_t ins_charge_status; // 仪表电池充放电状态:00H-无效,10H-充电,20H-放电
uint8_t dyn_charge_status; // 动力电池充放电状态:00H-无效,10H-充电,20H-放电
uint8_t ins_emergency_status; // 仪表电池紧急状态
uint8_t dyn_emergency_status; // 动力电池紧急状态
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Ω
// ==================== SOC门限/功率限制当前值 ====================
uint8_t ins_soc_threshold1; // 仪表电池充电一级功率SOC门限当前值
uint8_t ins_soc_threshold2; // 仪表电池充电二级功率SOC门限当前值
uint8_t ins_soc_threshold3; // 仪表电池充电三级功率SOC门限当前值
uint8_t ins_power_limit1; // 仪表通路燃料电池输出功率一级限制当前值
uint8_t ins_power_limit2; // 仪表通路燃料电池输出功率二级限制当前值
uint8_t dyn_soc_threshold1; // 动力电池充电一级功率SOC门限当前值
uint8_t dyn_soc_threshold2; // 动力电池充电二级功率SOC门限当前值
uint8_t dyn_soc_threshold3; // 动力电池充电三级功率SOC门限当前值
uint8_t dyn_power_limit1; // 动力通路燃料电池输出功率一级限制当前值
uint8_t dyn_power_limit2; // 动力通路燃料电池输出功率二级限制当前值
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Ω
// ==================== 设备在线状态 ====================
uint32_t device_online_flag1; // 设备在线状态标志字1:byte0仪表锂电 byte1动力锂电 byte2燃电锂电 byte3高压配电板
uint32_t device_online_flag2; // 设备在线状态标志字2:byte0低压配电板 byte1复合管控主机 byte2复合管控备机
uint32_t reserved3; // 保留字段
uint8_t ins_emergency_status; // 仪表电池紧急状态:0x00无报警 0x3A=4级报警 0x3B=3级报警 0x3C=2级报警 0x3D=1级报警
uint8_t dyn_emergency_status; // 动力电池紧急状态:0x00无报警 0x3A=4级报警 0x3B=3级报警 0x3C=2级报警 0x3D=1级报警
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; // 主机心跳计数器或标志值
// ==================== 帧尾 ====================
uint8_t heartbeat; // 通信心跳:0~255,每次+1
uint8_t emergency_command; // 应急指令:Bit0降载请求 Bit1上浮请求 Bit2排气中(预留) Bit3排气完成(预留)
} 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;
unsigned char insSocHold1; // 仪表电池充电一级功率SOC门限(预留,不使用)
unsigned char insSocHold2; // 仪表电池充电二级功率SOC门限(预留,不使用)
unsigned char insSocHold3; // 仪表电池充电三级功率SOC门限(预留,不使用)
unsigned char insOutputPower; // 仪表锂电池输出功率(预留,不使用)
unsigned char reserved1; // 预留
unsigned char powerSocHold1; // 动力电池充电一级功率SOC门限(缺省75)
unsigned char powerSocHold2; // 动力电池充电二级功率SOC门限(缺省85)
unsigned char powerSocHold3; // 动力电池充电三级功率SOC门限(缺省93)
unsigned char powerOutputPower; // 动力电池输出功率(预留,不使用)
unsigned char reserved2; // 预留
unsigned int reserved3; // 预留
unsigned int reserved4; // 预留
}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;
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,单位°
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-备用
unsigned char reservedCmd5; // 预留指令5
unsigned char reservedCmd6; // 预留指令6
unsigned char insBatCmd; // 仪表锂电池启停指令:00H-无效, 0x10H-启动, 0x20H-关闭
unsigned char powerBatCmd; // 动力锂电池启停指令:00H-无效, 0x10H-启动, 0x20H-关闭
unsigned short powerBatEfficiency; // 动力锂电池功率配置值:0~400,单位kW,最大功率配置
unsigned char heartbeat; // 通信心跳:0~255,每次+1
unsigned char masterState; // 主机状态:运行AAH, 关机FFH, 故障其他
unsigned int paceholder1; // 预留
unsigned int paceholder2; // 预留
}ccuColCmd;
typedef struct disHighVolBusState
@@ -578,7 +645,9 @@ namespace FcuCommand {
EmergencyStop = 0x05,
Supply = 0x06,
WarmUp = 0x07,
Maintenance = 0x08
Maintenance = 0x08,
PowerOn = 0x0A,
PowerOff = 0x0B
};
}
+63 -15
View File
@@ -36,26 +36,63 @@ const std::vector<SQLite::TableDesc>& SQLite::allTables()
COL(ccuState, fc_mode),
COL(ccuState, fc_status),
COL(ccuState, fc_fault_level),
COL(ccuState, output_power_limit),
COL(ccuState, total_generation_time),
COL(ccuState, total_generation_power),
COL(ccuState, fault_level_1),
COL(ccuState, fault_level_2),
COL(ccuState, fault_level_3),
COL(ccuState, fault_level_4),
COL(ccuState, total_generation_time),
COL(ccuState, output_power_limit),
COL(ccuState, total_generation_power),
COL(ccuState, hydrogen_capacity),
COL(ccuState, liquid_oxygen_capacity),
COL(ccuState, fc1_min_cell_voltage),
COL(ccuState, fc1_min_cell_pos),
COL(ccuState, fc1_avg_cell_voltage),
COL(ccuState, fc2_min_cell_voltage),
COL(ccuState, fc2_min_cell_pos),
COL(ccuState, fc2_avg_cell_voltage),
COL(ccuState, palladium_temp),
COL(ccuState, buffer_tank_pressure),
COL(ccuState, flue_total_emission),
COL(ccuState, flue_pressure),
COL(ccuState, reactor_pressure),
COL(ccuState, electric_valve_open),
COL(ccuState, main_pipe_pressure),
COL(ccuState, aux_pipe_pressure),
COL(ccuState, dcdc1_in_voltage),
COL(ccuState, dcdc1_in_current),
COL(ccuState, dcdc2_in_voltage),
COL(ccuState, dcdc2_in_current),
COL(ccuState, dcdc_out_voltage),
COL(ccuState, dcdc_out_current),
COL(ccuState, dcdc_control_voltage),
COL(ccuState, dcdc_aux_out_voltage),
COL(ccuState, methanol_total_usage),
COL(ccuState, methanol_solution_feed),
COL(ccuState, oxygen_water_tank_level),
COL(ccuState, hydrogen_water_tank_level),
COL(ccuState, ballast_water_tank_level),
COL(ccuState, exhaust_in_pressure),
COL(ccuState, exhaust_out_pressure),
COL(ccuState, exhaust_run_freq),
COL(ccuState, exhaust_in_temp),
COL(ccuState, exhaust_out_temp),
COL(ccuState, exhaust_in_water_pressure),
COL(ccuState, exhaust_out_water_pressure),
COL(ccuState, tank_lo2_pressure),
COL(ccuState, tank_co2_pressure),
COL(ccuState, tank_lo2_level),
COL(ccuState, alloy_h2_flow),
COL(ccuState, fc_h2_flow),
COL(ccuState, fc_o2_flow),
COL(ccuState, emergency_float_depth),
COL(ccuState, emergency_float_time),
COL(ccuState, cabin_pressure1),
COL(ccuState, cabin_pressure2),
COL(ccuState, cabin_temp1),
COL(ccuState, cabin_temp2),
COL(ccuState, cabin_humidity1),
COL(ccuState, cabin_humidity2),
COL(ccuState, flame_detector1),
COL(ccuState, flame_detector2),
COL(ccuState, h2_concentration1),
COL(ccuState, h2_concentration2),
COL(ccuState, h2_concentration3),
@@ -63,6 +100,8 @@ const std::vector<SQLite::TableDesc>& SQLite::allTables()
COL(ccuState, o2_concentration2),
COL(ccuState, ch3oh_concentration1),
COL(ccuState, ch3oh_concentration2),
COL(ccuState, flame_detector1),
COL(ccuState, flame_detector2),
COL_ARR(ccuState, reserved1, 0, "reserved1_1"),
COL_ARR(ccuState, reserved1, 1, "reserved1_2"),
COL(ccuState, emergency_battery1_voltage),
@@ -120,22 +159,21 @@ const std::vector<SQLite::TableDesc>& SQLite::allTables()
COL(ccuState, dyn_resistance_neg),
COL(ccuState, ins_emergency_status),
COL(ccuState, dyn_emergency_status),
COL_ARR(ccuState, reserved3, 0, "reserved3_1"),
COL_ARR(ccuState, reserved3, 1, "reserved3_2"),
COL(ccuState, ins_soc_threshold1),
COL(ccuState, ins_soc_threshold2),
COL(ccuState, ins_soc_threshold3),
COL(ccuState, ins_power_limit1),
COL(ccuState, ins_power_limit2),
COL(ccuState, dyn_soc_threshold1),
COL(ccuState, dyn_soc_threshold2),
COL(ccuState, dyn_soc_threshold3),
COL(ccuState, ins_power_limit1),
COL(ccuState, ins_power_limit2),
COL(ccuState, dyn_power_limit1),
COL(ccuState, dyn_power_limit2),
COL(ccuState, device_online_flag1),
COL(ccuState, device_online_flag2),
COL(ccuState, reserved4),
COL(ccuState, reserved3),
COL(ccuState, heartbeat),
COL(ccuState, emergency_command),
}},
{ "disHighVolBusState", {
COL(disHighVolBusState, fuelCellCircuitBreaker),
@@ -247,7 +285,15 @@ const std::vector<SQLite::TableDesc>& SQLite::allTables()
COL_AS(ccuColCmd, fcmode, "workCondition"),
COL(ccuColCmd, fcuCmd),
COL(ccuColCmd, fcuPowerEfficiency),
COL(ccuColCmd, insChargeEnable),
COL(ccuColCmd, pitchData1),
COL(ccuColCmd, pitchData2),
COL(ccuColCmd, rollData1),
COL(ccuColCmd, rollData2),
COL(ccuColCmd, emergencyAllow),
COL(ccuColCmd, depth),
COL(ccuColCmd, supplyExhaustCmd),
COL(ccuColCmd, reservedCmd5),
COL(ccuColCmd, reservedCmd6),
COL(ccuColCmd, insBatCmd),
COL(ccuColCmd, powerBatCmd),
COL(ccuColCmd, powerBatEfficiency),
@@ -260,13 +306,15 @@ const std::vector<SQLite::TableDesc>& SQLite::allTables()
COL(ccuSetParmCmd, insSocHold1),
COL(ccuSetParmCmd, insSocHold2),
COL(ccuSetParmCmd, insSocHold3),
COL(ccuSetParmCmd, fcuToInsLimit1),
COL_AS(ccuSetParmCmd, fcuToinsLimit2, "fcuToInsLimit2"),
COL(ccuSetParmCmd, insOutputPower),
COL(ccuSetParmCmd, reserved1),
COL(ccuSetParmCmd, powerSocHold1),
COL(ccuSetParmCmd, powerSocHold2),
COL(ccuSetParmCmd, powerSocHold3),
COL(ccuSetParmCmd, fcuToPowerLimit1),
COL(ccuSetParmCmd, fcuToPowerLimit2),
COL(ccuSetParmCmd, powerOutputPower),
COL(ccuSetParmCmd, reserved2),
COL(ccuSetParmCmd, reserved3),
COL(ccuSetParmCmd, reserved4),
}},
{ "disHighVolBusCmd", {
COL(disHighVolBusCmd, fuelCellCircuitBreaker),
+148 -28
View File
@@ -79,12 +79,12 @@ public:
//燃料电池系统参数
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-四级
uint8_t fc_fault_level; // 系统故障等级:00H-无故障, 01H-一级, 02H-二级, 03H-三级, 04H-四级
//功率和时间参数
float output_power_limit; // 系统输出功率限制值,单位:W
float total_generation_time; // 系统累计发电时间,单位:0.1小时
float total_generation_power; // 系统累计发电总功率,单位:W
float total_generation_power; // 系统发电功率,单位:kW(协议原始值分辨率0.01kW)
//故障信息
uint16_t fc_fault_level_1; // 一级故障位
@@ -96,6 +96,64 @@ public:
float hydrogen_capacity; // 储氢罐剩余容量百分比,单位:%
float liquid_oxygen_capacity; // 液氧罐剩余容量百分比,单位:%
//FC单片电压
float fc1_min_cell_voltage; // I#FC最低单片电压,单位:mV
uint8_t fc1_min_cell_pos; // I#FC最低单片电压位置,单位:节
float fc1_avg_cell_voltage; // I#FC平均单片电压,单位:mV
float fc2_min_cell_voltage; // 2#FC最低单片电压,单位:mV
uint8_t fc2_min_cell_pos; // 2#FC最低单片电压位置,单位:节
float fc2_avg_cell_voltage; // 2#FC平均单片电压,单位:mV
//甲醇制氢装置
float buffer_tank_pressure; // 甲醇制氢装置缓冲罐压力,单位:kPa
float flue_total_emission; // 甲醇制氢装置烟气累计排放量,单位:kg
float flue_pressure; // 甲醇制氢装置烟气压力,单位:MPa
float reactor_pressure; // 甲醇制氢装置反应器压力,单位:MPa
float electric_valve_open; // 甲醇制氢装置电动阀开度,单位:%
//DC/DC
float dcdc1_in_voltage; // DC/DC通道1输入电压,单位:V
float dcdc1_in_current; // DC/DC通道1输入电流,单位:A
float dcdc2_in_voltage; // DC/DC通道2输入电压,单位:V
float dcdc2_in_current; // DC/DC通道2输入电流,单位:A
float dcdc_out_voltage; // DC/DC输出电压,单位:V
float dcdc_out_current; // DC/DC输出电流,单位:A
float dcdc_control_voltage; // DC/DC控制电源电压,单位:V
float dcdc_aux_out_voltage; // DC/DC辅电输出电压,单位:V
//甲醇/水系统
float methanol_total_usage; // 甲醇累计使用量,单位:kg
float methanol_solution_feed; // 甲醇溶液进料量,单位:mL/min
float oxygen_water_tank_level; // 氧侧生成水箱液位,单位:mm
float hydrogen_water_tank_level; // 氢侧生成水箱液位,单位:mm
float ballast_water_tank_level; // 配重水箱液位,单位:mm
//尾气装置
float exhaust_in_pressure; // 尾气装置进气压力,单位:MPa
float exhaust_out_pressure; // 尾气装置排气压力,单位:MPa
float exhaust_run_freq; // 尾气装置运行频率,单位:Hz
float exhaust_in_temp; // 尾气装置进气温度,单位:℃
float exhaust_out_temp; // 尾气装置排气温度,单位:℃
float exhaust_in_water_pressure; // 尾气装置进水压力,单位:kPa
float exhaust_out_water_pressure; // 尾气装置排水压力,单位:kPa
//一体化罐装置
float tank_lo2_pressure; // 一体化罐装置液氧罐压力,单位:MPa
float tank_co2_pressure; // 一体化罐装置二氧化碳压力,单位:MPa
float tank_lo2_level; // 一体化罐装置液氧罐液位,单位:mm
//供氢/供氧流量
float alloy_h2_flow; // 合金供氢流量,单位:L/min
float fc_h2_flow; // FC供氢流量,单位:L/min
float fc_o2_flow; // FC供氧流量,单位:L/min
//应急上浮
float emergency_float_depth; // 应急上浮深度,单位:m
float emergency_float_time; // 应急上浮时间,单位:min
//帧尾
uint8_t emergency_command; // 应急指令:Bit0降载请求 Bit1上浮请求
//温度压力参数
float palladium_temp; // 钯膜最高温度,单位:℃
float main_pipe_pressure; // 主水路压力,单位:0.01kPa
@@ -556,6 +614,21 @@ public:
output_power_limit(0.0f), total_generation_time(0.0f), total_generation_power(0.0f),
fc_fault_level_1(0), fc_fault_level_2(0), fc_fault_level_3(0), fc_fault_level_4(0),
hydrogen_capacity(0.0f), liquid_oxygen_capacity(0.0f),
fc1_min_cell_voltage(0.0f), fc1_min_cell_pos(0), fc1_avg_cell_voltage(0.0f),
fc2_min_cell_voltage(0.0f), fc2_min_cell_pos(0), fc2_avg_cell_voltage(0.0f),
buffer_tank_pressure(0.0f), flue_total_emission(0.0f), flue_pressure(0.0f),
reactor_pressure(0.0f), electric_valve_open(0.0f),
dcdc1_in_voltage(0.0f), dcdc1_in_current(0.0f), dcdc2_in_voltage(0.0f), dcdc2_in_current(0.0f),
dcdc_out_voltage(0.0f), dcdc_out_current(0.0f), dcdc_control_voltage(0.0f), dcdc_aux_out_voltage(0.0f),
methanol_total_usage(0.0f), methanol_solution_feed(0.0f),
oxygen_water_tank_level(0.0f), hydrogen_water_tank_level(0.0f), ballast_water_tank_level(0.0f),
exhaust_in_pressure(0.0f), exhaust_out_pressure(0.0f), exhaust_run_freq(0.0f),
exhaust_in_temp(0.0f), exhaust_out_temp(0.0f),
exhaust_in_water_pressure(0.0f), exhaust_out_water_pressure(0.0f),
tank_lo2_pressure(0.0f), tank_co2_pressure(0.0f), tank_lo2_level(0.0f),
alloy_h2_flow(0.0f), fc_h2_flow(0.0f), fc_o2_flow(0.0f),
emergency_float_depth(0.0f), emergency_float_time(0.0f),
emergency_command(0),
palladium_temp(0.0f), main_pipe_pressure(0.0f), aux_pipe_pressure(0.0f),
cabin_pressure1(0.0f), cabin_pressure2(0.0f), cabin_temp1(0.0f), cabin_temp2(0.0f),
cabin_humidity1(0.0f), cabin_humidity2(0.0f),
@@ -697,7 +770,7 @@ public:
//功率和时间参数
output_power_limit = data.output_power_limit;
total_generation_time = data.total_generation_time * 0.1;
total_generation_power = data.total_generation_power;
total_generation_power = data.total_generation_power * 0.01; // 分辨率0.01,单位kW
//故障信息
#ifdef DEBUG
@@ -710,9 +783,64 @@ public:
//燃料容量
hydrogen_capacity = static_cast<int>(data.hydrogen_capacity);
liquid_oxygen_capacity = static_cast<int>(data.liquid_oxygen_capacity);
//FC单片电压(分辨率10mV)
fc1_min_cell_voltage = data.fc1_min_cell_voltage * 10.0;
fc1_min_cell_pos = data.fc1_min_cell_pos;
fc1_avg_cell_voltage = data.fc1_avg_cell_voltage * 10.0;
fc2_min_cell_voltage = data.fc2_min_cell_voltage * 10.0;
fc2_min_cell_pos = data.fc2_min_cell_pos;
fc2_avg_cell_voltage = data.fc2_avg_cell_voltage * 10.0;
//甲醇制氢装置
buffer_tank_pressure = data.buffer_tank_pressure * 0.1; // kPa
flue_total_emission = data.flue_total_emission * 0.1; // kg
flue_pressure = data.flue_pressure * 0.001; // MPa
reactor_pressure = data.reactor_pressure * 0.001; // MPa
electric_valve_open = data.electric_valve_open * 0.1; // %
//DC/DC
dcdc1_in_voltage = data.dcdc1_in_voltage * 0.1; // V
dcdc1_in_current = data.dcdc1_in_current * 0.1; // A
dcdc2_in_voltage = data.dcdc2_in_voltage * 0.1; // V
dcdc2_in_current = data.dcdc2_in_current * 0.1; // A
dcdc_out_voltage = data.dcdc_out_voltage * 0.1; // V
dcdc_out_current = data.dcdc_out_current * 0.1; // A
dcdc_control_voltage = data.dcdc_control_voltage * 0.25; // V
dcdc_aux_out_voltage = data.dcdc_aux_out_voltage * 0.125; // V
//甲醇/水系统
methanol_total_usage = data.methanol_total_usage * 0.1; // kg
methanol_solution_feed = data.methanol_solution_feed; // mL/min
oxygen_water_tank_level = data.oxygen_water_tank_level * 0.01; // mm
hydrogen_water_tank_level = data.hydrogen_water_tank_level * 0.01; // mm
ballast_water_tank_level = data.ballast_water_tank_level * 0.01; // mm
//尾气装置
exhaust_in_pressure = data.exhaust_in_pressure * 0.01; // MPa
exhaust_out_pressure = data.exhaust_out_pressure * 0.01; // MPa
exhaust_run_freq = data.exhaust_run_freq * 0.01; // Hz
exhaust_in_temp = data.exhaust_in_temp * 0.01; // ℃
exhaust_out_temp = data.exhaust_out_temp * 0.01; // ℃
exhaust_in_water_pressure = data.exhaust_in_water_pressure * 0.01; // kPa
exhaust_out_water_pressure = data.exhaust_out_water_pressure * 0.01; // kPa
//一体化罐装置
tank_lo2_pressure = data.tank_lo2_pressure * 0.01; // MPa
tank_co2_pressure = data.tank_co2_pressure * 0.01; // MPa
tank_lo2_level = data.tank_lo2_level * 0.01; // mm
//供氢/供氧流量
alloy_h2_flow = data.alloy_h2_flow * 0.01; // L/min
fc_h2_flow = data.fc_h2_flow * 0.01; // L/min
fc_o2_flow = data.fc_o2_flow * 0.01; // L/min
//应急上浮
emergency_float_depth = data.emergency_float_depth; // m
emergency_float_time = data.emergency_float_time; // min
//温度压力参数
palladium_temp = data.palladium_temp;
palladium_temp = data.palladium_temp * 0.1; // 钯膜最高温度,单位:℃
main_pipe_pressure = data.main_pipe_pressure * 0.01;
aux_pipe_pressure = data.aux_pipe_pressure * 0.01;
@@ -842,34 +970,26 @@ public:
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 || 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;
// 新协议不再包含漏水/充电等级字段,默认置为正常
dyn_water_leak_status = 0;
ins_water_leak_dcdc_status = 0;
dyn_charge_level = 0;
water_leak_status_from_insbat = 0;
water_leak_status_from_dynbat = 0;
dcdc_status_from_insbat = 0;
leakStatus[LEAKAGE_LI_BATTERY] = false;
//应急电池(新协议未注明分辨率,按原始值显示)
emergency_battery1_voltage = data.emergency_battery1_voltage;
emergency_battery1_current = data.emergency_battery1_current;
emergency_battery1_max_temp = data.emergency_battery1_max_temp;
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_voltage = data.emergency_battery2_voltage;
emergency_battery2_current = data.emergency_battery2_current;
emergency_battery2_max_temp = data.emergency_battery2_max_temp;
emergency_battery2_fault_word = data.emergency_battery2_fault_word;
//心跳计数器
//通信心跳与应急指令
heartbeat = data.heartbeat;
emergency_command = data.emergency_command;
}
std::set<unsigned int> disSysFaultCode;
mutable std::mutex faultCodeMutex;
+8 -6
View File
@@ -2,16 +2,18 @@
#define CCUUDPMSG__H
#include "../pmSysvariable.h"
#define CCU_UDPMSG_START1 0x20
#define CCU_UDPMSG_START2 0x20
#define CCU_UDPMSG_START1 0x40
#define CCU_UDPMSG_START2 0x40
#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
// 控制主机 <-> 复合管控器 通信协议(2026-08-24修订)
// 域起始符统一为 0x40 0x40;多字节数据小端序;域字节数为整个协议包的数据长度
#define CCU_CMD_ID 0x0001
#define CCU_SET_PARM_ID 0x0002
#define CCU_SET_PARM_FB_ID 0x0003
#define CCU_STATE_FB_ID 0x0004
#define DIS_HVBUS_FB_ID 0x0005
#define DIS_HVBUSA_FB_ID 0x0006
+8 -6
View File
@@ -191,7 +191,7 @@ bool udpComm::pushMsgToQueue(unsigned char *m, size_t len)
//输入消息
switch (id)
{
case CCU_STATE_FB_ID: //CCU_STATE_FB 0x0051
case CCU_STATE_FB_ID: //CCU_STATE_FB 0x0004
{
msg_CcuStateFbMsg s;
if(getMsgFromBuff((char *)Buff, len, s))
@@ -216,7 +216,7 @@ bool udpComm::pushMsgToQueue(unsigned char *m, size_t len)
}
break;
}
case CCU_SET_PARM_FB_ID: //CCU_SET_PARM_FB 0x0040
case CCU_SET_PARM_FB_ID: //CCU_SET_PARM_FB 0x0003
{
msg_CcuSetParmFbMsg s;
if(getMsgFromBuff((char *)Buff, len, s))
@@ -562,7 +562,8 @@ bool udpComm::sendCcuColCmd(const ccuColCmd cmd)
msg->header.id = CCU_CMD_ID;
msg->header.start1 = CCU_UDPMSG_START1;
msg->header.start2 = CCU_UDPMSG_START2;
msg->header.length = sizeof(ccuColCmd);
// 域字节数表示整个协议包的数据长度(0x002D = 45字节)
msg->header.length = sizeof(msg_CcuColCmdMsg);
// 确保日期获取成功
if(!getDate(msg->date.year,msg->date.month,msg->date.day,
@@ -575,7 +576,7 @@ bool udpComm::sendCcuColCmd(const ccuColCmd cmd)
msg->cmd = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_CcuColCmdMsg));
msg->checkCode = static_cast<cChecksum>(cc & 0xFF);
msg->checkCode = cc;
bool sendResult = false;
try {
@@ -599,11 +600,12 @@ bool udpComm::sendCcuSetParmCmd(const ccuSetParmCmd cmd)
msg->header.id = CCU_SET_PARM_ID;
msg->header.start1 = CCU_UDPMSG_START1;
msg->header.start2 = CCU_UDPMSG_START2;
msg->header.length = sizeof(ccuSetParmCmd);
// 域字节数表示整个协议包的数据长度(0x001C = 28字节)
msg->header.length = sizeof(msg_CcuSetParmMsg);
msg->cmd = cmd;
msgBuf = reinterpret_cast<unsigned char *>(msg);
cc = calculateChecksum(msgBuf,sizeof(msg_CcuSetParmMsg));
msg->checkCode = static_cast<cChecksum>(cc & 0xFF);
msg->checkCode = cc;
try
{
sendMsg(msgBuf,sizeof(msg_CcuSetParmMsg));
+69 -28
View File
@@ -43,29 +43,68 @@ static void fillCcuState(msg_CcuStateFbMsg& m, unsigned int beat) {
m.header.start1 = CCU_UDPMSG_START1;
m.header.start2 = CCU_UDPMSG_START2;
m.header.id = CCU_STATE_FB_ID;
m.header.length = sizeof(ccuState);
m.header.length = sizeof(msg_CcuStateFbMsg); // 域字节数为整个协议包长度
m.data.fc_mode = 0x02; // 自动
m.data.fc_status = 0x04; // 运行
m.data.fc_status = 0x06; // 运行
m.data.fc_fault_level = 0x00; // 无故障
m.data.output_power_limit = 30000;
m.data.total_generation_time = 1234;
m.data.total_generation_power = 8000;
m.data.hydrogen_capacity = 70;
m.data.liquid_oxygen_capacity = 65;
m.data.palladium_temp = 60;
m.data.main_pipe_pressure = 200;
m.data.aux_pipe_pressure = 180;
m.data.cabin_pressure1 = 101;
m.data.cabin_pressure2 = 101;
m.data.cabin_temp1 = 25;
m.data.cabin_temp2 = 26;
m.data.cabin_humidity1 = 50;
m.data.cabin_humidity2 = 52;
m.data.fc1_min_cell_voltage = 85; // 850mV
m.data.fc1_min_cell_pos = 12;
m.data.fc1_avg_cell_voltage = 90; // 900mV
m.data.fc2_min_cell_voltage = 84;
m.data.fc2_min_cell_pos = 8;
m.data.fc2_avg_cell_voltage = 89;
m.data.palladium_temp = 600; // 60.0℃
m.data.buffer_tank_pressure = 200; // 20.0kPa
m.data.flue_total_emission = 500; // 50.0kg
m.data.flue_pressure = 20000; // 20.000MPa
m.data.reactor_pressure = 30000; // 30.000MPa
m.data.electric_valve_open = 500; // 50.0%
m.data.main_pipe_pressure = 2000; // 20.00kPa
m.data.aux_pipe_pressure = 1800;
m.data.dcdc1_in_voltage = 700; // 70.0V
m.data.dcdc1_in_current = 120; // 12.0A
m.data.dcdc2_in_voltage = 700;
m.data.dcdc2_in_current = 120;
m.data.dcdc_out_voltage = 4800; // 480.0V
m.data.dcdc_out_current = 160; // 16.0A
m.data.dcdc_control_voltage = 100; // 25.0V
m.data.dcdc_aux_out_voltage = 100; // 12.5V
m.data.methanol_total_usage = 1000; // 100.0kg
m.data.methanol_solution_feed = 100; // 100mL/min
m.data.oxygen_water_tank_level = 50000; // 500.00mm
m.data.hydrogen_water_tank_level = 45000;
m.data.ballast_water_tank_level = 40000;
m.data.exhaust_in_pressure = 2000; // 20.00MPa
m.data.exhaust_out_pressure = 1800;
m.data.exhaust_run_freq = 5000; // 50.00Hz
m.data.exhaust_in_temp = 6000; // 60.00℃
m.data.exhaust_out_temp = 5500;
m.data.exhaust_in_water_pressure = 3000; // 30.00kPa
m.data.exhaust_out_water_pressure = 2800;
m.data.tank_lo2_pressure = 1000; // 10.00MPa
m.data.tank_co2_pressure = 800;
m.data.tank_lo2_level = 20000; // 200.00mm
m.data.alloy_h2_flow = 500; // 5.00L/min
m.data.fc_h2_flow = 450;
m.data.fc_o2_flow = 300;
m.data.emergency_float_depth = 50; // 50m
m.data.emergency_float_time = 20; // 20min
m.data.cabin_pressure1 = 10100; // 101.00kPa
m.data.cabin_pressure2 = 10100;
m.data.cabin_temp1 = 2500; // 25.00℃
m.data.cabin_temp2 = 2600;
m.data.cabin_humidity1 = 5000; // 50.00%RH
m.data.cabin_humidity2 = 5200;
m.data.flame_detector1 = 0x00;
m.data.flame_detector2 = 0x00;
m.data.h2_concentration1 = 5;
m.data.o2_concentration1 = 2000;
m.data.ch3oh_concentration1 = 3;
m.data.h2_concentration1 = 500; // 5.00%LEL
m.data.o2_concentration1 = 200000; // 2000.00%Vol
m.data.ch3oh_concentration1 = 300;
m.data.emergency_battery1_voltage = 240;
m.data.emergency_battery1_current = 10;
m.data.emergency_battery1_max_temp = 30;
@@ -73,13 +112,13 @@ static void fillCcuState(msg_CcuStateFbMsg& m, unsigned int beat) {
m.data.emergency_battery2_current = 10;
m.data.emergency_battery2_max_temp = 31;
m.data.cabin_ox_concentration = 2000;
m.data.cabin_temperature = 25;
m.data.cabin_humidity = 50;
m.data.cabin_pressure = 101;
m.data.cabin_temperature = 250;
m.data.cabin_humidity = 500;
m.data.cabin_pressure = 1010;
m.data.power_cabin_ox_concentration = 2000;
m.data.power_cabin_temperature = 28;
m.data.power_cabin_humidity = 48;
m.data.power_cabin_pressure = 101;
m.data.power_cabin_temperature = 280;
m.data.power_cabin_humidity = 480;
m.data.power_cabin_pressure = 1010;
m.data.ins_relay_status1 = 0x11;
m.data.ins_relay_status2 = 0x12;
m.data.dyn_relay_status1 = 0x11;
@@ -94,14 +133,14 @@ static void fillCcuState(msg_CcuStateFbMsg& m, unsigned int beat) {
m.data.dyn_power_input = 30;
m.data.ins_charge_status = 0x20; // 放电
m.data.dyn_charge_status = 0x20;
m.data.ins_voltage_link = 2400; // 240.0V
m.data.ins_voltage_link = 2400; // 24.00V
m.data.ins_voltage_pack = 2350;
m.data.ins_current = 400; // 20A
m.data.ins_current = 400; // 20.0A
m.data.ins_resistance_pos = 999;
m.data.ins_resistance_neg = 999;
m.data.dyn_voltage_link = 5200; // 520.0V
m.data.dyn_voltage_link = 5200; // 52.00V
m.data.dyn_voltage_pack = 5150;
m.data.dyn_current = 600; // 30A
m.data.dyn_current = 600; // 30.0A
m.data.dyn_resistance_pos = 999;
m.data.dyn_resistance_neg = 999;
m.data.ins_emergency_status = 0x00;
@@ -112,12 +151,14 @@ static void fillCcuState(msg_CcuStateFbMsg& m, unsigned int beat) {
m.data.dyn_soc_threshold1 = 90;
m.data.dyn_soc_threshold2 = 80;
m.data.dyn_soc_threshold3 = 70;
m.data.ins_power_limit1 = 8;
m.data.ins_power_limit2 = 4;
m.data.dyn_power_limit1 = 8;
m.data.dyn_power_limit2 = 4;
m.data.device_online_flag1 = 0x000000FF;
m.data.device_online_flag2 = 0x0000001F;
m.data.dyn_charge_level = 0x01;
m.data.dyn_water_leak_status = 0x00;
m.data.ins_water_leak_dcdc_status = 0x00;
m.data.heartbeat = beat;
m.data.heartbeat = static_cast<uint8_t>(beat);
m.data.emergency_command = 0x03;
m.checkCode = ccuChecksum(reinterpret_cast<unsigned char*>(&m), sizeof(m));
}
+2 -2
View File
@@ -169,11 +169,11 @@ TEST(SqliteTest, InsertCcuSetParmCmdReadBack)
std::string path = tmpPath("setparmcmd");
{
SQLite db(path); ASSERT_TRUE(db.createTable());
ccuSetParmCmd c{}; c.fcuToInsLimit1 = 7; c.powerSocHold2 = 88;
ccuSetParmCmd c{}; c.insSocHold1 = 7; c.powerSocHold2 = 88;
EXPECT_TRUE(db.insertData(c));
}
long long v = 0;
EXPECT_TRUE(rawQueryInt(path, "SELECT fcuToInsLimit1 FROM ccuSetParmCmd", v));
EXPECT_TRUE(rawQueryInt(path, "SELECT insSocHold1 FROM ccuSetParmCmd", v));
EXPECT_EQ(v, 7);
EXPECT_TRUE(rawQueryInt(path, "SELECT powerSocHold2 FROM ccuSetParmCmd", v));
EXPECT_EQ(v, 88);
+3 -5
View File
@@ -155,8 +155,6 @@ TEST(SystemDataTest, CcuStateScalingAndRelayBits)
c.ins_relay_status1 = 0x12; // 高4位=1(充电), 低4位=2(正极)
c.ins_relay_status2 = 0x34;
c.ins_voltage_link = 4800; // ×0.1 = 480.0
c.ins_water_leak_dcdc_status = 0x11; // 高4位=1 漏水, 低4位=1 DCDC故障
c.dyn_water_leak_status = 0x21; // 高4位=2, 低4位=1
sd.updateSystemData(c);
EXPECT_EQ(sd.ins_soc, 60);
EXPECT_EQ(sd.dyn_soc, 80);
@@ -165,9 +163,9 @@ TEST(SystemDataTest, CcuStateScalingAndRelayBits)
EXPECT_EQ(sd.ins_precharge_relay_status, 4u); // 低4位 of status2
EXPECT_EQ(sd.ins_neg_relay_status, 3u); // 高4位 of status2
EXPECT_NEAR(sd.ins_voltage_link, 480.0, 0.01);
EXPECT_EQ(sd.water_leak_status_from_insbat, 1u);
EXPECT_EQ(sd.dcdc_status_from_insbat, 1u);
EXPECT_TRUE(sd.leakStatus[SystemData::LEAKAGE_LI_BATTERY]);
EXPECT_EQ(sd.water_leak_status_from_insbat, 0u);
EXPECT_EQ(sd.dcdc_status_from_insbat, 0u);
EXPECT_FALSE(sd.leakStatus[SystemData::LEAKAGE_LI_BATTERY]);
}
TEST(SystemDataTest, FaultCodeSetAndLevel)
+1 -2
View File
@@ -54,7 +54,7 @@ ccuState createTestCcuState() {
state.ins_soc = 85;
state.dyn_soc = 90;
state.heartbeat = 12345;
state.heartbeat = 123;
return state;
}
@@ -64,7 +64,6 @@ ccuColCmd createTestCcuColCmd() {
cmd.fcmode = 2;
cmd.fcuCmd = 2;
cmd.fcuPowerEfficiency = 80;
cmd.insChargeEnable = 1;
cmd.insBatCmd = 1;
cmd.powerBatCmd = 1;
cmd.powerBatEfficiency = 85;