新应用 src/pCanBridge(独立 MOOS App,单通道): - CanEndpoint:TCP Client 连接 CANET/USBCAN-8E-U 工作端口 (目标 IP/端口可配置,默认 192.168.0.222:4001=CAN0), 接收线程流式缓冲按 13 字节切帧,过滤无效 DLC(>8), 断线自动重连(非阻塞 connect+超时 / SO_RCVTIMEO 读超时)。 - 帧协议(CANET-8E-U 手册 8.1):byte0=帧信息(bit7 FF/bit6 RTR/ bit3~0 DLC),byte1~4=ID 大端,byte5~12=数据。 - MOOS 映射:变量名 CAN_0x%08X=CAN ID(不缩减),m_sVal=二进制 data(逐帧透传不限流),m_sSrcAux=通道名(can_channel_name), m_dfVal2=原始帧信息字节(用于区分标准/扩展、数据/远程帧)。 - CanDbStore:每帧落库 SQLite(can_frame 表:time/channel/ frame_info/can_id/dlc/hex,含 time、can_id 索引),参考 pCCU DbStore:预处理语句 + WAL + synchronous=NORMAL,路径由 dbpath 配置;复用 pPowerManger 的 sqlite3 amalgamation。 - AppCast 输出连接状态/收帧/发布/落库统计。 配套: - missions/h100.moos 新增 pCanBridge 配置块(板卡 dbpath 指向 data 目录)。 - build-board.sh / deploy.sh 支持 pCanBridge:产物部署 + pCanBridge.service 安装,服务列表加入第 5 个服务。 - .gitignore 忽略 bin/pCanBridge 与本机默认数据库。 已验证(板卡 192.168.0.223 实测):服务 active,连接设备正常, ~60 帧/s 透传,落库 9000+ 行 integrity ok、无丢帧。
170 lines
5.4 KiB
C++
170 lines
5.4 KiB
C++
#include "CanBridge.h"
|
||
#include "CanBridge_Info.h"
|
||
#include "MBUtils.h"
|
||
#include "MOOS/libMOOS/Comms/MOOSMsg.h"
|
||
#include <cstdio>
|
||
|
||
using namespace std;
|
||
|
||
namespace canbridge {
|
||
|
||
//---------------------------------------------------------
|
||
// Constructor / Destructor
|
||
|
||
CanBridge::CanBridge() {}
|
||
|
||
CanBridge::~CanBridge() {
|
||
if (m_endpoint) {
|
||
m_endpoint->stop();
|
||
delete m_endpoint;
|
||
m_endpoint = nullptr;
|
||
}
|
||
if (m_db) {
|
||
m_db->close();
|
||
delete m_db;
|
||
m_db = nullptr;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// OnStartUp:读取配置并启动 CAN 链路
|
||
|
||
bool CanBridge::OnStartUp() {
|
||
AppCastingMOOSApp::OnStartUp();
|
||
|
||
STRING_LIST sParams;
|
||
m_MissionReader.EnableVerbatimQuoting(false);
|
||
if (!m_MissionReader.GetConfiguration(GetAppName(), sParams))
|
||
reportConfigWarning("No config block found for " + GetAppName());
|
||
|
||
STRING_LIST::iterator p;
|
||
for (p = sParams.begin(); p != sParams.end(); p++) {
|
||
string orig = *p;
|
||
string line = *p;
|
||
string param = stripBlankEnds(tolower(biteStringX(line, '=')));
|
||
string value = stripBlankEnds(line);
|
||
|
||
bool handled = true;
|
||
if (param == "can_host") m_canHost = value;
|
||
else if (param == "can_port") m_canPort = atol(value.c_str());
|
||
else if (param == "can_channel_name") m_channelName = value;
|
||
else if (param == "dbpath") m_dbPath = value;
|
||
else handled = false;
|
||
|
||
if (!handled)
|
||
reportUnhandledConfigWarning(orig);
|
||
}
|
||
|
||
registerVariables();
|
||
|
||
// 数据库(CAN 帧落库)
|
||
m_db = new CanDbStore(m_dbPath);
|
||
if (!m_db->open()) {
|
||
reportRunWarning("CAN db open failed: " + m_dbPath + " (" + m_db->lastError() + ")");
|
||
delete m_db;
|
||
m_db = nullptr;
|
||
}
|
||
|
||
// CAN 链路(TCP Client,收帧回调在接收线程中触发)
|
||
m_endpoint = new CanEndpoint();
|
||
m_endpoint->configure(m_canHost, m_canPort);
|
||
m_endpoint->setFrameCallback(
|
||
[this](uint8_t fi, uint32_t id, const uint8_t* data, int dlc) {
|
||
handleFrame(fi, id, data, dlc);
|
||
});
|
||
if (!m_endpoint->start()) {
|
||
reportRunWarning("CAN endpoint start failed: " + m_canHost);
|
||
}
|
||
|
||
cout << "pCanBridge started: " << m_canHost << ":" << m_canPort
|
||
<< " (" << m_channelName << "), db: " << m_dbPath << endl;
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// OnConnectToServer
|
||
|
||
bool CanBridge::OnConnectToServer() {
|
||
registerVariables();
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// registerVariables:纯发布应用,无需注册订阅变量
|
||
|
||
void CanBridge::registerVariables() {
|
||
AppCastingMOOSApp::RegisterVariables();
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// OnNewMail:当前无订阅指令,预留
|
||
|
||
bool CanBridge::OnNewMail(MOOSMSG_LIST &NewMail) {
|
||
AppCastingMOOSApp::OnNewMail(NewMail);
|
||
MOOSMSG_LIST::iterator p;
|
||
for (p = NewMail.begin(); p != NewMail.end(); p++) {
|
||
CMOOSMsg &msg = *p;
|
||
(void)msg;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// Iterate:周期任务
|
||
|
||
bool CanBridge::Iterate() {
|
||
AppCastingMOOSApp::Iterate();
|
||
AppCastingMOOSApp::PostReport();
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// handleFrame:CAN 帧 -> CMOOSMsg(二进制) -> MOOSDB
|
||
//
|
||
// 在 CanEndpoint 接收线程中调用;m_Comms::Post 线程安全。
|
||
|
||
void CanBridge::handleFrame(uint8_t frameInfo, uint32_t id,
|
||
const uint8_t* data, int dlc) {
|
||
// 落库(WAL + 预处理语句,~60帧/秒无压力)
|
||
if (m_db) m_db->onFrame(m_channelName, frameInfo, id, data, dlc);
|
||
|
||
char key[32];
|
||
std::snprintf(key, sizeof(key), "CAN_0x%08X", id);
|
||
|
||
// 二进制构造:m_cDataType = MOOS_BINARY_STRING
|
||
CMOOSMsg msg(MOOS_NOTIFY, key,
|
||
static_cast<unsigned int>(dlc),
|
||
const_cast<uint8_t*>(data));
|
||
msg.SetSourceAux(m_channelName); // 通道 -> m_sSrcAux
|
||
msg.SetDoubleAux(static_cast<double>(frameInfo)); // FF/RTR/DLC -> m_dfVal2
|
||
m_Comms.Post(msg);
|
||
++m_notifyCount;
|
||
}
|
||
|
||
//---------------------------------------------------------
|
||
// buildReport:AppCasting 报告
|
||
|
||
bool CanBridge::buildReport() {
|
||
m_msgs << "============================================" << "\n";
|
||
m_msgs << "pCanBridge CAN->MOOSDB 透传" << "\n";
|
||
m_msgs << "============================================" << "\n";
|
||
if (m_endpoint) {
|
||
m_msgs << "目标: " << m_endpoint->host() << ":"
|
||
<< m_endpoint->port() << " (" << m_channelName << ")" << "\n";
|
||
m_msgs << "连接状态: " << (m_endpoint->isConnected() ? "已连接" : "断开") << "\n";
|
||
m_msgs << "收帧数: " << m_endpoint->frameCount() << "\n";
|
||
m_msgs << "发布数: " << m_notifyCount << "\n";
|
||
m_msgs << "错误数: " << m_endpoint->errorCount() << "\n";
|
||
m_msgs << "重连次数: " << m_endpoint->reconnectCount() << "\n";
|
||
m_msgs << "最近收帧: " << m_endpoint->lastFrameTime() << "\n";
|
||
}
|
||
if (m_db) {
|
||
m_msgs << "数据库: " << m_dbPath
|
||
<< (m_db->isOpen() ? " (已打开)" : " (未打开)") << "\n";
|
||
m_msgs << "落库记录数: " << m_db->count() << "\n";
|
||
}
|
||
return true;
|
||
}
|
||
|
||
} // namespace canbridge
|