201 lines
11 KiB
C++
201 lines
11 KiB
C++
#ifndef CAR_H
|
||
#define CAR_H
|
||
|
||
#include <QByteArray>
|
||
#include <QObject>
|
||
#include <QSet>
|
||
#include <QString>
|
||
#include <QTimer>
|
||
#include <QVector>
|
||
#include "global.h"
|
||
|
||
class QUdpSocket;
|
||
|
||
// 摇杆量协议范围 0~4096,中位 2048(分类一各轴、Vehi_gear/Vehi_turning 通用)
|
||
static const int kRcAxisMid = 2048;
|
||
|
||
// 自检项数目(④自检界面与本地自检序列共用,顺序即界面显示顺序):
|
||
// 0 通信系统(All_Comm_FaultCode=0) → 1~8 各分系统通信(码1~8) → 9 清水液位
|
||
// (A_LiquidLevel_Low) → 10~13 球阀 D1/D2 开启、关闭(MBV_D1_Sta / MBV_D2_Sta)
|
||
static const int kSelfCheckItemCount = 14;
|
||
|
||
// 球阀 4 项的自检起始下标:10=D1开启 11=D1关闭 12=D2开启 13=D2关闭。
|
||
// 这 4 项不能只靠本端计时通过——要先下发 Decontam_DebugCmd,再等
|
||
// MBV_D1_Sta / MBV_D2_Sta 反馈与指令一致;不一致即超时判故障。
|
||
static const int kSelfCheckValveFirst = 10;
|
||
|
||
// 球阀等待反馈的节拍数(自检定时器 450ms/拍):18 拍 ≈ 8s 未到位即判故障。
|
||
static const int kValveWaitTicks = 18;
|
||
|
||
// 演示故障所在的自检项(config.ini 的 net/selfcheck_fault),只作用于 0~9 段,
|
||
// 免得覆盖球阀 4 项的真实反馈结果。9 = 清水液位。
|
||
static const int kSelfCheckDemoFaultIndex = 9;
|
||
|
||
// CAN 反馈只给故障码与故障等级,这里带上一个故障所需的全部要素。
|
||
// text() 渲染的是完整一行:<组名>:[<码>-]<描述>[,<N>级],
|
||
// “底盘电机故障:18-电机过温警告,1级”;各卡片那一行太窄,只用 desc(+等级后缀)。
|
||
struct CanFault {
|
||
QString system; // 故障历史分组(机器人底盘/洗消系统/气体检测仪)
|
||
QString group; // 组名(底盘电机故障/BMS系统故障/…),完整一行才用得到
|
||
QString code; // 追溯码,如 MCU18 / SOCL1 / COMM3,供故障历史去重与故障查询
|
||
QString codeNum; // 完整一行里的码,如 "18";空表示该故障没有码(等级型故障)
|
||
QString desc; // 描述,不含等级,如“电机过温警告”
|
||
int level = 0; // 0=不显示等级;1~3 → 尾部追加“,N级”
|
||
QString text() const; // 完整一行,如“底盘电机故障:18-电机过温警告,1级”
|
||
};
|
||
|
||
// ===================== 车端通信层(CAN 报文经 /dev/ttyS7 串口收发) =====================
|
||
// 依据《洗消机器人遥控器通信协议20260810》sheet RC 实现的 19 条报文:
|
||
// 上行 分类一 7 条(遥控器 → 车端):Chassis_ControlCmd / Arm_ControlCmd1 / Arm_ControlCmd2 /
|
||
// Decontam_ControlCmd / Decontam_DebugCmd / Detector_ControlCmd / Emergency_Cmd,
|
||
// 按协议「报文周期时间」列分 50 / 100 / 1000ms 三组重发;
|
||
// 下行 分类二/三 12 条(车端 → 遥控器):底盘 4 条、机械臂 4 条、洗消 2 条、气体检测仪 2 条,
|
||
// 按 canId 分流转 handleFrame() 写入 Telemetry。
|
||
// 全部为 29 位扩展帧、信号一律 Intel(小端)位序。
|
||
// 传输走 UDP:bind 本机 9000 端口收下行反馈,上行命令发往车端 192.168.1.224:3001。
|
||
// UDP 载荷统一为 16 字节:帧头 0xAA + canId + DLC + 8 字节数据 + 校验和 + 帧尾 0xBB。
|
||
// 通信超时判定:连续 2s 未收到任何合法帧则判为通信异常(最慢的下行周期 1000ms)。
|
||
class CarCommunication : public QObject
|
||
{
|
||
Q_OBJECT
|
||
public:
|
||
explicit CarCommunication(Telemetry* tel, QObject* parent = nullptr);
|
||
~CarCommunication() override;
|
||
|
||
bool start();
|
||
void stop();
|
||
|
||
// 行车/机械臂模式(面板旋钮或遥控器报文)→ Arm_ControlCmd1 的 Arm_modeCmd / Arm_enable
|
||
void setArmMode(bool arm) { m_armMode = arm; }
|
||
|
||
// 遥控器是否正在操作(由 AppContext 维护)。面板那两路入口据此让位:
|
||
// 两端都能操作,但遥控器一动手就以遥控器为准;遥控器松手后由页面重发按住的方向/轴。
|
||
void setRcActive(bool on) { m_rcActive = on; }
|
||
|
||
// ---------- 上行命令(分类一)----------
|
||
// 命令只更新本端控制状态,实际报文由周期定时器编码发送(CAN 控制帧本就是周期报文)。
|
||
// 底盘/机械臂各有**两个入口**:面板一个、遥控器一个(名字带 Rc)。两路写的是同一份
|
||
// 轴值缓存,差别只在“遥控器在操作时面板那一路直接忽略”。
|
||
// turn 非空("left"/"right")时与 dir 同时下发,支持边前进/后退边转向
|
||
void sendChassisMove(const QString& dir, double speed, const QString& turn = QString());
|
||
void sendChassisMoveRc(const QString& dir, double speed, const QString& turn = QString());
|
||
void sendArmMove(const QString& axis, int dir); // dir: +1 / -1 / 0(停止)
|
||
void sendArmMoveRc(const QString& axis, int dir);
|
||
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(); // 启动本端自检序列(不发 CAN)
|
||
void sendEstop();
|
||
void sendEstopReset();
|
||
|
||
signals:
|
||
void telemetryUpdated(); // 一批数据报解析完(页面据此刷新,同批多帧只发一次)
|
||
// 顶部左区那行通信状态变化(Telemetry::commText)。周期报文内容不变时不发。
|
||
void commTextChanged();
|
||
void selfCheckItem(int index, bool ok); // 自检单项结果(0~kSelfCheckItemCount-1)
|
||
void selfCheckItemBusy(int index); // 自检单项开始执行(已下发指令、正等反馈)
|
||
void selfCheckDone(bool allOk); // 自检完成
|
||
void faultAppended(const QString& system, const QString& code, const QString& desc);
|
||
|
||
private slots:
|
||
void onDatagram();
|
||
void onSendFast(); // 50ms 组:底盘 / 机械臂 / 急停
|
||
void onSendMid(); // 100ms 组:洗消指令 / 洗消调试
|
||
void onSendSlow(); // 1000ms 组:气体检测仪
|
||
void onHeartbeatTimeout();
|
||
void onSelfCheckTick();
|
||
|
||
private:
|
||
// 收包:按 canId 分流;发包:16 字节封帧后写 UDP
|
||
void handleFrame(quint32 canId, const quint8* d);
|
||
void sendFrame(quint32 canId, const quint8* data);
|
||
void cacheFaults();
|
||
void updateCommText(); // 重建顶部那行通信状态(Telemetry::commText)
|
||
void updateWashSwing(); // 洗消作业期间的末端俯仰摆动(每 50ms 一拍)
|
||
void setCommOk(bool ok);
|
||
void finishSelfCheckItem(bool ok); // 记录单项结果并推进到下一项
|
||
|
||
Telemetry* m_tel = nullptr;
|
||
QUdpSocket* m_sock = nullptr;
|
||
QTimer* m_hbTimer = nullptr;
|
||
QTimer* m_fastTimer = nullptr;
|
||
QTimer* m_midTimer = nullptr;
|
||
QTimer* m_slowTimer = nullptr;
|
||
QTimer* m_selfCheckTimer = nullptr;
|
||
|
||
bool m_lastCommOk = true;
|
||
int m_commFaultCode = 0; // 最近的 All_Comm_FaultCode,0 = 通信正常
|
||
|
||
bool m_armMode = false;
|
||
bool m_rcActive = false; // 遥控器正在操作:面板那两路入口让位(见 setRcActive)
|
||
|
||
// 本地自检序列(CAN 矩阵无自检报文,见 .cpp TODO)
|
||
int m_selfCheckIndex = 0;
|
||
bool m_selfCheckFault = true;
|
||
int m_selfCheckWait = 0; // 当前项已等待的节拍数:0 = 尚未下发指令
|
||
bool m_selfCheckAllOk = true; // 已跑完各项的累计结果,决定 selfCheckDone 的入参
|
||
// 自检要临时置调试模式才能操作球阀,结束后恢复原值,免得把整车留在调试模式
|
||
bool m_debugModeBeforeCheck = false;
|
||
|
||
// ---- 分类一 上行控制状态 ----
|
||
// Chassis_ControlCmd 0x18208081
|
||
quint16 m_gear = kRcAxisMid; // Vehi_gear:后 0 / 中 2048 / 前 4096
|
||
quint16 m_turning = kRcAxisMid; // Vehi_turning:左 0 / 中 2048 / 右 4096
|
||
quint16 m_targetSpd = 0; // Vehi_TargetSpd,精度 0.01,0~500(0~5.00 km/h)
|
||
|
||
// Arm_ControlCmd1/2 0x18218081 / 0x18228081 —— 六轴位 0~4096
|
||
quint16 m_updown = kRcAxisMid;
|
||
quint16 m_leftright = kRcAxisMid;
|
||
quint16 m_frontrear = kRcAxisMid;
|
||
quint16 m_roll = kRcAxisMid;
|
||
quint16 m_pitch = kRcAxisMid;
|
||
quint16 m_yaw = kRcAxisMid;
|
||
// 洗消作业期间的末端俯仰摆动:回中 → 正弦摆动 → 还原(见 updateWashSwing,50ms/拍)
|
||
enum SwingState { SwingOff, SwingCentering, SwingSine, SwingRestoring };
|
||
SwingState m_swingState = SwingOff;
|
||
int m_swingPhase = 0; // 正弦相位,0..kWashSwingTicks
|
||
int m_swingTick = 0; // 过渡段已走的拍数
|
||
int m_swingFrom = kRcAxisMid; // 还原过渡的起始轴值
|
||
quint16 m_pitchBeforeWash = kRcAxisMid; // 洗消前的俯仰轴值,作业结束后还原
|
||
quint8 m_homingPause = 0x55; // System_homing_pause:0xAA=系统回位 0x55=暂停回位
|
||
|
||
// Decontam_ControlCmd 0x18238081
|
||
// 四个参数缓存与 config.ini 的 param/* 同源:构造时按存盘值初始化,
|
||
// 页面保存时经 sendSetParam 更新,周期帧只读缓存(不再每帧读文件)。
|
||
quint8 m_decontamMode = 0; // 2=手动消除 3=手动消毒
|
||
bool m_decontamStart = false;
|
||
int m_plungerFlow = 20; // PlungerPump_flow,L/min,0~24(param/pump_flow)
|
||
int m_foamRatio = 6; // Expansion_ratioCmd,倍,6~12(param/foam_ratio)
|
||
int m_foamPercent = 3; // Foam_ratio,%,0~10(param/foam_percent)
|
||
int m_airFlow = 100; // AirComp_flow,L/min,0~340(param/air_flow)
|
||
|
||
// Decontam_DebugCmd 0x18248081
|
||
bool m_debugMode = false; // 0=正常模式 1=调试模式(球阀/泵操作置 1)
|
||
bool m_plungerOn = false, m_airOn = false, m_vaneOn = false;
|
||
bool m_d1Open = false, m_d2Open = false;
|
||
quint16 m_airRpm = 0, m_plungerRpm = 0, m_vaneRpm = 0;
|
||
|
||
// Detector_ControlCmd 0x18258081 / Emergency_Cmd 0x18268081
|
||
quint8 m_detectorCmd = 0x55; // 0xAA=启动检测 0x55=停止检测
|
||
quint8 m_emergStop = 0; // 0x55=急停 0=正常
|
||
|
||
// ---- 下行故障缓存 ----
|
||
// 底盘故障由 VSFB1~4 四帧共同构成,各自重建后再合并,避免互相覆盖。
|
||
// 气体检测仪告警周期 1000ms、洗消故障 200ms,分开存放,否则后到的会把先到的清掉。
|
||
QVector<CanFault> m_vsf1Faults, m_vsf2Faults, m_vsf3Faults, m_vsf4Faults;
|
||
QVector<CanFault> m_washFaults, m_pumpFaults, m_detectorFaults;
|
||
// 整系统通信故障码单独一份:它是全系统级的,不属于任何一张卡片,
|
||
// 只进顶部那行通信状态与故障历史,不并进 chassisFaults。
|
||
QVector<CanFault> m_commFaults;
|
||
QSet<QString> m_reportedFaults; // 已记入故障历史的“系统|码”,周期性报文只上报新增故障
|
||
};
|
||
|
||
#endif // CAR_H
|