feat:初始化wuhan_QT界面代码
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
#include "armpage.h"
|
||||
#include "titlebar.h"
|
||||
#include "statusstrip.h"
|
||||
#include "alarmbanner.h"
|
||||
#include "datacard.h"
|
||||
#include "udpcommunication.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#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(0, 0, 0, 0);
|
||||
leftLay->setSpacing(8);
|
||||
|
||||
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("系统回位/暂停", "", left);
|
||||
m_returnBtn->setCheckable(true);
|
||||
m_returnBtn->setFixedSize(180, 90);
|
||||
m_returnBtn->setStyleSheet(QString("font-size:22px;background-color:%1;color:%2;border:1px solid %3;border-radius:12px;")
|
||||
.arg(C::PANEL_HI, C::TEXT, C::BORDER));
|
||||
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(180);
|
||||
main->addWidget(left);
|
||||
|
||||
// 中央卡片区(底盘/洗消/气体检测仪)
|
||||
QWidget* center = new QWidget(this);
|
||||
QVBoxLayout* centerLay = new QVBoxLayout(center);
|
||||
centerLay->setContentsMargins(0, 0, 0, 0);
|
||||
centerLay->setSpacing(12);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
|
||||
m_washCard = new DataCard("洗消", center);
|
||||
m_washCard->addField("A液位", "100L");
|
||||
m_washCard->addField("泡沫液位", "30L");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", center);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
|
||||
centerLay->addWidget(m_chassisCard, 1);
|
||||
centerLay->addWidget(m_washCard, 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_armCard = new DataCard("机械臂参数", right);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
m_armCard->addField("速度", "0");
|
||||
m_armCard->addField("J1", "30°");
|
||||
m_armCard->addField("J2", "20°");
|
||||
m_armCard->addField("J3", "0°");
|
||||
m_armCard->addField("J4", "90°");
|
||||
m_armCard->addField("J5", "45°");
|
||||
m_armCard->addField("J6", "0°");
|
||||
rightLay->addWidget(m_armCard, 1);
|
||||
|
||||
// 末端 6 姿态按钮(2 列 × 3 行)
|
||||
QGridLayout* endGrid = new QGridLayout();
|
||||
endGrid->setSpacing(8);
|
||||
auto addEnd = [&](const QString& t, const QString& axis, int dir, int r, int c) {
|
||||
QPushButton* b = makeMoveButton(t, axis, dir);
|
||||
b->setFixedHeight(80);
|
||||
m_endButtons << b;
|
||||
endGrid->addWidget(b, r, c);
|
||||
};
|
||||
addEnd("末端仰", "pitch", +1, 0, 0);
|
||||
addEnd("末端俯", "pitch", -1, 0, 1);
|
||||
addEnd("末端正偏航", "yaw", +1, 1, 0);
|
||||
addEnd("末端负偏航", "yaw", -1, 1, 1);
|
||||
addEnd("末端左横滚", "roll", -1, 2, 0);
|
||||
addEnd("末端右横滚", "roll", +1, 2, 1);
|
||||
rightLay->addLayout(endGrid);
|
||||
|
||||
right->setFixedWidth(430);
|
||||
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(), &UdpCommunication::telemetryUpdated, this, &ArmPage::refresh);
|
||||
connect(ctx, &AppContext::modeChanged, this, [this](Mode) { updateModeGating(); });
|
||||
connect(ctx, &AppContext::estopChanged, this, [this](bool) { updateModeGating(); });
|
||||
updateModeGating();
|
||||
refresh();
|
||||
}
|
||||
|
||||
QPushButton* ArmPage::makeMoveButton(const QString& text, const QString& axis, int dir)
|
||||
{
|
||||
QPushButton* b = button(text, "", this);
|
||||
b->setFixedSize(180, 90);
|
||||
b->setStyleSheet(QString("font-size:24px;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, axis, dir]() {
|
||||
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;
|
||||
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("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef ARMPAGE_H
|
||||
#define ARMPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
#include <QVector>
|
||||
|
||||
class TitleBar;
|
||||
class StatusStrip;
|
||||
class AlarmBanner;
|
||||
class DataCard;
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
class QTimer;
|
||||
|
||||
// ===================== ⑦ 机械臂界面 =====================
|
||||
class ArmPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ArmPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void updateModeGating();
|
||||
void onReturnToggled(bool on);
|
||||
|
||||
private:
|
||||
QPushButton* makeMoveButton(const QString& text, const QString& axis, int dir);
|
||||
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
StatusStrip* m_statusStrip = nullptr;
|
||||
AlarmBanner* m_banner = nullptr;
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
DataCard* m_washCard = nullptr;
|
||||
DataCard* m_gasCard = nullptr;
|
||||
DataCard* m_armCard = nullptr;
|
||||
QPushButton* m_returnBtn = nullptr;
|
||||
QLabel* m_returnStatus = nullptr;
|
||||
QTimer* m_statusTimer = nullptr;
|
||||
QVector<QPushButton*> m_moveButtons;
|
||||
QVector<QPushButton*> m_endButtons;
|
||||
QString m_prevReturnState = "idle";
|
||||
};
|
||||
|
||||
#endif // ARMPAGE_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "basepage.h"
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
BasePage::BasePage(AppContext* ctx, QWidget* parent)
|
||||
: QWidget(parent), m_ctx(ctx)
|
||||
{
|
||||
setObjectName("pageRoot");
|
||||
setStyleSheet(QString("QWidget#pageRoot{background-color:%1;}").arg(C::BG));
|
||||
}
|
||||
|
||||
QLabel* BasePage::label(const QString& text, int px, const QString& color, bool bold, QWidget* parent)
|
||||
{
|
||||
QLabel* l = new QLabel(text, parent);
|
||||
l->setStyleSheet(QString("color:%1;font-size:%2px;%3").arg(color).arg(px)
|
||||
.arg(bold ? "font-weight:700;" : ""));
|
||||
return l;
|
||||
}
|
||||
|
||||
QPushButton* BasePage::button(const QString& text, const QString& variant, QWidget* parent)
|
||||
{
|
||||
QPushButton* b = new QPushButton(text, parent);
|
||||
if (!variant.isEmpty())
|
||||
b->setProperty("variant", variant);
|
||||
return b;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef BASEPAGE_H
|
||||
#define BASEPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QEvent>
|
||||
#include "appcontext.h"
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
// ===================== 页面基类 =====================
|
||||
// 所有页面继承此基类,持有应用上下文,统一提供页面跳转信号与控件构造助手
|
||||
class BasePage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BasePage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
AppContext* ctx() const { return m_ctx; }
|
||||
|
||||
// 控件构造助手
|
||||
static QLabel* label(const QString& text, int px, const QString& color = C::TEXT,
|
||||
bool bold = false, QWidget* parent = nullptr);
|
||||
static QPushButton* button(const QString& text, const QString& variant = "",
|
||||
QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void navigate(PageId id); // 请求切换到指定页面
|
||||
|
||||
protected:
|
||||
AppContext* m_ctx = nullptr;
|
||||
};
|
||||
|
||||
#endif // BASEPAGE_H
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "bootpasswordpage.h"
|
||||
#include "numpaddialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include "global.h"
|
||||
|
||||
BootPasswordPage::BootPasswordPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
QVBoxLayout* root = new QVBoxLayout(this);
|
||||
root->setContentsMargins(0, 0, 0, 0);
|
||||
root->addStretch(1);
|
||||
|
||||
QWidget* col = new QWidget(this);
|
||||
col->setFixedWidth(800);
|
||||
QVBoxLayout* lay = new QVBoxLayout(col);
|
||||
lay->setSpacing(24);
|
||||
lay->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
// 品牌区
|
||||
QLabel* brand = label("“玉兔”工业无线遥控器", 32, C::TEXT, true, col);
|
||||
brand->setAlignment(Qt::AlignCenter);
|
||||
QLabel* model = label("YTZT6K8EART2-aL25", 20, C::TEXT_SUB, false, col);
|
||||
model->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(brand);
|
||||
lay->addWidget(model);
|
||||
lay->addSpacing(20);
|
||||
|
||||
// 标题
|
||||
QLabel* title = label("请输入开机密码", 28, C::TEXT, true, col);
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(title);
|
||||
|
||||
// 密码输入框(只读回显,点击弹出数字键盘)
|
||||
m_input = new QLineEdit(col);
|
||||
m_input->setReadOnly(true);
|
||||
m_input->setEchoMode(QLineEdit::Password);
|
||||
m_input->setAlignment(Qt::AlignCenter);
|
||||
m_input->setFixedSize(400, 88);
|
||||
m_input->setPlaceholderText("点击输入密码");
|
||||
m_input->setProperty("clickable", true);
|
||||
lay->addWidget(m_input, 0, Qt::AlignHCenter);
|
||||
m_input->installEventFilter(this);
|
||||
m_input->setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
// 错误提示(默认隐藏)
|
||||
m_error = label("密码输入有误,请重新输入", 18, C::DANGER, true, col);
|
||||
m_error->setAlignment(Qt::AlignCenter);
|
||||
m_error->hide();
|
||||
lay->addWidget(m_error);
|
||||
|
||||
// 确认 / 取消
|
||||
QHBoxLayout* btnRow = new QHBoxLayout();
|
||||
btnRow->setSpacing(40);
|
||||
btnRow->addStretch(1);
|
||||
QPushButton* ok = button("确认", "primary", col);
|
||||
ok->setFixedSize(180, 72);
|
||||
connect(ok, &QPushButton::clicked, this, &BootPasswordPage::onConfirm);
|
||||
QPushButton* cancel = button("取消", "", col);
|
||||
cancel->setFixedSize(180, 72);
|
||||
connect(cancel, &QPushButton::clicked, this, &BootPasswordPage::onCancel);
|
||||
btnRow->addWidget(ok);
|
||||
btnRow->addWidget(cancel);
|
||||
btnRow->addStretch(1);
|
||||
lay->addLayout(btnRow);
|
||||
|
||||
// 忘记密码链接
|
||||
QPushButton* forget = button("忘记密码/修改密码", "link", col);
|
||||
forget->setFixedHeight(44);
|
||||
forget->setCursor(Qt::PointingHandCursor);
|
||||
connect(forget, &QPushButton::clicked, this, &BootPasswordPage::onForget);
|
||||
lay->addWidget(forget);
|
||||
|
||||
root->addWidget(col, 0, Qt::AlignHCenter);
|
||||
root->addStretch(1);
|
||||
|
||||
refreshInput();
|
||||
}
|
||||
|
||||
// 点击密码输入框 → 弹出数字键盘(拦截鼠标点击)
|
||||
bool BootPasswordPage::eventFilter(QObject* obj, QEvent* ev)
|
||||
{
|
||||
if (obj == m_input && ev->type() == QEvent::MouseButtonPress) {
|
||||
onInputClicked();
|
||||
return true;
|
||||
}
|
||||
return BasePage::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
void BootPasswordPage::onInputClicked()
|
||||
{
|
||||
if (m_locked)
|
||||
return;
|
||||
NumPadDialog dlg(this);
|
||||
dlg.setMaxLen(4);
|
||||
dlg.setValue(m_value);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
m_value = dlg.value();
|
||||
m_error->hide();
|
||||
refreshInput();
|
||||
}
|
||||
}
|
||||
|
||||
void BootPasswordPage::onConfirm()
|
||||
{
|
||||
if (m_locked)
|
||||
return;
|
||||
|
||||
if (m_value == ctx()->password()) {
|
||||
m_wrongCount = 0;
|
||||
clearInput();
|
||||
emit navigate(PageSelfCheck);
|
||||
} else {
|
||||
m_wrongCount++;
|
||||
clearInput();
|
||||
if (m_wrongCount >= 5) {
|
||||
// 【设计建议】连续 5 次错误锁定 30s
|
||||
m_locked = true;
|
||||
m_error->setText("连续输入错误,已锁定 30 秒");
|
||||
m_error->show();
|
||||
QTimer::singleShot(30000, this, [this]() {
|
||||
m_locked = false;
|
||||
m_wrongCount = 0;
|
||||
m_error->hide();
|
||||
});
|
||||
} else {
|
||||
emit navigate(PageWarning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BootPasswordPage::onCancel()
|
||||
{
|
||||
clearInput();
|
||||
}
|
||||
|
||||
void BootPasswordPage::onForget()
|
||||
{
|
||||
clearInput();
|
||||
emit navigate(PageMgmtLogin);
|
||||
}
|
||||
|
||||
void BootPasswordPage::clearInput()
|
||||
{
|
||||
m_value.clear();
|
||||
refreshInput();
|
||||
}
|
||||
|
||||
void BootPasswordPage::focusInput()
|
||||
{
|
||||
onInputClicked();
|
||||
}
|
||||
|
||||
void BootPasswordPage::refreshInput()
|
||||
{
|
||||
m_input->setText(m_value);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef BOOTPASSWORDPAGE_H
|
||||
#define BOOTPASSWORDPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
|
||||
// ===================== ③ 开机密码主界面 =====================
|
||||
class BootPasswordPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BootPasswordPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
void clearInput();
|
||||
void focusInput();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void onInputClicked();
|
||||
void onConfirm();
|
||||
void onCancel();
|
||||
void onForget();
|
||||
|
||||
private:
|
||||
void refreshInput();
|
||||
|
||||
QLineEdit* m_input = nullptr;
|
||||
QLabel* m_error = nullptr;
|
||||
QString m_value;
|
||||
int m_wrongCount = 0;
|
||||
bool m_locked = false;
|
||||
};
|
||||
|
||||
#endif // BOOTPASSWORDPAGE_H
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "chassispage.h"
|
||||
#include "titlebar.h"
|
||||
#include "statusstrip.h"
|
||||
#include "alarmbanner.h"
|
||||
#include "datacard.h"
|
||||
#include "paramcontrol.h"
|
||||
#include "udpcommunication.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#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, "车速已保存");
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef CHASSISPAGE_H
|
||||
#define CHASSISPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
#include <QVector>
|
||||
|
||||
class TitleBar;
|
||||
class StatusStrip;
|
||||
class AlarmBanner;
|
||||
class DataCard;
|
||||
class ParamControl;
|
||||
class QPushButton;
|
||||
class QTimer;
|
||||
|
||||
// ===================== ⑥ 机器人底盘界面 =====================
|
||||
class ChassisPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ChassisPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void updateModeGating();
|
||||
void onSpeedChanged(double v);
|
||||
void onSpeedSaved();
|
||||
|
||||
private:
|
||||
QPushButton* makeDirButton(const QString& text, const QString& dir);
|
||||
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
StatusStrip* m_statusStrip = nullptr;
|
||||
AlarmBanner* m_banner = nullptr;
|
||||
DataCard* m_washCard = nullptr;
|
||||
DataCard* m_armCard = nullptr;
|
||||
DataCard* m_gasCard = nullptr;
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
ParamControl* m_speed = nullptr;
|
||||
QTimer* m_saveTimer = nullptr;
|
||||
int m_chassisFieldFault = -1;
|
||||
QVector<QPushButton*> m_dirButtons;
|
||||
};
|
||||
|
||||
#endif // CHASSISPAGE_H
|
||||
@@ -0,0 +1,247 @@
|
||||
#include "debugmaintainpage.h"
|
||||
#include "titlebar.h"
|
||||
#include "alarmbanner.h"
|
||||
#include "datacard.h"
|
||||
#include "ledindicator.h"
|
||||
#include "paramcontrol.h"
|
||||
#include "udpcommunication.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include "global.h"
|
||||
|
||||
DebugMaintainPage::DebugMaintainPage(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_warn = new AlarmBanner(this);
|
||||
m_warn->showAlarm("警告:该模式下无保护功能,操作请谨慎!");
|
||||
root->addWidget(m_warn);
|
||||
|
||||
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(16);
|
||||
leftLay->addWidget(makeValveGroup(1));
|
||||
leftLay->addWidget(makeValveGroup(2));
|
||||
leftLay->addStretch(1);
|
||||
left->setFixedWidth(300);
|
||||
main->addWidget(left);
|
||||
|
||||
// 中央卡片区
|
||||
QWidget* center = new QWidget(this);
|
||||
QVBoxLayout* centerLay = new QVBoxLayout(center);
|
||||
centerLay->setContentsMargins(0, 0, 0, 0);
|
||||
centerLay->setSpacing(10);
|
||||
|
||||
QHBoxLayout* miniRow = new QHBoxLayout();
|
||||
miniRow->setSpacing(10);
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
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");
|
||||
miniRow->addWidget(m_chassisCard, 1);
|
||||
miniRow->addWidget(m_armCard, 1);
|
||||
miniRow->addWidget(m_gasCard, 1);
|
||||
centerLay->addLayout(miniRow, 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("柱塞泵故障代码", "");
|
||||
centerLay->addWidget(m_washCard, 1);
|
||||
|
||||
main->addWidget(center, 1);
|
||||
|
||||
// 右泵控制区
|
||||
QWidget* right = new QWidget(this);
|
||||
QVBoxLayout* rightLay = new QVBoxLayout(right);
|
||||
rightLay->setContentsMargins(0, 0, 0, 0);
|
||||
rightLay->setSpacing(8);
|
||||
rightLay->addWidget(makePumpGroup("air", "空压机"));
|
||||
rightLay->addWidget(makePumpGroup("plunger", "柱塞泵"));
|
||||
rightLay->addWidget(makePumpGroup("leaf", "叶片泵"));
|
||||
rightLay->addStretch(1);
|
||||
right->setFixedWidth(420);
|
||||
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(36, 0, 24, 0);
|
||||
bottomLay->setSpacing(48);
|
||||
m_ledPlunger = new LedIndicator("柱塞泵", bottom);
|
||||
m_ledAir = new LedIndicator("空压机", bottom);
|
||||
m_ledLeaf = new LedIndicator("叶片泵", bottom);
|
||||
m_ledLevel = new LedIndicator("液位低", bottom);
|
||||
bottomLay->addWidget(m_ledPlunger);
|
||||
bottomLay->addWidget(m_ledAir);
|
||||
bottomLay->addWidget(m_ledLeaf);
|
||||
bottomLay->addWidget(m_ledLevel);
|
||||
bottomLay->addStretch(1);
|
||||
QPushButton* back = button("返回", "", bottom);
|
||||
back->setFixedSize(144, 76);
|
||||
connect(back, &QPushButton::clicked, this, [this]() { emit navigate(PageJob); });
|
||||
bottomLay->addWidget(back);
|
||||
root->addWidget(bottom);
|
||||
|
||||
connect(ctx->comm(), &UdpCommunication::telemetryUpdated, this, &DebugMaintainPage::refresh);
|
||||
refresh();
|
||||
}
|
||||
|
||||
QWidget* DebugMaintainPage::makeValveGroup(int valve)
|
||||
{
|
||||
QWidget* grp = new QWidget(this);
|
||||
grp->setProperty("card", true);
|
||||
grp->setStyleSheet(QString("QWidget[card=\"true\"]{background-color:%1;border:2px solid %2;border-radius:12px;}")
|
||||
.arg(C::PANEL, C::BORDER));
|
||||
QVBoxLayout* lay = new QVBoxLayout(grp);
|
||||
lay->setContentsMargins(12, 12, 12, 12);
|
||||
lay->setSpacing(8);
|
||||
|
||||
QLabel* title = label(QString("D%1 球阀").arg(valve), 20, C::TEXT, true, grp);
|
||||
lay->addWidget(title);
|
||||
|
||||
// 控制开/关(toggle)
|
||||
QPushButton* ctrl = button(QString("D%1 控制:关闭").arg(valve), "", grp);
|
||||
ctrl->setCheckable(true);
|
||||
ctrl->setFixedHeight(64);
|
||||
connect(ctrl, &QPushButton::toggled, this, [this, valve, ctrl](bool on) {
|
||||
ctrl->setText(QString("D%1 控制:%2").arg(valve).arg(on ? "开启" : "关闭"));
|
||||
ctx()->comm()->sendBallValve(valve, on);
|
||||
});
|
||||
lay->addWidget(ctrl);
|
||||
|
||||
// 开启到位 / 关闭到位
|
||||
LedIndicator* open = new LedIndicator("开启到位", grp);
|
||||
open->setState(LedIndicator::Off);
|
||||
LedIndicator* close = new LedIndicator("关闭到位", grp);
|
||||
close->setState(LedIndicator::Red);
|
||||
lay->addWidget(open);
|
||||
lay->addWidget(close);
|
||||
|
||||
// 故障提示
|
||||
QLabel* fault = label("", 16, C::DANGER, true, grp);
|
||||
fault->setAlignment(Qt::AlignCenter);
|
||||
fault->setProperty("alert", true);
|
||||
fault->hide();
|
||||
lay->addWidget(fault);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QWidget* DebugMaintainPage::makePumpGroup(const QString& pump, const QString& name)
|
||||
{
|
||||
QWidget* grp = new QWidget(this);
|
||||
grp->setProperty("card", true);
|
||||
grp->setStyleSheet(QString("QWidget[card=\"true\"]{background-color:%1;border:2px solid %2;border-radius:12px;}")
|
||||
.arg(C::PANEL, C::BORDER));
|
||||
QVBoxLayout* lay = new QVBoxLayout(grp);
|
||||
lay->setContentsMargins(12, 8, 12, 8);
|
||||
lay->setSpacing(4);
|
||||
|
||||
ParamControl* rpm = new ParamControl(name + "转速", "rpm", 10, 0, 2000, false, false, grp);
|
||||
rpm->setValue(200);
|
||||
connect(rpm, &ParamControl::valueChanged, this, [this, pump](double v) {
|
||||
ctx()->comm()->sendPumpRpm(pump, static_cast<int>(v));
|
||||
});
|
||||
lay->addWidget(rpm);
|
||||
|
||||
QPushButton* power = button("上电", "", grp);
|
||||
power->setCheckable(true);
|
||||
power->setFixedHeight(56);
|
||||
connect(power, &QPushButton::toggled, this, [this, pump, power](bool on) {
|
||||
power->setText(on ? "上电中" : "上电");
|
||||
ctx()->comm()->sendPumpPower(pump, on);
|
||||
});
|
||||
lay->addWidget(power);
|
||||
|
||||
if (pump == "air") { m_airRpm = rpm; m_airPower = power; }
|
||||
else if (pump == "plunger") { m_plungerRpm = rpm; m_plungerPower = power; }
|
||||
else { m_leafRpm = rpm; m_leafPower = power; }
|
||||
return grp;
|
||||
}
|
||||
|
||||
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));
|
||||
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(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_v1Open->setState(t.d1open ? LedIndicator::Green : LedIndicator::Off);
|
||||
m_v1Close->setState(t.d1close ? LedIndicator::Red : LedIndicator::Off);
|
||||
m_v2Open->setState(t.d2open ? LedIndicator::Green : LedIndicator::Off);
|
||||
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(); }
|
||||
|
||||
// 泵上电状态同步
|
||||
if (m_airPower) m_airPower->setChecked(t.air);
|
||||
if (m_plungerPower) m_plungerPower->setChecked(t.plunger);
|
||||
if (m_leafPower) m_leafPower->setChecked(t.leaf);
|
||||
|
||||
// 底部指示灯
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef DEBUGMAINTAINPAGE_H
|
||||
#define DEBUGMAINTAINPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class TitleBar;
|
||||
class AlarmBanner;
|
||||
class DataCard;
|
||||
class LedIndicator;
|
||||
class ParamControl;
|
||||
class QPushButton;
|
||||
class QLabel;
|
||||
class QCheckBox;
|
||||
|
||||
// ===================== ⑪ 调试维护界面(洗消调试) =====================
|
||||
class DebugMaintainPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DebugMaintainPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
// 单个球阀组:控制开/关 + 开启到位 + 关闭到位 + 故障提示
|
||||
QWidget* makeValveGroup(int valve);
|
||||
// 单个泵组:转速调节 + 上电启停
|
||||
QWidget* makePumpGroup(const QString& pump, const QString& name);
|
||||
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
AlarmBanner* m_warn = nullptr;
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
DataCard* m_armCard = nullptr;
|
||||
DataCard* m_gasCard = nullptr;
|
||||
DataCard* m_washCard = nullptr;
|
||||
|
||||
// 球阀
|
||||
QPushButton* m_valve1Btn = nullptr;
|
||||
QPushButton* m_valve2Btn = nullptr;
|
||||
LedIndicator* m_v1Open = nullptr;
|
||||
LedIndicator* m_v1Close = nullptr;
|
||||
LedIndicator* m_v2Open = nullptr;
|
||||
LedIndicator* m_v2Close = nullptr;
|
||||
QLabel* m_valveFault1 = nullptr;
|
||||
QLabel* m_valveFault2 = nullptr;
|
||||
|
||||
// 泵
|
||||
ParamControl* m_airRpm = nullptr;
|
||||
ParamControl* m_plungerRpm = nullptr;
|
||||
ParamControl* m_leafRpm = nullptr;
|
||||
QPushButton* m_airPower = nullptr;
|
||||
QPushButton* m_plungerPower = nullptr;
|
||||
QPushButton* m_leafPower = nullptr;
|
||||
|
||||
// 底部指示灯
|
||||
LedIndicator* m_ledPlunger = nullptr;
|
||||
LedIndicator* m_ledAir = nullptr;
|
||||
LedIndicator* m_ledLeaf = nullptr;
|
||||
LedIndicator* m_ledLevel = nullptr;
|
||||
};
|
||||
|
||||
#endif // DEBUGMAINTAINPAGE_H
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "faultquerypage.h"
|
||||
#include "faulttable.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include "global.h"
|
||||
|
||||
FaultQueryPage::FaultQueryPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
m_categories << "机器人底盘" << "机械臂" << "洗消系统" << "气体检测仪";
|
||||
|
||||
QVBoxLayout* root = new QVBoxLayout(this);
|
||||
root->setContentsMargins(36, 20, 36, 20);
|
||||
root->setSpacing(16);
|
||||
|
||||
// 顶条:标题 + 汇总数
|
||||
m_title = label("历史故障", 28, C::TEXT, true, this);
|
||||
root->addWidget(m_title);
|
||||
|
||||
QHBoxLayout* main = new QHBoxLayout();
|
||||
main->setSpacing(28);
|
||||
|
||||
// 表格
|
||||
m_table = new FaultTable(this);
|
||||
connect(m_table, &FaultTable::rowClicked, this, &FaultQueryPage::onRowClicked);
|
||||
main->addWidget(m_table, 1);
|
||||
|
||||
// 分类按钮列(垂直居中 + 平均分布)
|
||||
QWidget* right = new QWidget(this);
|
||||
QVBoxLayout* rightLay = new QVBoxLayout(right);
|
||||
rightLay->setContentsMargins(0, 0, 0, 0);
|
||||
rightLay->setSpacing(8);
|
||||
for (const QString& cat : m_categories) {
|
||||
QPushButton* b = button(cat + "故障", "", right);
|
||||
b->setCheckable(true);
|
||||
b->setFixedHeight(110);
|
||||
b->setStyleSheet(QString("font-size:24px;background-color:%1;color:%2;border:1px solid %3;border-radius:12px;")
|
||||
.arg(C::PANEL_HI, C::TEXT, C::BORDER));
|
||||
connect(b, &QPushButton::toggled, this, &FaultQueryPage::onCategoryToggled);
|
||||
m_categoryButtons << b;
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(b);
|
||||
}
|
||||
rightLay->addStretch(1);
|
||||
right->setFixedWidth(360);
|
||||
main->addWidget(right);
|
||||
|
||||
root->addLayout(main, 1);
|
||||
|
||||
// 底部:返回
|
||||
QHBoxLayout* bottomLay = new QHBoxLayout();
|
||||
bottomLay->addStretch(1);
|
||||
QPushButton* back = button("返回", "", this);
|
||||
back->setFixedSize(144, 76);
|
||||
connect(back, &QPushButton::clicked, this, [this]() { emit navigate(m_returnPage); });
|
||||
bottomLay->addWidget(back);
|
||||
root->addLayout(bottomLay);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void FaultQueryPage::onCategoryToggled(bool on)
|
||||
{
|
||||
// 单选筛选:确定当前选中的分类
|
||||
QString selected;
|
||||
for (int i = 0; i < m_categoryButtons.size(); ++i) {
|
||||
if (m_categoryButtons[i]->isChecked()) {
|
||||
selected = m_categories[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_filter = selected;
|
||||
Q_UNUSED(on);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void FaultQueryPage::refresh()
|
||||
{
|
||||
// 按分类过滤
|
||||
QVector<FaultRecord> data;
|
||||
for (const FaultRecord& r : ctx()->faultHistory) {
|
||||
if (m_filter.isEmpty() || r.system == m_filter)
|
||||
data.append(r);
|
||||
}
|
||||
|
||||
m_table->setData(data);
|
||||
|
||||
if (m_filter.isEmpty())
|
||||
m_title->setText(QString("历史故障(共 %1 条)").arg(data.size()));
|
||||
else
|
||||
m_title->setText(QString("%1历史故障(共 %2 条)").arg(m_filter).arg(data.size()));
|
||||
}
|
||||
|
||||
void FaultQueryPage::onRowClicked(int globalIndex)
|
||||
{
|
||||
// 找到当前过滤后列表中的记录
|
||||
QVector<FaultRecord> data;
|
||||
for (const FaultRecord& r : ctx()->faultHistory) {
|
||||
if (m_filter.isEmpty() || r.system == m_filter)
|
||||
data.append(r);
|
||||
}
|
||||
if (globalIndex < 0 || globalIndex >= data.size())
|
||||
return;
|
||||
const FaultRecord& r = data[globalIndex];
|
||||
QMessageBox::information(this, "故障详情",
|
||||
QString("故障时间:%1\n分系统:%2\n故障代码:%3\n故障描述:%4")
|
||||
.arg(r.time, r.system, r.code, r.desc));
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef FAULTQUERYPAGE_H
|
||||
#define FAULTQUERYPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
#include <QVector>
|
||||
|
||||
class FaultTable;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
// ===================== ⑫ 故障查询界面 =====================
|
||||
class FaultQueryPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FaultQueryPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
void setReturnPage(PageId id) { m_returnPage = id; }
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void onCategoryToggled(bool on);
|
||||
void onRowClicked(int globalIndex);
|
||||
|
||||
private:
|
||||
FaultTable* m_table = nullptr;
|
||||
QLabel* m_title = nullptr;
|
||||
QVector<QPushButton*> m_categoryButtons;
|
||||
QVector<QString> m_categories;
|
||||
QString m_filter; // 空 = 全部
|
||||
PageId m_returnPage = PageJob;
|
||||
};
|
||||
|
||||
#endif // FAULTQUERYPAGE_H
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "gasdetectorpage.h"
|
||||
#include "titlebar.h"
|
||||
#include "statusstrip.h"
|
||||
#include "datacard.h"
|
||||
#include "udpcommunication.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include "global.h"
|
||||
|
||||
GasDetectorPage::GasDetectorPage(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);
|
||||
|
||||
QHBoxLayout* main = new QHBoxLayout();
|
||||
main->setContentsMargins(36, 16, 36, 16);
|
||||
main->setSpacing(32);
|
||||
|
||||
// 中央卡片区(2×2)
|
||||
QWidget* center = new QWidget(this);
|
||||
QGridLayout* grid = new QGridLayout(center);
|
||||
grid->setSpacing(16);
|
||||
grid->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
|
||||
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("有毒物种类", "--");
|
||||
m_gasCard->addField("有毒物等级", "--");
|
||||
m_gasConcField = m_gasCard->addField("浓度", "0 ppb", 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);
|
||||
main->addWidget(center, 1);
|
||||
|
||||
// 右侧按钮列(垂直居中 + 平均分布)
|
||||
QWidget* right = new QWidget(this);
|
||||
QVBoxLayout* rightLay = new QVBoxLayout(right);
|
||||
rightLay->setContentsMargins(0, 0, 0, 0);
|
||||
rightLay->setSpacing(8);
|
||||
|
||||
m_start = button("启动", "primary", right);
|
||||
m_start->setFixedHeight(180);
|
||||
connect(m_start, &QPushButton::clicked, this, [this]() {
|
||||
this->ctx()->comm()->sendGasStart();
|
||||
this->ctx()->gasRunning = true;
|
||||
updateGating();
|
||||
});
|
||||
|
||||
m_stop = button("停机", "", right);
|
||||
m_stop->setFixedHeight(180);
|
||||
connect(m_stop, &QPushButton::clicked, this, [this]() {
|
||||
this->ctx()->comm()->sendGasStop();
|
||||
this->ctx()->gasRunning = false;
|
||||
updateGating();
|
||||
});
|
||||
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(m_start);
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(m_stop);
|
||||
rightLay->addStretch(1);
|
||||
right->setFixedWidth(360);
|
||||
main->addWidget(right);
|
||||
|
||||
root->addLayout(main, 1);
|
||||
|
||||
m_statusStrip = new StatusStrip(ctx, this);
|
||||
connect(m_statusStrip, &StatusStrip::backClicked, this, [this]() {
|
||||
if (this->ctx()->gasRunning) {
|
||||
// 【设计建议】检测中返回弹提示
|
||||
if (ConfirmDialog::confirm(this, "提示", "检测仍在进行,是否退出?"))
|
||||
emit navigate(PageJob);
|
||||
} else {
|
||||
emit navigate(PageJob);
|
||||
}
|
||||
});
|
||||
root->addWidget(m_statusStrip);
|
||||
|
||||
connect(ctx->comm(), &UdpCommunication::telemetryUpdated, this, &GasDetectorPage::refresh);
|
||||
updateGating();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void GasDetectorPage::updateGating()
|
||||
{
|
||||
bool running = ctx()->gasRunning;
|
||||
m_start->setEnabled(!running);
|
||||
m_stop->setEnabled(running);
|
||||
}
|
||||
|
||||
void GasDetectorPage::refresh()
|
||||
{
|
||||
const Telemetry& t = ctx()->telemetry;
|
||||
|
||||
// 同步运行状态(以遥测为准)
|
||||
if (t.gasHost == "detecting") ctx()->gasRunning = true;
|
||||
else if (t.gasHost == "standby") ctx()->gasRunning = 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_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, t.gasType);
|
||||
m_gasCard->setField(2, t.gasLevel);
|
||||
m_gasCard->setField(m_gasConcField, QString("%1 ppb").arg(t.gasConc, 0, 'f', 0));
|
||||
// 超阈值红闪
|
||||
m_gasCard->setAlarm(m_gasConcField, t.gasConc > 500.0);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef GASDETECTORPAGE_H
|
||||
#define GASDETECTORPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class TitleBar;
|
||||
class StatusStrip;
|
||||
class DataCard;
|
||||
class QPushButton;
|
||||
|
||||
// ===================== ⑧ 气体检测仪界面 =====================
|
||||
class GasDetectorPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GasDetectorPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void updateGating();
|
||||
|
||||
private:
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
StatusStrip* m_statusStrip = nullptr;
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
DataCard* m_washCard = nullptr;
|
||||
DataCard* m_armCard = nullptr;
|
||||
DataCard* m_gasCard = nullptr;
|
||||
QPushButton* m_start = nullptr;
|
||||
QPushButton* m_stop = nullptr;
|
||||
int m_gasConcField = -1;
|
||||
};
|
||||
|
||||
#endif // GASDETECTORPAGE_H
|
||||
@@ -0,0 +1,125 @@
|
||||
#include "jobselectpage.h"
|
||||
#include "titlebar.h"
|
||||
#include "statusstrip.h"
|
||||
#include "datacard.h"
|
||||
#include "udpcommunication.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include "global.h"
|
||||
|
||||
JobSelectPage::JobSelectPage(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);
|
||||
|
||||
QHBoxLayout* main = new QHBoxLayout();
|
||||
main->setContentsMargins(36, 20, 36, 20);
|
||||
main->setSpacing(32);
|
||||
|
||||
// 左侧菜单(垂直居中 + 平均分布)
|
||||
QWidget* left = new QWidget(this);
|
||||
QVBoxLayout* leftLay = new QVBoxLayout(left);
|
||||
leftLay->setContentsMargins(0, 0, 0, 0);
|
||||
leftLay->setSpacing(8);
|
||||
auto addMenu = [&](const QString& t, PageId id) {
|
||||
QPushButton* b = button(t, "", left);
|
||||
b->setFixedHeight(130);
|
||||
b->setStyleSheet(QString("font-size:24px;background-color:%1;color:%2;border:1px solid %3;border-radius:12px;")
|
||||
.arg(C::PANEL_HI, C::TEXT, C::BORDER));
|
||||
connect(b, &QPushButton::clicked, this, [this, id]() { emit navigate(id); });
|
||||
leftLay->addStretch(1);
|
||||
leftLay->addWidget(b);
|
||||
};
|
||||
addMenu("机器人底盘", PageChassis);
|
||||
addMenu("机械臂", PageArm);
|
||||
addMenu("气体检测仪", PageGas);
|
||||
addMenu("调试维护", PageDebug);
|
||||
addMenu("故障查询", PageFault);
|
||||
leftLay->addStretch(1);
|
||||
left->setFixedWidth(240);
|
||||
main->addWidget(left);
|
||||
|
||||
// 中央卡片区(2×2)
|
||||
QWidget* center = new QWidget(this);
|
||||
QGridLayout* grid = new QGridLayout(center);
|
||||
grid->setSpacing(16);
|
||||
grid->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_chassisCard = new DataCard("底盘", center);
|
||||
m_chassisCard->addField("电量", "85%");
|
||||
m_chassisCard->addField("电压", "360V");
|
||||
m_chassisCard->addField("故障", "无");
|
||||
|
||||
m_armCard = new DataCard("机械臂", center);
|
||||
m_armCard->addField("末端位置", "XYZ");
|
||||
m_armCard->addField("姿态", "RX/RY/RZ");
|
||||
|
||||
m_washCard = new DataCard("洗消", center);
|
||||
m_washCard->addField("A液位", "100L");
|
||||
m_washCard->addField("泡沫液位", "30L");
|
||||
|
||||
m_gasCard = new DataCard("气体检测仪", center);
|
||||
m_gasCard->addField("主机状态", "待机");
|
||||
m_gasCard->addField("浓度", "0 ppb");
|
||||
|
||||
grid->addWidget(m_chassisCard, 0, 0);
|
||||
grid->addWidget(m_armCard, 0, 1);
|
||||
grid->addWidget(m_washCard, 1, 0);
|
||||
grid->addWidget(m_gasCard, 1, 1);
|
||||
main->addWidget(center, 1);
|
||||
|
||||
// 右侧按钮列(垂直居中 + 平均分布)
|
||||
QWidget* right = new QWidget(this);
|
||||
QVBoxLayout* rightLay = new QVBoxLayout(right);
|
||||
rightLay->setContentsMargins(0, 0, 0, 0);
|
||||
rightLay->setSpacing(8);
|
||||
QPushButton* clean = button("手动消除", "primary", right);
|
||||
clean->setFixedHeight(170);
|
||||
connect(clean, &QPushButton::clicked, this, [this]() { emit navigate(PageClean); });
|
||||
QPushButton* disinfect = button("手动消毒", "primary", right);
|
||||
disinfect->setFixedHeight(170);
|
||||
connect(disinfect, &QPushButton::clicked, this, [this]() { emit navigate(PageDisinfect); });
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(clean);
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(disinfect);
|
||||
rightLay->addStretch(1);
|
||||
right->setFixedWidth(360);
|
||||
main->addWidget(right);
|
||||
|
||||
root->addLayout(main, 1);
|
||||
|
||||
m_statusStrip = new StatusStrip(ctx, this);
|
||||
m_statusStrip->setBackVisible(false);
|
||||
root->addWidget(m_statusStrip);
|
||||
|
||||
connect(ctx->comm(), &UdpCommunication::telemetryUpdated, this, &JobSelectPage::refresh);
|
||||
refresh();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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_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);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef JOBSELECTPAGE_H
|
||||
#define JOBSELECTPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class TitleBar;
|
||||
class StatusStrip;
|
||||
class DataCard;
|
||||
|
||||
// ===================== ⑤ 作业选择界面 =====================
|
||||
class JobSelectPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JobSelectPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
StatusStrip* m_statusStrip = nullptr;
|
||||
DataCard* m_chassisCard = nullptr;
|
||||
DataCard* m_armCard = nullptr;
|
||||
DataCard* m_washCard = nullptr;
|
||||
DataCard* m_gasCard = nullptr;
|
||||
};
|
||||
|
||||
#endif // JOBSELECTPAGE_H
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "manualcleanpage.h"
|
||||
#include "paramcontrol.h"
|
||||
#include "udpcommunication.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include "global.h"
|
||||
|
||||
ManualCleanPage::ManualCleanPage(AppContext* ctx, QWidget* parent)
|
||||
: WashBasePage(ctx, "手动消除", "洗消(手动消除)", parent)
|
||||
{
|
||||
double flow = ctx->getDouble(ctx->keyPumpFlow(), 20);
|
||||
m_pumpFlow = new ParamControl("柱塞泵流量", "L/min", 0.5, 0.0, 100.0, true, false, this);
|
||||
m_pumpFlow->setValue(flow);
|
||||
connect(m_pumpFlow, &ParamControl::saveClicked, this, [this]() {
|
||||
this->ctx()->setDouble(this->ctx()->keyPumpFlow(), m_pumpFlow->value());
|
||||
this->ctx()->comm()->sendSetParam(this->ctx()->keyPumpFlow(), m_pumpFlow->value());
|
||||
ConfirmDialog::toast(this, "已保存");
|
||||
});
|
||||
paramsLayout()->addWidget(m_pumpFlow);
|
||||
}
|
||||
|
||||
void ManualCleanPage::resetFactory()
|
||||
{
|
||||
// 出厂值 20
|
||||
m_pumpFlow->setValue(20);
|
||||
ctx()->setDouble(ctx()->keyPumpFlow(), 20);
|
||||
ConfirmDialog::toast(this, "已恢复出厂值");
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef MANUALCLEANPAGE_H
|
||||
#define MANUALCLEANPAGE_H
|
||||
|
||||
#include "washbasepage.h"
|
||||
|
||||
class ParamControl;
|
||||
|
||||
// ===================== ⑨ 手动消除界面 =====================
|
||||
class ManualCleanPage : public WashBasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ManualCleanPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void resetFactory() override;
|
||||
QString washModeTag() const override { return "clean"; }
|
||||
|
||||
private:
|
||||
ParamControl* m_pumpFlow = nullptr;
|
||||
};
|
||||
|
||||
#endif // MANUALCLEANPAGE_H
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "manualdisinfectpage.h"
|
||||
#include "paramcontrol.h"
|
||||
#include "udpcommunication.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include "global.h"
|
||||
|
||||
ManualDisinfectPage::ManualDisinfectPage(AppContext* ctx, QWidget* parent)
|
||||
: WashBasePage(ctx, "手动消毒", "洗消(手动消毒)", parent)
|
||||
{
|
||||
// 发泡倍数(出厂 6)
|
||||
m_foamRatio = new ParamControl("发泡倍数", "倍", 0.5, 0.0, 20.0, true, false, this);
|
||||
m_foamRatio->setValue(ctx->getDouble(ctx->keyFoamRatio(), 6));
|
||||
connect(m_foamRatio, &ParamControl::saveClicked, this, [this]() {
|
||||
this->ctx()->setDouble(this->ctx()->keyFoamRatio(), m_foamRatio->value());
|
||||
ConfirmDialog::toast(this, "已保存");
|
||||
});
|
||||
|
||||
// 泡沫比例(出厂 3%)
|
||||
m_foamPercent = new ParamControl("泡沫比例", "%", 0.5, 0.0, 20.0, true, false, this);
|
||||
m_foamPercent->setValue(ctx->getDouble(ctx->keyFoamPercent(), 3));
|
||||
connect(m_foamPercent, &ParamControl::saveClicked, this, [this]() {
|
||||
this->ctx()->setDouble(this->ctx()->keyFoamPercent(), m_foamPercent->value());
|
||||
ConfirmDialog::toast(this, "已保存");
|
||||
});
|
||||
|
||||
// 空压机流量(出厂 100)
|
||||
m_airFlow = new ParamControl("空压机流量", "L/min", 1, 0.0, 500.0, true, false, this);
|
||||
m_airFlow->setValue(ctx->getDouble(ctx->keyAirFlow(), 100));
|
||||
connect(m_airFlow, &ParamControl::saveClicked, this, [this]() {
|
||||
this->ctx()->setDouble(this->ctx()->keyAirFlow(), m_airFlow->value());
|
||||
ConfirmDialog::toast(this, "已保存");
|
||||
});
|
||||
|
||||
paramsLayout()->addWidget(m_foamRatio);
|
||||
paramsLayout()->addWidget(m_foamPercent);
|
||||
paramsLayout()->addWidget(m_airFlow);
|
||||
}
|
||||
|
||||
void ManualDisinfectPage::resetFactory()
|
||||
{
|
||||
m_foamRatio->setValue(6);
|
||||
m_foamPercent->setValue(3);
|
||||
m_airFlow->setValue(100);
|
||||
ctx()->setDouble(ctx()->keyFoamRatio(), 6);
|
||||
ctx()->setDouble(ctx()->keyFoamPercent(), 3);
|
||||
ctx()->setDouble(ctx()->keyAirFlow(), 100);
|
||||
ConfirmDialog::toast(this, "已恢复出厂值");
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef MANUALDISINFECTPAGE_H
|
||||
#define MANUALDISINFECTPAGE_H
|
||||
|
||||
#include "washbasepage.h"
|
||||
|
||||
class ParamControl;
|
||||
|
||||
// ===================== ⑩ 手动消毒界面 =====================
|
||||
class ManualDisinfectPage : public WashBasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ManualDisinfectPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void resetFactory() override;
|
||||
QString washModeTag() const override { return "disinfect"; }
|
||||
|
||||
private:
|
||||
ParamControl* m_foamRatio = nullptr; // 发泡倍数
|
||||
ParamControl* m_foamPercent = nullptr; // 泡沫比例
|
||||
ParamControl* m_airFlow = nullptr; // 空压机流量
|
||||
};
|
||||
|
||||
#endif // MANUALDISINFECTPAGE_H
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "passwordmgmtloginpage.h"
|
||||
#include "numpaddialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include "global.h"
|
||||
|
||||
PasswordMgmtLoginPage::PasswordMgmtLoginPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
QVBoxLayout* root = new QVBoxLayout(this);
|
||||
root->addStretch(1);
|
||||
|
||||
QWidget* col = new QWidget(this);
|
||||
col->setFixedWidth(700);
|
||||
QVBoxLayout* lay = new QVBoxLayout(col);
|
||||
lay->setSpacing(24);
|
||||
lay->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QLabel* title = label("密码管理登录界面", 28, C::TEXT, true, col);
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(title);
|
||||
|
||||
QLabel* sub = label("请输入管理员密码", 18, C::TEXT_SUB, false, col);
|
||||
sub->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(sub);
|
||||
|
||||
m_input = new QLineEdit(col);
|
||||
m_input->setReadOnly(true);
|
||||
m_input->setEchoMode(QLineEdit::Password);
|
||||
m_input->setAlignment(Qt::AlignCenter);
|
||||
m_input->setFixedSize(400, 88);
|
||||
m_input->setFocusPolicy(Qt::NoFocus);
|
||||
m_input->installEventFilter(this);
|
||||
lay->addWidget(m_input, 0, Qt::AlignHCenter);
|
||||
|
||||
m_error = label("管理员密码错误", 18, C::DANGER, true, col);
|
||||
m_error->setAlignment(Qt::AlignCenter);
|
||||
m_error->hide();
|
||||
lay->addWidget(m_error);
|
||||
|
||||
QHBoxLayout* btnRow = new QHBoxLayout();
|
||||
btnRow->addStretch(1);
|
||||
QPushButton* ok = button("确认", "primary", col);
|
||||
ok->setFixedSize(180, 72);
|
||||
connect(ok, &QPushButton::clicked, this, &PasswordMgmtLoginPage::onConfirm);
|
||||
QPushButton* cancel = button("取消", "", col);
|
||||
cancel->setFixedSize(180, 72);
|
||||
connect(cancel, &QPushButton::clicked, this, &PasswordMgmtLoginPage::onCancel);
|
||||
btnRow->addWidget(ok);
|
||||
btnRow->addWidget(cancel);
|
||||
btnRow->addStretch(1);
|
||||
lay->addLayout(btnRow);
|
||||
|
||||
root->addWidget(col, 0, Qt::AlignHCenter);
|
||||
root->addStretch(1);
|
||||
}
|
||||
|
||||
bool PasswordMgmtLoginPage::eventFilter(QObject* obj, QEvent* ev)
|
||||
{
|
||||
if (obj == m_input && ev->type() == QEvent::MouseButtonPress) {
|
||||
onInputClicked();
|
||||
return true;
|
||||
}
|
||||
return BasePage::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
void PasswordMgmtLoginPage::onInputClicked()
|
||||
{
|
||||
NumPadDialog dlg(this);
|
||||
dlg.setMaxLen(4);
|
||||
dlg.setValue(m_value);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
m_value = dlg.value();
|
||||
m_input->setText(m_value);
|
||||
m_error->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void PasswordMgmtLoginPage::onConfirm()
|
||||
{
|
||||
if (m_value == ctx()->adminPassword()) {
|
||||
clearInput();
|
||||
emit navigate(PageMgmt);
|
||||
} else {
|
||||
m_error->show();
|
||||
QTimer::singleShot(3000, this, [this]() { m_error->hide(); });
|
||||
}
|
||||
}
|
||||
|
||||
void PasswordMgmtLoginPage::onCancel()
|
||||
{
|
||||
clearInput();
|
||||
emit navigate(PageBoot);
|
||||
}
|
||||
|
||||
void PasswordMgmtLoginPage::clearInput()
|
||||
{
|
||||
m_value.clear();
|
||||
m_input->setText(m_value);
|
||||
m_error->hide();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef PASSWORDMGMTLOGINPAGE_H
|
||||
#define PASSWORDMGMTLOGINPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
|
||||
// ===================== 密码管理登录界面 =====================
|
||||
class PasswordMgmtLoginPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PasswordMgmtLoginPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
void clearInput();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void onInputClicked();
|
||||
void onConfirm();
|
||||
void onCancel();
|
||||
|
||||
private:
|
||||
QLineEdit* m_input = nullptr;
|
||||
QLabel* m_error = nullptr;
|
||||
QString m_value;
|
||||
};
|
||||
|
||||
#endif // PASSWORDMGMTLOGINPAGE_H
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "passwordmgmtpage.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include "global.h"
|
||||
|
||||
PasswordMgmtPage::PasswordMgmtPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
QVBoxLayout* root = new QVBoxLayout(this);
|
||||
root->addStretch(1);
|
||||
|
||||
QWidget* col = new QWidget(this);
|
||||
col->setFixedWidth(700);
|
||||
QVBoxLayout* lay = new QVBoxLayout(col);
|
||||
lay->setSpacing(24);
|
||||
lay->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QLabel* title = label("密码管理界面", 28, C::TEXT, true, col);
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(title);
|
||||
|
||||
// 功能选项列表
|
||||
QWidget* option = new QWidget(col);
|
||||
option->setProperty("card", true);
|
||||
option->setStyleSheet(QString("QWidget[card=\"true\"]{background-color:%1;border:2px solid %2;border-radius:12px;}")
|
||||
.arg(C::PANEL, C::BORDER));
|
||||
QVBoxLayout* optLay = new QVBoxLayout(option);
|
||||
m_reset = new QCheckBox("重置密码功能", option);
|
||||
m_reset->setChecked(true);
|
||||
m_reset->setStyleSheet(QString(
|
||||
"QCheckBox{color:%1;font-size:26px;spacing:12px;}"
|
||||
"QCheckBox::indicator{width:32px;height:32px;border:2px solid %2;border-radius:6px;background-color:%3;}"
|
||||
"QCheckBox::indicator:checked{background-color:%4;border-color:%4;image:none;}")
|
||||
.arg(C::TEXT, C::BORDER, C::BG, C::PRIMARY));
|
||||
optLay->addWidget(m_reset);
|
||||
lay->addWidget(option);
|
||||
|
||||
// 确认 / 退出
|
||||
QHBoxLayout* btnRow = new QHBoxLayout();
|
||||
btnRow->addStretch(1);
|
||||
QPushButton* ok = button("确认", "primary", col);
|
||||
ok->setFixedSize(180, 72);
|
||||
connect(ok, &QPushButton::clicked, this, [this]() { emit navigate(PageReset); });
|
||||
QPushButton* exit = button("退出", "", col);
|
||||
exit->setFixedSize(180, 72);
|
||||
connect(exit, &QPushButton::clicked, this, [this]() { emit navigate(PageBoot); });
|
||||
btnRow->addWidget(ok);
|
||||
btnRow->addWidget(exit);
|
||||
btnRow->addStretch(1);
|
||||
lay->addLayout(btnRow);
|
||||
|
||||
root->addWidget(col, 0, Qt::AlignHCenter);
|
||||
root->addStretch(1);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef PASSWORDMGMTPAGE_H
|
||||
#define PASSWORDMGMTPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class QCheckBox;
|
||||
|
||||
// ===================== 密码管理界面 =====================
|
||||
class PasswordMgmtPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PasswordMgmtPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
QCheckBox* m_reset = nullptr;
|
||||
};
|
||||
|
||||
#endif // PASSWORDMGMTPAGE_H
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "passwordresetpage.h"
|
||||
#include "numpaddialog.h"
|
||||
#include "confirmdialog.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include "global.h"
|
||||
|
||||
PasswordResetPage::PasswordResetPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
QVBoxLayout* root = new QVBoxLayout(this);
|
||||
root->addStretch(1);
|
||||
|
||||
QWidget* col = new QWidget(this);
|
||||
col->setFixedWidth(700);
|
||||
QVBoxLayout* lay = new QVBoxLayout(col);
|
||||
lay->setSpacing(24);
|
||||
lay->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QLabel* title = label("重置密码界面", 28, C::TEXT, true, col);
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(title);
|
||||
|
||||
m_input = new QLineEdit(col);
|
||||
m_input->setReadOnly(true);
|
||||
m_input->setEchoMode(QLineEdit::Password);
|
||||
m_input->setAlignment(Qt::AlignCenter);
|
||||
m_input->setFixedSize(400, 88);
|
||||
m_input->setFocusPolicy(Qt::NoFocus);
|
||||
m_input->installEventFilter(this);
|
||||
lay->addWidget(m_input, 0, Qt::AlignHCenter);
|
||||
|
||||
m_hint = label("请输入 4 位有效密码", 18, C::TEXT_SUB, false, col);
|
||||
m_hint->setAlignment(Qt::AlignCenter);
|
||||
lay->addWidget(m_hint);
|
||||
|
||||
QHBoxLayout* btnRow = new QHBoxLayout();
|
||||
btnRow->addStretch(1);
|
||||
QPushButton* ok = button("确认", "primary", col);
|
||||
ok->setFixedSize(160, 72);
|
||||
connect(ok, &QPushButton::clicked, this, &PasswordResetPage::onConfirm);
|
||||
QPushButton* cancel = button("取消", "", col);
|
||||
cancel->setFixedSize(160, 72);
|
||||
connect(cancel, &QPushButton::clicked, this, &PasswordResetPage::onCancel);
|
||||
QPushButton* exit = button("退出", "", col);
|
||||
exit->setFixedSize(160, 72);
|
||||
connect(exit, &QPushButton::clicked, this, &PasswordResetPage::onCancel);
|
||||
btnRow->addWidget(ok);
|
||||
btnRow->addWidget(cancel);
|
||||
btnRow->addWidget(exit);
|
||||
btnRow->addStretch(1);
|
||||
lay->addLayout(btnRow);
|
||||
|
||||
root->addWidget(col, 0, Qt::AlignHCenter);
|
||||
root->addStretch(1);
|
||||
}
|
||||
|
||||
bool PasswordResetPage::eventFilter(QObject* obj, QEvent* ev)
|
||||
{
|
||||
if (obj == m_input && ev->type() == QEvent::MouseButtonPress) {
|
||||
onInputClicked();
|
||||
return true;
|
||||
}
|
||||
return BasePage::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
void PasswordResetPage::onInputClicked()
|
||||
{
|
||||
NumPadDialog dlg(this);
|
||||
dlg.setMaxLen(4);
|
||||
dlg.setValue(m_value);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
m_value = dlg.value();
|
||||
m_input->setText(m_value);
|
||||
m_hint->setText("请输入 4 位有效密码");
|
||||
m_hint->setStyleSheet(QString("color:%1;font-size:20px;").arg(C::TEXT_SUB));
|
||||
}
|
||||
}
|
||||
|
||||
void PasswordResetPage::onConfirm()
|
||||
{
|
||||
// 校验:4 位数字
|
||||
static const QRegularExpression re("^\\d{4}$");
|
||||
if (!re.match(m_value).hasMatch()) {
|
||||
m_hint->setText("密码须为 4 位数字");
|
||||
m_hint->setStyleSheet(QString("color:%1;font-size:20px;font-weight:700;").arg(C::DANGER));
|
||||
return;
|
||||
}
|
||||
ctx()->setPassword(m_value);
|
||||
clearInput();
|
||||
ConfirmDialog::toast(this, "修改成功");
|
||||
emit navigate(PageBoot);
|
||||
}
|
||||
|
||||
void PasswordResetPage::onCancel()
|
||||
{
|
||||
clearInput();
|
||||
emit navigate(PageBoot);
|
||||
}
|
||||
|
||||
void PasswordResetPage::clearInput()
|
||||
{
|
||||
m_value.clear();
|
||||
m_input->setText(m_value);
|
||||
m_hint->setText("请输入 4 位有效密码");
|
||||
m_hint->setStyleSheet(QString("color:%1;font-size:20px;").arg(C::TEXT_SUB));
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef PASSWORDRESETPAGE_H
|
||||
#define PASSWORDRESETPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
|
||||
// ===================== 密码重置界面 =====================
|
||||
class PasswordResetPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PasswordResetPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
void clearInput();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void onInputClicked();
|
||||
void onConfirm();
|
||||
void onCancel();
|
||||
|
||||
private:
|
||||
QLineEdit* m_input = nullptr;
|
||||
QLabel* m_hint = nullptr;
|
||||
QString m_value;
|
||||
};
|
||||
|
||||
#endif // PASSWORDRESETPAGE_H
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "selfcheckpage.h"
|
||||
#include "titlebar.h"
|
||||
#include "ledindicator.h"
|
||||
#include "udpcommunication.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QProgressBar>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include "global.h"
|
||||
|
||||
SelfCheckPage::SelfCheckPage(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);
|
||||
|
||||
// 主区
|
||||
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* faultQuery = button("故障查询", "", left);
|
||||
faultQuery->setFixedHeight(120);
|
||||
connect(faultQuery, &QPushButton::clicked, this, [this]() { emit navigate(PageFault); });
|
||||
m_ignore = button("忽略故障", "", left);
|
||||
m_ignore->setFixedHeight(120);
|
||||
connect(m_ignore, &QPushButton::clicked, this, &SelfCheckPage::onIgnoreFault);
|
||||
m_ignore->setEnabled(false);
|
||||
leftLay->addStretch(1);
|
||||
leftLay->addWidget(faultQuery);
|
||||
leftLay->addStretch(1);
|
||||
leftLay->addWidget(m_ignore);
|
||||
leftLay->addStretch(1);
|
||||
left->setFixedWidth(270);
|
||||
main->addWidget(left);
|
||||
|
||||
// 中央区
|
||||
QWidget* center = new QWidget(this);
|
||||
QVBoxLayout* centerLay = new QVBoxLayout(center);
|
||||
centerLay->setContentsMargins(0, 0, 0, 0);
|
||||
centerLay->setSpacing(12);
|
||||
|
||||
m_status = label("自检中", 36, C::WARNING, true, center);
|
||||
m_status->setAlignment(Qt::AlignCenter);
|
||||
centerLay->addWidget(m_status);
|
||||
|
||||
// 10 个自检项,两列
|
||||
QGridLayout* grid = new QGridLayout();
|
||||
grid->setSpacing(12);
|
||||
const QString names[10] = {
|
||||
"通信系统", "底盘通信", "机械臂通信", "洗消通信", "传感器信号",
|
||||
"清水液位", "球阀D1开启", "球阀D1关闭", "球阀D2开启", "球阀D2关闭"
|
||||
};
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
LedIndicator* led = new LedIndicator(names[i], center);
|
||||
led->setState(LedIndicator::Off);
|
||||
QLabel* res = label("待检测", 20, C::TEXT_SUB, false, center);
|
||||
res->setFixedWidth(120);
|
||||
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);
|
||||
m_leds.append(led);
|
||||
m_results.append(res);
|
||||
}
|
||||
centerLay->addLayout(grid, 1);
|
||||
|
||||
// 进度条
|
||||
m_progress = new QProgressBar(center);
|
||||
m_progress->setRange(0, 100);
|
||||
m_progress->setValue(0);
|
||||
m_progress->setFixedHeight(54);
|
||||
m_progress->setFormat("%p%(0/10)");
|
||||
centerLay->addWidget(m_progress);
|
||||
|
||||
main->addWidget(center, 1);
|
||||
|
||||
// 右按钮列(垂直居中 + 平均分布)
|
||||
QWidget* right = new QWidget(this);
|
||||
QVBoxLayout* rightLay = new QVBoxLayout(right);
|
||||
rightLay->setContentsMargins(0, 0, 0, 0);
|
||||
rightLay->setSpacing(8);
|
||||
QPushButton* help = button("操作说明", "", right);
|
||||
help->setFixedHeight(120);
|
||||
connect(help, &QPushButton::clicked, this, &SelfCheckPage::onHelp);
|
||||
QPushButton* start = button("启动自检", "primary", right);
|
||||
start->setFixedHeight(120);
|
||||
connect(start, &QPushButton::clicked, this, &SelfCheckPage::startSelfCheck);
|
||||
m_job = button("作业选择", "", right);
|
||||
m_job->setFixedHeight(120);
|
||||
connect(m_job, &QPushButton::clicked, this, &SelfCheckPage::onJobSelect);
|
||||
m_job->setEnabled(false);
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(help);
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(start);
|
||||
rightLay->addStretch(1);
|
||||
rightLay->addWidget(m_job);
|
||||
rightLay->addStretch(1);
|
||||
right->setFixedWidth(270);
|
||||
main->addWidget(right);
|
||||
|
||||
root->addLayout(main, 1);
|
||||
|
||||
// 连接通信信号
|
||||
connect(ctx->comm(), &UdpCommunication::selfCheckItem, this, &SelfCheckPage::onSelfCheckItem);
|
||||
connect(ctx->comm(), &UdpCommunication::selfCheckDone, this, &SelfCheckPage::onSelfCheckDone);
|
||||
}
|
||||
|
||||
void SelfCheckPage::showEvent(QShowEvent* ev)
|
||||
{
|
||||
BasePage::showEvent(ev);
|
||||
if (!m_autoStarted) {
|
||||
m_autoStarted = true;
|
||||
startSelfCheck();
|
||||
}
|
||||
}
|
||||
|
||||
void SelfCheckPage::startSelfCheck()
|
||||
{
|
||||
m_done = false;
|
||||
m_job->setEnabled(false);
|
||||
m_ignore->setEnabled(false);
|
||||
m_progress->setValue(0);
|
||||
m_progress->setFormat("%p%(0/10)");
|
||||
m_status->setText("自检中");
|
||||
m_status->setStyleSheet(QString("color:%1;font-size:36px;font-weight:700;").arg(C::WARNING));
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
m_leds[i]->setState(LedIndicator::Off);
|
||||
m_results[i]->setText("待检测");
|
||||
m_results[i]->setStyleSheet(QString("color:%1;font-size:22px;").arg(C::TEXT_SUB));
|
||||
}
|
||||
ctx()->comm()->sendSelfCheckStart();
|
||||
}
|
||||
|
||||
void SelfCheckPage::resetAll()
|
||||
{
|
||||
m_autoStarted = false;
|
||||
}
|
||||
|
||||
void SelfCheckPage::onSelfCheckItem(int index, bool ok)
|
||||
{
|
||||
if (index < 0 || index >= 10)
|
||||
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:22px;").arg(C::SUCCESS));
|
||||
} else {
|
||||
m_leds[index]->setState(LedIndicator::RedBlink);
|
||||
m_results[index]->setText("异常");
|
||||
m_results[index]->setStyleSheet(QString("color:%1;font-size:22px;font-weight:700;").arg(C::DANGER));
|
||||
}
|
||||
int pct = (index + 1) * 10;
|
||||
m_progress->setValue(pct);
|
||||
m_progress->setFormat(QString("%p%(%1/10)").arg(index + 1));
|
||||
}
|
||||
|
||||
void SelfCheckPage::onSelfCheckDone(bool allOk)
|
||||
{
|
||||
m_done = true;
|
||||
if (allOk) {
|
||||
m_status->setText("自检完成");
|
||||
m_status->setStyleSheet(QString("color:%1;font-size:36px;font-weight:700;").arg(C::SUCCESS));
|
||||
m_job->setEnabled(true);
|
||||
// 【设计建议】自检完成 2s 后自动进入作业选择
|
||||
QTimer::singleShot(2000, this, [this]() {
|
||||
if (m_done && isVisible())
|
||||
emit navigate(PageJob);
|
||||
});
|
||||
} else {
|
||||
m_status->setText("无法完成自检 请检查设备");
|
||||
m_status->setStyleSheet(QString("color:%1;font-size:36px;font-weight:700;").arg(C::DANGER));
|
||||
startBlink(m_status, "blink");
|
||||
m_ignore->setEnabled(true);
|
||||
m_job->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SelfCheckPage::onIgnoreFault()
|
||||
{
|
||||
emit navigate(PageJob);
|
||||
}
|
||||
|
||||
void SelfCheckPage::onJobSelect()
|
||||
{
|
||||
emit navigate(PageJob);
|
||||
}
|
||||
|
||||
void SelfCheckPage::onHelp()
|
||||
{
|
||||
QMessageBox::information(this, "操作说明",
|
||||
"系统自检将依次检测:通信系统、底盘通信、机械臂通信、洗消通信、"
|
||||
"传感器信号、清水液位及电动球阀 D1/D2 的开启与关闭。\n"
|
||||
"全部正常后方可进入作业选择界面。");
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef SELFCHECKPAGE_H
|
||||
#define SELFCHECKPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
#include <QVector>
|
||||
|
||||
class TitleBar;
|
||||
class QLabel;
|
||||
class QProgressBar;
|
||||
class QPushButton;
|
||||
class LedIndicator;
|
||||
|
||||
// ===================== ④ 自检界面(系统启动及自检) =====================
|
||||
class SelfCheckPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SelfCheckPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
|
||||
void startSelfCheck();
|
||||
void resetAll();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void onSelfCheckItem(int index, bool ok);
|
||||
void onSelfCheckDone(bool allOk);
|
||||
void onIgnoreFault();
|
||||
void onJobSelect();
|
||||
void onHelp();
|
||||
|
||||
private:
|
||||
TitleBar* m_titleBar = nullptr;
|
||||
QLabel* m_status = nullptr;
|
||||
QVector<LedIndicator*> m_leds;
|
||||
QVector<QLabel*> m_results;
|
||||
QProgressBar* m_progress = nullptr;
|
||||
QPushButton* m_ignore = nullptr;
|
||||
QPushButton* m_job = nullptr;
|
||||
bool m_autoStarted = false;
|
||||
bool m_done = false;
|
||||
};
|
||||
|
||||
#endif // SELFCHECKPAGE_H
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "warningpage.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include "global.h"
|
||||
|
||||
WarningPage::WarningPage(AppContext* ctx, QWidget* parent)
|
||||
: BasePage(ctx, parent)
|
||||
{
|
||||
QVBoxLayout* root = new QVBoxLayout(this);
|
||||
root->addStretch(1);
|
||||
|
||||
QWidget* col = new QWidget(this);
|
||||
QVBoxLayout* lay = new QVBoxLayout(col);
|
||||
lay->setSpacing(40);
|
||||
lay->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
QLabel* txt = label("密码输入有误,请重新输入", 26, C::DANGER, true, col);
|
||||
txt->setAlignment(Qt::AlignCenter);
|
||||
txt->setProperty("alert", true);
|
||||
startBlink(txt, "blink");
|
||||
lay->addWidget(txt);
|
||||
|
||||
QPushButton* retry = button("重新输入", "primary", col);
|
||||
retry->setFixedSize(220, 76);
|
||||
connect(retry, &QPushButton::clicked, this, [this]() { emit navigate(PageBoot); });
|
||||
lay->addWidget(retry, 0, Qt::AlignHCenter);
|
||||
|
||||
root->addWidget(col, 0, Qt::AlignHCenter);
|
||||
root->addStretch(1);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef WARNINGPAGE_H
|
||||
#define WARNINGPAGE_H
|
||||
|
||||
#include "basepage.h"
|
||||
|
||||
// ===================== 密码警告界面 =====================
|
||||
class WarningPage : public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WarningPage(AppContext* ctx, QWidget* parent = nullptr);
|
||||
};
|
||||
|
||||
#endif // WARNINGPAGE_H
|
||||
@@ -0,0 +1,251 @@
|
||||
#include "washbasepage.h"
|
||||
#include "titlebar.h"
|
||||
#include "alarmbanner.h"
|
||||
#include "datacard.h"
|
||||
#include "ledindicator.h"
|
||||
#include "udpcommunication.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("柱塞泵故障代码", "");
|
||||
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);
|
||||
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);
|
||||
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(), &UdpCommunication::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();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef WASHBASEPAGE_H
|
||||
#define WASHBASEPAGE_H
|
||||
|
||||
#include "basepage.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
|
||||
Reference in New Issue
Block a user