Files
QT_wuhan_YT/pages/washpage.cpp
T
2026-10-14 18:22:41 +08:00

370 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "washpage.h"
#include "titlebar.h"
#include "alarmbanner.h"
#include "datacard.h"
#include "ledindicator.h"
#include "paramcontrol.h"
#include "car.h"
#include "confirmdialog.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QTimer>
#include "global.h"
WashPage::WashPage(AppContext* ctx, Mode mode, QWidget* parent)
: BasePage(ctx, parent), m_mode(mode)
{
const bool clean = (mode == Clean);
QVBoxLayout* root = new QVBoxLayout(this);
root->setContentsMargins(0, 0, 0, 0);
root->setSpacing(0);
m_titleBar = new TitleBar(ctx, this);
m_titleBar->setTitle(clean ? "消除作业" : "消毒作业");
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(600); // 需容纳最长的“末端位置:X:1200 Y:300 Z:800”(42px 字号约 523px)
main->addSpacing(60); // 左列整体右移的量;空白由中间拉伸列让出,卡片宽度不变
main->addWidget(left);
// 中央洗消卡
m_washCard = new DataCard("洗消", this);
m_fMode = m_washCard->addField("洗消模式", "--");
m_fLevelA = m_washCard->addField("A组分液位", "100L");
m_fLevelFoam = m_washCard->addField("泡沫箱液位", "30L");
m_fFlowA = m_washCard->addField("A药剂流量", "0 L/min");
m_fFlowB = m_washCard->addField("B药剂流量", "0 L/min");
m_fGasFlow = m_washCard->addField("气体流量", "0 L/min");
m_fPress = m_washCard->addField("管路压力", "0 MPa");
m_fAirPress = m_washCard->addField("空压机压力", "0 MPa");
m_fFoam = m_washCard->addField("发泡倍数", "0");
m_fAirFault = m_washCard->addField("空压机故障代码", "");
m_fPlungerFault = m_washCard->addField("柱塞泵故障代码", "");
main->addSpacing(60);
main->addWidget(m_washCard, 1);
// 右侧操作列
QWidget* right = new QWidget(this);
QVBoxLayout* rightLay = new QVBoxLayout(right);
rightLay->setContentsMargins(20, 20, 20, 10);
rightLay->setSpacing(60);
m_start = button("启动", "primary", right);
m_start->setFixedSize(180, 100);
connect(m_start, &QPushButton::clicked, this, &WashPage::onStart);
m_stop = button("停止", "danger", right);
m_stop->setFixedSize(180, 100);
connect(m_stop, &QPushButton::clicked, this, &WashPage::onStop);
rightLay->addWidget(m_start, 0, Qt::AlignHCenter);
rightLay->addWidget(m_stop, 0, Qt::AlignHCenter);
// 参数区:按模式不同
m_paramsLayout = new QVBoxLayout();
m_paramsLayout->setSpacing(8);
rightLay->addLayout(m_paramsLayout);
rightLay->addStretch(1);
if (clean) {
// 柱塞泵流量(出厂 20;协议 PlungerPump_flow 0-24 L/min)
m_pumpFlow = new ParamControl("柱塞泵流量", "L/min", 0.1, 0.0, 24.0, true, false, this);
m_pumpFlow->setValue(ctx->getDouble(ctx->keyPumpFlow(), 20));
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());
refreshPumpValues(); // 底部数值跟着更新
ConfirmDialog::toast(this, "已保存");
});
m_paramsLayout->addWidget(m_pumpFlow);
} else {
// 发泡倍数(出厂 6;协议 Expansion_ratioCmd 6-12 倍)
m_foamRatio = new ParamControl("发泡倍数", "倍", 0.1, 6.0, 12.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());
this->ctx()->comm()->sendSetParam(this->ctx()->keyFoamRatio(), m_foamRatio->value());
ConfirmDialog::toast(this, "已保存");
});
m_paramsLayout->addWidget(m_foamRatio);
// 泡沫比例(出厂 3%;协议 Foam_ratio 0-10%)
m_foamPercent = new ParamControl("泡沫比例", "%", 0.1, 0.0, 10.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());
this->ctx()->comm()->sendSetParam(this->ctx()->keyFoamPercent(), m_foamPercent->value());
ConfirmDialog::toast(this, "已保存");
});
m_paramsLayout->addWidget(m_foamPercent);
// 空压机流量(出厂 100;协议 AirComp_flow 0-340 L/min)
m_airFlow = new ParamControl("空压机流量", "L/min", 1, 0.0, 340.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());
this->ctx()->comm()->sendSetParam(this->ctx()->keyAirFlow(), m_airFlow->value());
refreshPumpValues(); // 底部数值跟着更新
ConfirmDialog::toast(this, "已保存");
});
m_paramsLayout->addWidget(m_airFlow);
}
right->setFixedWidth(500); // 参数行需容纳 输入框150 + 单位约101 + 保存140 + 两个间距24 ≈ 415px
main->addSpacing(60); // 洗消卡 ↔ 右列的间隙;空白由中间拉伸列让出,卡片文字不受影响
main->addWidget(right);
root->addLayout(main, 1);
// 底部条:左右两个按钮纵向占满两行,中间上下两排(泵数值 / 指示灯)居中
QWidget* bottom = new QWidget(this);
bottom->setObjectName("WashBottom");
bottom->setFixedHeight(160);
// 用 #WashBottom 限定选择器:不带选择器的样式表会连子控件一起套上边框和底色
bottom->setStyleSheet(QString("QWidget#WashBottom{background-color:%1;border-top:1px solid %2;}")
.arg(C::PANEL, C::BORDER));
QHBoxLayout* bottomRoot = new QHBoxLayout(bottom);
bottomRoot->setContentsMargins(20, 8, 20, 8);
bottomRoot->setSpacing(12);
// 左:恢复出厂值(占满两行高度)
m_factory = button("恢复出厂值", "", bottom);
m_factory->setFixedSize(220, 100);
m_factory->setStyleSheet(cancelButtonStyle(40)); // 选中(悬停/按下/焦点)红底白字
connect(m_factory, &QPushButton::clicked, this, &WashPage::onFactory);
bottomRoot->addWidget(m_factory, 0, Qt::AlignVCenter);
// 中:上排三泵数值、下排指示灯,两排都水平居中
QVBoxLayout* midLay = new QVBoxLayout();
midLay->setSpacing(6);
QHBoxLayout* valueLay = new QHBoxLayout();
valueLay->setSpacing(40);
valueLay->addStretch(1);
auto addFlow = [&](const QString& name) {
valueLay->addWidget(label(name + ":", 40, C::TEXT_SUB, false, bottom));
QLabel* v = label("--", 40, C::TEXT, true, bottom);
valueLay->addWidget(v);
return v;
};
m_valPlunger = addFlow("柱塞泵");
m_valAir = addFlow("空压机");
m_valLeaf = addFlow("叶片泵");
refreshPumpValues(); // 开机先显示参数的出厂默认值
valueLay->addStretch(1);
QHBoxLayout* ledLay = new QHBoxLayout();
ledLay->setSpacing(10); // 8 个灯 40px 字号后总宽约 1300px
ledLay->addStretch(1);
auto makeLed = [&](const QString& text) {
LedIndicator* led = new LedIndicator(text, bottom);
led->setTextPixelSize(40); // 字号与卡片标题一致
ledLay->addWidget(led);
return led;
};
m_ledRun = makeLed("启停");
m_ledPlunger = makeLed("柱塞泵");
m_ledAir = makeLed("空压机");
m_ledLeaf = makeLed("叶片泵");
m_ledLevel = makeLed("液位低");
m_ledD1 = makeLed("D1");
m_ledD2 = makeLed("D2");
m_ledFault = makeLed("系统故障");
ledLay->addStretch(1);
midLay->addStretch(1);
midLay->addLayout(valueLay);
midLay->addLayout(ledLay);
midLay->addStretch(1);
bottomRoot->addLayout(midLay, 1);
// 右:返回(占满两行高度)
m_back = button("返回", "", bottom);
m_back->setFixedSize(170, 100);
m_back->setStyleSheet(cancelButtonStyle(40)); // 选中(悬停/按下/焦点)红底白字
connect(m_back, &QPushButton::clicked, this, &WashPage::onBack);
bottomRoot->addWidget(m_back, 0, Qt::AlignVCenter);
root->addWidget(bottom);
// 故障代码轮流循环显示(2s/条)
m_faultTimer = new QTimer(this);
m_faultTimer->setInterval(2000);
connect(m_faultTimer, &QTimer::timeout, this, &WashPage::rotateFaults);
m_faultTimer->start();
connect(ctx->comm(), &CarCommunication::telemetryUpdated, this, &WashPage::refresh);
updateGating();
refresh();
}
QString WashPage::washModeTag() const
{
return m_mode == Clean ? "clean" : "disinfect";
}
// 底部三个泵的数值:柱塞泵/空压机跟右侧参数的设定值(没改过就是出厂默认值),
// 叶片泵本页没有对应参数,先取遥测的实时流量
void WashPage::refreshPumpValues()
{
// 这里读的是配置值,一并按协议量程夹取(柱塞泵 0-24、空压机 0-340 L/min),
// 否则旧配置里超范围的历史值会与控件显示、实际上报的数值对不上
m_valPlunger->setText(QString("%1 L/min").arg(qBound(0.0, ctx()->getDouble(ctx()->keyPumpFlow(), 20), 24.0), 0, 'f', 1));
m_valAir->setText(QString("%1 L/min").arg(qBound(0.0, ctx()->getDouble(ctx()->keyAirFlow(), 100), 340.0), 0, 'f', 1));
m_valLeaf->setText(QString("%1 L/min").arg(ctx()->telemetry.flowB, 0, 'f', 1));
}
void WashPage::onStart()
{
ctx()->comm()->sendWashStart(washModeTag());
ctx()->washRunning = true;
updateGating();
}
void WashPage::onStop()
{
ctx()->comm()->sendWashStop();
ctx()->washRunning = false;
updateGating();
}
void WashPage::onBack()
{
if (ctx()->washRunning) {
ConfirmDialog::toast(this, "请先停止");
return;
}
emit navigate(PageJob);
}
void WashPage::onFactory()
{
if (ConfirmDialog::confirm(this, "恢复出厂值", "确定将参数恢复为出厂值?"))
resetFactory();
}
void WashPage::resetFactory()
{
if (m_mode == Clean) {
m_pumpFlow->setValue(20); // 出厂 20
ctx()->setDouble(ctx()->keyPumpFlow(), 20);
ctx()->comm()->sendSetParam(ctx()->keyPumpFlow(), 20);
} else {
m_foamRatio->setValue(6); // 出厂 6
m_foamPercent->setValue(3); // 出厂 3%
m_airFlow->setValue(100); // 出厂 100
ctx()->setDouble(ctx()->keyFoamRatio(), 6);
ctx()->setDouble(ctx()->keyFoamPercent(), 3);
ctx()->setDouble(ctx()->keyAirFlow(), 100);
ctx()->comm()->sendSetParam(ctx()->keyFoamRatio(), 6);
ctx()->comm()->sendSetParam(ctx()->keyFoamPercent(), 3);
ctx()->comm()->sendSetParam(ctx()->keyAirFlow(), 100);
}
refreshPumpValues(); // 底部数值跟着回到出厂值
ConfirmDialog::toast(this, "已恢复出厂值");
}
void WashPage::updateGating()
{
bool running = ctx()->washRunning;
m_start->setEnabled(!running);
m_stop->setEnabled(running);
m_back->setEnabled(!running);
}
void WashPage::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);
// 底部三个泵的数值
refreshPumpValues();
// 故障代码显示(静态更新,循环由 rotateFaults 驱动)
rotateFaults();
}
void WashPage::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();
}