Files
QT_wuhan_YT/pages/arm.cpp
T
2026-10-14 09:59:42 +08:00

244 lines
9.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "arm.h"
#include "titlebar.h"
#include "statusstrip.h"
#include "alarmbanner.h"
#include "datacard.h"
#include "car.h"
#include "confirmdialog.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
//#include <QGridLayout> // 末端按钮原为 2×3 网格,改为竖列后暂不需要
#include <QPushButton>
#include <QLabel>
#include <QTimer>
#include "global.h"
ArmPage::ArmPage(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);
// 左控制列(6 运动按钮 + 系统回位/暂停)
QWidget* left = new QWidget(this);
QVBoxLayout* leftLay = new QVBoxLayout(left);
leftLay->setContentsMargins(10,10,20,10);
leftLay->setSpacing(30);
auto addMove = [&](const QString& t, const QString& axis, int dir) {
QPushButton* b = makeMoveButton(t, axis, dir);
m_moveButtons << b;
leftLay->addWidget(b);
};
addMove("左平移", "x", -1);
addMove("右平移", "x", +1);
addMove("上升", "z", +1);
addMove("下降", "z", -1);
addMove("前平移", "y", +1);
addMove("后平移", "y", -1);
m_returnBtn = button("系统回位/\n暂停", "", left);
m_returnBtn->setCheckable(true);
m_returnBtn->setFixedSize(270, 120); // 两行文字,故加高
m_returnBtn->setStyleSheet(QString(
"QPushButton{font-size:40px;background-color:%1;color:%2;border:1px solid %3;border-radius:12px;}"
"QPushButton:hover,QPushButton:focus,QPushButton:checked"
"{background-color:%4;color:#FFFFFF;border:2px solid #8ABFFF;}")
.arg(C::PANEL_HI, C::TEXT, C::BORDER, C::PRIMARY));
connect(m_returnBtn, &QPushButton::toggled, this, &ArmPage::onReturnToggled);
leftLay->addWidget(m_returnBtn);
m_returnStatus = label("", 18, C::TEXT_SUB, false, left);
m_returnStatus->setAlignment(Qt::AlignCenter);
m_returnStatus->setWordWrap(true);
leftLay->addWidget(m_returnStatus);
leftLay->addStretch(1);
left->setFixedWidth(300); // 270 按钮 + 左右边距 30,避免按钮被裁
main->addWidget(left);
// 中央区:左侧三张状态卡 + 机械臂参数卡,并排放在中间
QWidget* center = new QWidget(this);
QHBoxLayout* centerLay = new QHBoxLayout(center);
centerLay->setContentsMargins(0, 0, 0, 0);
centerLay->setSpacing(12);
QWidget* cardsCol = new QWidget(center);
QVBoxLayout* cardsLay = new QVBoxLayout(cardsCol);
cardsLay->setContentsMargins(0, 0, 0, 0);
cardsLay->setSpacing(12);
m_chassisCard = new DataCard("底盘", cardsCol);
m_chassisCard->addField("底盘电量SOC", "85%");
m_chassisCard->addField("底盘电压", "360V");
m_chassisCard->addField("底盘故障", "无");
m_washCard = new DataCard("洗消", cardsCol);
m_washCard->addField("A组分/清水液位", "100L");
m_washCard->addField("泡沫箱液位", "30L");
m_gasCard = new DataCard("气体检测仪", cardsCol);
m_gasCard->addField("主机状态", "点火状态");
m_gasCard->addField("有毒物种类", "S级");
m_gasCard->addField("有毒物等级", "二级");
cardsLay->addWidget(m_chassisCard, 1);
cardsLay->addWidget(m_washCard, 1);
cardsLay->addWidget(m_gasCard, 1);
m_armCard = new DataCard("机械臂参数", center);
m_armCard->addField("末端位置", "XYZ");
m_armCard->addField("末端姿态", "RX/RY/RZ");
m_armCard->addField("末端速度", "0");
m_armCard->addField("Joint1关节角", "30°");
m_armCard->addField("Joint2关节角", "20°");
m_armCard->addField("Joint3关节角", "0°");
m_armCard->addField("Joint4关节角", "90°");
m_armCard->addField("Joint5关节角", "45°");
m_armCard->addField("Joint6关节角", "0°");
centerLay->addWidget(cardsCol, 1);
centerLay->addWidget(m_armCard, 1);
main->addWidget(center, 1);
// 右控制列(末端 6 姿态按钮,与左侧按钮列对称)
QWidget* right = new QWidget(this);
QVBoxLayout* rightLay = new QVBoxLayout(right);
rightLay->setContentsMargins(10, 20, 20, 20);
rightLay->setSpacing(15);
auto addEnd = [&](const QString& t, const QString& axis, int dir) {
QPushButton* b = makeMoveButton(t, axis, dir);
m_endButtons << b;
rightLay->addWidget(b);
};
addEnd("末端仰", "pitch", +1);
addEnd("末端俯", "pitch", -1);
addEnd("末端正偏航", "yaw", +1);
addEnd("末端负偏航", "yaw", -1);
addEnd("末端左横滚", "roll", -1);
addEnd("末端右横滚", "roll", +1);
right->setFixedWidth(300); // 与左列同宽,按钮 270 不被裁
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);
m_statusTimer = new QTimer(this);
m_statusTimer->setSingleShot(true);
m_statusTimer->setInterval(3000);
connect(m_statusTimer, &QTimer::timeout, this, [this]() {
m_returnStatus->setText("");
});
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &ArmPage::refresh);
connect(ctx, &AppContext::modeChanged, this, [this](Mode) { updateModeGating(); });
connect(ctx, &AppContext::estopChanged, this, [this](bool) { updateModeGating(); });
connect(ctx, &AppContext::rcDrivingChanged, this, [this](bool) { updateModeGating(); });
updateModeGating();
refresh();
}
QPushButton* ArmPage::makeMoveButton(const QString& text, const QString& axis, int dir)
{
QPushButton* b = button(text, "", this);
b->setFixedSize(270, 76);
b->setStyleSheet(QString(
"QPushButton{font-size:45px;background-color:%1;color:%2;border:1px solid %3;border-radius:12px;}"
"QPushButton:hover,QPushButton:focus,QPushButton:pressed"
"{background-color:%4;color:#FFFFFF;border:2px solid #8ABFFF;}")
.arg(C::PANEL_HI, C::TEXT, C::BORDER, C::PRIMARY));
connect(b, &QPushButton::pressed, this, [this, axis, dir]() {
if (ctx()->rcDriving()) {
ConfirmDialog::toast(this, "遥控器操作中");
return;
}
if (ctx()->mode != Mode::Arm) {
ConfirmDialog::toast(this, "请切换至机械臂模式");
return;
}
ctx()->comm()->sendArmMove(axis, dir);
});
connect(b, &QPushButton::released, this, [this, axis]() {
ctx()->comm()->sendArmMove(axis, 0);
});
return b;
}
void ArmPage::updateModeGating()
{
// 非机械臂模式、急停触发或遥控器正在操作时禁用动作按钮(遥控器优先)
bool arm = (ctx()->mode == Mode::Arm) && !ctx()->estopTriggered && !ctx()->rcDriving();
for (QPushButton* b : m_moveButtons) b->setEnabled(arm);
for (QPushButton* b : m_endButtons) b->setEnabled(arm);
}
void ArmPage::onReturnToggled(bool on)
{
if (on) ctx()->comm()->sendArmReturn();
else ctx()->comm()->sendArmPause();
}
void ArmPage::refresh()
{
const Telemetry& t = ctx()->telemetry;
if (!t.commOk) {
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, t.chassisFaults.isEmpty() ? "无" : t.chassisFaults.join(";"));
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_gasCard->setField(0, t.gasHost == "detecting" ? "检测中" : (t.gasHost == "fault" ? "故障" : "待机"));
m_gasCard->setField(1, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0));
m_armCard->setField(0, t.pos);
m_armCard->setField(1, t.pose);
m_armCard->setField(2, QString("%1").arg(t.vel, 0, 'f', 1));
for (int i = 0; i < 6 && i < t.js.size(); ++i)
m_armCard->setField(3 + i, QString("%1°").arg(t.js[i], 0, 'f', 0));
// 回位状态机
if (t.returnState != m_prevReturnState) {
m_prevReturnState = t.returnState;
if (t.returnState == "done") {
m_returnBtn->setChecked(false);
m_returnStatus->setText("机械臂系统完成回位");
m_returnStatus->setStyleSheet(QString("color:%1;font-size:22px;font-weight:700;").arg(C::SUCCESS));
m_statusTimer->start();
} else if (t.returnState == "fault") {
m_returnBtn->setChecked(false);
m_returnStatus->setText("机械臂系统回位异常");
m_returnStatus->setStyleSheet(QString("color:%1;font-size:22px;font-weight:700;").arg(C::DANGER));
} else if (t.returnState == "returning") {
m_returnStatus->setText("回位中…");
m_returnStatus->setStyleSheet(QString("color:%1;font-size:22px;font-weight:700;").arg(C::WARNING));
} else {
m_returnStatus->setText("");
}
}
}