523调试

This commit is contained in:
zjk
2025-05-23 15:38:23 +08:00
parent 3a14a3cff8
commit ee050bb58b
13 changed files with 1209 additions and 939 deletions
-1
View File
@@ -81,7 +81,6 @@ public:
bool operate_LVA_reservedCircuit2_Breaker(char opt);
bool operate_LVA_mergencyLithiumBatteryGroup1Circuit_Breaker(char opt);
//================================复合管控器操作函数====================================
bool operate_CCU_fcmod(char opt);
// 发送CCU控制命令
bool sendCcuCommand(const ccuColCmd cmd);
bool sendDisSysCommand(const disSysBreakerList& cmd); //已废弃,不同时操作多个继电器设备
+1 -1
View File
@@ -338,7 +338,7 @@ void PowerManger::initControlCmd()
m_targetDisBreakerState.bus4Breaker.reservedCircuitBreaker2 = 0;
m_targetDisBreakerState.bus4Breaker.emergencyLithiumBatteryGroup1CircuitBreaker = 1; //紧急锂电池默认接入
//================初始化复合管控=============================
m_CcuColCmd.workCondition = 0x00; //默认无效
m_CcuColCmd.fcmode = 0x00; //默认无效
m_CcuColCmd.fcuCmd = 0x00; //无效指令
m_CcuColCmd.fcuPowerEfficiency = 15; //默认为15kw
m_CcuColCmd.fcuInsEfficiency = 00; //预留指令,无效
@@ -0,0 +1,447 @@
#ifndef HTTP_SERVER_BATTERYOPERATION_H
#define HTTP_SERVER_BATTERYOPERATION_H
#include <string>
const std::string BATTERY_OPERATION_HTML = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>电池操作</title>
<link rel="stylesheet" href="/static/style.css">
<style>
:root {
--primary-color: #2563eb;
--primary-hover: #1d4ed8;
--bg-color: #f8fafc;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--bg-color);
color: #1a1a1a;
}
.container {
display: grid;
grid-template-areas:
"sidebar main-content";
grid-template-columns: 220px 1fr;
grid-template-rows: 1fr;
min-height: 100vh;
gap: 0;
}
.header {
grid-area: unset;
background: white;
padding: 0 0.5rem;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
right: 0;
left: 220px;
height: 36px;
z-index: 10;
}
.header h1 {
font-size: 1.25rem;
font-weight: 600;
color: #1a1a1a;
}
.sidebar {
grid-area: sidebar;
background: white;
width: 220px;
height: 100vh;
position: fixed;
left: 0;
top: 0;
padding: 0.5rem 0.25rem;
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.1);
z-index: 20;
}
.sidebar-menu {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.sidebar-button {
display: flex;
align-items: center;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
color: #4b5563;
text-decoration: none;
transition: all 0.2s ease;
}
.sidebar-button:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.sidebar-icon {
min-width: 1.5rem;
margin-right: 1rem;
font-size: 1.25rem;
}
.main-content {
grid-area: main-content;
padding: 1px;
margin-top: 36px;
}
.control-panel {
background: white;
border-radius: 4px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin: 20px;
}
.panel-title {
font-size: 1.2rem;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e7eb;
}
.control-group {
margin-bottom: 20px;
}
.control-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.radio-group {
display: flex;
gap: 16px;
margin-bottom: 12px;
}
.radio-option {
display: flex;
align-items: center;
gap: 8px;
}
.select-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 4px;
background-color: white;
margin-bottom: 12px;
}
.slider-container {
display: flex;
align-items: center;
gap: 12px;
}
.slider {
flex-grow: 1;
height: 6px;
-webkit-appearance: none;
background: #d1d5db;
border-radius: 3px;
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
background: var(--primary-color);
border-radius: 50%;
cursor: pointer;
}
.slider-value {
min-width: 50px;
text-align: right;
}
.button-group {
display: flex;
gap: 12px;
margin-top: 8px;
}
.button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background-color: #e5e7eb;
color: #1a1a1a;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.button:hover {
background-color: #d1d5db;
}
.button-secondary {
background-color: #e5e7eb;
color: #1a1a1a;
}
.button-secondary:hover {
background-color: #d1d5db;
}
.button-active {
background-color: var(--primary-color);
color: white;
}
.button-active:hover {
background-color: var(--primary-hover);
}
.form-submit {
margin-top: 24px;
width: 100%;
padding: 12px;
font-size: 1rem;
}
.status-message {
margin-top: 16px;
padding: 12px;
border-radius: 4px;
background-color: #f0fdf4;
color: #166534;
display: none;
}
.status-message.error {
background-color: #fef2f2;
color: #991b1b;
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="sidebar-menu">
<a href="/" class="sidebar-button">
<span class="sidebar-icon">🏠</span>
<span>系统主页</span>
</a>
<a href="/data" class="sidebar-button">
<span class="sidebar-icon">📊</span>
<span>系统数据</span>
</a>
<a href="/dashboard" class="sidebar-button">
<span class="sidebar-icon">⚡</span>
<span>配电操作</span>
</a>
<a href="/battery" class="sidebar-button">
<span class="sidebar-icon">🔋</span>
<span>电池操作</span>
</a>
<a href="/logs" class="sidebar-button">
<span class="sidebar-icon">📝</span>
<span>系统日志</span>
</a>
</div>
</div>
<div class="header">
<h1>电池操作系统</h1>
</div>
<div class="main-content">
<div class="control-panel">
<div class="panel-title">燃料电池控制</div>
<form id="batteryForm" method="POST" action="/battery/control">
<div class="control-group">
<label class="control-label">燃料电池模式设定</label>
<div class="radio-group">
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="00H" checked>
<span>无效</span>
</label>
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="01H">
<span>调试</span>
</label>
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="02H">
<span>自动</span>
</label>
<label class="radio-option">
<input type="radio" name="fuelCellMode" value="03H">
<span>补给</span>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">燃料电池指令</label>
<select class="select-control" name="fuelCellCommand">
<option value="00H">无效</option>
<option value="01H">自检</option>
<option value="02H">启动</option>
<option value="03H">停机</option>
<option value="04H">复位</option>
<option value="05H">紧急停机</option>
<option value="06H">补给</option>
<option value="07H">制氢预热</option>
<option value="08H">停机维护</option>
</select>
</div>
<div class="control-group">
<label class="control-label">燃料电池动力通路功率配置 (kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="fuelCellPower" min="0" max="20" value="0" step="1">
<span class="slider-value">0</span>
</div>
</div>
<div class="panel-title">锂电池控制</div>
<div class="control-group">
<label class="control-label">仪表锂电池启停指令</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="instrumentBattery" data-value="00H">无效</button>
<button type="button" class="button button-secondary" data-command="instrumentBattery" data-value="10H">启动</button>
<button type="button" class="button button-secondary" data-command="instrumentBattery" data-value="20H">关闭</button>
</div>
<input type="hidden" name="instrumentBattery" value="00H">
</div>
<div class="control-group">
<label class="control-label">动力锂电池启停指令</label>
<div class="button-group">
<button type="button" class="button button-active" data-command="powerBattery" data-value="00H">无效</button>
<button type="button" class="button button-secondary" data-command="powerBattery" data-value="10H">启动</button>
<button type="button" class="button button-secondary" data-command="powerBattery" data-value="20H">关闭</button>
</div>
<input type="hidden" name="powerBattery" value="00H">
</div>
<div class="control-group">
<label class="control-label">动力锂电池电池功率配置值 (kW)</label>
<div class="slider-container">
<input type="range" class="slider" name="powerBatteryPower" min="0" max="400" value="0" step="10">
<span class="slider-value">0</span>
</div>
</div>
<button type="submit" class="button form-submit">提交配置</button>
<div id="statusMessage" class="status-message"></div>
</form>
</div>
</div>
</div>
<script>
// 更新滑块值显示
document.querySelectorAll('.slider').forEach(slider => {
const valueDisplay = slider.nextElementSibling;
valueDisplay.textContent = slider.value;
slider.addEventListener('input', () => {
valueDisplay.textContent = slider.value;
});
});
// 处理按钮组选择
document.querySelectorAll('.button-group button').forEach(button => {
button.addEventListener('click', () => {
const group = button.closest('.button-group');
const hiddenInput = group.nextElementSibling;
const command = button.dataset.command;
const value = button.dataset.value;
// 更新按钮样式
group.querySelectorAll('button').forEach(btn => {
btn.classList.remove('button-active');
btn.classList.add('button-secondary');
});
button.classList.remove('button-secondary');
button.classList.add('button-active');
// 更新隐藏输入值
hiddenInput.value = value;
});
});
// 处理表单提交
document.getElementById('batteryForm').addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const statusMessage = document.getElementById('statusMessage');
try {
// 将表单数据转换为URL编码格式
const params = new URLSearchParams();
for (const [key, value] of formData.entries()) {
params.append(key, value);
}
const response = await fetch(form.action, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
const result = await response.json();
if (result.success) {
statusMessage.textContent = '配置提交成功';
statusMessage.className = 'status-message';
statusMessage.style.display = 'block';
} else {
statusMessage.textContent = result.message || '配置提交失败';
statusMessage.className = 'status-message error';
statusMessage.style.display = 'block';
}
} catch (error) {
statusMessage.textContent = '网络错误: ' + error.message;
statusMessage.className = 'status-message error';
statusMessage.style.display = 'block';
}
setTimeout(() => {
statusMessage.style.display = 'none';
}, 5000);
});
</script>
</body>
</html>
)";
#endif // HTTP_SERVER_BATTERYOPERATION_H
+4 -9
View File
@@ -974,16 +974,16 @@ function updateSystemData(data) {
// 继电器状态
if (data.ins_relay_status1 !== undefined) {
document.getElementById('ins-relay-status1').textContent = getRelayStatusText(data.ins_relay_status1);
document.getElementById('ins-relay-status1').textContent = (data.ins_relay_status1);
}
if (data.ins_relay_status2 !== undefined) {
document.getElementById('ins-relay-status2').textContent = getRelayStatusText(data.ins_relay_status2);
document.getElementById('ins-relay-status2').textContent = (data.ins_relay_status2);
}
if (data.dyn_relay_status1 !== undefined) {
document.getElementById('dyn-relay-status1').textContent = getRelayStatusText(data.dyn_relay_status1);
document.getElementById('dyn-relay-status1').textContent = (data.dyn_relay_status1);
}
if (data.dyn_relay_status2 !== undefined) {
document.getElementById('dyn-relay-status2').textContent = getRelayStatusText(data.dyn_relay_status2);
document.getElementById('dyn-relay-status2').textContent = (data.dyn_relay_status2);
}
// 报警标志
@@ -1601,11 +1601,6 @@ function getCoolingStatusText(status) {
return statuses[status] || '未知状态';
}
// 继电器状态文本转换
function getRelayStatusText(status) {
return status === 1 ? '闭合' : '断开';
}
// 折叠面板功能
const coll = document.getElementsByClassName("collapsible");
for (let i = 0; i < coll.length; i++) {
@@ -10,6 +10,7 @@
#include "logpage.h"
#include "testpage.h"
#include "degubpage.h"
#include "batteryoperation.h"
using json = Json::Value;
@@ -90,6 +91,55 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_
} else if (mg_match(hm->uri, mg_str("/debug"), NULL)) {
// 返回调试页面HTML
mg_http_reply(c, 200, "Content-Type: text/html; charset=utf-8\r\n", DEBUGPAGE_HTML.c_str());
} 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("/battery/control"), NULL)) {
// 处理电池控制POST请求
if (mg_strcmp(hm->method, mg_str("POST")) == 0) {
// 解析表单数据
struct mg_str body = hm->body;
unsigned char fuelCellMode = 0;
unsigned char fuelCellCommand = 0;
unsigned short fuelCellPower = 0;
unsigned char instrumentBattery = 0;
unsigned char powerBattery = 0;
unsigned short powerBatteryPower = 0;
char tmp[16] = {0};
mg_http_get_var(&body, "fuelCellMode", tmp, sizeof(tmp));
fuelCellMode = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "fuelCellCommand", tmp, sizeof(tmp));
fuelCellCommand = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "fuelCellPower", tmp, sizeof(tmp));
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, "powerBattery", tmp, sizeof(tmp));
powerBattery = (unsigned char)strtoul(tmp, NULL, 16);
mg_http_get_var(&body, "powerBatteryPower", tmp, sizeof(tmp));
powerBatteryPower = (unsigned short)atoi(tmp);
// 记录接收到的配置
LOG_F(INFO, "Received battery control config: "
"fuelCellMode=%02XH, fuelCellCommand=%02XH, fuelCellPower=%d, "
"instrumentBattery=%02XH, powerBattery=%02XH, powerBatteryPower=%d",
fuelCellMode, fuelCellCommand, fuelCellPower,
instrumentBattery, powerBattery, powerBatteryPower);
m_powerManger->m_CcuColCmd.fcmode = fuelCellMode;
m_powerManger->m_CcuColCmd.fcuCmd = fuelCellCommand;
m_powerManger->m_CcuColCmd.fcuPowerEfficiency = fuelCellPower;
m_powerManger->m_CcuColCmd.insBatCmd = instrumentBattery;
m_powerManger->m_CcuColCmd.powerBatCmd = powerBattery;
m_powerManger->m_CcuColCmd.powerBatEfficiency = powerBatteryPower;
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("/ws/debug"), NULL)) {
// 升级到调试WebSocket连接
LOG_F(INFO, "Upgrading to Debug WebSocket: id=%lu fd=%d", c->id, c->fd);
+4
View File
@@ -211,6 +211,10 @@ body {
<span class="sidebar-icon">📝</span>
<span>系统日志</span>
</a>
<a href="/battery" class="sidebar-button">
<span class="sidebar-icon">🔋</span>
<span>电池操作</span>
</a>
<a href="/testpage" class="sidebar-button">
<span class="sidebar-icon">🧪</span>
<span>测试页面</span>
+1 -1
View File
@@ -136,7 +136,7 @@ typedef struct {
}ccuSetParmCmd;
typedef struct {
unsigned char workCondition;
unsigned char fcmode;
unsigned char fcuCmd;
unsigned char fcuPowerEfficiency;
unsigned char fcuInsEfficiency;
+1 -1
View File
@@ -608,7 +608,7 @@ bool SQLite::insertData(const ccuColCmd cmd) {
int idx = 1;
#define BIND_INT(x) sqlite3_bind_int(stmt, idx++, static_cast<int>(x))
BIND_INT(cmd.workCondition);
BIND_INT(cmd.fcmode);
BIND_INT(cmd.fcuCmd);
BIND_INT(cmd.fcuPowerEfficiency);
BIND_INT(cmd.fcuInsEfficiency);
+13 -3
View File
@@ -523,9 +523,19 @@ bool udpComm::sendCcuColCmd(const ccuColCmd cmd)
}
sendMsg(msgBuf,sizeof(msg_CcuColCmdMsg));
}
catch(const std::exception& e)
catch(const std::invalid_argument& e)
{
std::cerr << e.what() << '\n';
std::cerr << "Invalid argument error in CCU command: " << e.what() << '\n';
LOG_F(ERROR, "Invalid argument in CCU command: %s", e.what());
delete msg;
return false;
}
catch(const std::out_of_range& e)
{
std::cerr << "Out of range error in CCU command: " << e.what() << '\n';
LOG_F(ERROR, "Out of range in CCU command: %s", e.what());
delete msg;
return false;
}
delete msg;
return true;
@@ -584,4 +594,4 @@ bool udpComm::getDate(unsigned short &year, unsigned char &month, unsigned char
milliseconds = static_cast<unsigned char>(ms);
return true;
}
}