新版布局
This commit is contained in:
+62
-40
@@ -5,7 +5,6 @@
|
||||
#include "alarmbanner.h"
|
||||
#include "datacard.h"
|
||||
#include "car.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
//#include <QGridLayout> // 末端按钮原为 2×3 网格,改为竖列后暂不需要
|
||||
@@ -82,33 +81,32 @@ ArmPage::ArmPage(AppContext* ctx, QWidget* parent)
|
||||
cardsLay->setSpacing(12);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", cardsCol);
|
||||
m_chassisCard->addField("底盘电量SOC", "85%");
|
||||
m_chassisCard->addField("底盘电压", "360V");
|
||||
m_chassisCard->addField("底盘故障", "无");
|
||||
m_chassisCard->addField("底盘电量SOC", "--");
|
||||
m_chassisCard->addField("底盘电压", "--");
|
||||
|
||||
m_washCard = new DataCard("洗消", cardsCol);
|
||||
m_washCard->addField("A组分/清水液位", "100L");
|
||||
m_washCard->addField("泡沫箱液位", "30L");
|
||||
m_washCard->addField("A组分/清水液位", "--");
|
||||
m_washCard->addField("泡沫箱液位", "--");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", cardsCol);
|
||||
m_gasCard->addField("主机状态", "点火状态");
|
||||
m_gasCard->addField("有毒物种类", "S级");
|
||||
m_gasCard->addField("有毒物等级", "二级");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
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°");
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("末端姿态", "--");
|
||||
m_armCard->addField("末端速度", "--");
|
||||
m_armCard->addField("Joint1关节角", "--");
|
||||
m_armCard->addField("Joint2关节角", "--");
|
||||
m_armCard->addField("Joint3关节角", "--");
|
||||
m_armCard->addField("Joint4关节角", "--");
|
||||
m_armCard->addField("Joint5关节角", "--");
|
||||
m_armCard->addField("Joint6关节角", "--");
|
||||
|
||||
centerLay->addWidget(cardsCol, 1);
|
||||
centerLay->addWidget(m_armCard, 1);
|
||||
@@ -151,29 +149,43 @@ ArmPage::ArmPage(AppContext* ctx, QWidget* parent)
|
||||
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(); });
|
||||
connect(ctx, &AppContext::rcDrivingChanged, this, [this](bool) {
|
||||
updateModeGating();
|
||||
// 同底盘页:让位给遥控器期间按下的轴被忽略,遥控器松手后把按着的键重发一遍。
|
||||
// 注意写 this->ctx():构造函数形参也叫 ctx,直接写 ctx() 会被它遮住
|
||||
if (this->ctx()->rcDriving()) return;
|
||||
for (QPushButton* b : m_moveButtons)
|
||||
if (b->isDown())
|
||||
this->ctx()->comm()->sendArmMove(b->property("armAxis").toString(),
|
||||
b->property("armDir").toInt());
|
||||
});
|
||||
updateModeGating();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void ArmPage::showEvent(QShowEvent* ev)
|
||||
{
|
||||
BasePage::showEvent(ev);
|
||||
// 每次进入机械臂页自动切到机械臂模式:Arm_ControlCmd1 的 Arm_modeCmd/Arm_enable
|
||||
// 挂在它上面,不切的话车端会忽略这里发出的动作指令。遥控器在线时模式由遥控器报文
|
||||
// 决定,不抢——和底盘页进页面切行车模式是同一套处理。
|
||||
if (!ctx()->rc()->linkOk())
|
||||
ctx()->setMode(Mode::Arm);
|
||||
}
|
||||
|
||||
QPushButton* ArmPage::makeMoveButton(const QString& text, const QString& axis, int dir)
|
||||
{
|
||||
QPushButton* b = button(text, "", this);
|
||||
b->setFixedSize(270, 76);
|
||||
// 记下这根按钮管哪个轴/往哪边,好让"遥控器松手后重发按住的键"找得到它
|
||||
b->setProperty("armAxis", axis);
|
||||
b->setProperty("armDir", dir);
|
||||
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]() {
|
||||
@@ -184,10 +196,11 @@ QPushButton* ArmPage::makeMoveButton(const QString& text, const QString& axis, i
|
||||
|
||||
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);
|
||||
// 和底盘页一样:现在只有**急停**会禁用动作按钮。模式不再限制(进页面即用),
|
||||
// 遥控器也不再让位——面板和遥控器可以同时操作,两边各发各的。
|
||||
const bool ok = !ctx()->estopTriggered;
|
||||
for (QPushButton* b : m_moveButtons) b->setEnabled(ok);
|
||||
for (QPushButton* b : m_endButtons) b->setEnabled(ok);
|
||||
}
|
||||
|
||||
void ArmPage::onReturnToggled(bool on)
|
||||
@@ -206,20 +219,29 @@ void ArmPage::refresh()
|
||||
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(";"));
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
|
||||
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_washCard->setField(0, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(1, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setAlarm(0, t.levelALow);
|
||||
m_washCard->setAlarm(1, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
m_gasCard->setField(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType); // 有毒物种类,未收到报文时本身就是 "--"
|
||||
m_gasCard->setField(2, t.gasLevel); // 有毒物等级
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
|
||||
// 本卡没有故障行:机械臂那四帧只有位置/姿态/关节角,协议里没有机械臂故障信号
|
||||
m_armCard->setField(0, t.pos);
|
||||
m_armCard->setField(1, t.pose);
|
||||
m_armCard->setField(2, QString("%1").arg(t.vel, 0, 'f', 1));
|
||||
m_armCard->setField(2, dashIf(t.hasArmPose, 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));
|
||||
m_armCard->setField(3 + i, dashIf(t.hasJoints, QString("%1°").arg(t.js[i], 0, 'f', 0)));
|
||||
|
||||
// 回位状态机
|
||||
if (t.returnState != m_prevReturnState) {
|
||||
|
||||
@@ -19,6 +19,9 @@ class ArmPage : public BasePage
|
||||
public:
|
||||
explicit ArmPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void updateModeGating();
|
||||
|
||||
+92
-47
@@ -12,6 +12,12 @@
|
||||
#include <QTimer>
|
||||
#include "global.h"
|
||||
|
||||
namespace {
|
||||
// 收起姿态取全零:任一关节角绝对值超过这个角度即认为机械臂已展开。
|
||||
// 单位与 CAN 下发的关节角一致(car.cpp 直接写入 t.js,未经缩放)。
|
||||
const double kArmStowedTolDeg = 5.0;
|
||||
}
|
||||
|
||||
ChassisPage::ChassisPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
@@ -63,16 +69,17 @@ ChassisPage::ChassisPage(AppContext* ctx, QWidget* parent)
|
||||
centerLay->setSpacing(12);
|
||||
|
||||
m_washCard = new DataCard("洗消", center);
|
||||
m_washCard->addField("A液位", "100L");
|
||||
m_washCard->addField("泡沫液位", "30L");
|
||||
m_washCard->addField("A组分/清水液位", "--");
|
||||
m_washCard->addField("泡沫箱液位", "--");
|
||||
|
||||
m_armCard = new DataCard("机械臂", center);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("末端姿态", "--");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", center);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
|
||||
centerLay->addWidget(m_washCard, 1);
|
||||
centerLay->addWidget(m_armCard, 1);
|
||||
@@ -88,11 +95,15 @@ ChassisPage::ChassisPage(AppContext* ctx, QWidget* parent)
|
||||
chassisLay->setSpacing(12);
|
||||
|
||||
m_chassisCard = new DataCard("底盘状态", chassisCol);
|
||||
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("故障", "无");
|
||||
m_chassisCard->addField("底盘电量SOC", "--");
|
||||
m_chassisCard->addField("底盘电压U", "--");
|
||||
m_chassisCard->addField("底盘车速V", "--");
|
||||
m_chassisCard->addField("档位状态", "--");
|
||||
m_chassisCard->addField("驻车状态", "--");
|
||||
m_chassisCard->addField("四驱模式", "--");
|
||||
m_chassisCard->addField("底盘Ready状态", "--");
|
||||
m_chassisCard->addField("方向盘角度", "--");
|
||||
m_chassisCard->addField("角度对中状态", "--");
|
||||
chassisLay->addWidget(m_chassisCard, 1);
|
||||
main->addWidget(chassisCol, 1); // 与中间卡片列平均分配中间区域
|
||||
main->addSpacing(17); // 底盘列 ↔ 车速设置列:由 32 减小到 17(底盘列整体右移 15px)
|
||||
@@ -119,7 +130,6 @@ ChassisPage::ChassisPage(AppContext* ctx, QWidget* parent)
|
||||
root->addLayout(main, 1);
|
||||
|
||||
m_statusStrip = new StatusStrip(ctx, this);
|
||||
m_statusStrip->setModeVisible(false); // 本页固定行车模式,底部不显示模式徽标
|
||||
connect(m_statusStrip, &StatusStrip::backClicked, this, [this]() { emit navigate(PageJob); });
|
||||
root->addWidget(m_statusStrip);
|
||||
|
||||
@@ -132,7 +142,14 @@ ChassisPage::ChassisPage(AppContext* ctx, QWidget* parent)
|
||||
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &ChassisPage::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(); });
|
||||
connect(ctx, &AppContext::rcDrivingChanged, this, [this](bool) {
|
||||
updateModeGating();
|
||||
// 让位给遥控器时按下的方向是被忽略掉的,遥控器一松手就重发一遍——
|
||||
// 否则手一直按着也看不出恢复,车端停在遥控器最后给的那个值上。
|
||||
// 注意写 this->ctx():构造函数形参也叫 ctx,直接写 ctx() 会被它遮住
|
||||
if (!this->ctx()->rcDriving() && !m_heldDirs.isEmpty())
|
||||
sendDirCommand();
|
||||
});
|
||||
updateModeGating();
|
||||
refresh();
|
||||
}
|
||||
@@ -155,20 +172,14 @@ QPushButton* ChassisPage::makeDirButton(const QString& text, const QString& dir)
|
||||
"{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, dir]() {
|
||||
if (ctx()->rcDriving()) {
|
||||
ConfirmDialog::toast(this, "遥控器操作中");
|
||||
return;
|
||||
}
|
||||
if (ctx()->mode != Mode::Drive) {
|
||||
ConfirmDialog::toast(this, "请切换至行车模式");
|
||||
return;
|
||||
}
|
||||
m_heldDirs.insert(dir);
|
||||
sendDirCommand();
|
||||
updateAlarm();
|
||||
});
|
||||
connect(b, &QPushButton::released, this, [this, dir]() {
|
||||
m_heldDirs.remove(dir);
|
||||
sendDirCommand();
|
||||
updateAlarm();
|
||||
});
|
||||
return b;
|
||||
}
|
||||
@@ -192,46 +203,80 @@ void ChassisPage::sendDirCommand()
|
||||
|
||||
void ChassisPage::updateModeGating()
|
||||
{
|
||||
// 非行车模式、急停触发或遥控器正在操作时禁用方向按钮(遥控器优先)
|
||||
const bool drive = (ctx()->mode == Mode::Drive) && !ctx()->estopTriggered && !ctx()->rcDriving();
|
||||
// 现在只有**急停**会禁用方向按钮:模式不再限制(进页面即用),遥控器也不再让位——
|
||||
// 面板和遥控器可以同时操作,两边各发各的,由车端处理。
|
||||
const bool ok = !ctx()->estopTriggered;
|
||||
for (QPushButton* b : m_dirButtons)
|
||||
b->setEnabled(drive);
|
||||
b->setEnabled(ok);
|
||||
|
||||
// 让位时清掉本地按住状态:按钮被禁用后不会再收到 released,否则方向会被一直挂住。
|
||||
// 让位给遥控器时不补发 stop,否则会覆盖遥控器刚下发的那条指令。
|
||||
if (!drive && !m_heldDirs.isEmpty()) {
|
||||
// 急停把按钮禁掉时清掉按住状态并补一条 stop。(Qt 禁用时会补发 released,
|
||||
// 各按钮自己那一遍已经发过了,这里只是兜底。)
|
||||
if (!ok && !m_heldDirs.isEmpty()) {
|
||||
m_heldDirs.clear();
|
||||
if (!ctx()->rcDriving())
|
||||
ctx()->comm()->sendChassisMove("stop", 0);
|
||||
ctx()->comm()->sendChassisMove("stop", 0);
|
||||
}
|
||||
|
||||
updateAlarm();
|
||||
}
|
||||
|
||||
// 任一关节角偏离收起姿态(全零)即认为机械臂已展开
|
||||
bool ChassisPage::armNotStowed() const
|
||||
{
|
||||
const QVector<double>& js = ctx()->telemetry.js;
|
||||
for (double a : js)
|
||||
if (a > kArmStowedTolDeg || a < -kArmStowedTolDeg)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 面板方向键按住,或遥控器正在推杆,都算正在驱动底盘
|
||||
bool ChassisPage::chassisDriving() const
|
||||
{
|
||||
return !m_heldDirs.isEmpty() || ctx()->rcDriving();
|
||||
}
|
||||
|
||||
// 通信异常优先;其次是“机械臂已展开且正在驱动底盘”
|
||||
void ChassisPage::updateAlarm()
|
||||
{
|
||||
if (!ctx()->telemetry.commOk)
|
||||
m_banner->showAlarm("通信异常");
|
||||
else if (armNotStowed() && chassisDriving())
|
||||
m_banner->showAlarm("机械臂未回位,请谨慎操作");
|
||||
else
|
||||
m_banner->clear();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
updateAlarm();
|
||||
|
||||
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());
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setField(2, dashIf(t.hasSpeed, QString("%1 km/h").arg(t.speed, 0, 'f', 2)));
|
||||
m_chassisCard->setField(3, dashIf(t.hasChassis4, gearLabel(t.gearPos)));
|
||||
m_chassisCard->setField(4, dashIf(t.hasChassis4, parkingLabel(t.parking)));
|
||||
m_chassisCard->setField(5, dashIf(t.hasChassis4, awdLabel(t.awdMode)));
|
||||
m_chassisCard->setField(6, dashIf(t.hasSpeed, rdyLabel(t.rdyStatus)));
|
||||
m_chassisCard->setField(7, dashIf(t.hasChassis4, QString("%1°").arg(t.steerAngle, 0, 'f', 0)));
|
||||
m_chassisCard->setField(8, dashIf(t.hasChassis4, alignLabel(t.alignStatus)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
|
||||
m_washCard->setField(0, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(1, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setAlarm(0, t.levelALow);
|
||||
m_washCard->setAlarm(1, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
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));
|
||||
m_gasCard->setField(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType); // 有毒物种类,未收到报文时本身就是 "--"
|
||||
m_gasCard->setField(2, t.gasLevel); // 有毒物等级
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
}
|
||||
|
||||
void ChassisPage::onSpeedChanged(double v)
|
||||
|
||||
+3
-1
@@ -32,6 +32,9 @@ private slots:
|
||||
private:
|
||||
QPushButton* makeDirButton(const QString& text, const QString& dir);
|
||||
void sendDirCommand(); // 按当前按住的方向组合下发(档位与转向可同时)
|
||||
void updateAlarm(); // 横幅:通信异常 / 机械臂未回位时驱动底盘
|
||||
bool armNotStowed() const; // 任一关节角偏离收起姿态(全零)
|
||||
bool chassisDriving() const; // 面板方向键按住,或遥控器正在推杆
|
||||
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
StatusStrip* m_statusStrip = nullptr;
|
||||
@@ -42,7 +45,6 @@ private:
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
ParamControl* m_speed = nullptr;
|
||||
QTimer* m_saveTimer = nullptr;
|
||||
int m_chassisFieldFault = -1;
|
||||
QVector<QPushButton*> m_dirButtons;
|
||||
QSet<QString> m_heldDirs; // 当前按住的方向,可多键同时按
|
||||
};
|
||||
|
||||
+132
-55
@@ -9,8 +9,13 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QDateTime>
|
||||
#include "global.h"
|
||||
|
||||
// 球阀“开启/关闭故障”的判据:下发指令后这么久还等不到对应到位反馈就判故障。
|
||||
// 取 8s,与④自检里球阀四项的等待时长(kValveWaitTicks 18 拍 × 450ms ≈ 8s)一致。
|
||||
static const qint64 kValveFaultMs = 8000;
|
||||
|
||||
DebugMaintainPage::DebugMaintainPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
@@ -36,8 +41,42 @@ DebugMaintainPage::DebugMaintainPage(AppContext* ctx, QWidget* parent)
|
||||
QVBoxLayout* leftLay = new QVBoxLayout(left);
|
||||
leftLay->setContentsMargins(20, 10, 10, 10);
|
||||
leftLay->setSpacing(60);
|
||||
leftLay->addWidget(makeValveGroup(1));
|
||||
leftLay->addWidget(makeValveGroup(2));
|
||||
// 故障提示作为球阀盒子的**上一条**:常占位(无故障时清空文字、留出这一条的高度),
|
||||
// 出现故障不会把下面的盒子顶下去
|
||||
auto addValve = [&](int valve) {
|
||||
QWidget* box = new QWidget(left);
|
||||
QVBoxLayout* boxLay = new QVBoxLayout(box);
|
||||
boxLay->setContentsMargins(0, 0, 0, 0);
|
||||
boxLay->setSpacing(8);
|
||||
// 不能走 label():那个辅助函数会把 color 写进行内样式,而控件自身的样式表优先级高于
|
||||
// 应用级样式表,会把 QSS 里 [alert] / [blink] 的红色和明暗闪烁一起盖掉(只剩常亮红字)。
|
||||
// 这里只给字号字重,颜色交给应用级样式表的告警规则。
|
||||
QLabel* fault = new QLabel(box);
|
||||
fault->setAlignment(Qt::AlignCenter);
|
||||
fault->setProperty("alert", true);
|
||||
fault->setStyleSheet("font-size:28px;font-weight:700;");
|
||||
fault->setFixedHeight(40); // 常占位:把这一条的高度钉死
|
||||
boxLay->addWidget(fault);
|
||||
boxLay->addWidget(makeValveGroup(valve));
|
||||
leftLay->addWidget(box);
|
||||
return fault;
|
||||
};
|
||||
m_valveFault1 = addValve(1);
|
||||
m_valveFault2 = addValve(2);
|
||||
|
||||
// 三个泵的上电指示灯放在 D2 球阀下方的空位里,纵向居中排列。
|
||||
// 指示规则不变(上电=绿、否则灭),仍由 refresh() 按车端反馈刷新。
|
||||
auto addPumpLed = [&](const QString& text) {
|
||||
LedIndicator* led = new LedIndicator(text, left);
|
||||
led->setTextPixelSize(40);
|
||||
// 圆点在前(LedIndicator 的默认):竖排下来像一张状态清单
|
||||
leftLay->addWidget(led, 0, Qt::AlignHCenter);
|
||||
return led;
|
||||
};
|
||||
m_ledPlunger = addPumpLed("柱塞泵");
|
||||
m_ledAir = addPumpLed("空压机");
|
||||
m_ledLeaf = addPumpLed("叶片泵");
|
||||
|
||||
leftLay->addStretch(1);
|
||||
left->setFixedWidth(300);
|
||||
main->addWidget(left);
|
||||
@@ -54,27 +93,33 @@ DebugMaintainPage::DebugMaintainPage(AppContext* ctx, QWidget* parent)
|
||||
centerLeftLay->setSpacing(10);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", centerLeft);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("底盘电量SOC", "--");
|
||||
m_chassisCard->addField("底盘电压U", "--");
|
||||
m_armCard = new DataCard("机械臂", centerLeft);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
// 这一列只有 474px,位置行在 42px 下约 530px,单独把数值字号收到 34(约 463px)
|
||||
m_armCard->setValueFontPx(34);
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("末端姿态", "--");
|
||||
m_gasCard = new DataCard("气体检测仪", centerLeft);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
centerLeftLay->addWidget(m_chassisCard, 1);
|
||||
centerLeftLay->addWidget(m_armCard, 1);
|
||||
centerLeftLay->addWidget(m_gasCard, 1);
|
||||
centerLay->addWidget(centerLeft, 1);
|
||||
|
||||
// 与消除/消毒作业页的洗消卡同一套字段(本页只多了右侧的泵控制)
|
||||
m_washCard = new DataCard("洗消参数", center);
|
||||
m_washCard->addField("洗消模式", "--");
|
||||
m_washCard->addField("A组分液位", "100L");
|
||||
m_washCard->addField("A药剂流量", "0 L/min");
|
||||
m_washCard->addField("管路压力", "0 MPa");
|
||||
m_washCard->addField("发泡倍数", "0");
|
||||
m_washCard->addField("空压机故障代码", "");
|
||||
m_washCard->addField("柱塞泵故障代码", "");
|
||||
m_washCard->addField("A组分液位", "--");
|
||||
m_washCard->addField("B组分液位", "--");
|
||||
m_washCard->addField("A药剂流量", "--");
|
||||
m_washCard->addField("B药剂流量", "--");
|
||||
m_washCard->addField("气体流量", "--");
|
||||
m_washCard->addField("管路压力", "--");
|
||||
m_washCard->addField("空压机压力", "--");
|
||||
m_washCard->addField("发泡倍数", "--");
|
||||
centerLay->addWidget(m_washCard, 1);
|
||||
|
||||
main->addWidget(center, 1);
|
||||
@@ -101,8 +146,6 @@ DebugMaintainPage::DebugMaintainPage(AppContext* ctx, QWidget* parent)
|
||||
bottom->setStyleSheet(QString("QWidget#DebugBottom{background-color:%1;border-top:1px solid %2;}")
|
||||
.arg(C::PANEL, C::BORDER));
|
||||
QHBoxLayout* bottomLay = new QHBoxLayout(bottom);
|
||||
// 左起 364 = 36(页面边距) + 300(球阀列) + 28(列间距),与中央“底盘”列左边缘对齐
|
||||
bottomLay->setContentsMargins(364, 0, 24, 0);
|
||||
bottomLay->setSpacing(48);
|
||||
auto makeLed = [&](const QString& text) {
|
||||
LedIndicator* led = new LedIndicator(text, bottom);
|
||||
@@ -111,10 +154,14 @@ DebugMaintainPage::DebugMaintainPage(AppContext* ctx, QWidget* parent)
|
||||
bottomLay->addWidget(led);
|
||||
return led;
|
||||
};
|
||||
m_ledPlunger = makeLed("柱塞泵");
|
||||
m_ledAir = makeLed("空压机");
|
||||
m_ledLeaf = makeLed("叶片泵");
|
||||
m_ledLevel = makeLed("液位低");
|
||||
// 三个泵的指示灯已经搬进各自的泵组里(放在按钮行左侧),底栏只留两路液位低
|
||||
m_ledLevelA = makeLed("A组分/清水液位低");
|
||||
m_ledLevelB = makeLed("B组分液位低");
|
||||
// 这两盏灯居中于中央卡片区:卡片区 x ∈ [364, 1436](36 页边距 + 300 球阀列 + 28 列间距
|
||||
// 起,宽 1072),中点 900。用 sizeHint 量出两盏灯连间距的实际宽度再定位,改字号也不会偏。
|
||||
const int levelW = m_ledLevelA->sizeHint().width() + bottomLay->spacing()
|
||||
+ m_ledLevelB->sizeHint().width();
|
||||
bottomLay->setContentsMargins((364 + 1436) / 2 - levelW / 2, 0, 24, 0);
|
||||
bottomLay->addStretch(1);
|
||||
QPushButton* back = button("返回", "", bottom);
|
||||
back->setFixedSize(160, 76);
|
||||
@@ -159,7 +206,7 @@ QWidget* DebugMaintainPage::makeValveGroup(int valve)
|
||||
QLabel* title = label(QString("D%1 球阀").arg(valve), 30, C::TEXT, true, grp);
|
||||
lay->addWidget(title);
|
||||
|
||||
// 控制开/关(toggle):文字固定,底色表示状态——未选中=关闭(红)、选中=开启(绿)
|
||||
// 控制开/关(toggle):文字固定,底色表示状态——未选中=关闭(灰)、选中=开启(绿)
|
||||
QPushButton* ctrl = button(QString("D%1 控制").arg(valve), "", grp);
|
||||
ctrl->setCheckable(true);
|
||||
ctrl->setFixedSize(180, 64); // 窄按钮,与泵组的“上电”一致
|
||||
@@ -167,16 +214,19 @@ QWidget* DebugMaintainPage::makeValveGroup(int valve)
|
||||
ctrl->setStyleSheet(QString("QPushButton{background-color:%1;color:#FFFFFF;border:none;}"
|
||||
"QPushButton:checked{background-color:%2;color:#FFFFFF;border:none;}"
|
||||
"QPushButton:focus{border:4px solid #8ABFFF;}")
|
||||
.arg(C::DANGER).arg(C::SUCCESS));
|
||||
.arg(C::DISABLED).arg(C::SUCCESS));
|
||||
connect(ctrl, &QPushButton::toggled, this, [this, valve](bool on) {
|
||||
ctx()->comm()->sendBallValve(valve, on);
|
||||
// 记下这一拍想要哪个阀位并开始等反馈;超时还没到位就在 refresh() 里判故障
|
||||
if (valve == 1) { m_v1WantOpen = on ? 1 : 0; m_v1CmdMs = QDateTime::currentMSecsSinceEpoch(); m_v1Fault = false; }
|
||||
else { m_v2WantOpen = on ? 1 : 0; m_v2CmdMs = QDateTime::currentMSecsSinceEpoch(); m_v2Fault = false; }
|
||||
});
|
||||
lay->addWidget(ctrl, 0, Qt::AlignHCenter);
|
||||
|
||||
// 开启到位 / 关闭到位(同一行,居中)。未收到车端遥测时保持缺省显示
|
||||
// (开启绿 / 关闭红),首次收到遥测后由 refresh() 按实际到位状态刷新
|
||||
// 开启到位 / 关闭到位(同一行,居中)。缺省状态按“阀体默认关着”给:
|
||||
// 开启到位灭(灰)、关闭到位红。首次收到遥测后由 refresh() 按实际到位状态刷新
|
||||
LedIndicator* open = new LedIndicator("开启", grp);
|
||||
open->setState(LedIndicator::Green);
|
||||
open->setState(LedIndicator::Off);
|
||||
LedIndicator* close = new LedIndicator("关闭", grp);
|
||||
close->setState(LedIndicator::Red);
|
||||
QHBoxLayout* ioRow = new QHBoxLayout();
|
||||
@@ -187,23 +237,16 @@ QWidget* DebugMaintainPage::makeValveGroup(int valve)
|
||||
ioRow->addStretch(1);
|
||||
lay->addLayout(ioRow);
|
||||
|
||||
// 故障提示
|
||||
QLabel* fault = label("", 16, C::DANGER, true, grp);
|
||||
fault->setAlignment(Qt::AlignCenter);
|
||||
fault->setProperty("alert", true);
|
||||
fault->hide();
|
||||
lay->addWidget(fault);
|
||||
// 故障提示不在盒子里——它是盒子**上方**独立的一条,由构造函数的 addValve 建好
|
||||
|
||||
if (valve == 1) {
|
||||
m_valve1Btn = ctrl;
|
||||
m_v1Open = open;
|
||||
m_v1Close = close;
|
||||
m_valveFault1 = fault;
|
||||
} else {
|
||||
m_valve2Btn = ctrl;
|
||||
m_v2Open = open;
|
||||
m_v2Close = close;
|
||||
m_valveFault2 = fault;
|
||||
}
|
||||
return grp;
|
||||
}
|
||||
@@ -257,6 +300,7 @@ QWidget* DebugMaintainPage::makePumpGroup(const QString& pump, const QString& na
|
||||
start->setChecked(false);
|
||||
ctx()->comm()->sendPumpPower(pump, false);
|
||||
});
|
||||
// 圆点+名称不在这里了——三个泵的上电指示灯已经搬到左列 D2 球阀下方(见构造函数)
|
||||
QHBoxLayout* pwrRow = new QHBoxLayout();
|
||||
pwrRow->setSpacing(12);
|
||||
pwrRow->addStretch(1);
|
||||
@@ -265,9 +309,9 @@ QWidget* DebugMaintainPage::makePumpGroup(const QString& pump, const QString& na
|
||||
pwrRow->addStretch(1);
|
||||
lay->addLayout(pwrRow);
|
||||
|
||||
if (pump == "air") { m_airRpm = rpm; m_airStart = start; m_airStop = stop; }
|
||||
if (pump == "air") { m_airRpm = rpm; m_airStart = start; m_airStop = stop; }
|
||||
else if (pump == "plunger") { m_plungerRpm = rpm; m_plungerStart = start; m_plungerStop = stop; }
|
||||
else { m_leafRpm = rpm; m_leafStart = start; m_leafStop = stop; }
|
||||
else { m_leafRpm = rpm; m_leafStart = start; m_leafStop = stop; }
|
||||
return grp;
|
||||
}
|
||||
|
||||
@@ -275,22 +319,32 @@ void DebugMaintainPage::refresh()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
|
||||
m_chassisCard->setField(0, QString("%1%").arg(t.soc));
|
||||
m_chassisCard->setField(1, QString("%1V").arg(t.volt, 0, 'f', 0));
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
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));
|
||||
m_gasCard->setField(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType); // 有毒物种类,未收到报文时本身就是 "--"
|
||||
m_gasCard->setField(2, t.gasLevel); // 有毒物等级
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
|
||||
m_washCard->setField(0, t.washMode);
|
||||
m_washCard->setField(1, QString("%1L").arg(t.levelA, 0, 'f', 0));
|
||||
m_washCard->setField(2, QString("%1 L/min").arg(t.flowA, 0, 'f', 1));
|
||||
m_washCard->setField(3, QString("%1 MPa").arg(t.press, 0, 'f', 2));
|
||||
m_washCard->setField(4, QString("%1").arg(t.foam, 0, 'f', 1));
|
||||
m_washCard->setField(5, t.pumpFaults.value(0));
|
||||
m_washCard->setField(6, t.pumpFaults.value(0));
|
||||
m_washCard->setField(1, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(2, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setField(3, dashIf(t.hasWash, QString("%1 L/min").arg(t.flowA, 0, 'f', 1)));
|
||||
m_washCard->setField(4, dashIf(t.hasWash, QString("%1 L/min").arg(t.flowB, 0, 'f', 1)));
|
||||
m_washCard->setField(5, dashIf(t.hasWash, QString("%1 L/min").arg(t.gasFlow, 0, 'f', 1)));
|
||||
m_washCard->setField(6, dashIf(t.hasWashPress, QString("%1 MPa").arg(t.press, 0, 'f', 2)));
|
||||
m_washCard->setField(7, dashIf(t.hasWashPress, QString("%1 MPa").arg(t.airPress, 0, 'f', 2)));
|
||||
m_washCard->setField(8, dashIf(t.hasWash, QString("%1").arg(t.foam, 0, 'f', 1)));
|
||||
m_washCard->setAlarm(1, t.levelALow);
|
||||
m_washCard->setAlarm(2, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
// 球阀到位反馈:未收到过车端遥测时不动,保留构造时的缺省显示(开启绿 / 关闭红)
|
||||
// 球阀到位反馈:未收到过车端遥测时不动,保留构造时的缺省显示(开启灰 / 关闭红)
|
||||
if (m_telemetrySeen) {
|
||||
m_v1Open->setState(t.d1open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_v1Close->setState(t.d1close ? LedIndicator::Red : LedIndicator::Off);
|
||||
@@ -298,22 +352,45 @@ void DebugMaintainPage::refresh()
|
||||
m_v2Close->setState(t.d2close ? LedIndicator::Red : LedIndicator::Off);
|
||||
}
|
||||
|
||||
// 球阀故障提示
|
||||
bool v1Fault = t.valveFault.contains("D1");
|
||||
bool v2Fault = t.valveFault.contains("D2");
|
||||
if (v1Fault) { m_valveFault1->setText(t.valveFault); m_valveFault1->show(); startBlink(m_valveFault1, "blink"); }
|
||||
else { stopBlink(m_valveFault1, "blink"); m_valveFault1->hide(); }
|
||||
if (v2Fault) { m_valveFault2->setText(t.valveFault); m_valveFault2->show(); startBlink(m_valveFault2, "blink"); }
|
||||
else { stopBlink(m_valveFault2, "blink"); m_valveFault2->hide(); }
|
||||
// 球阀故障:协议里没有“D1开启故障/关闭故障”这种信号,只能自己推——
|
||||
// 指令下发后 kValveFaultMs 内 MBV_Dn_Sta 还没变成想要的样子,就判该方向故障;
|
||||
// 反馈一旦对上(到位)就解除。cmdMs 为 0 表示没有待确认的指令,不判。
|
||||
const qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
auto judgeValve = [&](qint64& cmdMs, int wantOpen, bool fbOpen, bool& faulted,
|
||||
QLabel* lab, int valve) {
|
||||
if (cmdMs != 0) {
|
||||
if (fbOpen == (wantOpen != 0)) { // 到位:解除等待
|
||||
cmdMs = 0;
|
||||
faulted = false;
|
||||
} else if (now - cmdMs > kValveFaultMs) {
|
||||
faulted = true;
|
||||
}
|
||||
}
|
||||
// 这一条是**常占位**的:只切文字与闪烁,不动显隐,免得盒子上下跳。
|
||||
// "当前有没有报故障"就看文字空不空——省掉再存一个状态。
|
||||
if (faulted != !lab->text().isEmpty())
|
||||
return;
|
||||
if (faulted) {
|
||||
lab->setText(QString("D%1球阀 %2 故障").arg(valve).arg(wantOpen ? "开启" : "关闭"));
|
||||
startBlink(lab, "blink");
|
||||
} else {
|
||||
stopBlink(lab, "blink");
|
||||
lab->clear();
|
||||
}
|
||||
};
|
||||
judgeValve(m_v1CmdMs, m_v1WantOpen, t.d1open, m_v1Fault, m_valveFault1, 1);
|
||||
judgeValve(m_v2CmdMs, m_v2WantOpen, t.d2open, m_v2Fault, m_valveFault2, 2);
|
||||
|
||||
// 泵启停状态同步(互斥组内先置“启动”再置“停止”,两者互补,结果总是一选中一未选)
|
||||
if (m_airStart) { m_airStart->setChecked(t.air); m_airStop->setChecked(!t.air); }
|
||||
if (m_plungerStart){ m_plungerStart->setChecked(t.plunger); m_plungerStop->setChecked(!t.plunger); }
|
||||
if (m_leafStart) { m_leafStart->setChecked(t.leaf); m_leafStop->setChecked(!t.leaf); }
|
||||
|
||||
// 底部指示灯
|
||||
// 底部指示灯:泵的三个已经挪进泵组,这里只剩两路液位低。
|
||||
// 用协议位(A_LiquidLevel_Low / B_LiquidLevel_Low),不按 levelA<10 猜阈值
|
||||
m_ledPlunger->setState(t.plunger ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledAir->setState(t.air ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLeaf->setState(t.leaf ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLevel->setState(t.levelA < 10 ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
m_ledLevelA->setState(t.levelALow ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
m_ledLevelB->setState(t.levelBLow ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
}
|
||||
|
||||
+15
-3
@@ -58,15 +58,27 @@ private:
|
||||
QPushButton* m_leafStart = nullptr;
|
||||
QPushButton* m_leafStop = nullptr;
|
||||
|
||||
// 底部指示灯
|
||||
// 泵上电指示灯(在各自泵组的按钮行左侧,由 makePumpGroup 填)+ 底部的两路液位低
|
||||
LedIndicator* m_ledPlunger = nullptr;
|
||||
LedIndicator* m_ledAir = nullptr;
|
||||
LedIndicator* m_ledLeaf = nullptr;
|
||||
LedIndicator* m_ledLevel = nullptr;
|
||||
LedIndicator* m_ledLevelA = nullptr; // A组分/清水液位低
|
||||
LedIndicator* m_ledLevelB = nullptr; // B组分液位低
|
||||
|
||||
// 是否收到过车端遥测:未收到时球阀到位灯保持“开启=绿、关闭=红”的缺省显示
|
||||
// 是否收到过车端遥测:未收到时球阀到位灯保持“开启=灰、关闭=红”的缺省显示
|
||||
bool m_telemetrySeen = false;
|
||||
|
||||
// 球阀“开启/关闭故障”是**超时未到位**推出来的——协议里没有这两个故障信号,
|
||||
// 球阀下行只有 MBV_Dn_Sta 一个到位位(关闭到位由它取反得到)。
|
||||
// 下发指令时记下时刻与想要的阀位;kValveFaultMs 内反馈还没变成想要的样子就判故障,
|
||||
// 反馈对上(到位)或重新下发指令则解除。cmdMs 为 0 表示当前没有待确认的指令。
|
||||
qint64 m_v1CmdMs = 0;
|
||||
qint64 m_v2CmdMs = 0;
|
||||
int m_v1WantOpen = 0; // 最后一次指令想要的阀位:0=关闭 1=开启
|
||||
int m_v2WantOpen = 0;
|
||||
bool m_v1Fault = false;
|
||||
bool m_v2Fault = false;
|
||||
|
||||
// “返回”去向:本页有两个入口(系统首页 / 作业选择),由 MainWindow 记录来源后原路返回
|
||||
PageId m_returnPage = PageJob;
|
||||
};
|
||||
|
||||
+30
-25
@@ -25,36 +25,36 @@ GasDetectorPage::GasDetectorPage(AppContext* ctx, QWidget* parent)
|
||||
main->setContentsMargins(36, 16, 36, 16);
|
||||
main->setSpacing(32);
|
||||
|
||||
// 中央卡片区(2×2)
|
||||
// 中央卡片区:左列 底盘/洗消/机械臂 纵向排列,右列由气体检测仪独占并跨满三行
|
||||
QWidget* center = new QWidget(this);
|
||||
QGridLayout* grid = new QGridLayout(center);
|
||||
grid->setSpacing(20);
|
||||
grid->setContentsMargins(20, 0, 20, 0);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
m_chassisCard->addField("电量", "--");
|
||||
m_chassisCard->addField("电压", "--");
|
||||
|
||||
m_washCard = new DataCard("洗消", center);
|
||||
m_washCard->addField("A液位", "100L");
|
||||
m_washCard->addField("泡沫液位", "30L");
|
||||
m_washCard->addField("A液位", "--");
|
||||
m_washCard->addField("泡沫液位", "--");
|
||||
|
||||
m_armCard = new DataCard("机械臂", center);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("姿态", "--");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", center);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
m_gasConcField = m_gasCard->addField("浓度", "0 ppb", true);
|
||||
m_gasConcField = m_gasCard->addField("浓度", "--", true);
|
||||
m_gasCard->setHighlight(true); // 主色描边突出
|
||||
|
||||
grid->addWidget(m_chassisCard, 0, 0);
|
||||
grid->addWidget(m_washCard, 0, 1);
|
||||
grid->addWidget(m_gasCard, 1, 0);
|
||||
grid->addWidget(m_armCard, 1, 1);
|
||||
grid->addWidget(m_washCard, 1, 0);
|
||||
grid->addWidget(m_armCard, 2, 0);
|
||||
// 本页主角,独占右列:跨 3 行填满
|
||||
grid->addWidget(m_gasCard, 0, 1, 3, 1);
|
||||
main->addWidget(center, 1);
|
||||
|
||||
// 右侧按钮列(垂直居中 + 平均分布)
|
||||
@@ -90,7 +90,6 @@ GasDetectorPage::GasDetectorPage(AppContext* ctx, QWidget* parent)
|
||||
root->addLayout(main, 1);
|
||||
|
||||
m_statusStrip = new StatusStrip(ctx, this);
|
||||
m_statusStrip->setModeVisible(false); // 本页不显示模式徽标
|
||||
connect(m_statusStrip, &StatusStrip::backClicked, this, [this]() {
|
||||
if (this->ctx()->gasRunning) {
|
||||
if (ConfirmDialog::confirm(this, "提示", "检测仍在进行,是否退出?"))
|
||||
@@ -117,24 +116,30 @@ void GasDetectorPage::refresh()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
|
||||
// 同步运行状态(以遥测为准)
|
||||
if (t.gasHost == "detecting") ctx()->gasRunning = true;
|
||||
else if (t.gasHost == "standby") ctx()->gasRunning = false;
|
||||
// 同步运行状态(以遥测为准):未收到报文(gasHost 为空)时按未运行处理
|
||||
ctx()->gasRunning = (t.gasHost == "detecting");
|
||||
updateGating();
|
||||
|
||||
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(";"));
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
|
||||
m_washCard->setField(0, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(1, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setAlarm(0, t.levelALow);
|
||||
m_washCard->setAlarm(1, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
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(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType);
|
||||
m_gasCard->setField(2, t.gasLevel);
|
||||
m_gasCard->setField(m_gasConcField, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0));
|
||||
// 超阈值红闪
|
||||
m_gasCard->setField(m_gasConcField, dashIf(t.hasGasConc, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0)));
|
||||
// 主机状态为“故障”与浓度超限是两回事,各自独立闪
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setAlarm(m_gasConcField, t.gasConc > 500.0);
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
}
|
||||
|
||||
+33
-23
@@ -60,21 +60,23 @@ HomePage::HomePage(AppContext* ctx, QWidget* parent)
|
||||
grid->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
m_chassisCard->addField("底盘电量SOC", "--");
|
||||
m_chassisCard->addField("底盘电压U", "--");
|
||||
|
||||
m_armCard = new DataCard("机械臂", center);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
// 位置/姿态两行的数值最长约 554px,超出 2×2 卡片的内容区 522px,单独把字号收小一号
|
||||
m_armCard->setValueFontPx(38);
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("末端姿态", "--");
|
||||
|
||||
m_washCard = new DataCard("洗消", center);
|
||||
m_washCard->addField("A液位", "100L");
|
||||
m_washCard->addField("泡沫液位", "30L");
|
||||
m_washCard->addField("A组分/清水液位", "--");
|
||||
m_washCard->addField("泡沫箱液位", "--");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", center);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
|
||||
// 左列 底盘/机械臂,右列 洗消/气体检测仪
|
||||
grid->addWidget(m_chassisCard, 0, 0);
|
||||
@@ -113,8 +115,7 @@ HomePage::HomePage(AppContext* ctx, QWidget* parent)
|
||||
root->addLayout(main, 1);
|
||||
|
||||
m_statusStrip = new StatusStrip(ctx, this);
|
||||
m_statusStrip->setBackVisible(false);
|
||||
m_statusStrip->setModeVisible(false); // 本页不显示模式徽标(菜单已移到左右两列)
|
||||
m_statusStrip->setBackVisible(false); // 本页「返回」在右侧按钮列,底条不重复放
|
||||
root->addWidget(m_statusStrip);
|
||||
|
||||
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &HomePage::refresh);
|
||||
@@ -124,26 +125,35 @@ HomePage::HomePage(AppContext* ctx, QWidget* parent)
|
||||
void HomePage::refresh()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
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_chassisCard->setAlarm(2, !t.chassisFaults.isEmpty());
|
||||
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
|
||||
m_armCard->setField(0, t.pos);
|
||||
m_armCard->setField(1, t.pose);
|
||||
// 机械臂卡没有故障行:协议里机械臂那四帧只有位置/姿态/关节角,没有故障信号
|
||||
|
||||
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_washCard->setField(0, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(1, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setAlarm(0, t.levelALow);
|
||||
m_washCard->setAlarm(1, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
m_gasCard->setField(0, t.gasHost == "detecting" ? "检测中" : (t.gasHost == "fault" ? "故障" : "待机"));
|
||||
m_gasCard->setField(1, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0));
|
||||
m_gasCard->setAlarm(1, t.gasConc > 500.0);
|
||||
m_gasCard->setField(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType); // 有毒物种类,未收到报文时本身就是 "--"
|
||||
m_gasCard->setField(2, t.gasLevel); // 有毒物等级
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
}
|
||||
|
||||
void HomePage::onHelp()
|
||||
{
|
||||
// 正文按 2+1 断句。字号 34 时最长一行 918px,正文区 944px,不会被 wordWrap 二次折行
|
||||
ConfirmDialog::message(this, "操作说明",
|
||||
"系统自检将依次检测:通信系统、底盘通信、机械臂通信、洗消通信、"
|
||||
"传感器信号、清水液位及电动球阀 D1/D2 的开启与关闭。\n"
|
||||
"全部正常后方可进入作业选择界面。");
|
||||
"自检过程主要检测各分系统通信、传感器信号数据是否正常、\n"
|
||||
"电动球阀D1~D2是否能正常开启和关闭。\n"
|
||||
"自检正常或人为点击“忽略故障”,则进入“系统首页面”。",
|
||||
1000, 34);
|
||||
}
|
||||
|
||||
+28
-20
@@ -61,21 +61,23 @@ JobSelectPage::JobSelectPage(AppContext* ctx, QWidget* parent)
|
||||
grid->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
m_chassisCard->addField("底盘电量SOC", "--");
|
||||
m_chassisCard->addField("底盘电压U", "--");
|
||||
|
||||
m_armCard = new DataCard("机械臂", center);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
// 位置/姿态两行的数值最长约 554px,超出 2×2 卡片的内容区 522px,单独把字号收小一号
|
||||
m_armCard->setValueFontPx(38);
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("末端姿态", "--");
|
||||
|
||||
m_washCard = new DataCard("洗消", center);
|
||||
m_washCard->addField("A液位", "100L");
|
||||
m_washCard->addField("泡沫液位", "30L");
|
||||
m_washCard->addField("A组分/清水液位", "--");
|
||||
m_washCard->addField("泡沫箱液位", "--");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", center);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
|
||||
// 左列 底盘/机械臂,右列 洗消/气体检测仪
|
||||
grid->addWidget(m_chassisCard, 0, 0);
|
||||
@@ -116,8 +118,7 @@ JobSelectPage::JobSelectPage(AppContext* ctx, QWidget* parent)
|
||||
root->addLayout(main, 1);
|
||||
|
||||
m_statusStrip = new StatusStrip(ctx, this);
|
||||
m_statusStrip->setBackVisible(false);
|
||||
m_statusStrip->setModeVisible(false); // 本页不显示模式徽标(返回按钮已移到右侧列)
|
||||
m_statusStrip->setBackVisible(false); // 本页「返回」在右侧按钮列,底条不重复放
|
||||
root->addWidget(m_statusStrip);
|
||||
|
||||
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &JobSelectPage::refresh);
|
||||
@@ -127,18 +128,25 @@ JobSelectPage::JobSelectPage(AppContext* ctx, QWidget* parent)
|
||||
void JobSelectPage::refresh()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
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_chassisCard->setAlarm(2, !t.chassisFaults.isEmpty());
|
||||
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
|
||||
m_armCard->setField(0, t.pos);
|
||||
m_armCard->setField(1, t.pose);
|
||||
// 机械臂卡没有故障行:协议里机械臂那四帧只有位置/姿态/关节角,没有故障信号
|
||||
|
||||
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_washCard->setField(0, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(1, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setAlarm(0, t.levelALow);
|
||||
m_washCard->setAlarm(1, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
m_gasCard->setField(0, t.gasHost == "detecting" ? "检测中" : (t.gasHost == "fault" ? "故障" : "待机"));
|
||||
m_gasCard->setField(1, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0));
|
||||
m_gasCard->setAlarm(1, t.gasConc > 500.0);
|
||||
m_gasCard->setField(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType); // 有毒物种类,未收到报文时本身就是 "--"
|
||||
m_gasCard->setField(2, t.gasLevel); // 有毒物等级
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
}
|
||||
|
||||
+44
-27
@@ -68,39 +68,45 @@ SelfCheckPage::SelfCheckPage(AppContext* ctx, QWidget* parent)
|
||||
centerLay->addWidget(m_status);
|
||||
centerLay->addSpacing(16);
|
||||
|
||||
// 10 个自检项,两列
|
||||
// 14 个自检项,两列各 7 行
|
||||
const int rows = kSelfCheckItemCount / 2;
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
grid->setSpacing(8);
|
||||
const QString names[10] = {
|
||||
"通信系统", "底盘通信", "机械臂通信", "洗消通信", "传感器信号",
|
||||
"清水液位", "球阀D1开启", "球阀D1关闭", "球阀D2开启", "球阀D2关闭"
|
||||
const QString names[kSelfCheckItemCount] = {
|
||||
// All_Comm_FaultCode:0=通信正常,1~8=各分系统通信异常
|
||||
"通信系统", "底盘电机通信", "底盘BMS通信", "机械臂通信", "空压机通信",
|
||||
"柱塞泵通信", "叶片泵通信", "气体检测仪通信", "遥控器通信",
|
||||
// A_LiquidLevel_Low + B_LiquidLevel_Low
|
||||
"清水液位",
|
||||
// MBV_D1_Sta / MBV_D2_Sta
|
||||
"球阀D1开启", "球阀D1关闭", "球阀D2开启", "球阀D2关闭"
|
||||
};
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
for (int i = 0; i < kSelfCheckItemCount; ++i) {
|
||||
LedIndicator* led = new LedIndicator(names[i], center);
|
||||
led->setFixedHeight(80);
|
||||
led->setRadius(26);
|
||||
led->setTextPixelSize(60);
|
||||
QLabel* res = label("", 50, C::TEXT_SUB, false, center);
|
||||
res->setFixedWidth(200);
|
||||
led->setFixedHeight(68);
|
||||
led->setRadius(22);
|
||||
led->setTextPixelSize(44); // 两列各 7 行,字号收窄以保证最长项“气体检测仪通信”不被裁切
|
||||
QLabel* res = label("", 44, C::TEXT_SUB, false, center);
|
||||
res->setFixedWidth(160);
|
||||
QWidget* row = new QWidget(center);
|
||||
QHBoxLayout* rowLay = new QHBoxLayout(row);
|
||||
rowLay->setContentsMargins(0, 0, 0, 0);
|
||||
rowLay->addWidget(led, 1);
|
||||
rowLay->addWidget(res);
|
||||
grid->addWidget(row, i % 5, i / 5);
|
||||
grid->addWidget(row, i % rows, i / rows);
|
||||
m_leds.append(led);
|
||||
m_results.append(res);
|
||||
}
|
||||
resetItems(); // 初始化 10 个自检项
|
||||
resetItems(); // 初始化 14 个自检项
|
||||
centerLay->addLayout(grid);
|
||||
|
||||
centerLay->addSpacing(50); // 文字与进度条之间的间隙
|
||||
centerLay->addSpacing(40); // 文字与进度条之间的间隙
|
||||
|
||||
// 进度条(位于屏幕约 3/4 高度处,文字全部在其上方)
|
||||
m_progress = new QProgressBar(center);
|
||||
m_progress->setRange(0, 100);
|
||||
m_progress->setValue(0);
|
||||
m_progress->setFixedHeight(60);
|
||||
m_progress->setFixedHeight(56);
|
||||
centerLay->addWidget(m_progress);
|
||||
|
||||
centerLay->addStretch(3); // 下方留白(更大)→ 进度条落在约 3/4 处
|
||||
@@ -130,6 +136,7 @@ SelfCheckPage::SelfCheckPage(AppContext* ctx, QWidget* parent)
|
||||
root->addLayout(main, 1);
|
||||
|
||||
// 连接通信信号
|
||||
connect(ctx->comm(), &CarCommunication::selfCheckItemBusy, this, &SelfCheckPage::onSelfCheckItemBusy);
|
||||
connect(ctx->comm(), &CarCommunication::selfCheckItem, this, &SelfCheckPage::onSelfCheckItem);
|
||||
connect(ctx->comm(), &CarCommunication::selfCheckDone, this, &SelfCheckPage::onSelfCheckDone);
|
||||
}
|
||||
@@ -150,13 +157,13 @@ void SelfCheckPage::showEvent(QShowEvent* ev)
|
||||
});
|
||||
}
|
||||
|
||||
// 复位 10 个自检项:指示灯置灰、结果“待检测”
|
||||
// 复位 14 个自检项:指示灯置灰、结果“待检测”
|
||||
void SelfCheckPage::resetItems()
|
||||
{
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
for (int i = 0; i < kSelfCheckItemCount; ++i) {
|
||||
m_leds[i]->setState(LedIndicator::Off);
|
||||
m_results[i]->setText("待检测");
|
||||
m_results[i]->setStyleSheet(QString("color:%1;font-size:60px;").arg(C::TEXT_SUB));
|
||||
m_results[i]->setStyleSheet(QString("color:%1;font-size:44px;").arg(C::TEXT_SUB));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,22 +184,31 @@ void SelfCheckPage::resetAll()
|
||||
m_autoStarted = false;
|
||||
}
|
||||
|
||||
// 单项开始执行(球阀项要下发指令并等车端反馈,可能持续数秒):先亮黄灯、文字置“检测中”
|
||||
void SelfCheckPage::onSelfCheckItemBusy(int index)
|
||||
{
|
||||
if (index < 0 || index >= kSelfCheckItemCount)
|
||||
return;
|
||||
m_leds[index]->setState(LedIndicator::Yellow);
|
||||
m_results[index]->setText("检测中");
|
||||
m_results[index]->setStyleSheet(QString("color:%1;font-size:44px;").arg(C::TEXT_SUB));
|
||||
}
|
||||
|
||||
void SelfCheckPage::onSelfCheckItem(int index, bool ok)
|
||||
{
|
||||
if (index < 0 || index >= 10)
|
||||
if (index < 0 || index >= kSelfCheckItemCount)
|
||||
return;
|
||||
m_leds[index]->setState(LedIndicator::Yellow); // 检测中黄闪 → 结果
|
||||
// 结果态
|
||||
if (ok) {
|
||||
m_leds[index]->setState(LedIndicator::Green);
|
||||
m_results[index]->setText("正常");
|
||||
m_results[index]->setStyleSheet(QString("color:%1;font-size:60px;").arg(C::SUCCESS));
|
||||
m_results[index]->setStyleSheet(QString("color:%1;font-size:44px;").arg(C::SUCCESS));
|
||||
} else {
|
||||
m_leds[index]->setState(LedIndicator::RedBlink);
|
||||
m_results[index]->setText("异常");
|
||||
m_results[index]->setStyleSheet(QString("color:%1;font-size:60px;font-weight:700;").arg(C::DANGER));
|
||||
m_results[index]->setText("故障");
|
||||
m_results[index]->setStyleSheet(QString("color:%1;font-size:44px;font-weight:700;").arg(C::DANGER));
|
||||
}
|
||||
m_progress->setValue((index + 1) * 10);
|
||||
m_progress->setValue((index + 1) * 100 / kSelfCheckItemCount);
|
||||
}
|
||||
|
||||
void SelfCheckPage::onSelfCheckDone(bool allOk)
|
||||
@@ -224,7 +240,6 @@ void SelfCheckPage::onSelfCheckDone(bool allOk)
|
||||
|
||||
void SelfCheckPage::onIgnoreFault()
|
||||
{
|
||||
// emit navigate(PageJob); // 原:直接进作业选择页
|
||||
emit navigate(PageHome); // 忽略故障后进入系统首页面
|
||||
}
|
||||
|
||||
@@ -235,8 +250,10 @@ void SelfCheckPage::onJobSelect()
|
||||
|
||||
void SelfCheckPage::onHelp()
|
||||
{
|
||||
// 与系统首页面的「操作说明」同一份文案(2+1 行)
|
||||
ConfirmDialog::message(this, "操作说明",
|
||||
"系统自检将依次检测:通信系统、底盘通信、机械臂通信、洗消通信、"
|
||||
"传感器信号、清水液位及电动球阀 D1/D2 的开启与关闭。\n"
|
||||
"全部正常后方可进入作业选择界面。");
|
||||
"自检过程主要检测各分系统通信、传感器信号数据是否正常、\n"
|
||||
"电动球阀D1~D2是否能正常开启和关闭。\n"
|
||||
"自检正常或人为点击“忽略故障”,则进入“系统首页面”。",
|
||||
1000, 34);
|
||||
}
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ protected:
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void onSelfCheckItemBusy(int index);
|
||||
void onSelfCheckItem(int index, bool ok);
|
||||
void onSelfCheckDone(bool allOk);
|
||||
void onIgnoreFault();
|
||||
@@ -31,7 +32,7 @@ private slots:
|
||||
void onHelp();
|
||||
|
||||
private:
|
||||
void resetItems(); // 复位 10 个自检项(指示灯置灰、结果“待检测”)
|
||||
void resetItems(); // 复位 14 个自检项(指示灯置灰、结果“待检测”)
|
||||
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
QLabel* m_status = nullptr;
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
#include "washbase.h"
|
||||
#include "titlebar.h"
|
||||
#include "alarmbanner.h"
|
||||
#include "datacard.h"
|
||||
#include "ledindicator.h"
|
||||
#include "car.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include "global.h"
|
||||
|
||||
WashBasePage::WashBasePage(AppContext* ctx, const QString& title, const QString& washTitle,
|
||||
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(title);
|
||||
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(28);
|
||||
|
||||
// 左侧状态卡(底盘/机械臂/气体检测仪)
|
||||
QWidget* left = new QWidget(this);
|
||||
QVBoxLayout* leftLay = new QVBoxLayout(left);
|
||||
leftLay->setContentsMargins(0, 0, 0, 0);
|
||||
leftLay->setSpacing(10);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", left);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
|
||||
m_armCard = new DataCard("机械臂", left);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", left);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
|
||||
leftLay->addWidget(m_chassisCard, 1);
|
||||
leftLay->addWidget(m_armCard, 1);
|
||||
leftLay->addWidget(m_gasCard, 1);
|
||||
left->setFixedWidth(280);
|
||||
main->addWidget(left);
|
||||
|
||||
// 中央洗消卡
|
||||
m_washCard = new DataCard(washTitle, this);
|
||||
m_fMode = m_washCard->addField("洗消模式", "--");
|
||||
m_fLevelA = m_washCard->addField("A组分液位", "100L");
|
||||
m_fLevelFoam = m_washCard->addField("泡沫箱液位", "30L");
|
||||
m_fFlowA = m_washCard->addField("A药剂流量", "0 L/min");
|
||||
m_fFlowB = m_washCard->addField("B药剂流量", "0 L/min");
|
||||
m_fGasFlow = m_washCard->addField("气体流量", "0 L/min");
|
||||
m_fPress = m_washCard->addField("管路压力", "0 MPa");
|
||||
m_fAirPress = m_washCard->addField("空压机压力", "0 MPa");
|
||||
m_fFoam = m_washCard->addField("发泡倍数", "0");
|
||||
m_fAirFault = m_washCard->addField("空压机故障代码", "");
|
||||
m_fPlungerFault = m_washCard->addField("柱塞泵故障代码", "");
|
||||
m_washCard->indentRows(); // 行文字缩进到中间偏左(卡片很宽,贴左缘会显得空)
|
||||
main->addWidget(m_washCard, 1);
|
||||
|
||||
// 右侧操作列
|
||||
QWidget* right = new QWidget(this);
|
||||
QVBoxLayout* rightLay = new QVBoxLayout(right);
|
||||
rightLay->setContentsMargins(0, 0, 0, 0);
|
||||
rightLay->setSpacing(10);
|
||||
|
||||
m_start = button("启动", "primary", right);
|
||||
m_start->setFixedSize(354, 112);
|
||||
connect(m_start, &QPushButton::clicked, this, &WashBasePage::onStart);
|
||||
|
||||
m_stop = button("停止", "danger", right);
|
||||
m_stop->setFixedSize(354, 112);
|
||||
connect(m_stop, &QPushButton::clicked, this, &WashBasePage::onStop);
|
||||
|
||||
rightLay->addWidget(m_start);
|
||||
rightLay->addWidget(m_stop);
|
||||
|
||||
// 参数区(子类填充)
|
||||
m_paramsLayout = new QVBoxLayout();
|
||||
m_paramsLayout->setSpacing(8);
|
||||
rightLay->addLayout(m_paramsLayout);
|
||||
rightLay->addStretch(1);
|
||||
|
||||
right->setFixedWidth(380);
|
||||
main->addWidget(right);
|
||||
|
||||
root->addLayout(main, 1);
|
||||
|
||||
// 底部指示灯条
|
||||
QWidget* bottom = new QWidget(this);
|
||||
bottom->setFixedHeight(94);
|
||||
bottom->setStyleSheet(QString("background-color:%1;border-top:1px solid %2;")
|
||||
.arg(C::PANEL, C::BORDER));
|
||||
QHBoxLayout* bottomLay = new QHBoxLayout(bottom);
|
||||
bottomLay->setContentsMargins(24, 0, 24, 0);
|
||||
bottomLay->setSpacing(18);
|
||||
|
||||
m_factory = button("恢复出厂值", "", bottom);
|
||||
m_factory->setFixedSize(220, 64);
|
||||
m_factory->setStyleSheet(cancelButtonStyle(40)); // 选中(悬停/按下/焦点)红底白字
|
||||
connect(m_factory, &QPushButton::clicked, this, &WashBasePage::onFactory);
|
||||
bottomLay->addWidget(m_factory);
|
||||
bottomLay->addSpacing(20);
|
||||
|
||||
m_ledRun = new LedIndicator("启停", bottom);
|
||||
m_ledPlunger = new LedIndicator("柱塞泵", bottom);
|
||||
m_ledAir = new LedIndicator("空压机", bottom);
|
||||
m_ledLeaf = new LedIndicator("叶片泵", bottom);
|
||||
m_ledLevel = new LedIndicator("液位低", bottom);
|
||||
m_ledD1 = new LedIndicator("D1", bottom);
|
||||
m_ledD2 = new LedIndicator("D2", bottom);
|
||||
m_ledFault = new LedIndicator("系统故障", bottom);
|
||||
|
||||
bottomLay->addWidget(m_ledRun);
|
||||
bottomLay->addWidget(m_ledPlunger);
|
||||
bottomLay->addWidget(m_ledAir);
|
||||
bottomLay->addWidget(m_ledLeaf);
|
||||
bottomLay->addWidget(m_ledLevel);
|
||||
bottomLay->addWidget(m_ledD1);
|
||||
bottomLay->addWidget(m_ledD2);
|
||||
bottomLay->addWidget(m_ledFault);
|
||||
bottomLay->addStretch(1);
|
||||
|
||||
m_back = button("返回", "", bottom);
|
||||
m_back->setFixedSize(144, 76);
|
||||
m_back->setStyleSheet(cancelButtonStyle(40)); // 选中(悬停/按下/焦点)红底白字
|
||||
connect(m_back, &QPushButton::clicked, this, &WashBasePage::onBack);
|
||||
bottomLay->addWidget(m_back);
|
||||
|
||||
root->addWidget(bottom);
|
||||
|
||||
// 故障代码轮流循环显示(2s/条)
|
||||
m_faultTimer = new QTimer(this);
|
||||
m_faultTimer->setInterval(2000);
|
||||
connect(m_faultTimer, &QTimer::timeout, this, &WashBasePage::rotateFaults);
|
||||
m_faultTimer->start();
|
||||
|
||||
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &WashBasePage::refresh);
|
||||
updateGating();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void WashBasePage::onStart()
|
||||
{
|
||||
ctx()->comm()->sendWashStart(washModeTag());
|
||||
ctx()->washRunning = true;
|
||||
updateGating();
|
||||
onWashStateChanged(true);
|
||||
}
|
||||
|
||||
void WashBasePage::onStop()
|
||||
{
|
||||
ctx()->comm()->sendWashStop();
|
||||
ctx()->washRunning = false;
|
||||
updateGating();
|
||||
onWashStateChanged(false);
|
||||
}
|
||||
|
||||
void WashBasePage::onBack()
|
||||
{
|
||||
if (ctx()->washRunning) {
|
||||
ConfirmDialog::toast(this, "请先停止");
|
||||
return;
|
||||
}
|
||||
emit navigate(PageJob);
|
||||
}
|
||||
|
||||
void WashBasePage::onFactory()
|
||||
{
|
||||
if (ConfirmDialog::confirm(this, "恢复出厂值", "确定将参数恢复为出厂值?"))
|
||||
resetFactory();
|
||||
}
|
||||
|
||||
void WashBasePage::updateGating()
|
||||
{
|
||||
bool running = ctx()->washRunning;
|
||||
m_start->setEnabled(!running);
|
||||
m_stop->setEnabled(running);
|
||||
m_back->setEnabled(!running);
|
||||
}
|
||||
|
||||
void WashBasePage::refresh()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
|
||||
if (!t.commOk) m_banner->showAlarm("通信异常");
|
||||
else m_banner->clear();
|
||||
|
||||
// 以泵状态同步运行状态
|
||||
if (t.plunger || t.air || t.leaf) ctx()->washRunning = true;
|
||||
else if (t.washMode == "--") ctx()->washRunning = false;
|
||||
updateGating();
|
||||
|
||||
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_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));
|
||||
|
||||
m_washCard->setField(m_fMode, t.washMode);
|
||||
m_washCard->setField(m_fLevelA, QString("%1L").arg(t.levelA, 0, 'f', 0));
|
||||
m_washCard->setField(m_fLevelFoam, QString("%1L").arg(t.levelFoam, 0, 'f', 0));
|
||||
m_washCard->setField(m_fFlowA, QString("%1 L/min").arg(t.flowA, 0, 'f', 1));
|
||||
m_washCard->setField(m_fFlowB, QString("%1 L/min").arg(t.flowB, 0, 'f', 1));
|
||||
m_washCard->setField(m_fGasFlow, QString("%1 L/min").arg(t.gasFlow, 0, 'f', 1));
|
||||
m_washCard->setField(m_fPress, QString("%1 MPa").arg(t.press, 0, 'f', 2));
|
||||
m_washCard->setField(m_fAirPress, QString("%1 MPa").arg(t.airPress, 0, 'f', 2));
|
||||
m_washCard->setField(m_fFoam, QString("%1").arg(t.foam, 0, 'f', 1));
|
||||
|
||||
// 指示灯
|
||||
m_ledRun->setState(ctx()->washRunning ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledPlunger->setState(t.plunger ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledAir->setState(t.air ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLeaf->setState(t.leaf ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLevel->setState(t.levelA < 10 ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
m_ledD1->setState(t.d1open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledD2->setState(t.d2open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledFault->setState((!t.washFaults.isEmpty() || !t.pumpFaults.isEmpty())
|
||||
? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
|
||||
// 故障代码显示(静态更新,循环由 rotateFaults 驱动)
|
||||
rotateFaults();
|
||||
}
|
||||
|
||||
void WashBasePage::rotateFaults()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
QStringList all = t.washFaults + t.pumpFaults;
|
||||
if (all.isEmpty()) {
|
||||
m_washCard->setField(m_fAirFault, "");
|
||||
m_washCard->setField(m_fPlungerFault, "");
|
||||
return;
|
||||
}
|
||||
m_faultIndex = m_faultIndex % all.size();
|
||||
QString code = all[m_faultIndex];
|
||||
m_washCard->setField(m_fAirFault, code);
|
||||
m_washCard->setField(m_fPlungerFault, code);
|
||||
m_faultIndex = (m_faultIndex + 1) % all.size();
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifndef WASHBASEPAGE_H
|
||||
#define WASHBASEPAGE_H
|
||||
|
||||
#include "base.h"
|
||||
#include <QVector>
|
||||
|
||||
class TitleBar;
|
||||
class AlarmBanner;
|
||||
class DataCard;
|
||||
class LedIndicator;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
class QTimer;
|
||||
|
||||
// ===================== 洗消页面基类(⑨消除作业 / ⑩消毒作业 共用) =====================
|
||||
// 左侧状态卡 + 中央洗消参数卡 + 右侧启动/停止与参数 + 底部指示灯条
|
||||
class WashBasePage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// title:页面标题;washTitle:中央洗消卡标题
|
||||
WashBasePage(AppContext* ctx, const QString& title, const QString& washTitle,
|
||||
QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
// 子类向右侧参数区添加参数控件
|
||||
QVBoxLayout* paramsLayout() const { return m_paramsLayout; }
|
||||
|
||||
// 启动/停止状态变化通知
|
||||
virtual void onWashStateChanged(bool running) { Q_UNUSED(running); }
|
||||
|
||||
// 恢复出厂值(子类实现)
|
||||
virtual void resetFactory() = 0;
|
||||
|
||||
// 洗消模式标识:"clean" / "disinfect"
|
||||
virtual QString washModeTag() const = 0;
|
||||
|
||||
private slots:
|
||||
void onStart();
|
||||
void onStop();
|
||||
void onBack();
|
||||
void onFactory();
|
||||
void refresh();
|
||||
void rotateFaults();
|
||||
|
||||
private:
|
||||
void updateGating();
|
||||
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
AlarmBanner* m_banner = nullptr;
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
DataCard* m_armCard = nullptr;
|
||||
DataCard* m_gasCard = nullptr;
|
||||
DataCard* m_washCard = nullptr;
|
||||
QPushButton* m_start = nullptr;
|
||||
QPushButton* m_stop = nullptr;
|
||||
QPushButton* m_back = nullptr;
|
||||
QPushButton* m_factory = nullptr;
|
||||
QVBoxLayout* m_paramsLayout = nullptr;
|
||||
|
||||
// 底部指示灯
|
||||
LedIndicator* m_ledRun = nullptr;
|
||||
LedIndicator* m_ledPlunger = nullptr;
|
||||
LedIndicator* m_ledAir = nullptr;
|
||||
LedIndicator* m_ledLeaf = nullptr;
|
||||
LedIndicator* m_ledLevel = nullptr;
|
||||
LedIndicator* m_ledD1 = nullptr;
|
||||
LedIndicator* m_ledD2 = nullptr;
|
||||
LedIndicator* m_ledFault = nullptr;
|
||||
|
||||
// 中央洗消卡字段索引
|
||||
int m_fMode = -1, m_fLevelA = -1, m_fLevelFoam = -1;
|
||||
int m_fFlowA = -1, m_fFlowB = -1, m_fGasFlow = -1;
|
||||
int m_fPress = -1, m_fAirPress = -1, m_fFoam = -1;
|
||||
int m_fAirFault = -1, m_fPlungerFault = -1;
|
||||
|
||||
QTimer* m_faultTimer = nullptr;
|
||||
int m_faultIndex = 0;
|
||||
};
|
||||
|
||||
#endif // WASHBASEPAGE_H
|
||||
+86
-83
@@ -10,7 +10,6 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include "global.h"
|
||||
|
||||
WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
@@ -40,17 +39,17 @@ WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
leftLay->setSpacing(10);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", left);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
m_chassisCard->addField("底盘电量SOC", "--");
|
||||
m_chassisCard->addField("底盘电压U", "--");
|
||||
|
||||
m_armCard = new DataCard("机械臂", left);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
m_armCard->addField("末端位置", "--");
|
||||
m_armCard->addField("末端姿态", "--");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", left);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
m_gasCard->addField("主机状态", "--");
|
||||
m_gasCard->addField("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
|
||||
leftLay->addWidget(m_chassisCard, 1);
|
||||
leftLay->addWidget(m_armCard, 1);
|
||||
@@ -62,16 +61,14 @@ WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
// 中央洗消卡
|
||||
m_washCard = new DataCard("洗消", this);
|
||||
m_fMode = m_washCard->addField("洗消模式", "--");
|
||||
m_fLevelA = m_washCard->addField("A组分液位", "100L");
|
||||
m_fLevelFoam = m_washCard->addField("泡沫箱液位", "30L");
|
||||
m_fFlowA = m_washCard->addField("A药剂流量", "0 L/min");
|
||||
m_fFlowB = m_washCard->addField("B药剂流量", "0 L/min");
|
||||
m_fGasFlow = m_washCard->addField("气体流量", "0 L/min");
|
||||
m_fPress = m_washCard->addField("管路压力", "0 MPa");
|
||||
m_fAirPress = m_washCard->addField("空压机压力", "0 MPa");
|
||||
m_fFoam = m_washCard->addField("发泡倍数", "0");
|
||||
m_fAirFault = m_washCard->addField("空压机故障代码", "");
|
||||
m_fPlungerFault = m_washCard->addField("柱塞泵故障代码", "");
|
||||
m_fLevelA = m_washCard->addField("A组分液位", "--");
|
||||
m_fLevelFoam = m_washCard->addField("B组分液位", "--");
|
||||
m_fFlowA = m_washCard->addField("A药剂流量", "--");
|
||||
m_fFlowB = m_washCard->addField("B药剂流量", "--");
|
||||
m_fGasFlow = m_washCard->addField("气体流量", "--");
|
||||
m_fPress = m_washCard->addField("管路压力", "--");
|
||||
m_fAirPress = m_washCard->addField("空压机压力", "--");
|
||||
m_fFoam = m_washCard->addField("发泡倍数", "--");
|
||||
main->addSpacing(60);
|
||||
main->addWidget(m_washCard, 1);
|
||||
|
||||
@@ -148,10 +145,11 @@ WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
|
||||
root->addLayout(main, 1);
|
||||
|
||||
// 底部条:左右两个按钮纵向占满两行,中间上下两排(泵数值 / 指示灯)居中
|
||||
// 底部条:左右两个按钮纵向占满,中间三排(泵数值 / 指示灯 / 两路液位低)居中。
|
||||
// 高度由 160 加到 188:中间那列三排约 164px 再加 8+8 的上下边距,160 装不下第三排
|
||||
QWidget* bottom = new QWidget(this);
|
||||
bottom->setObjectName("WashBottom");
|
||||
bottom->setFixedHeight(160);
|
||||
bottom->setFixedHeight(188);
|
||||
// 用 #WashBottom 限定选择器:不带选择器的样式表会连子控件一起套上边框和底色
|
||||
bottom->setStyleSheet(QString("QWidget#WashBottom{background-color:%1;border-top:1px solid %2;}")
|
||||
.arg(C::PANEL, C::BORDER));
|
||||
@@ -166,7 +164,7 @@ WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
connect(m_factory, &QPushButton::clicked, this, &WashPage::onFactory);
|
||||
bottomRoot->addWidget(m_factory, 0, Qt::AlignVCenter);
|
||||
|
||||
// 中:上排三泵数值、下排指示灯,两排都水平居中
|
||||
// 中:上排三泵数值、中排指示灯、下排两路液位低,三排都水平居中
|
||||
QVBoxLayout* midLay = new QVBoxLayout();
|
||||
midLay->setSpacing(6);
|
||||
|
||||
@@ -186,27 +184,36 @@ WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
valueLay->addStretch(1);
|
||||
|
||||
QHBoxLayout* ledLay = new QHBoxLayout();
|
||||
ledLay->setSpacing(10); // 8 个灯 40px 字号后总宽约 1300px
|
||||
ledLay->setSpacing(10);
|
||||
ledLay->addStretch(1);
|
||||
auto makeLed = [&](const QString& text) {
|
||||
// 灯要分两行放,故把目标行当参数传进来
|
||||
auto makeLed = [&](QHBoxLayout* row, const QString& text) {
|
||||
LedIndicator* led = new LedIndicator(text, bottom);
|
||||
led->setTextPixelSize(40); // 字号与卡片标题一致
|
||||
ledLay->addWidget(led);
|
||||
row->addWidget(led);
|
||||
return led;
|
||||
};
|
||||
m_ledRun = makeLed("启停");
|
||||
m_ledPlunger = makeLed("柱塞泵");
|
||||
m_ledAir = makeLed("空压机");
|
||||
m_ledLeaf = makeLed("叶片泵");
|
||||
m_ledLevel = makeLed("液位低");
|
||||
m_ledD1 = makeLed("D1");
|
||||
m_ledD2 = makeLed("D2");
|
||||
m_ledFault = makeLed("系统故障");
|
||||
m_ledRun = makeLed(ledLay, "启停");
|
||||
m_ledPlunger = makeLed(ledLay, "柱塞泵");
|
||||
m_ledAir = makeLed(ledLay, "空压机");
|
||||
m_ledLeaf = makeLed(ledLay, "叶片泵");
|
||||
m_ledD1 = makeLed(ledLay, "D1");
|
||||
m_ledD2 = makeLed(ledLay, "D2");
|
||||
ledLay->addStretch(1);
|
||||
|
||||
// 下一行:液位低拆成 A / B 两路。原来那颗笼统的「液位低」和「系统故障」都撤了——
|
||||
// 后者在洗消卡片的故障行里已经有了,底部再挂一颗是重复的
|
||||
QHBoxLayout* levelLay = new QHBoxLayout();
|
||||
levelLay->setSpacing(40);
|
||||
levelLay->addStretch(1);
|
||||
m_ledLevelA = makeLed(levelLay, "A组分/清水液位低");
|
||||
m_ledLevelB = makeLed(levelLay, "B组分液位低");
|
||||
levelLay->addStretch(1);
|
||||
|
||||
midLay->addStretch(1);
|
||||
midLay->addLayout(valueLay);
|
||||
midLay->addLayout(ledLay);
|
||||
midLay->addLayout(levelLay);
|
||||
midLay->addStretch(1);
|
||||
bottomRoot->addLayout(midLay, 1);
|
||||
|
||||
@@ -219,12 +226,6 @@ WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
|
||||
|
||||
root->addWidget(bottom);
|
||||
|
||||
// 故障代码轮流循环显示(2s/条)
|
||||
m_faultTimer = new QTimer(this);
|
||||
m_faultTimer->setInterval(2000);
|
||||
connect(m_faultTimer, &QTimer::timeout, this, &WashPage::rotateFaults);
|
||||
m_faultTimer->start();
|
||||
|
||||
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &WashPage::refresh);
|
||||
updateGating();
|
||||
refresh();
|
||||
@@ -243,26 +244,30 @@ void WashPage::refreshPumpValues()
|
||||
// 否则旧配置里超范围的历史值会与控件显示、实际上报的数值对不上
|
||||
m_valPlunger->setText(QString("%1 L/min").arg(qBound(0.0, ctx()->getDouble(ctx()->keyPumpFlow(), 20), 24.0), 0, 'f', 1));
|
||||
m_valAir->setText(QString("%1 L/min").arg(qBound(0.0, ctx()->getDouble(ctx()->keyAirFlow(), 100), 340.0), 0, 'f', 1));
|
||||
m_valLeaf->setText(QString("%1 L/min").arg(ctx()->telemetry.flowB, 0, 'f', 1));
|
||||
m_valLeaf->setText(dashIf(ctx()->telemetry.hasWash, QString("%1 L/min").arg(ctx()->telemetry.flowB, 0, 'f', 1)));
|
||||
}
|
||||
|
||||
void WashPage::onStart()
|
||||
{
|
||||
ctx()->comm()->sendWashStart(washModeTag());
|
||||
ctx()->washRunning = true;
|
||||
// 同时把机器强制到机械臂模式(见 AppContext::setWashRunning),否则 Arm_enable=0,
|
||||
// 末端那条俯仰摆动指令车端不会理
|
||||
updateGating();
|
||||
// 启停灯立刻跟着变,不等下一帧遥测——否则车端不回话时按钮看着像没反应
|
||||
updateLeds();
|
||||
}
|
||||
|
||||
void WashPage::onStop()
|
||||
{
|
||||
ctx()->comm()->sendWashStop();
|
||||
ctx()->washRunning = false;
|
||||
ctx()->setWashRunning(false);
|
||||
updateGating();
|
||||
updateLeds();
|
||||
}
|
||||
|
||||
void WashPage::onBack()
|
||||
{
|
||||
if (ctx()->washRunning) {
|
||||
if (ctx()->isWashRunning()) {
|
||||
ConfirmDialog::toast(this, "请先停止");
|
||||
return;
|
||||
}
|
||||
@@ -298,7 +303,7 @@ void WashPage::resetFactory()
|
||||
|
||||
void WashPage::updateGating()
|
||||
{
|
||||
bool running = ctx()->washRunning;
|
||||
bool running = ctx()->isWashRunning();
|
||||
m_start->setEnabled(!running);
|
||||
m_stop->setEnabled(running);
|
||||
m_back->setEnabled(!running);
|
||||
@@ -312,58 +317,56 @@ void WashPage::refresh()
|
||||
else m_banner->clear();
|
||||
|
||||
// 以泵状态同步运行状态
|
||||
if (t.plunger || t.air || t.leaf) ctx()->washRunning = true;
|
||||
else if (t.washMode == "--") ctx()->washRunning = false;
|
||||
if (t.plunger || t.air || t.leaf) ctx()->setWashRunning(true);
|
||||
else if (t.washMode == "--") ctx()->setWashRunning(false);
|
||||
updateGating();
|
||||
|
||||
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(";"));
|
||||
// 未收到对应报文的数值统一显示 "--"(dashIf)
|
||||
m_chassisCard->setField(0, dashIf(t.hasBattery, QString("%1%").arg(t.soc)));
|
||||
m_chassisCard->setField(1, dashIf(t.hasBattery, QString("%1V").arg(t.volt, 0, 'f', 0)));
|
||||
m_chassisCard->setFaults(t.chassisFaults);
|
||||
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));
|
||||
m_gasCard->setField(0, gasHostLabel(t));
|
||||
m_gasCard->setField(1, t.gasType); // 有毒物种类,未收到报文时本身就是 "--"
|
||||
m_gasCard->setField(2, t.gasLevel); // 有毒物等级
|
||||
m_gasCard->setAlarm(0, t.gasHost == "fault");
|
||||
m_gasCard->setFaults(t.detectorFaults);
|
||||
|
||||
m_washCard->setField(m_fMode, t.washMode);
|
||||
m_washCard->setField(m_fLevelA, QString("%1L").arg(t.levelA, 0, 'f', 0));
|
||||
m_washCard->setField(m_fLevelFoam, QString("%1L").arg(t.levelFoam, 0, 'f', 0));
|
||||
m_washCard->setField(m_fFlowA, QString("%1 L/min").arg(t.flowA, 0, 'f', 1));
|
||||
m_washCard->setField(m_fFlowB, QString("%1 L/min").arg(t.flowB, 0, 'f', 1));
|
||||
m_washCard->setField(m_fGasFlow, QString("%1 L/min").arg(t.gasFlow, 0, 'f', 1));
|
||||
m_washCard->setField(m_fPress, QString("%1 MPa").arg(t.press, 0, 'f', 2));
|
||||
m_washCard->setField(m_fAirPress, QString("%1 MPa").arg(t.airPress, 0, 'f', 2));
|
||||
m_washCard->setField(m_fFoam, QString("%1").arg(t.foam, 0, 'f', 1));
|
||||
m_washCard->setField(m_fLevelA, dashIf(t.hasWash, QString("%1L").arg(t.levelA, 0, 'f', 0)));
|
||||
m_washCard->setField(m_fLevelFoam, dashIf(t.hasWash, QString("%1L").arg(t.levelFoam, 0, 'f', 0)));
|
||||
m_washCard->setField(m_fFlowA, dashIf(t.hasWash, QString("%1 L/min").arg(t.flowA, 0, 'f', 1)));
|
||||
m_washCard->setField(m_fFlowB, dashIf(t.hasWash, QString("%1 L/min").arg(t.flowB, 0, 'f', 1)));
|
||||
m_washCard->setField(m_fGasFlow, dashIf(t.hasWash, QString("%1 L/min").arg(t.gasFlow, 0, 'f', 1)));
|
||||
m_washCard->setField(m_fPress, dashIf(t.hasWashPress, QString("%1 MPa").arg(t.press, 0, 'f', 2)));
|
||||
m_washCard->setField(m_fAirPress, dashIf(t.hasWashPress, QString("%1 MPa").arg(t.airPress, 0, 'f', 2)));
|
||||
m_washCard->setField(m_fFoam, dashIf(t.hasWash, QString("%1").arg(t.foam, 0, 'f', 1)));
|
||||
m_washCard->setAlarm(m_fLevelA, t.levelALow);
|
||||
m_washCard->setAlarm(m_fLevelFoam, t.levelBLow);
|
||||
m_washCard->setFaults(washCardFaults(t));
|
||||
|
||||
// 指示灯
|
||||
m_ledRun->setState(ctx()->washRunning ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledPlunger->setState(t.plunger ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledAir->setState(t.air ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLeaf->setState(t.leaf ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLevel->setState(t.levelA < 10 ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
m_ledD1->setState(t.d1open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledD2->setState(t.d2open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledFault->setState((!t.washFaults.isEmpty() || !t.pumpFaults.isEmpty())
|
||||
? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
updateLeds();
|
||||
|
||||
// 底部三个泵的数值
|
||||
refreshPumpValues();
|
||||
|
||||
// 故障代码显示(静态更新,循环由 rotateFaults 驱动)
|
||||
rotateFaults();
|
||||
}
|
||||
|
||||
void WashPage::rotateFaults()
|
||||
// 底栏指示灯。单独拆出来是为了让 onStart/onStop 能直接调它:
|
||||
// 遥测还没回来时 refresh() 里的“以泵状态同步运行状态”会把刚置上的 washRunning 又抹掉,
|
||||
// 所以启停的即时反馈不能靠整个 refresh()。
|
||||
void WashPage::updateLeds()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
QStringList all = t.washFaults + t.pumpFaults;
|
||||
if (all.isEmpty()) {
|
||||
m_washCard->setField(m_fAirFault, "");
|
||||
m_washCard->setField(m_fPlungerFault, "");
|
||||
return;
|
||||
}
|
||||
m_faultIndex = m_faultIndex % all.size();
|
||||
QString code = all[m_faultIndex];
|
||||
m_washCard->setField(m_fAirFault, code);
|
||||
m_washCard->setField(m_fPlungerFault, code);
|
||||
m_faultIndex = (m_faultIndex + 1) % all.size();
|
||||
|
||||
m_ledRun->setState(ctx()->isWashRunning() ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledPlunger->setState(t.plunger ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledAir->setState(t.air ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledLeaf->setState(t.leaf ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledD1->setState(t.d1open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_ledD2->setState(t.d2open ? LedIndicator::Green : LedIndicator::Off);
|
||||
// 液位低拆成 A / B 两路,各用各自的协议位(A_LiquidLevel_Low / B_LiquidLevel_Low);
|
||||
// 不再按 levelA<10 猜阈值,免得卡片液位行红闪、这颗灯却不亮
|
||||
m_ledLevelA->setState(t.levelALow ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
m_ledLevelB->setState(t.levelBLow ? LedIndicator::RedBlink : LedIndicator::Off);
|
||||
}
|
||||
|
||||
+6
-9
@@ -11,7 +11,6 @@ class ParamControl;
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
class QVBoxLayout;
|
||||
class QTimer;
|
||||
|
||||
// ===================== 洗消页(⑨消除作业 / ⑩消毒作业 共用) =====================
|
||||
// 自包含页面:三列骨架、中央洗消卡、右侧启停与参数区、底部指示灯条都在本文件里。
|
||||
@@ -30,10 +29,10 @@ private slots:
|
||||
void onBack();
|
||||
void onFactory();
|
||||
void refresh();
|
||||
void rotateFaults();
|
||||
|
||||
private:
|
||||
void updateGating();
|
||||
void updateLeds(); // 底栏指示灯(启停 / 泵 / 液位低 / 球阀 / 系统故障)
|
||||
void resetFactory();
|
||||
void refreshPumpValues(); // 刷新底部三个泵的数值
|
||||
QString washModeTag() const; // "clean" / "disinfect"
|
||||
@@ -58,15 +57,17 @@ private:
|
||||
ParamControl* m_foamPercent = nullptr;
|
||||
ParamControl* m_airFlow = nullptr;
|
||||
|
||||
// 底部指示灯
|
||||
// 底部指示灯(第一行)
|
||||
LedIndicator* m_ledRun = nullptr;
|
||||
LedIndicator* m_ledPlunger = nullptr;
|
||||
LedIndicator* m_ledAir = nullptr;
|
||||
LedIndicator* m_ledLeaf = nullptr;
|
||||
LedIndicator* m_ledLevel = nullptr;
|
||||
LedIndicator* m_ledD1 = nullptr;
|
||||
LedIndicator* m_ledD2 = nullptr;
|
||||
LedIndicator* m_ledFault = nullptr;
|
||||
|
||||
// 底部第二行:两路液位低(协议里 A_LiquidLevel_Low / B_LiquidLevel_Low 本就是两个位)
|
||||
LedIndicator* m_ledLevelA = nullptr;
|
||||
LedIndicator* m_ledLevelB = nullptr;
|
||||
|
||||
// 底部第二行:三个泵的当前流量值
|
||||
QLabel* m_valPlunger = nullptr;
|
||||
@@ -77,10 +78,6 @@ private:
|
||||
int m_fMode = -1, m_fLevelA = -1, m_fLevelFoam = -1;
|
||||
int m_fFlowA = -1, m_fFlowB = -1, m_fGasFlow = -1;
|
||||
int m_fPress = -1, m_fAirPress = -1, m_fFoam = -1;
|
||||
int m_fAirFault = -1, m_fPlungerFault = -1;
|
||||
|
||||
QTimer* m_faultTimer = nullptr;
|
||||
int m_faultIndex = 0;
|
||||
};
|
||||
|
||||
#endif // WASHPAGE_H
|
||||
|
||||
Reference in New Issue
Block a user