新增复合管控器(pCCU) + 惯导数据解析 + 设备控制页增强 + 0824协议文档归档
- 新增 pCCU 复合管控器程序(src/pCCU):FC/PM 双链路(UDP)、协议编解码器 (FcProtocol/PmProtocol/Frame/Message/MessageRegistry)、快照与落库(DbStore)、 内置 WebServer;挂入 src/CMakeLists.txt 与板卡 mission(h100.moos) - 惯导/深度数据:新增 uDevice_insData 消息解析,接入 UpperCommManager, pPowerMangerHost 注册监视,UpmsgTest 覆盖 - pPowerMangerHost 设备控制页增强(web/index.html) - 部署/运维脚本改为 4 个 systemd 服务(moosdb/pPowerManger/pPowerMangerHost/pCCU): deploy/clean-data/fetch-data 同步适配 - 协议文档:归档 0824 控制主机与复合管控器通信协议 xlsx 及关联 docx,移除旧 md - 测试:新增 pccu 单元/集成测试与 disSysTest Python 配电仿真 - 清理:移除未使用的 CCU 状态调试打印 coutMsg;.gitignore 补充编译产物/临时文件忽略
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
#ifndef PCCU_FC_LINK_MANAGER_H
|
||||
#define PCCU_FC_LINK_MANAGER_H
|
||||
|
||||
#include "LinkManager.h"
|
||||
#include "../protocol/FcProtocol.h"
|
||||
|
||||
namespace ccu {
|
||||
|
||||
//============================================================================
|
||||
// FcLinkManager:面向燃料电池控制器的链路。
|
||||
//
|
||||
// - 本地监听:燃料电池状态反馈端口(默认 6000)
|
||||
// - 发送目标:燃料电池控制器(默认 192.168.1.162:7000)
|
||||
// 端口/地址均可由 .moos 配置覆盖。
|
||||
//============================================================================
|
||||
|
||||
class FcLinkManager : public LinkManager {
|
||||
public:
|
||||
FcLinkManager(long localPort,
|
||||
const std::string& fcHost, long fcPort)
|
||||
: LinkManager("fc", localPort), m_fcHost(fcHost), m_fcPort(fcPort) {
|
||||
registerFcMessages(registry());
|
||||
}
|
||||
|
||||
void setFcAddress(const std::string& host, long port) {
|
||||
m_fcHost = host; m_fcPort = port;
|
||||
}
|
||||
const std::string& fcHost() const { return m_fcHost; }
|
||||
long fcPort() const { return m_fcPort; }
|
||||
|
||||
protected:
|
||||
std::string defaultRemoteHost() const override { return m_fcHost; }
|
||||
long defaultRemotePort() const override { return m_fcPort; }
|
||||
|
||||
private:
|
||||
std::string m_fcHost;
|
||||
long m_fcPort;
|
||||
};
|
||||
|
||||
} // namespace ccu
|
||||
|
||||
#endif // PCCU_FC_LINK_MANAGER_H
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "LinkManager.h"
|
||||
#include "../protocol/Frame.h"
|
||||
#include "MOOS/libMOOS/Utils/MOOSUtilityFunctions.h"
|
||||
#include "loguru.hpp"
|
||||
|
||||
namespace ccu {
|
||||
|
||||
LinkManager::LinkManager(const std::string& linkName, long localPort)
|
||||
: m_linkName(linkName), m_localPort(localPort) {}
|
||||
|
||||
LinkManager::~LinkManager() {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool LinkManager::start() {
|
||||
if (m_running) return true;
|
||||
if (!m_endpoint.init(m_localPort)) {
|
||||
LOG_F(ERROR, "[%s] bind failed on port %ld", m_linkName.c_str(), m_localPort);
|
||||
return false;
|
||||
}
|
||||
m_running = true;
|
||||
m_rxThread = std::thread([this]() { rxThreadFunc(); });
|
||||
LOG_F(INFO, "[%s] listening on local port %ld", m_linkName.c_str(), m_localPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinkManager::stop() {
|
||||
if (!m_running) return;
|
||||
m_running = false;
|
||||
if (m_rxThread.joinable()) m_rxThread.join();
|
||||
}
|
||||
|
||||
bool LinkManager::sendMessage(uint16_t id, const void* value) {
|
||||
return sendMessageTo(id, value, defaultRemoteHost(), defaultRemotePort());
|
||||
}
|
||||
|
||||
bool LinkManager::sendMessageTo(uint16_t id, const void* value,
|
||||
const std::string& host, long port) {
|
||||
if (host.empty() || port <= 0) return false;
|
||||
Message* msg = m_registry.find(id);
|
||||
if (!msg) {
|
||||
LOG_F(ERROR, "[%s] unknown message id 0x%04X", m_linkName.c_str(), id);
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> frame = msg->encode(value);
|
||||
if (!m_endpoint.sendTo(host, port, frame)) {
|
||||
LOG_F(ERROR, "[%s] send msg 0x%04X to %s:%ld failed",
|
||||
m_linkName.c_str(), id, host.c_str(), port);
|
||||
return false;
|
||||
}
|
||||
++m_txCount;
|
||||
if (m_logSink) m_logSink->onRawFrame(1, id, m_linkName, frame, true);
|
||||
if (m_onRawFrame) m_onRawFrame(1, frame, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkManager::handleIncoming(const std::vector<uint8_t>& frame) {
|
||||
if (frame.size() < FRAME_HEADER_LEN) return false;
|
||||
|
||||
uint16_t id = 0;
|
||||
if (!Frame::parseId(frame, id)) {
|
||||
LOG_F(WARNING, "[%s] bad frame header", m_linkName.c_str());
|
||||
++m_errorCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
Message* msg = m_registry.find(id);
|
||||
if (!msg) {
|
||||
LOG_F(WARNING, "[%s] unknown msg id 0x%04X, len=%zu",
|
||||
m_linkName.c_str(), id, frame.size());
|
||||
++m_errorCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checksumOk = msg->checksum().verify(frame);
|
||||
++m_rxCount;
|
||||
m_lastRxTime = MOOSTime(false);
|
||||
|
||||
// 原始帧落库 + 回调
|
||||
if (m_logSink) m_logSink->onRawFrame(0, id, m_linkName, frame, checksumOk);
|
||||
if (m_onRawFrame) m_onRawFrame(0, frame, checksumOk);
|
||||
|
||||
if (!checksumOk) {
|
||||
LOG_F(WARNING, "[%s] checksum error msg 0x%04X", m_linkName.c_str(), id);
|
||||
++m_errorCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解码交给上层(CCU 层知道具体消息类型)
|
||||
if (m_onMessage) m_onMessage(msg, frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinkManager::rxThreadFunc() {
|
||||
uint8_t buf[2048];
|
||||
while (m_running) {
|
||||
int n = m_endpoint.receive(buf, sizeof(buf));
|
||||
if (n > 0) {
|
||||
std::vector<uint8_t> frame(buf, buf + n);
|
||||
handleIncoming(frame);
|
||||
} else {
|
||||
// 无数据,短暂休眠避免忙等
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ccu
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef PCCU_LINK_MANAGER_H
|
||||
#define PCCU_LINK_MANAGER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include "UdpEndpoint.h"
|
||||
#include "../protocol/MessageRegistry.h"
|
||||
#include "../protocol/Message.h"
|
||||
|
||||
namespace ccu {
|
||||
|
||||
// 落库接口:由 DbStore 实现,供链路层记录原始帧(实现收发数据入库)
|
||||
class ILinkLogSink {
|
||||
public:
|
||||
virtual ~ILinkLogSink() = default;
|
||||
// direction: 0=收 1=发;data 为原始完整帧;checksumOk 为校验结果
|
||||
virtual void onRawFrame(int direction, uint16_t msgId, const std::string& link,
|
||||
const std::vector<uint8_t>& data, bool checksumOk) = 0;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
// LinkManager:单条 UDP 链路的管理者。
|
||||
//
|
||||
// 职责:
|
||||
// - 绑定本地端口并维护独立接收线程
|
||||
// - 持有该链路的 MessageRegistry(收发共用,id 按链路隔离)
|
||||
// - 收帧:解析 id -> 查注册表 -> 校验 -> 回调 onMessage(携带消息对象与原始帧)
|
||||
// - 发帧:编码 -> 发送 -> 落库
|
||||
//
|
||||
// 派生类(FcLinkManager / PmLinkManager)仅需提供链路名与默认收发地址。
|
||||
//============================================================================
|
||||
|
||||
class LinkManager {
|
||||
public:
|
||||
LinkManager(const std::string& linkName, long localPort);
|
||||
virtual ~LinkManager();
|
||||
|
||||
// 启动:绑定端口 + 启动接收线程。返回是否成功。
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
bool isRunning() const { return m_running; }
|
||||
|
||||
// 注册表(收发共用)
|
||||
MessageRegistry& registry() { return m_registry; }
|
||||
const MessageRegistry& registry() const { return m_registry; }
|
||||
|
||||
// 收帧回调:msg 为注册表中命中的消息对象(可调用 decode 解码),frame 为原始完整帧
|
||||
void setOnMessage(std::function<void(Message* msg, const std::vector<uint8_t>& frame)> cb) {
|
||||
m_onMessage = std::move(cb);
|
||||
}
|
||||
|
||||
// 原始帧回调(用于 Web/调试展示原始报文)
|
||||
void setOnRawFrame(std::function<void(int direction, const std::vector<uint8_t>& frame, bool checksumOk)> cb) {
|
||||
m_onRawFrame = std::move(cb);
|
||||
}
|
||||
|
||||
// 落库接口(可空)
|
||||
void setLogSink(ILinkLogSink* sink) { m_logSink = sink; }
|
||||
|
||||
// 编码并发送一条已注册消息;返回是否成功
|
||||
bool sendMessage(uint16_t id, const void* value);
|
||||
|
||||
// 编码并发送一条已注册消息,同时指定目标地址(覆盖默认)
|
||||
bool sendMessageTo(uint16_t id, const void* value,
|
||||
const std::string& host, long port);
|
||||
|
||||
// 处理一帧收到的原始数据(供接收线程调用,也可测试时手动喂入)
|
||||
bool handleIncoming(const std::vector<uint8_t>& frame);
|
||||
|
||||
// 链路状态:最近一次收到消息的时间(MOOSTime),0 表示从未收到
|
||||
double lastRxTime() const { return m_lastRxTime; }
|
||||
long localPort() const { return m_localPort; }
|
||||
const std::string& linkName() const { return m_linkName; }
|
||||
|
||||
// 收发统计
|
||||
unsigned long rxCount() const { return m_rxCount; }
|
||||
unsigned long txCount() const { return m_txCount; }
|
||||
unsigned long errorCount() const { return m_errorCount; }
|
||||
|
||||
protected:
|
||||
// 派生类提供发送默认目标
|
||||
virtual std::string defaultRemoteHost() const { return ""; }
|
||||
virtual long defaultRemotePort() const { return 0; }
|
||||
|
||||
private:
|
||||
void rxThreadFunc();
|
||||
|
||||
std::string m_linkName;
|
||||
long m_localPort;
|
||||
UdpEndpoint m_endpoint;
|
||||
MessageRegistry m_registry;
|
||||
|
||||
std::atomic<bool> m_running{false};
|
||||
std::thread m_rxThread;
|
||||
|
||||
std::function<void(Message*, const std::vector<uint8_t>&)> m_onMessage;
|
||||
std::function<void(int, const std::vector<uint8_t>&, bool)> m_onRawFrame;
|
||||
ILinkLogSink* m_logSink = nullptr;
|
||||
|
||||
double m_lastRxTime = 0;
|
||||
unsigned long m_rxCount = 0;
|
||||
unsigned long m_txCount = 0;
|
||||
unsigned long m_errorCount = 0;
|
||||
};
|
||||
|
||||
} // namespace ccu
|
||||
|
||||
#endif // PCCU_LINK_MANAGER_H
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef PCCU_PM_LINK_MANAGER_H
|
||||
#define PCCU_PM_LINK_MANAGER_H
|
||||
|
||||
#include "LinkManager.h"
|
||||
#include "../protocol/PmProtocol.h"
|
||||
|
||||
namespace ccu {
|
||||
|
||||
//============================================================================
|
||||
// PmLinkManager:面向控制主机 pPowerManger 的链路。
|
||||
//
|
||||
// - 本地监听:控制主机指令端口(默认 7000)
|
||||
// - 发送目标:控制主机(默认 192.168.0.140:5001)
|
||||
// 端口/地址均可由 .moos 配置覆盖。
|
||||
//============================================================================
|
||||
|
||||
class PmLinkManager : public LinkManager {
|
||||
public:
|
||||
PmLinkManager(long localPort,
|
||||
const std::string& pmHost, long pmPort)
|
||||
: LinkManager("pm", localPort), m_pmHost(pmHost), m_pmPort(pmPort) {
|
||||
registerPmMessages(registry());
|
||||
}
|
||||
|
||||
void setPmAddress(const std::string& host, long port) {
|
||||
m_pmHost = host; m_pmPort = port;
|
||||
}
|
||||
const std::string& pmHost() const { return m_pmHost; }
|
||||
long pmPort() const { return m_pmPort; }
|
||||
|
||||
protected:
|
||||
std::string defaultRemoteHost() const override { return m_pmHost; }
|
||||
long defaultRemotePort() const override { return m_pmPort; }
|
||||
|
||||
private:
|
||||
std::string m_pmHost;
|
||||
long m_pmPort;
|
||||
};
|
||||
|
||||
} // namespace ccu
|
||||
|
||||
#endif // PCCU_PM_LINK_MANAGER_H
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "UdpEndpoint.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace ccu {
|
||||
|
||||
UdpEndpoint::UdpEndpoint() : m_socket(nullptr), m_localPort(0) {}
|
||||
|
||||
UdpEndpoint::~UdpEndpoint() {
|
||||
if (m_socket) {
|
||||
delete m_socket;
|
||||
m_socket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool UdpEndpoint::init(long localPort) {
|
||||
m_localPort = localPort;
|
||||
m_socket = new XPCUdpSocket(localPort);
|
||||
try {
|
||||
m_socket->vBindSocket();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[UdpEndpoint] bind failed on port " << localPort
|
||||
<< ": " << e.what() << std::endl;
|
||||
delete m_socket;
|
||||
m_socket = nullptr;
|
||||
return false;
|
||||
} catch (...) {
|
||||
std::cerr << "[UdpEndpoint] bind failed on port " << localPort
|
||||
<< ": unknown error" << std::endl;
|
||||
delete m_socket;
|
||||
m_socket = nullptr;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UdpEndpoint::sendTo(const std::string& host, long port, const std::vector<uint8_t>& data) {
|
||||
if (!m_socket || data.empty()) return false;
|
||||
try {
|
||||
m_socket->iSendMessageTo(const_cast<uint8_t*>(data.data()),
|
||||
static_cast<int>(data.size()),
|
||||
port, host);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int UdpEndpoint::receive(uint8_t* buf, size_t len) {
|
||||
if (!m_socket || !buf || len == 0) return 0;
|
||||
try {
|
||||
return m_socket->iRecieveMessage(buf, static_cast<int>(len));
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ccu
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef PCCU_UDP_ENDPOINT_H
|
||||
#define PCCU_UDP_ENDPOINT_H
|
||||
|
||||
#define UNIX
|
||||
#include "MOOS/libMOOS/Comms/XPCUdpSocket.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ccu {
|
||||
|
||||
//============================================================================
|
||||
// UdpEndpoint:单个 UDP 套接字的封装。
|
||||
//
|
||||
// 职责:
|
||||
// - 绑定本地端口
|
||||
// - 向指定远端 (host:port) 发送数据报
|
||||
// - 阻塞接收数据报
|
||||
// 不关心协议内容,仅负责字节的收发。
|
||||
//============================================================================
|
||||
|
||||
class UdpEndpoint {
|
||||
public:
|
||||
UdpEndpoint();
|
||||
~UdpEndpoint();
|
||||
|
||||
// 绑定本地端口;返回是否成功(端口占用/权限失败返回 false)
|
||||
bool init(long localPort);
|
||||
|
||||
// 向指定远端发送完整数据报
|
||||
bool sendTo(const std::string& host, long port, const std::vector<uint8_t>& data);
|
||||
|
||||
// 阻塞接收一帧;返回接收字节数,0 或负值表示无数据/错误
|
||||
int receive(uint8_t* buf, size_t len);
|
||||
|
||||
bool isOpen() const { return m_socket != nullptr; }
|
||||
long localPort() const { return m_localPort; }
|
||||
|
||||
private:
|
||||
XPCUdpSocket* m_socket = nullptr;
|
||||
long m_localPort = 0;
|
||||
};
|
||||
|
||||
} // namespace ccu
|
||||
|
||||
#endif // PCCU_UDP_ENDPOINT_H
|
||||
Reference in New Issue
Block a user