调整通讯协议udp+uart
This commit is contained in:
+39
-43
@@ -1,16 +1,18 @@
|
||||
#include "car.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QSerialPort>
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkDatagram>
|
||||
#include <QSettings>
|
||||
#include <QUdpSocket>
|
||||
#include <QtGlobal>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
// ===================== 线格式 =====================
|
||||
// 一帧 = 32 位帧头 + 32 位 CAN ID + DLC + 8 字节数据 + 8 位校验和,共 18 字节。
|
||||
// 多字节字段按主机小端排列,与车载端网关一致;帧头用于在字节流中定位报文起始。
|
||||
// 一个 UDP 数据报承载 = 32 位帧头 + 32 位 CAN ID + DLC + 8 字节数据 + 8 位校验和,共 18 字节。
|
||||
// 多字节字段按主机小端排列,与车载端网关一致;帧头与校验和用于确认数据报内容合法。
|
||||
#pragma pack(push, 1)
|
||||
struct CanFrame {
|
||||
quint32 header; // 固定帧头 0x5AA5FF00(线上字节序 00 FF A5 5A)
|
||||
@@ -25,6 +27,11 @@ const quint32 kFrameHeader = 0x5AA5FF00u;
|
||||
const int kFrameLen = int(sizeof(CanFrame));
|
||||
const int kDataLen = 8;
|
||||
|
||||
// 车端 CAN 的 UDP 端口:本机 9000 收(车端反馈),上行命令发往 9001
|
||||
const quint16 kCanRxPort = 9000;
|
||||
const quint16 kCanTxPort = 9001;
|
||||
const char* kCanTxHost = "127.0.0.1";
|
||||
|
||||
quint8 canChecksum(quint32 canId, quint8 dlc, const quint8* data)
|
||||
{
|
||||
quint8 sum = 0;
|
||||
@@ -224,13 +231,11 @@ const char* const kSysDetector = "气体检测仪";
|
||||
CarCommunication::CarCommunication(Telemetry* tel, QObject* parent)
|
||||
: QObject(parent), m_tel(tel)
|
||||
{
|
||||
m_serial = new QSerialPort(this);
|
||||
connect(m_serial, &QSerialPort::readyRead, this, &CarCommunication::onReadyRead);
|
||||
m_sock = new QUdpSocket(this);
|
||||
connect(m_sock, &QUdpSocket::readyRead, this, &CarCommunication::onDatagram);
|
||||
|
||||
// 串口配置与 AppContext 共用 config.ini(默认 /dev/ttyS7,500000 8N1)
|
||||
// 端口固定为 kCanRxPort/kCanTxPort 常量,config.ini 只剩自检演示开关要读
|
||||
QSettings s(cfgPath(), QSettings::IniFormat);
|
||||
m_device = s.value("car/device", "/dev/ttyS7").toString();
|
||||
m_baud = s.value("car/baud", 500000).toInt();
|
||||
m_selfCheckFault = s.value("net/selfcheck_fault", true).toBool();
|
||||
|
||||
// 洗消四参数按存盘值初始化,否则开机帧发的是硬编码初值而非用户设定值。
|
||||
@@ -273,23 +278,17 @@ CarCommunication::~CarCommunication()
|
||||
|
||||
bool CarCommunication::start()
|
||||
{
|
||||
if (m_serial->isOpen())
|
||||
if (m_sock->state() == QAbstractSocket::BoundState)
|
||||
return true;
|
||||
|
||||
m_serial->setPortName(m_device);
|
||||
m_serial->setBaudRate(m_baud);
|
||||
m_serial->setDataBits(QSerialPort::Data8);
|
||||
m_serial->setStopBits(QSerialPort::OneStop);
|
||||
m_serial->setParity(QSerialPort::NoParity);
|
||||
m_serial->setFlowControl(QSerialPort::NoFlowControl);
|
||||
|
||||
if (!m_serial->open(QIODevice::ReadWrite)) {
|
||||
// 开发机(Windows)没有 /dev/ttyS7 时走这里:不启动定时器,界面显示通信异常
|
||||
qWarning() << "车端串口打开失败:" << m_device << m_serial->errorString();
|
||||
m_sock->abort();
|
||||
// AnyIPv4:回环与各本机网卡都能收到;若只允许本机回环,改成 QHostAddress::LocalHost
|
||||
if (!m_sock->bind(QHostAddress::AnyIPv4, kCanRxPort)) {
|
||||
// 端口被占用时走这里:不启动定时器,界面显示通信异常
|
||||
qWarning() << "车端 UDP 端口绑定失败:" << kCanRxPort << m_sock->errorString();
|
||||
setCommOk(false);
|
||||
return false;
|
||||
}
|
||||
m_buf.clear();
|
||||
m_hbTimer->start();
|
||||
m_fastTimer->start();
|
||||
m_midTimer->start();
|
||||
@@ -304,8 +303,7 @@ void CarCommunication::stop()
|
||||
m_midTimer->stop();
|
||||
m_slowTimer->stop();
|
||||
m_selfCheckTimer->stop();
|
||||
if (m_serial->isOpen()) m_serial->close();
|
||||
m_buf.clear();
|
||||
m_sock->close();
|
||||
}
|
||||
|
||||
void CarCommunication::setCommOk(bool ok)
|
||||
@@ -317,28 +315,25 @@ void CarCommunication::setCommOk(bool ok)
|
||||
}
|
||||
|
||||
// ===================== 收包与分流 =====================
|
||||
void CarCommunication::onReadyRead()
|
||||
// UDP 是数据报:单个数据报内含一条或多条完整帧,逐条校验即可,
|
||||
// 不再需要串口时代那套跨读字节流的残帧缓冲与滑窗重同步。
|
||||
void CarCommunication::onDatagram()
|
||||
{
|
||||
m_buf.append(m_serial->readAll());
|
||||
|
||||
// 串口是字节流:逐字节找帧头,校验不过则右移一字节继续找(容忍粘包、拆包与杂包)。
|
||||
// 尾部不足一帧的残段留在 m_buf 里等下次数据。
|
||||
int off = 0;
|
||||
while (off + kFrameLen <= m_buf.size()) {
|
||||
CanFrame f;
|
||||
std::memcpy(&f, m_buf.constData() + off, kFrameLen);
|
||||
if (f.header == kFrameHeader && f.dlc == kDataLen
|
||||
&& f.checksum == canChecksum(f.canId, f.dlc, f.data)) {
|
||||
handleFrame(f.canId, f.data);
|
||||
off += kFrameLen;
|
||||
} else {
|
||||
++off;
|
||||
while (m_sock->hasPendingDatagrams()) {
|
||||
const QByteArray d = m_sock->receiveDatagram().data();
|
||||
int off = 0;
|
||||
while (off + kFrameLen <= d.size()) {
|
||||
CanFrame f;
|
||||
std::memcpy(&f, d.constData() + off, kFrameLen);
|
||||
if (f.header == kFrameHeader && f.dlc == kDataLen
|
||||
&& f.checksum == canChecksum(f.canId, f.dlc, f.data)) {
|
||||
handleFrame(f.canId, f.data);
|
||||
off += kFrameLen;
|
||||
} else {
|
||||
++off; // 数据报内可能有杂字节,向后找下一个帧头
|
||||
}
|
||||
}
|
||||
}
|
||||
if (off > 0) m_buf.remove(0, off);
|
||||
|
||||
// 防御:长期收不到帧头时缓冲会一直涨,超过上限只保留尾部一帧长度
|
||||
if (m_buf.size() > 4096) m_buf.remove(0, m_buf.size() - kFrameLen);
|
||||
}
|
||||
|
||||
void CarCommunication::handleFrame(quint32 canId, const quint8* d)
|
||||
@@ -633,7 +628,7 @@ void CarCommunication::cacheFaults()
|
||||
// ===================== 发包 =====================
|
||||
void CarCommunication::sendFrame(quint32 canId, const quint8* data)
|
||||
{
|
||||
if (!m_serial->isOpen()) return;
|
||||
if (m_sock->state() != QAbstractSocket::BoundState) return;
|
||||
|
||||
CanFrame f;
|
||||
f.header = kFrameHeader;
|
||||
@@ -641,7 +636,8 @@ void CarCommunication::sendFrame(quint32 canId, const quint8* data)
|
||||
f.dlc = kDataLen;
|
||||
std::memcpy(f.data, data, kDataLen);
|
||||
f.checksum = canChecksum(canId, f.dlc, f.data);
|
||||
m_serial->write(reinterpret_cast<const char*>(&f), kFrameLen);
|
||||
m_sock->writeDatagram(reinterpret_cast<const char*>(&f), kFrameLen,
|
||||
QHostAddress(kCanTxHost), kCanTxPort);
|
||||
}
|
||||
|
||||
// 50ms 组:Chassis_ControlCmd / Arm_ControlCmd1 / Arm_ControlCmd2 / Emergency_Cmd
|
||||
|
||||
Reference in New Issue
Block a user