增加通讯协议

This commit is contained in:
xu
2026-10-14 09:59:42 +08:00
parent 32878a9a80
commit 950e9e6c9e
102 changed files with 5538 additions and 2679 deletions
+157 -9
View File
@@ -1,9 +1,26 @@
#include "appcontext.h"
#include "udpcommunication.h"
#include "mockvehicle.h"
#include "car.h"
#include <QApplication>
#include <QDateTime>
namespace {
// 遥控器摇杆满量程 0~4096、中位 2048;取 ±10% 死区,即 1638~2458 视为中立
const int kAxisMid = 2048;
const int kAxisDead = 410;
int axisDir(int v)
{
if (v > kAxisMid + kAxisDead) return +1;
if (v < kAxisMid - kAxisDead) return -1;
return 0;
}
// 六个机械臂轴,顺序与 sendArmMove 的轴名一致(见 pages/arm.cpp)
const char* const kArmAxes[6] = {"z", "x", "y", "roll", "pitch", "yaw"};
} // namespace
AppContext* AppContext::instance()
{
static AppContext* s = nullptr;
@@ -25,18 +42,31 @@ AppContext::AppContext(QObject* parent)
if (!m_settings->contains(keyFoamPercent()))m_settings->setValue(keyFoamPercent(), 3);
if (!m_settings->contains(keyAirFlow())) m_settings->setValue(keyAirFlow(), 100);
if (!m_settings->contains("param/pwd")) m_settings->setValue("param/pwd", "6789");
if (!m_settings->contains("param/admin_pwd"))m_settings->setValue("param/admin_pwd", "6789");
// 通信层(UDP 收包发包)
m_comm = new UdpCommunication(&telemetry, this);
// 通信层(19 条 CAN 报文经 /dev/ttyS7 收发,见 comm/car.h)
m_comm = new CarCommunication(&telemetry, this);
// 内置模拟车载端(离线演示:回环 UDP 收发)
m_vehicle = new MockVehicle(this);
// 遥控器(串口 /dev/ttyS5,24 字节二进制帧,见 comm/uart.h)
m_rc = new RcCommunication(this);
// 通信层故障信号 → 写入故障历史
connect(m_comm, &UdpCommunication::faultAppended,
connect(m_comm, &CarCommunication::faultAppended,
this, &AppContext::appendFault);
// 遥控器报文 → 模式 / 急停 / 动作 / 显示
connect(m_rc, &RcCommunication::frameReceived, this, &AppContext::onRcFrame);
connect(m_rc, &RcCommunication::linkChanged, this, &AppContext::onRcLinkChanged);
#if 0
// 行车/机械臂模式改由底盘报文的 mode 字段决定(遥控器在线时让位给遥控器报文)。
// 停用原因:协议 sheet RC 的 12 条下行报文里没有该 mode 字段,telemetry.driveMode
// 无处可填,此连接只会一直把模式按 0(行车)写。待协议补充该信号后恢复。
connect(m_comm, &CarCommunication::telemetryUpdated, this, [this]() {
if (m_rc->linkOk()) return;
setMode(telemetry.driveMode == 0 ? Mode::Drive : Mode::Arm);
});
#endif
seedFaultHistory();
}
@@ -117,9 +147,10 @@ void AppContext::setPassword(const QString& pwd)
m_settings->sync();
}
// 管理员密码为固定值,不持久化、不可修改
QString AppContext::adminPassword() const
{
return m_settings->value("param/admin_pwd", "6789").toString();
return "9527";
}
void AppContext::resetEstop()
@@ -144,4 +175,121 @@ void AppContext::setMode(Mode m)
if (mode == m) return;
mode = m;
emit modeChanged(m);
if (m_comm) m_comm->setArmMode(m == Mode::Arm); // 同步到 Arm_ControlCmd1 的 Arm_modeCmd/Arm_enable
}
// ===================== 遥控器(24 字节二进制帧) =====================
void AppContext::onRcFrame(const RcFrame& f)
{
// 存入遥测快照,供界面显示
telemetry.rcBattery = f.battery;
telemetry.rcDriveMode = f.driveMode;
telemetry.rcArmMode = f.armMode;
telemetry.rcArmEnabled = f.armEnabled;
telemetry.rcChassisEnabled = f.chassisEnabled;
telemetry.rcEstop = f.estop;
telemetry.rcGear = f.gear;
telemetry.rcSteer = f.steer;
telemetry.rcPosZ = f.posZ;
telemetry.rcPosX = f.posX;
telemetry.rcPosY = f.posY;
telemetry.rcRoll = f.roll;
telemetry.rcPitch = f.pitch;
telemetry.rcYaw = f.yaw;
// 模式:bit1 机械臂模式须与 bit2 机械臂使能同时为 1 才视为机械臂
setMode((f.armMode && f.armEnabled) ? Mode::Arm : Mode::Drive);
// 急停与复位都由遥控器 bit4 下发:上升沿触发,下降沿复位。面板只显示横幅提示。
if (f.estop != m_rcEstopPrev) {
m_rcEstopPrev = f.estop;
if (f.estop) triggerEstop();
else resetEstop();
}
// 动作:与界面按钮同一套安全联锁(模式 + 使能 + 未急停)
applyRcChassis(f.gear, f.steer, f.chassisEnabled);
applyRcArm(f);
updateRcDriving();
}
void AppContext::applyRcChassis(int gear, int steer, bool enabled)
{
const bool allowed = enabled && !estopTriggered && (mode == Mode::Drive);
const int g = allowed ? axisDir(gear) : 0;
const int s = allowed ? axisDir(steer) : 0;
if (g == m_rcGearDir && s == m_rcSteerDir) return; // 方向未变,不重复发包
m_rcGearDir = g;
m_rcSteerDir = s;
sendChassisCommand();
}
void AppContext::applyRcArm(const RcFrame& f)
{
const bool allowed = f.armEnabled && !estopTriggered && (mode == Mode::Arm);
const int vals[6] = {f.posZ, f.posX, f.posY, f.roll, f.pitch, f.yaw};
for (int i = 0; i < 6; ++i) {
const int d = allowed ? axisDir(vals[i]) : 0;
if (d == m_rcAxisDir[i]) continue; // 方向未变,不重复发包
m_rcAxisDir[i] = d;
m_comm->sendArmMove(QString::fromLatin1(kArmAxes[i]), d);
}
}
// 档位与转向可同时下发(边前进/后退边转向);两者都回中才停车。
// 仅转向(档位回中)时发 stop + turn,由车载端决定是否原地转向。
void AppContext::sendChassisCommand()
{
QString dir = "stop";
if (m_rcGearDir > 0) dir = "fwd";
else if (m_rcGearDir < 0) dir = "back";
QString turn;
if (m_rcSteerDir > 0) turn = "right";
else if (m_rcSteerDir < 0) turn = "left";
if (dir == "stop" && turn.isEmpty()) {
m_comm->sendChassisMove("stop", 0);
return;
}
const double spd = (dir == "stop") ? 0.0 : getDouble(keySpeed(), 4.5);
m_comm->sendChassisMove(dir, spd, turn);
}
void AppContext::updateRcDriving()
{
// 遥控器正在操作:链路在线且任一已生效轴偏离中位
bool driving = (m_rcGearDir != 0) || (m_rcSteerDir != 0);
for (int i = 0; i < 6; ++i) {
if (m_rcAxisDir[i] != 0) { driving = true; break; }
}
if (driving == m_rcDriving) return;
m_rcDriving = driving;
emit rcDrivingChanged(driving);
}
void AppContext::onRcLinkChanged(bool ok)
{
telemetry.rcLinkOk = ok;
if (!ok) {
// 链路中断:撤销所有遥控器动作,否则最后一条指令会被一直挂住。
// 不动急停状态——丢包导致的 bit4 读为 0 不应被当成遥控器下发的复位。
if (m_rcGearDir != 0 || m_rcSteerDir != 0) {
m_rcGearDir = 0;
m_rcSteerDir = 0;
m_comm->sendChassisMove("stop", 0);
}
for (int i = 0; i < 6; ++i) {
if (m_rcAxisDir[i] == 0) continue;
m_rcAxisDir[i] = 0;
m_comm->sendArmMove(QString::fromLatin1(kArmAxes[i]), 0);
}
}
updateRcDriving();
}