feat:初始化wuhan_QT界面代码
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
#include "mockvehicle.h"
|
||||
#include <QSettings>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QHostAddress>
|
||||
#include <QNetworkDatagram>
|
||||
#include <QtMath>
|
||||
|
||||
MockVehicle::MockVehicle(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
QSettings s;
|
||||
m_localPort = s.value("net/local_port", 9000).toInt(); // 发送到遥控端
|
||||
m_remotePort = s.value("net/remote_port", 9001).toInt(); // 本端监听
|
||||
m_enabled = s.value("net/mock", true).toBool();
|
||||
m_plantSelfCheckFault = s.value("net/selfcheck_fault", false).toBool();
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_socket = new QUdpSocket(this);
|
||||
m_socket->bind(QHostAddress::AnyIPv4, m_remotePort,
|
||||
QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &MockVehicle::onReadyRead);
|
||||
|
||||
m_sendTimer = new QTimer(this);
|
||||
connect(m_sendTimer, &QTimer::timeout, this, &MockVehicle::onSendTick);
|
||||
m_sendTimer->start(500);
|
||||
|
||||
m_selfCheckTimer = new QTimer(this);
|
||||
connect(m_selfCheckTimer, &QTimer::timeout, this, &MockVehicle::onSelfCheckTick);
|
||||
}
|
||||
|
||||
void MockVehicle::sendJson(const QJsonObject& obj)
|
||||
{
|
||||
QByteArray data = QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
m_socket->writeDatagram(data, QHostAddress("127.0.0.1"), m_localPort);
|
||||
}
|
||||
|
||||
void MockVehicle::onReadyRead()
|
||||
{
|
||||
while (m_socket->hasPendingDatagrams()) {
|
||||
QNetworkDatagram dg = m_socket->receiveDatagram();
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(dg.data(), &err);
|
||||
if (err.error == QJsonParseError::NoError && doc.isObject())
|
||||
handleCommand(doc.object());
|
||||
}
|
||||
}
|
||||
|
||||
void MockVehicle::handleCommand(const QJsonObject& o)
|
||||
{
|
||||
const QString t = o.value("t").toString();
|
||||
|
||||
if (t == "chassis_move") {
|
||||
const QString dir = o.value("dir").toString();
|
||||
m_speed = o.value("speed").toDouble();
|
||||
if (dir == "stop") m_speed = 0;
|
||||
} else if (t == "arm_move") {
|
||||
int d = o.value("dir").toInt();
|
||||
if (d == 0) return;
|
||||
if (m_js.size() >= 6)
|
||||
m_js[1] = qBound(-90.0, m_js[1] + d * 1.0, 90.0);
|
||||
} else if (t == "arm_return") {
|
||||
m_armReturn = "returning";
|
||||
m_armHomed = false;
|
||||
} else if (t == "arm_pause") {
|
||||
m_armReturn = "idle";
|
||||
} else if (t == "gas_start") {
|
||||
m_gasRunning = true;
|
||||
} else if (t == "gas_stop") {
|
||||
m_gasRunning = false;
|
||||
} else if (t == "wash_start") {
|
||||
m_washRunning = true;
|
||||
m_washMode = (o.value("mode").toString() == "disinfect") ? "手动消毒" : "手动消除";
|
||||
m_pPlunger = true; m_pAir = true; m_pLeaf = true;
|
||||
} else if (t == "wash_stop") {
|
||||
m_washRunning = false;
|
||||
m_pPlunger = false; m_pAir = false; m_pLeaf = false;
|
||||
} else if (t == "ball_valve") {
|
||||
int v = o.value("valve").toInt();
|
||||
bool open = o.value("open").toBool();
|
||||
if (v == 1) m_valve1Open = open;
|
||||
else m_valve2Open = open;
|
||||
} else if (t == "pump_power") {
|
||||
const QString p = o.value("pump").toString();
|
||||
bool on = o.value("on").toBool();
|
||||
if (p == "plunger") m_pPlunger = on;
|
||||
else if (p == "air") m_pAir = on;
|
||||
else if (p == "leaf") m_pLeaf = on;
|
||||
} else if (t == "pump_rpm") {
|
||||
const QString p = o.value("pump").toString();
|
||||
int rpm = o.value("rpm").toInt();
|
||||
if (p == "plunger") m_plungerRpm = rpm;
|
||||
else if (p == "air") m_airRpm = rpm;
|
||||
else if (p == "leaf") m_leafRpm = rpm;
|
||||
} else if (t == "selfcheck_start") {
|
||||
m_selfCheckActive = true;
|
||||
m_selfCheckIndex = 0;
|
||||
m_selfCheckTimer->start(450);
|
||||
} else if (t == "estop") {
|
||||
m_estop = true;
|
||||
m_washRunning = false; m_gasRunning = false;
|
||||
m_pPlunger = false; m_pAir = false; m_pLeaf = false;
|
||||
QJsonObject o2; o2["t"] = "estop"; o2["triggered"] = true;
|
||||
sendJson(o2);
|
||||
} else if (t == "estop_reset") {
|
||||
m_estop = false;
|
||||
QJsonObject o2; o2["t"] = "estop"; o2["triggered"] = false;
|
||||
sendJson(o2);
|
||||
}
|
||||
// set_param 仅用于演示,无需额外处理
|
||||
}
|
||||
|
||||
void MockVehicle::onSendTick()
|
||||
{
|
||||
sendHeartbeat();
|
||||
sendTelemetry();
|
||||
}
|
||||
|
||||
void MockVehicle::sendHeartbeat()
|
||||
{
|
||||
QJsonObject o; o["t"] = "heartbeat"; o["ok"] = true;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void MockVehicle::sendTelemetry()
|
||||
{
|
||||
// 底盘
|
||||
{
|
||||
QJsonObject o;
|
||||
QJsonArray faults;
|
||||
if (m_estop) faults << QStringLiteral("急停触发");
|
||||
o["t"] = "chassis";
|
||||
o["soc"] = 85; o["volt"] = 360.0; o["speed"] = m_speed;
|
||||
o["handbrake"] = true; o["faults"] = faults;
|
||||
sendJson(o);
|
||||
}
|
||||
// 机械臂
|
||||
{
|
||||
if (m_armReturn == "returning") {
|
||||
bool allZero = true;
|
||||
for (double& a : m_js) { if (qAbs(a) > 1.0) a *= 0.7; else a = 0; if (qAbs(a) > 0.1) allZero = false; }
|
||||
if (allZero) { m_armReturn = "done"; m_armHomed = true; }
|
||||
}
|
||||
QJsonObject o;
|
||||
QJsonArray js;
|
||||
for (double a : m_js) js << a;
|
||||
o["t"] = "arm";
|
||||
o["js"] = js;
|
||||
o["pos"] = QString("X:%1 Y:%2 Z:%3").arg(1200).arg(300).arg(800);
|
||||
o["pose"] = "RX:0 RY:0 RZ:0";
|
||||
o["vel"] = (m_armReturn == "returning") ? 0.5 : 0.0;
|
||||
o["return_state"] = m_armReturn;
|
||||
o["homed"] = m_armHomed;
|
||||
sendJson(o);
|
||||
}
|
||||
// 洗消
|
||||
{
|
||||
QJsonObject o;
|
||||
o["t"] = "wash";
|
||||
o["mode"] = m_washRunning ? m_washMode : "--";
|
||||
o["flowA"] = m_washRunning ? 20.0 : 0.0;
|
||||
o["flowB"] = m_washRunning ? 0.5 : 0.0;
|
||||
o["gasFlow"] = m_washRunning ? 100.0 : 0.0;
|
||||
o["press"] = m_washRunning ? 0.3 : 0.0;
|
||||
o["airPress"] = m_washRunning ? 0.35 : 0.0;
|
||||
o["foam"] = (m_washMode == "手动消毒") ? 6.0 : 8.0;
|
||||
o["faults"] = QJsonArray();
|
||||
sendJson(o);
|
||||
}
|
||||
// 液位
|
||||
{
|
||||
QJsonObject o; o["t"] = "tank"; o["a"] = 100.0; o["foam"] = 30.0;
|
||||
sendJson(o);
|
||||
}
|
||||
// 气体检测仪
|
||||
{
|
||||
static int step = 0; step++;
|
||||
double conc = m_gasRunning ? 250.0 + 50.0 * qSin(step * 0.4) : 0.0;
|
||||
QJsonObject o;
|
||||
o["t"] = "gas";
|
||||
o["host"] = m_gasRunning ? "detecting" : "standby";
|
||||
o["type"] = m_gasRunning ? "S类" : "--";
|
||||
o["level"] = m_gasRunning ? "2级" : "--";
|
||||
o["conc"] = qMax(0.0, conc);
|
||||
sendJson(o);
|
||||
}
|
||||
// 球阀
|
||||
{
|
||||
QJsonObject o;
|
||||
o["t"] = "valve";
|
||||
o["d1open"] = m_valve1Open; o["d1close"] = !m_valve1Open;
|
||||
o["d2open"] = m_valve2Open; o["d2close"] = !m_valve2Open;
|
||||
o["fault"] = "";
|
||||
sendJson(o);
|
||||
}
|
||||
// 泵
|
||||
{
|
||||
QJsonObject o;
|
||||
o["t"] = "pump";
|
||||
o["plunger"] = m_pPlunger; o["air"] = m_pAir; o["leaf"] = m_pLeaf;
|
||||
o["faults"] = QJsonArray();
|
||||
sendJson(o);
|
||||
}
|
||||
}
|
||||
|
||||
void MockVehicle::onSelfCheckTick()
|
||||
{
|
||||
// 自检 10 项:通信系统/底盘通信/机械臂通信/洗消通信/传感器信号/清水液位/球阀D1开/球阀D1关/球阀D2开/球阀D2关
|
||||
bool ok = true;
|
||||
if (m_plantSelfCheckFault && m_selfCheckIndex == 6) ok = false; // 球阀D1关闭故障
|
||||
QJsonObject o; o["t"] = "selfcheck"; o["item"] = m_selfCheckIndex; o["ok"] = ok;
|
||||
sendJson(o);
|
||||
m_selfCheckIndex++;
|
||||
if (m_selfCheckIndex >= 10) {
|
||||
m_selfCheckTimer->stop();
|
||||
m_selfCheckActive = false;
|
||||
QJsonObject o2; o2["t"] = "selfcheck_done"; o2["all_ok"] = !m_plantSelfCheckFault;
|
||||
sendJson(o2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef MOCKVEHICLE_H
|
||||
#define MOCKVEHICLE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
// ===================== 内置模拟车载端(离线演示) =====================
|
||||
// 以独立 UDP 套接字在回环地址上与遥控端通信:向 UI 端口周期发送遥测,
|
||||
// 接收并响应 UI 下发的控制命令(行驶/机械臂/球阀/泵/洗消/自检/急停等)。
|
||||
class MockVehicle : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MockVehicle(QObject* parent = nullptr);
|
||||
bool enabled() const { return m_enabled; }
|
||||
|
||||
private slots:
|
||||
void onReadyRead();
|
||||
void onSendTick();
|
||||
void onSelfCheckTick();
|
||||
|
||||
private:
|
||||
void sendJson(const QJsonObject& obj);
|
||||
void sendTelemetry();
|
||||
void sendHeartbeat();
|
||||
void handleCommand(const QJsonObject& o);
|
||||
|
||||
QUdpSocket* m_socket = nullptr;
|
||||
QTimer* m_sendTimer = nullptr;
|
||||
QTimer* m_selfCheckTimer = nullptr;
|
||||
|
||||
int m_localPort = 9000; // 遥控端收包端口(本端发送目标)
|
||||
int m_remotePort = 9001; // 本端绑定端口(遥控端发包目标)
|
||||
bool m_enabled = true;
|
||||
bool m_plantSelfCheckFault = false;
|
||||
|
||||
// 模拟车辆状态
|
||||
bool m_estop = false;
|
||||
bool m_gasRunning = false;
|
||||
bool m_washRunning = false;
|
||||
QString m_washMode = "手动消除";
|
||||
bool m_valve1Open = false; // D1 控制状态(开/关)
|
||||
bool m_valve2Open = false;
|
||||
bool m_pPlunger = false, m_pAir = false, m_pLeaf = false;
|
||||
double m_speed = 3.45;
|
||||
int m_airRpm = 300, m_plungerRpm = 200, m_leafRpm = 200;
|
||||
QVector<double> m_js{30, 20, 0, 90, 45, 0};
|
||||
bool m_armHomed = true;
|
||||
QString m_armReturn = "idle";
|
||||
|
||||
// 自检序列
|
||||
bool m_selfCheckActive = false;
|
||||
int m_selfCheckIndex = 0;
|
||||
};
|
||||
|
||||
#endif // MOCKVEHICLE_H
|
||||
@@ -0,0 +1,242 @@
|
||||
#include "udpcommunication.h"
|
||||
#include <QSettings>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QHostAddress>
|
||||
#include <QDateTime>
|
||||
#include <QNetworkDatagram>
|
||||
|
||||
UdpCommunication::UdpCommunication(Telemetry* tel, QObject* parent)
|
||||
: QObject(parent), m_tel(tel)
|
||||
{
|
||||
m_socket = new QUdpSocket(this);
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &UdpCommunication::onReadyRead);
|
||||
|
||||
// 网络配置(默认回环:本地 9000 收,向 127.0.0.1:9001 发)
|
||||
QSettings s;
|
||||
m_localPort = s.value("net/local_port", 9000).toInt();
|
||||
m_remoteHost = s.value("net/remote_host", "127.0.0.1").toString();
|
||||
m_remotePort = s.value("net/remote_port", 9001).toInt();
|
||||
m_mock = s.value("net/mock", true).toBool();
|
||||
|
||||
// 通信超时判定:连续 2s 无心跳 → 通信异常
|
||||
m_hbTimer = new QTimer(this);
|
||||
m_hbTimer->setInterval(2000);
|
||||
connect(m_hbTimer, &QTimer::timeout, this, &UdpCommunication::onHeartbeatTimeout);
|
||||
}
|
||||
|
||||
bool UdpCommunication::start()
|
||||
{
|
||||
if (m_socket->state() != QAbstractSocket::UnconnectedState)
|
||||
return true;
|
||||
bool ok = m_socket->bind(QHostAddress::AnyIPv4, m_localPort,
|
||||
QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
|
||||
if (ok) m_hbTimer->start();
|
||||
return ok;
|
||||
}
|
||||
|
||||
void UdpCommunication::stop()
|
||||
{
|
||||
m_hbTimer->stop();
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
void UdpCommunication::sendJson(const QJsonObject& obj)
|
||||
{
|
||||
QByteArray data = QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
m_socket->writeDatagram(data, QHostAddress(m_remoteHost), m_remotePort);
|
||||
}
|
||||
|
||||
// ===================== 上行命令 =====================
|
||||
void UdpCommunication::sendChassisMove(const QString& dir, double speed)
|
||||
{
|
||||
QJsonObject o; o["t"] = "chassis_move"; o["dir"] = dir; o["speed"] = speed;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendArmMove(const QString& axis, int dir)
|
||||
{
|
||||
QJsonObject o; o["t"] = "arm_move"; o["axis"] = axis; o["dir"] = dir;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendArmReturn()
|
||||
{
|
||||
QJsonObject o; o["t"] = "arm_return";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendArmPause()
|
||||
{
|
||||
QJsonObject o; o["t"] = "arm_pause";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendGasStart()
|
||||
{
|
||||
QJsonObject o; o["t"] = "gas_start";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendGasStop()
|
||||
{
|
||||
QJsonObject o; o["t"] = "gas_stop";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendWashStart(const QString& mode)
|
||||
{
|
||||
QJsonObject o; o["t"] = "wash_start"; o["mode"] = mode;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendWashStop()
|
||||
{
|
||||
QJsonObject o; o["t"] = "wash_stop";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendBallValve(int valve, bool open)
|
||||
{
|
||||
QJsonObject o; o["t"] = "ball_valve"; o["valve"] = valve; o["open"] = open;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendPumpPower(const QString& pump, bool on)
|
||||
{
|
||||
QJsonObject o; o["t"] = "pump_power"; o["pump"] = pump; o["on"] = on;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendPumpRpm(const QString& pump, int rpm)
|
||||
{
|
||||
QJsonObject o; o["t"] = "pump_rpm"; o["pump"] = pump; o["rpm"] = rpm;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendSetParam(const QString& key, double value)
|
||||
{
|
||||
QJsonObject o; o["t"] = "set_param"; o["key"] = key; o["value"] = value;
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendSelfCheckStart()
|
||||
{
|
||||
QJsonObject o; o["t"] = "selfcheck_start";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendEstop()
|
||||
{
|
||||
QJsonObject o; o["t"] = "estop";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
void UdpCommunication::sendEstopReset()
|
||||
{
|
||||
QJsonObject o; o["t"] = "estop_reset";
|
||||
sendJson(o);
|
||||
}
|
||||
|
||||
// ===================== 收包与解析 =====================
|
||||
void UdpCommunication::onReadyRead()
|
||||
{
|
||||
while (m_socket->hasPendingDatagrams()) {
|
||||
QNetworkDatagram dg = m_socket->receiveDatagram();
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(dg.data(), &err);
|
||||
if (err.error != QJsonParseError::NoError || !doc.isObject())
|
||||
continue;
|
||||
parse(doc.object());
|
||||
}
|
||||
}
|
||||
|
||||
void UdpCommunication::parse(const QJsonObject& o)
|
||||
{
|
||||
const QString t = o.value("t").toString();
|
||||
|
||||
if (t == "heartbeat") {
|
||||
bool ok = o.value("ok").toBool();
|
||||
m_hbTimer->start(); // 重置超时计时
|
||||
if (ok != m_lastCommOk) {
|
||||
m_lastCommOk = ok;
|
||||
if (m_tel) m_tel->commOk = ok;
|
||||
emit commOkChanged(ok);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (t == "chassis") {
|
||||
m_tel->soc = o.value("soc").toInt();
|
||||
m_tel->volt = o.value("volt").toDouble();
|
||||
m_tel->speed = o.value("speed").toDouble();
|
||||
m_tel->handbrake = o.value("handbrake").toBool();
|
||||
m_tel->chassisFaults.clear();
|
||||
for (const QJsonValue& v : o.value("faults").toArray())
|
||||
m_tel->chassisFaults << v.toString();
|
||||
} else if (t == "arm") {
|
||||
const QJsonArray a = o.value("js").toArray();
|
||||
m_tel->js.clear();
|
||||
for (const QJsonValue& v : a) m_tel->js << v.toDouble();
|
||||
m_tel->pos = o.value("pos").toString();
|
||||
m_tel->pose = o.value("pose").toString();
|
||||
m_tel->vel = o.value("vel").toDouble();
|
||||
m_tel->returnState = o.value("return_state").toString("idle");
|
||||
m_tel->armHomed = o.value("homed").toBool();
|
||||
} else if (t == "wash") {
|
||||
m_tel->washMode = o.value("mode").toString("--");
|
||||
m_tel->flowA = o.value("flowA").toDouble();
|
||||
m_tel->flowB = o.value("flowB").toDouble();
|
||||
m_tel->gasFlow = o.value("gasFlow").toDouble();
|
||||
m_tel->press = o.value("press").toDouble();
|
||||
m_tel->airPress = o.value("airPress").toDouble();
|
||||
m_tel->foam = o.value("foam").toDouble();
|
||||
m_tel->washFaults.clear();
|
||||
for (const QJsonValue& v : o.value("faults").toArray())
|
||||
m_tel->washFaults << v.toString();
|
||||
} else if (t == "tank") {
|
||||
m_tel->levelA = o.value("a").toDouble();
|
||||
m_tel->levelFoam = o.value("foam").toDouble();
|
||||
} else if (t == "gas") {
|
||||
m_tel->gasHost = o.value("host").toString("standby");
|
||||
m_tel->gasType = o.value("type").toString("--");
|
||||
m_tel->gasLevel = o.value("level").toString("--");
|
||||
m_tel->gasConc = o.value("conc").toDouble();
|
||||
} else if (t == "valve") {
|
||||
m_tel->d1open = o.value("d1open").toBool();
|
||||
m_tel->d1close = o.value("d1close").toBool();
|
||||
m_tel->d2open = o.value("d2open").toBool();
|
||||
m_tel->d2close = o.value("d2close").toBool();
|
||||
m_tel->valveFault = o.value("fault").toString();
|
||||
if (!m_tel->valveFault.isEmpty())
|
||||
emit faultAppended("洗消系统", m_tel->valveFault, m_tel->valveFault);
|
||||
} else if (t == "pump") {
|
||||
m_tel->plunger = o.value("plunger").toBool();
|
||||
m_tel->air = o.value("air").toBool();
|
||||
m_tel->leaf = o.value("leaf").toBool();
|
||||
m_tel->pumpFaults.clear();
|
||||
for (const QJsonValue& v : o.value("faults").toArray())
|
||||
m_tel->pumpFaults << v.toString();
|
||||
} else if (t == "estop") {
|
||||
m_tel->estop = o.value("triggered").toBool();
|
||||
emit estopReceived(m_tel->estop);
|
||||
} else if (t == "selfcheck") {
|
||||
emit selfCheckItem(o.value("item").toInt(), o.value("ok").toBool());
|
||||
} else if (t == "selfcheck_done") {
|
||||
emit selfCheckDone(o.value("all_ok").toBool());
|
||||
} else {
|
||||
return; // 未知类型不触发刷新
|
||||
}
|
||||
|
||||
emit telemetryUpdated();
|
||||
}
|
||||
|
||||
void UdpCommunication::onHeartbeatTimeout()
|
||||
{
|
||||
if (m_lastCommOk) {
|
||||
m_lastCommOk = false;
|
||||
if (m_tel) m_tel->commOk = false;
|
||||
emit commOkChanged(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef UDPCOMMUNICATION_H
|
||||
#define UDPCOMMUNICATION_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QTimer>
|
||||
#include "global.h"
|
||||
|
||||
// ===================== UDP 通信层(收包 / 发包) =====================
|
||||
// 与车载端经 UDP 报文通信,JSON 文本协议:
|
||||
// 上行(UI→车载端)命令见各 sendXxx();
|
||||
// 下行(车载端→UI)遥测见 parse() 中 "t" 字段分支。
|
||||
// 本类同时承担“通信超时判定”:连续 2s 未收到心跳则判为通信异常。
|
||||
class UdpCommunication : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UdpCommunication(Telemetry* tel, QObject* parent = nullptr);
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
// 是否启用内置模拟车载端(离线演示)
|
||||
bool mockEnabled() const { return m_mock; }
|
||||
int localPort() const { return m_localPort; }
|
||||
|
||||
// ---------- 上行命令(发包) ----------
|
||||
void sendChassisMove(const QString& dir, double speed);
|
||||
void sendArmMove(const QString& axis, int dir); // dir: +1 / -1 / 0(停止)
|
||||
void sendArmReturn();
|
||||
void sendArmPause();
|
||||
void sendGasStart();
|
||||
void sendGasStop();
|
||||
void sendWashStart(const QString& mode); // "clean" / "disinfect"
|
||||
void sendWashStop();
|
||||
void sendBallValve(int valve, bool open); // valve: 1=D1, 2=D2
|
||||
void sendPumpPower(const QString& pump, bool on); // "plunger"/"air"/"leaf"
|
||||
void sendPumpRpm(const QString& pump, int rpm);
|
||||
void sendSetParam(const QString& key, double value);
|
||||
void sendSelfCheckStart();
|
||||
void sendEstop();
|
||||
void sendEstopReset();
|
||||
|
||||
signals:
|
||||
void telemetryUpdated(); // 任意遥测包到达(页面据此刷新)
|
||||
void commOkChanged(bool ok); // 通信状态变化
|
||||
void estopReceived(bool triggered); // 收到急停
|
||||
void selfCheckItem(int index, bool ok); // 自检单项结果(0~9)
|
||||
void selfCheckDone(bool allOk); // 自检完成
|
||||
void faultAppended(const QString& system, const QString& code, const QString& desc);
|
||||
|
||||
private slots:
|
||||
void onReadyRead();
|
||||
void onHeartbeatTimeout();
|
||||
|
||||
private:
|
||||
void sendJson(const QJsonObject& obj);
|
||||
void parse(const QJsonObject& obj);
|
||||
|
||||
Telemetry* m_tel = nullptr;
|
||||
QUdpSocket* m_socket = nullptr;
|
||||
QTimer* m_hbTimer = nullptr;
|
||||
int m_localPort = 9000;
|
||||
QString m_remoteHost = "127.0.0.1";
|
||||
int m_remotePort = 9001;
|
||||
bool m_mock = true;
|
||||
bool m_lastCommOk = true;
|
||||
};
|
||||
|
||||
#endif // UDPCOMMUNICATION_H
|
||||
Reference in New Issue
Block a user