#include "chassis.h" #include "titlebar.h" #include "statusstrip.h" #include "alarmbanner.h" #include "datacard.h" #include "paramcontrol.h" #include "udpcommunication.h" #include "confirmdialog.h" #include #include #include #include #include "global.h" ChassisPage::ChassisPage(AppContext* ctx, QWidget* parent) : BasePage(ctx, parent) { QVBoxLayout* root = new QVBoxLayout(this); root->setContentsMargins(0, 0, 0, 0); root->setSpacing(0); m_titleBar = new TitleBar(ctx, this); m_titleBar->setTitle("机器人底盘"); root->addWidget(m_titleBar); // 告警横幅(机械臂未回位 / 通信异常) m_banner = new AlarmBanner(this); root->addWidget(m_banner); QHBoxLayout* main = new QHBoxLayout(); main->setContentsMargins(36, 16, 36, 16); main->setSpacing(32); // 左控制列(垂直居中 + 平均分布) QWidget* left = new QWidget(this); QVBoxLayout* leftLay = new QVBoxLayout(left); leftLay->setContentsMargins(0, 0, 0, 0); leftLay->setSpacing(8); QPushButton* fwd = makeDirButton("前进", "fwd"); QPushButton* back = makeDirButton("后退", "back"); QPushButton* leftT = makeDirButton("左转向", "left"); QPushButton* rightT = makeDirButton("右转向", "right"); m_dirButtons << fwd << back << leftT << rightT; leftLay->addStretch(1); leftLay->addWidget(fwd); leftLay->addStretch(1); leftLay->addWidget(back); leftLay->addStretch(1); leftLay->addWidget(leftT); leftLay->addStretch(1); leftLay->addWidget(rightT); leftLay->addStretch(1); left->setFixedWidth(180); main->addWidget(left); // 中央卡片区(洗消/机械臂/气体检测仪) QWidget* center = new QWidget(this); QVBoxLayout* centerLay = new QVBoxLayout(center); centerLay->setContentsMargins(0, 0, 0, 0); centerLay->setSpacing(12); m_washCard = new DataCard("洗消", center); m_washCard->addField("A液位", "100L"); m_washCard->addField("泡沫液位", "30L"); m_armCard = new DataCard("机械臂", center); m_armCard->addField("末端位置", "XYZ"); m_armCard->addField("姿态", "RX/RY/RZ"); m_gasCard = new DataCard("气体检测仪", center); m_gasCard->addField("主机状态", "待机"); m_gasCard->addField("浓度", "0 ppb"); centerLay->addWidget(m_washCard, 1); centerLay->addWidget(m_armCard, 1); centerLay->addWidget(m_gasCard, 1); main->addWidget(center, 1); // 右参数区 QWidget* right = new QWidget(this); QVBoxLayout* rightLay = new QVBoxLayout(right); rightLay->setContentsMargins(0, 0, 0, 0); rightLay->setSpacing(12); m_chassisCard = new DataCard("底盘状态", right); m_chassisCard->addField("电量SOC", "85%"); m_chassisCard->addField("电压U", "360V"); m_chassisCard->addField("车速V", "3.45 km/h"); m_chassisCard->addField("手刹", "有效"); m_chassisFieldFault = m_chassisCard->addField("故障", "无"); rightLay->addWidget(m_chassisCard, 1); // 车速设置 double speed = ctx->getDouble(ctx->keySpeed(), 4.5); m_speed = new ParamControl("车速设置", "km/h", 0.5, 0.0, 10.0, false, false, right); m_speed->setValue(speed); m_speed->setStyleSheet("background:transparent;"); connect(m_speed, &ParamControl::valueChanged, this, &ChassisPage::onSpeedChanged); rightLay->addWidget(m_speed); right->setFixedWidth(380); main->addWidget(right); root->addLayout(main, 1); m_statusStrip = new StatusStrip(ctx, this); connect(m_statusStrip, &StatusStrip::backClicked, this, [this]() { emit navigate(PageJob); }); root->addWidget(m_statusStrip); // 车速自动保存(1s 防抖) m_saveTimer = new QTimer(this); m_saveTimer->setSingleShot(true); m_saveTimer->setInterval(1000); connect(m_saveTimer, &QTimer::timeout, this, &ChassisPage::onSpeedSaved); connect(ctx->comm(), &UdpCommunication::telemetryUpdated, this, &ChassisPage::refresh); connect(ctx, &AppContext::modeChanged, this, [this](Mode) { updateModeGating(); }); connect(ctx, &AppContext::estopChanged, this, [this](bool) { updateModeGating(); }); updateModeGating(); refresh(); } QPushButton* ChassisPage::makeDirButton(const QString& text, const QString& dir) { QPushButton* b = button(text, "", this); b->setFixedSize(180, 150); b->setStyleSheet(QString("font-size:26px;background-color:%1;color:%2;border:1px solid %3;border-radius:12px;") .arg(C::PANEL_HI, C::TEXT, C::BORDER)); connect(b, &QPushButton::pressed, this, [this, dir]() { if (ctx()->mode != Mode::Drive) { ConfirmDialog::toast(this, "请切换至行车模式"); return; } ctx()->comm()->sendChassisMove(dir, ctx()->getDouble(ctx()->keySpeed(), 4.5)); }); connect(b, &QPushButton::released, this, [this]() { ctx()->comm()->sendChassisMove("stop", 0); }); return b; } void ChassisPage::updateModeGating() { // 非行车模式或急停触发时禁用方向按钮 bool drive = (ctx()->mode == Mode::Drive) && !ctx()->estopTriggered; for (QPushButton* b : m_dirButtons) b->setEnabled(drive); } void ChassisPage::refresh() { const Telemetry& t = ctx()->telemetry; // 机械臂未回位 / 通信异常告警 if (!t.commOk) { m_banner->showAlarm("通信异常"); } else if (!t.armHomed && t.returnState != "idle") { m_banner->showAlarm("机械臂未回位,请谨慎操作"); } else { m_banner->clear(); } m_chassisCard->setField(0, QString("%1%").arg(t.soc)); m_chassisCard->setField(1, QString("%1V").arg(t.volt, 0, 'f', 0)); m_chassisCard->setField(2, QString("%1 km/h").arg(t.speed, 0, 'f', 2)); m_chassisCard->setField(3, t.handbrake ? "有效" : "无效"); m_chassisCard->setField(m_chassisFieldFault, t.chassisFaults.isEmpty() ? "无" : t.chassisFaults.join(";")); m_chassisCard->setAlarm(m_chassisFieldFault, !t.chassisFaults.isEmpty()); m_washCard->setField(0, QString("%1L").arg(t.levelA, 0, 'f', 0)); m_washCard->setField(1, QString("%1L").arg(t.levelFoam, 0, 'f', 0)); m_armCard->setField(0, t.pos); m_armCard->setField(1, t.pose); m_gasCard->setField(0, t.gasHost == "detecting" ? "检测中" : (t.gasHost == "fault" ? "故障" : "待机")); m_gasCard->setField(1, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0)); } void ChassisPage::onSpeedChanged(double v) { // 1s 防抖后自动保存 m_saveTimer->start(); ctx()->comm()->sendSetParam(ctx()->keySpeed(), v); } void ChassisPage::onSpeedSaved() { ctx()->setDouble(ctx()->keySpeed(), m_speed->value()); ConfirmDialog::toast(this, "车速已保存"); }