pCCU 新增真实CCU配电桥接(rcu链路原帧透传+网页显示)并适配板卡端口拓扑
- protocol/DisProtocol: 配电协议8条消息(指令0x0001~4/反馈0x0005~8, Sum8),复用pmSysvariable结构体+static_assert核对 - ChecksumPolicy 新增 Sum8;Frame 支持 1 字节校验和 - MessageRegistry 支持 (id+帧总长) 二维查找:配电指令与PM操控指令共用id按帧长区分(帧头length字段不可信) - LinkManager: 按实际帧长分发 + sendRaw原帧透传 + 非本协议帧头落库节流告警;UdpEndpoint sendTo 加锁 - comm/RcuLinkManager: 真实CCU链路;core/DisBridge: 配电桥接器(反馈转pPowerManger+解码显示,指令转真实CCU,0x0004仅显示不转发) - 网页新增"真实CCU"页签全字段展示;SnapshotBuilder realCcu 节 + rcu 链路描述 - h100.moos: 端口腾挪(真实CCU固定上报5001归pCCU,pPowerManger iport=5002),rcu_remote=192.168.0.140:7000 - 测试: pccuTest 新增配电/Sum8/长度分发/RCU注册表用例(176项);集成测试新增桥接字节一致+不误转发用例
This commit is contained in:
@@ -54,17 +54,46 @@ bool LinkManager::sendMessageTo(uint16_t id, const void* value,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkManager::sendRaw(const std::vector<uint8_t>& frame) {
|
||||
if (frame.empty()) return false;
|
||||
const std::string& host = defaultRemoteHost();
|
||||
long port = defaultRemotePort();
|
||||
if (host.empty() || port <= 0) return false;
|
||||
if (!m_endpoint.sendTo(host, port, frame)) {
|
||||
LOG_F(ERROR, "[%s] send raw frame (%zu B) to %s:%ld failed",
|
||||
m_linkName.c_str(), frame.size(), host.c_str(), port);
|
||||
return false;
|
||||
}
|
||||
++m_txCount;
|
||||
uint16_t id = 0;
|
||||
Frame::parseId(frame, id);
|
||||
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());
|
||||
// 非本协议帧(如真实CCU 混发的旧协议 0x20 0x20 状态帧、无效帧):
|
||||
// 原样落库便于排查,告警按 10s 节流避免刷屏
|
||||
++m_errorCount;
|
||||
if (m_logSink) m_logSink->onRawFrame(0, 0, m_linkName, frame, false);
|
||||
double now = MOOSTime(false);
|
||||
if (now - m_lastBadHdrLog >= 10.0) {
|
||||
m_lastBadHdrLog = now;
|
||||
LOG_F(WARNING, "[%s] 非本协议帧头(%02X %02X, %zu B),已忽略(10s节流)",
|
||||
m_linkName.c_str(), frame[0], frame[1], frame.size());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Message* msg = m_registry.find(id);
|
||||
// 优先按 id + 实际帧总长精确匹配(配电指令 0x0001~0x0004 与 PM 指令
|
||||
// 共用 id,仅帧长不同;帧头 length 字段不可信,须用实际收到的字节数),
|
||||
// 未命中回退该 id 的第一条(保持旧行为:decode 失败报错)
|
||||
Message* msg = m_registry.find(id, frame.size());
|
||||
if (!msg) {
|
||||
LOG_F(WARNING, "[%s] unknown msg id 0x%04X, len=%zu",
|
||||
m_linkName.c_str(), id, frame.size());
|
||||
|
||||
@@ -48,7 +48,9 @@ public:
|
||||
MessageRegistry& registry() { return m_registry; }
|
||||
const MessageRegistry& registry() const { return m_registry; }
|
||||
|
||||
// 收帧回调:msg 为注册表中命中的消息对象(可调用 decode 解码),frame 为原始完整帧
|
||||
// 收帧回调:msg 为注册表中命中的消息对象(可调用 decode 解码),frame 为原始完整帧。
|
||||
// 注意:同 id 不同长度的消息(如配电指令与 PM 指令)按帧总长精确区分,
|
||||
// 回调携带的 msg 即精确命中的对象;长度未命中时回退该 id 的第一条。
|
||||
void setOnMessage(std::function<void(Message* msg, const std::vector<uint8_t>& frame)> cb) {
|
||||
m_onMessage = std::move(cb);
|
||||
}
|
||||
@@ -68,6 +70,10 @@ public:
|
||||
bool sendMessageTo(uint16_t id, const void* value,
|
||||
const std::string& host, long port);
|
||||
|
||||
// 原样发送一帧到默认远端(桥接透传用,不做编码);
|
||||
// 消息 id 从帧头解析,收发计数与落库口径与 sendMessage 一致
|
||||
bool sendRaw(const std::vector<uint8_t>& frame);
|
||||
|
||||
// 处理一帧收到的原始数据(供接收线程调用,也可测试时手动喂入)
|
||||
bool handleIncoming(const std::vector<uint8_t>& frame);
|
||||
|
||||
@@ -102,6 +108,7 @@ private:
|
||||
ILinkLogSink* m_logSink = nullptr;
|
||||
|
||||
double m_lastRxTime = 0;
|
||||
double m_lastBadHdrLog = 0; // 非本协议帧头告警节流
|
||||
unsigned long m_rxCount = 0;
|
||||
unsigned long m_txCount = 0;
|
||||
unsigned long m_errorCount = 0;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "LinkManager.h"
|
||||
#include "../protocol/PmProtocol.h"
|
||||
#include "../protocol/DisProtocol.h"
|
||||
|
||||
namespace ccu {
|
||||
|
||||
@@ -11,6 +12,12 @@ namespace ccu {
|
||||
//
|
||||
// - 本地监听:控制主机指令端口(默认 7000)
|
||||
// - 发送目标:控制主机(默认 192.168.0.140:5001)
|
||||
//
|
||||
// 注册的消息:
|
||||
// - PM 协议 0x0001~0x0004(Sum32)
|
||||
// - 配电指令 0x0001~0x0004(Sum8,13~21B 短帧):pPowerManger 下发的
|
||||
// 配电指令与其操控/参数指令共用 id,仅帧长不同,由注册表按
|
||||
// (id+帧长) 区分,DisBridge 据此原桥接转发真实CCU
|
||||
// 端口/地址均可由 .moos 配置覆盖。
|
||||
//============================================================================
|
||||
|
||||
@@ -20,6 +27,7 @@ public:
|
||||
const std::string& pmHost, long pmPort)
|
||||
: LinkManager("pm", localPort), m_pmHost(pmHost), m_pmPort(pmPort) {
|
||||
registerPmMessages(registry());
|
||||
registerDisCmdMessages(registry());
|
||||
}
|
||||
|
||||
void setPmAddress(const std::string& host, long port) {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef PCCU_RCU_LINK_MANAGER_H
|
||||
#define PCCU_RCU_LINK_MANAGER_H
|
||||
|
||||
#include "LinkManager.h"
|
||||
#include "../protocol/PmProtocol.h"
|
||||
#include "../protocol/DisProtocol.h"
|
||||
|
||||
namespace ccu {
|
||||
|
||||
//============================================================================
|
||||
// RcuLinkManager:面向真实 CCU 设备的链路(配电桥接)。
|
||||
//
|
||||
// - 本地监听:rcu_local_port(默认 7100),接收真实CCU 上报的数据
|
||||
// - 发送目标:真实CCU(默认占位 192.168.0.200:7000,.moos 可配)
|
||||
//
|
||||
// 注册的消息:
|
||||
// - 配电反馈 0x0005~0x0008(Sum8):真实CCU 上报的配电状态,
|
||||
// 由 DisBridge 原帧转发给 pPowerManger 并解码显示
|
||||
// - 配电指令 0x0001~0x0004(Sum8):真实CCU 的指令回显仅识别,不转发
|
||||
// - PM 消息 0x0001~0x0004(Sum32):真实CCU 状态报文 0x0004 仅解码
|
||||
// 显示,不转发(避免与 pCCU 自身模拟的状态报文冲突)
|
||||
//============================================================================
|
||||
|
||||
class RcuLinkManager : public LinkManager {
|
||||
public:
|
||||
RcuLinkManager(long localPort,
|
||||
const std::string& rcuHost, long rcuPort)
|
||||
: LinkManager("rcu", localPort), m_rcuHost(rcuHost), m_rcuPort(rcuPort) {
|
||||
registerDisFbMessages(registry());
|
||||
registerDisCmdMessages(registry());
|
||||
registerPmMessages(registry());
|
||||
}
|
||||
|
||||
void setRcuAddress(const std::string& host, long port) {
|
||||
m_rcuHost = host; m_rcuPort = port;
|
||||
}
|
||||
const std::string& rcuHost() const { return m_rcuHost; }
|
||||
long rcuPort() const { return m_rcuPort; }
|
||||
|
||||
protected:
|
||||
std::string defaultRemoteHost() const override { return m_rcuHost; }
|
||||
long defaultRemotePort() const override { return m_rcuPort; }
|
||||
|
||||
private:
|
||||
std::string m_rcuHost;
|
||||
long m_rcuPort;
|
||||
};
|
||||
|
||||
} // namespace ccu
|
||||
|
||||
#endif // PCCU_RCU_LINK_MANAGER_H
|
||||
@@ -36,6 +36,7 @@ bool UdpEndpoint::init(long localPort) {
|
||||
bool UdpEndpoint::sendTo(const std::string& host, long port, const std::vector<uint8_t>& data) {
|
||||
if (!m_socket || data.empty()) return false;
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(m_sendMutex);
|
||||
m_socket->iSendMessageTo(const_cast<uint8_t*>(data.data()),
|
||||
static_cast<int>(data.size()),
|
||||
port, host);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#define UNIX
|
||||
#include "MOOS/libMOOS/Comms/XPCUdpSocket.h"
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -17,6 +18,8 @@ namespace ccu {
|
||||
// - 向指定远端 (host:port) 发送数据报
|
||||
// - 阻塞接收数据报
|
||||
// 不关心协议内容,仅负责字节的收发。
|
||||
// 发送可能来自多个线程(MOOS 主线程 + 各链路接收线程的桥接转发),
|
||||
// sendTo 内部加锁串行化。
|
||||
//============================================================================
|
||||
|
||||
class UdpEndpoint {
|
||||
@@ -39,6 +42,7 @@ public:
|
||||
private:
|
||||
XPCUdpSocket* m_socket = nullptr;
|
||||
long m_localPort = 0;
|
||||
std::mutex m_sendMutex; // 串行化并发 sendTo
|
||||
};
|
||||
|
||||
} // namespace ccu
|
||||
|
||||
Reference in New Issue
Block a user