#include "blastopepage.h" #include #include #include #include "../components/custommessagebox.h" #include "../components/loadingwidget.h" #include "../registryManager/registrymanager.h" #include "../utils/global.h" #include "../utils/logger.h" #include "countdownwidget.h" #include "ui_blastopepage.h" const int ColIndexBlastStatus = 6; const int ColIndexProgressBar = 7; const int ColIndexOpBtn = 8; const int ColIndexBlasterDev = 1; BlastOpePage::BlastOpePage(QWidget *parent) : QWidget(parent), ui(new Ui::BlastOpePage), dao(DatabaseManager::getInstance().getDatabase()) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); bool isAllSafetyInspectorConfirmed = true; const QJsonObject data = backendAPIManager::getSafetyInspect(QDate::currentDate().toString("yyyy-MM-dd")); for (const QJsonValue &value : data["safetyInspectors"].toArray()) { if (value.isObject()) { if (value.toObject()["confirmTime"].toString() == "") { isAllSafetyInspectorConfirmed = false; break; } } } if (!isAllSafetyInspectorConfirmed) { bool isContinue = CustomMessageBox::showConfirmDialog("警告", "安全检查没有全部确认,是否继续?", "继续", "取消", this); if (!isContinue) { return; } } if (isFaceVerificationEnabled) { InitFace(); } else { ui->setupUi(this); initPagination(); } } void BlastOpePage::showCountDownWidget(QString uuid, const QString &topic, const QString &message) { CountdownWidget *countdownWidget = new CountdownWidget(this); countdownWidget->resize(200, 200); int x = (this->width() - countdownWidget->width()) / 2; int y = (this->height() - countdownWidget->height()) / 2; countdownWidget->move(x, y); countdownWidget->show(); firingWidget *widget = firingWidgetByUuid.value(uuid); if (widget) { connect( countdownWidget, &CountdownWidget::countdownFinished, widget, [widget, topic, message, countdownWidget]() { widget->onCountdownFinished(topic, message); }, Qt::SingleShotConnection); } } void BlastOpePage::InitFace() { Logger::getInstance().info("start init face verification"); LoadingWidget::showLoading(this, "请求创建人脸识别..."); layout = new QVBoxLayout(this); // TODO: relase the qwebengineview when not successfully verified view = new QWebEngineView(this); view->setAttribute(Qt::WA_OpaquePaintEvent); QWebEnginePage *page = view->page(); QJsonObject metaInfo = getMetaInfo(); if (metaInfo["certName"] == "" || metaInfo["certNo"] == "") { QMessageBox::information(nullptr, "获取用户信息错误", "未获得用户的身份证信息,请联系管理员"); return; } QObject::connect(page, &QWebEnginePage::featurePermissionRequested, [this, page](const QUrl &securityOrigin, QWebEnginePage::Feature feature) { handleFeaturePermission(page, securityOrigin, feature); }); Logger::getInstance().info("FaceVerification: connect"); QUrl postUrl(apiBackendUrl.resolved(QUrl("h-face-verify/pc"))); QJsonObject response = sendPostRequest(postUrl, metaInfo); QString certifyUrl; if (response["code"] != 200) { Logger::getInstance().error( QString("创建人脸识别请求服务器返回错误: userName: %1. response: %2") .arg(metaInfo["certName"].toString(), QString::fromUtf8(QJsonDocument(response).toJson()))); QMessageBox::critical(nullptr, "错误", "无法创建人脸识别,请确认后台录入的身份信息正确"); return; } if (response.contains("data") && response["data"].isObject()) { LoadingWidget::showLoading(this, "人脸识别请求已创建..."); QJsonObject dataObject = response["data"].toObject(); if (dataObject.contains("ResultObject") && dataObject["ResultObject"].isObject()) { QJsonObject resultObject = dataObject["ResultObject"].toObject(); if (resultObject.contains("CertifyId") && resultObject["CertifyId"].isString()) { certifyId = resultObject["CertifyId"].toString(); } if (resultObject.contains("CertifyUrl") && resultObject["CertifyUrl"].isString()) { certifyUrl = resultObject["CertifyUrl"].toString(); } } } if (!certifyUrl.isEmpty()) { view->load(QUrl(certifyUrl)); layout->addWidget(view); layout->setStretchFactor(view, 1); QObject::connect(page, &QWebEnginePage::urlChanged, this, &BlastOpePage::onUrlChanged); } else { QMessageBox::information(nullptr, "提示", "人脸识别请求失败"); Logger::getInstance().error("FaceVerificationInit: Failed to get certifyUrl"); LoadingWidget::hideLoading(); } } void BlastOpePage::closeWebViewAndRestoreUI() { if (view) { layout->removeWidget(view); delete view; view = nullptr; } if (layout) { delete layout; layout = nullptr; } } // 槽函数:处理 URL 改变事件 void BlastOpePage::onUrlChanged(const QUrl &newUrl) { LoadingWidget::showLoading(this, "查询验证结果..."); if (newUrl.host() == "www.integrateblaster.com") { closeWebViewAndRestoreUI(); QNetworkAccessManager manager; QUrl requestUrl(apiBackendUrl.resolved(QUrl(QString("h-face-verify/certifyId/%1").arg(certifyId)))); QNetworkRequest request(requestUrl); QNetworkReply *reply = manager.get(request); QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); if (reply->error() == QNetworkReply::NoError) { QByteArray responseData = reply->readAll(); QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData); if (!jsonDoc.isNull() && jsonDoc.isObject()) { QJsonObject rootObj = jsonDoc.object(); QJsonObject dataObj = rootObj["data"].toObject(); QString message = dataObj["Message"].toString(); QJsonObject resultObj = dataObj["ResultObject"].toObject(); if (resultObj.isEmpty()) { Logger::getInstance().error(QString("获取认证初始化数据失败. message: %1.").arg(message)); int ret = QMessageBox::information(nullptr, "认证失败", message + " ,请重新认证!"); if (ret == QMessageBox::Ok) { InitFace(); } } else { QString passed = resultObj["Passed"].toString(); if (passed == "T") { ui->setupUi(this); initPagination(); Logger::getInstance().info(QString("进入认证界面")); LoadingWidget::hideLoading(); } else if (passed == "F") { int ret = QMessageBox::critical(nullptr, "提示", "操作失败,请重新认证!"); if (ret == QMessageBox::Ok) { InitFace(); } } } } } else { qDebug() << "Request failed:" << reply->errorString(); Logger::getInstance().error( QString("InitFaseVerification request failed. error message: %1").arg(reply->errorString())); } reply->deleteLater(); LoadingWidget::hideLoading(); } LoadingWidget::hideLoading(); } BlastOpePage::~BlastOpePage() { delete ui; if (view) { delete view; } if (layout) { delete layout; } } QJsonObject BlastOpePage::sendPostRequest(const QUrl &url, const QJsonObject &data) { QNetworkAccessManager manager; QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QJsonDocument doc(data); QByteArray postData = doc.toJson(); QNetworkReply *reply = manager.post(request, postData); QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); QJsonObject responseJson; if (reply->error() == QNetworkReply::NoError) { QByteArray responseData = reply->readAll(); QJsonDocument responseDoc = QJsonDocument::fromJson(responseData); if (!responseDoc.isNull() && responseDoc.isObject()) { responseJson = responseDoc.object(); } } else { qDebug() << "Error fetching content: " << reply->errorString(); } reply->deleteLater(); return responseJson; } void BlastOpePage::handleFeaturePermission(QWebEnginePage *page, const QUrl &securityOrigin, QWebEnginePage::Feature feature) { if (feature == QWebEnginePage::MediaAudioCapture || feature == QWebEnginePage::MediaAudioVideoCapture || feature == QWebEnginePage::MediaVideoCapture) { page->setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionGrantedByUser); } else { page->setFeaturePermission(securityOrigin, feature, QWebEnginePage::PermissionDeniedByUser); } } QJsonObject BlastOpePage::getMetaInfo() { QJsonObject metaInfo; QString certName; QString certNo; QMap userInfo = RegistryManager::instance()->getCurentLoginUser(); certName = userInfo.value("certName", ""); certNo = userInfo.value("identity", ""); metaInfo["certName"] = certName; metaInfo["certNo"] = certNo; return metaInfo; } void BlastOpePage::initPagination() { // // pageWidget = new PageWidget; // //connect(pageWidget, &PageWidget::currentPageChanged, this, &BlastOpePage::PageChanged); // connect(pageWidget->getComboBox(), QOverload::of(&QComboBox::currentIndexChanged), this, // &BlastOpePage::onComboBoxIndexChanged); // ui->verticalLayout_4->addWidget(pageWidget); m_pageSize = 100; m_currentPage = 1; RefreshData(); } void BlastOpePage::RefreshData() { loadDataAndDrawTable(m_currentPage, m_pageSize); } void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) { LoadingWidget::showLoading(this, "正在加载数据..."); QJsonObject extParams{{"blastStatus", QJsonArray({"2"})}}; QJsonObject result = backendAPIManager::getHProjects(currentPage, pageSize, extParams); QList> projectList = dao.getHProjectsFromJsonArray(result["data"].toObject()["list"].toArray()); QMap>> groupedProjects; for (const QSharedPointer &project : projectList) { QString key = project->getPcSn(); if (!groupedProjects.contains(key)) { groupedProjects[key] = QList>(); } groupedProjects[key].append(project); } totalCount = result["data"].toObject()["count"].toInt(); // pageWidget->setMaxPage(ceil(static_cast(totalCount) / pageSize)); // Clear previous data pcSnToFirstRowMap.clear(); progressBars.clear(); model = new QStandardItemModel(this); headers = { {"选择", ""}, {"井下起爆器", "pcSn"}, {"工程名称", "name"}, {"井下地址", "addressPath"}, {"雷管数量", "detSum"}, {"炸药量(kg)", "blastCount"}, {"起爆状态", "blastStatus"}, {"进度", ""}, {"操作", ""}, }; int headerCount = headers.size(); QStringList headerLabels; QMap propMap; for (int i = 0; i < headers.size(); ++i) { headerLabels << headers[i].label; propMap[i] = headers[i].prop; } model->setHorizontalHeaderLabels(headerLabels); // Create rows with sub-rows for each project, grouped by pcSn QStringList pcSnKeys = groupedProjects.keys(); int currentRow = 0; for (const QString &pcSn : pcSnKeys) { QList> projectsForPcSn = groupedProjects[pcSn]; pcSnToFirstRowMap[pcSn] = currentRow; // Store the first row index for this pcSn // Calculate aggregated blast status for this pcSn group QString combinedBlastStatus = ""; QSet statusSet; for (const QSharedPointer &project : projectsForPcSn) { QMetaProperty statusProp = project->metaObject()->property(project->metaObject()->indexOfProperty("blastStatus")); QString status = statusProp.read(project.data()).toString(); statusSet.insert(status); } // Determine the combined status based on all projects in the group if (statusSet.size() == 1) { // All projects have the same status combinedBlastStatus = statusSet.values().first(); } else { // Mixed statuses - use the most advanced status if (statusSet.contains(BlastStatus::Blasted)) { combinedBlastStatus = BlastStatus::Blasted; } else { combinedBlastStatus = BlastStatus::Registered; } } // Store all UUIDs for this pcSn group in the first row QStandardItem *uuidItem = new QStandardItem(); QStringList uuids; for (const QSharedPointer &project : projectsForPcSn) { uuids << project->getUuid(); } uuidItem->setData(uuids.join(","), Qt::UserRole); model->setItem(currentRow, headerCount, uuidItem); // Create a sub-row for each project in this pcSn group for (int subIndex = 0; subIndex < projectsForPcSn.size(); ++subIndex) { HProject &project = *projectsForPcSn[subIndex].data(); for (int col = 0; col < headerCount; ++col) { QString prop = propMap[col]; QStandardItem *item = nullptr; if (col == 0) { // Only show checkbox on the first row of each pcSn group if (subIndex == 0) { item = new QStandardItem(); item->setCheckable(true); item->setCheckState(Qt::Unchecked); item->setText(""); } else { item = new QStandardItem(""); } } else if (prop == "pcSn") { // Only show pcSn on the first row of each group if (subIndex == 0) { item = new QStandardItem(pcSn); } else { item = new QStandardItem(""); } } else if (prop == "blastStatus") { // Only show aggregated blast status on the first row of each group if (subIndex == 0) { QString statusText; if (combinedBlastStatus == BlastStatus::Registered) { statusText = "待起爆"; item = new QStandardItem(statusText); item->setForeground(QColor("#f3a3k'k1")); } else if (combinedBlastStatus == BlastStatus::Blasted) { statusText = "起 爆 完 成"; item = new QStandardItem(statusText); item->setForeground(QColor("#90d543")); } else { item = new QStandardItem(combinedBlastStatus); } } else { item = new QStandardItem(""); } } else if (col == ColIndexProgressBar || col == ColIndexOpBtn) { // Progress and operation columns will be handled separately with widgets // Only on the first row of each group if (subIndex == 0) { item = new QStandardItem(""); } else { item = new QStandardItem(""); } } else if (!prop.isEmpty()) { // Show individual project data for other columns QMetaProperty metaProp = project.metaObject()->property(project.metaObject()->indexOfProperty(prop.toUtf8())); QVariant value = metaProp.read(&project); item = new QStandardItem(value.toString()); } if (item) { item->setTextAlignment(Qt::AlignCenter); model->setItem(currentRow, col, item); } } currentRow++; } } ui->tableView->setModel(model); connectionItem = QObject::connect(model, &QStandardItemModel::itemChanged, this, &BlastOpePage::onItemCheckboxChanged); ui->tableView->setColumnWidth(0, 30); ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); // 禁止编辑 for (int i = 1; i < headerCount; ++i) { if (i == 8) { // Column: 进度 ui->tableView->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Custom); ui->tableView->setColumnWidth(i, 180); } else { ui->tableView->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Stretch); } } ui->tableView->setColumnHidden(headerCount, true); ui->tableView->setAlternatingRowColors(true); ui->tableView->verticalHeader()->setDefaultSectionSize(50); // Create progress bars and operation buttons only for the first row of each pcSn group for (const QString &pcSn : pcSnKeys) { QList> projectsForPcSn = groupedProjects[pcSn]; int firstRowIndex = pcSnToFirstRowMap[pcSn]; int groupRowSpan = projectsForPcSn.size(); // 初始化progressBars for the first row of this pcSn group int progressCol = headers.size() - 2; QProgressBar *progressBar1 = new QProgressBar(ui->tableView); QProgressBar *progressBar2 = new QProgressBar(ui->tableView); QProgressBar *progressBar3 = new QProgressBar(ui->tableView); QHBoxLayout *progressBarLayout = new QHBoxLayout; progressBars.append(ProgressBarTriple(progressBar1, progressBar2, progressBar3)); for (QProgressBar *pb : {progressBar1, progressBar2, progressBar3}) { pb->setRange(0, 100); pb->setValue(0); pb->setAlignment(Qt::AlignCenter); pb->setStyleSheet( "QProgressBar { border: 1px solid grey; border-radius: 5px; background-color: #EEEEEE; " "height: 10px; }" "QProgressBar::chunk { background-color: #05B8CC; width: 2px; margin: 0.5px; border - " "radius: 10px; }"); pb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); progressBarLayout->addWidget(pb); } progressBarLayout->setAlignment(Qt::AlignCenter); progressBarLayout->setContentsMargins(0, 0, 0, 0); progressBarLayout->setSpacing(0); QWidget *progressBarContainer = new QWidget(ui->tableView); progressBarContainer->setLayout(progressBarLayout); QModelIndex progressIndex = model->index(firstRowIndex, progressCol); if (progressIndex.isValid()) { ui->tableView->setIndexWidget(progressIndex, progressBarContainer); // Set row span to cover all sub-rows for this pcSn ui->tableView->setSpan(firstRowIndex, progressCol, groupRowSpan, 1); } // 创建操作按钮 for the first row of this pcSn group int operationCol = headers.size() - 1; QWidget *widget = new QWidget(ui->tableView); QPushButton *button = new QPushButton(widget); button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); button->setStyleSheet("QPushButton:disabled { background-color: #d3d3d3; color: gray; }"); QModelIndex statusIndex = model->index(firstRowIndex, ColIndexBlastStatus); if (statusIndex.isValid()) { QString blastStatus = model->data(statusIndex).toString(); if (blastStatus == "待起爆") { button->setText(startBlastButtonTxt); button->setEnabled(true); } } QHBoxLayout *layout = new QHBoxLayout(widget); layout->addWidget(button); layout->setAlignment(Qt::AlignCenter); layout->setContentsMargins(0, 0, 0, 0); widget->setLayout(layout); QModelIndex index = model->index(firstRowIndex, operationCol); if (index.isValid()) { ui->tableView->setIndexWidget(index, widget); // Set row span to cover all sub-rows for this pcSn ui->tableView->setSpan(firstRowIndex, operationCol, groupRowSpan, 1); connect(button, &QPushButton::clicked, [this, firstRowIndex, button]() { handleSingleBlastButtonClicked(firstRowIndex, button); }); } // Also set span for the pcSn column to merge cells ui->tableView->setSpan(firstRowIndex, ColIndexBlasterDev, groupRowSpan, 1); // Set span for the checkbox column ui->tableView->setSpan(firstRowIndex, 0, groupRowSpan, 1); // Set span for the blast status column ui->tableView->setSpan(firstRowIndex, ColIndexBlastStatus, groupRowSpan, 1); } LoadingWidget::hideLoading(); } // 切换页数 void BlastOpePage::PageChanged(int page) { m_currentPage = page; loadDataAndDrawTable(m_currentPage, m_pageSize); } void BlastOpePage::onComboBoxIndexChanged(int index) { // QVariant variant = pageWidget->getComboBox()->itemData(index); // int value = variant.toInt(); // m_pageSize = value; // m_currentPage = 1; // loadDataAndDrawTable(m_currentPage, m_pageSize); } void BlastOpePage::updateProgressBar(int firingStage, int row) { // Find the progress bar index for this row's pcSn group QString pcSn; QStandardItem *devItem = model->item(row, ColIndexBlasterDev); if (devItem) { pcSn = devItem->text(); if (pcSn.isEmpty()) { // This might be a sub-row, need to find the parent row with pcSn for (int i = row - 1; i >= 0; i--) { QStandardItem *parentDevItem = model->item(i, ColIndexBlasterDev); if (parentDevItem && !parentDevItem->text().isEmpty()) { pcSn = parentDevItem->text(); break; } } } } // Find the progress bar index for this pcSn QStringList pcSnKeys = QStringList(pcSnToFirstRowMap.keys()); int progressBarIndex = pcSnKeys.indexOf(pcSn); if (progressBarIndex >= 0 && progressBarIndex < progressBars.size()) { QProgressBar *progressBar1 = progressBars[progressBarIndex].bar1; QProgressBar *progressBar2 = progressBars[progressBarIndex].bar2; QProgressBar *progressBar3 = progressBars[progressBarIndex].bar3; // reset all progress bars for (QProgressBar *pb : {progressBar1, progressBar2, progressBar3}) { pb->setRange(0, 100); // 设置范围为0到100 pb->setValue(0); } switch (firingStage) { case FiringStages::QuickTesting: // 起爆检测状态 progressBar1->setRange(0, 0); // 设置范围为0到100 progressBar1->setValue(0); break; case FiringStages::QuickTestFinished: // 起爆检测完成 progressBar1->setValue(100); break; case FiringStages::NetCharging: // 充电开始 progressBar1->setValue(100); progressBar2->setRange(0, 0); // 设置范围为0到100 progressBar2->setValue(0); break; case FiringStages::NetChargingFinished: // 充电完成 progressBar1->setValue(100); progressBar2->setValue(100); break; case FiringStages::Blasting: progressBar1->setValue(100); progressBar2->setValue(100); progressBar3->setRange(0, 0); // 设置范围为0到100 progressBar3->setValue(0); break; case FiringStages::BlastFinished: // 充电完成 progressBar1->setValue(100); progressBar2->setValue(100); progressBar3->setValue(100); break; default: break; } } } // update tableView's fields about firing stage void BlastOpePage::onFiringStageUpdated(int stage, int row) { // update blast status item QModelIndex firingStatusItem = model->index(row, ColIndexBlastStatus); if (firingStatusItem.isValid()) { QColor customColor; QFont boldFont; boldFont.setBold(true); qDebug() << "opage::onFiringStageUpdated: stage = " << stage << ", row = " << row; switch (stage) { case FiringStages::Starting: model->setData(firingStatusItem, "待起爆"); customColor = QColor("#44035b"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); model->setData(firingStatusItem, boldFont, Qt::FontRole); break; case FiringStages::QuickTesting: model->setData(firingStatusItem, "起爆检测中..."); customColor = QColor("#44035b"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); model->setData(firingStatusItem, boldFont, Qt::FontRole); break; case FiringStages::QuickTestFinished: model->setData(firingStatusItem, "检测完成"); customColor = QColor("#404185"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::NetCharging: model->setData(firingStatusItem, "充电中 ..."); customColor = QColor("#31688e"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::NetChargingFinished: model->setData(firingStatusItem, "充电完成"); customColor = QColor("#1f918d"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::Blasting: model->setData(firingStatusItem, "起爆中..."); customColor = QColor("#38b775"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::BlastFinished: model->setData(firingStatusItem, "起爆完成"); customColor = QColor("#90d543"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::PendingTriggerButtonClick: model->setData(firingStatusItem, "按下双键起爆..."); customColor = QColor("#8e620"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::CancelConfirmed: model->setData(firingStatusItem, "已取消"); customColor = QColor("#893c3c"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; case FiringStages::ForceCanceled: model->setData(firingStatusItem, "已强制取消"); customColor = QColor("#893c3c"); model->setData(firingStatusItem, customColor, Qt::ForegroundRole); break; default: break; } } updateProgressBar(stage, row); handleUpdateOpButton(stage, row); } void BlastOpePage::handleSingleBlastButtonClicked(int row, QPushButton *button) { QStandardItem *devItem = model->item(row, ColIndexBlasterDev); QString blasterDevPcSn; if (devItem) { blasterDevPcSn = devItem->text(); } // Get all UUIDs for this pcSn group QStandardItem *uuidItem = model->item(row, headers.size()); QStringList uuids; if (uuidItem) { QString uuidData = uuidItem->data(Qt::UserRole).toString(); uuids = uuidData.split(",", Qt::SkipEmptyParts); } if (button->text() == startBlastButtonTxt) { button->setDisabled(true); QTimer::singleShot(5000, [button]() { if (button->isEnabled()) { return; } button->setEnabled(true); }); button->setMinimumWidth(80); button->setText(stopBlastButtonTxt); // Create firing widget for the pcSn group (use pcSn as identifier) // Use the first row index for this pcSn group for progress updates firingWidget *widget = new firingWidget(row, false, blasterDevPcSn); connect(widget, &firingWidget::updatefiringStage, this, &BlastOpePage::onFiringStageUpdated); connect(widget, &firingWidget::startCountdown, this, &BlastOpePage::showCountDownWidget); connect(widget, &firingWidget::updateProjectStatus, this, &BlastOpePage::handlerUpdateProjectStatus); connect(widget, &firingWidget::closeFiring, this, &BlastOpePage::destroyFiringWidget); if (isShowTriggeringWidget) { widget->show(); } widget->setAttribute(Qt::WA_DeleteOnClose); firingWidgetByUuid.insert(blasterDevPcSn, widget); widget->startBlasting(); } else if (button->text() == stopBlastButtonTxt) { bool isContinue = CustomMessageBox::showConfirmDialog("提示", "是否取消爆破?", "继续", "取消", this); if (!isContinue) { return; } firingWidget *widget = firingWidgetByUuid.value(blasterDevPcSn); if (widget) { widget->sendCancelFiringMsg(); } } else { Logger::getInstance().error( QString("handleSingleBlastButtonClicked: unknown button text %1").arg(button->text())); return; } } void BlastOpePage::handleUpdateOpButton(int stage, int row) { QModelIndex index = model->index(row, ColIndexOpBtn); if (!index.isValid()) { Logger::getInstance().error(QString("can not get updateOpBtnStage index %1").arg(row)); return; } QWidget *widget = ui->tableView->indexWidget(index); QPushButton *button = widget->findChild(); if (!button) { Logger::getInstance().error(QString("can not find button in row %1").arg(row)); return; } switch (stage) { case FiringStages::CancelConfirmed: button->setDisabled(true); button->setText(reStartButtonTxt); break; case FiringStages::Starting: button->setDisabled(true); break; case FiringStages::QuickTesting: button->setEnabled(true); button->setText(stopBlastButtonTxt); break; case FiringStages::QuickTestFinished: button->setEnabled(true); button->setText(stopBlastButtonTxt); break; case FiringStages::NetCharging: button->setEnabled(true); button->setText(stopBlastButtonTxt); break; case FiringStages::NetChargingFinished: button->setEnabled(true); button->setText(stopBlastButtonTxt); break; case FiringStages::PendingTriggerButtonClick: button->setEnabled(true); button->setText(stopBlastButtonTxt); break; case FiringStages::Blasting: button->setDisabled(true); button->setText("..."); break; case FiringStages::BlastFinished: button->setDisabled(true); button->setText(""); // 使用样式表设置图标居中 button->setStyleSheet( "QPushButton {" " padding: 0px;" " border: none;" " background-color: transparent;" "}"); button->setIcon(QIcon(":/icons/icons/svg/blast.svg")); button->setIconSize(QSize(32, 32)); break; case FiringStages::ForceCanceled: button->setDisabled(true); button->setText(reStartButtonTxt); break; default: button->setEnabled(true); break; } } void BlastOpePage::handlerUpdateProjectStatus(QString uuid, const QString &newStatus) { // dao.updateBlastStatusByUuid(uuid, newStatus); QJsonObject extParams; qDebug() << "handlerUpdateProjectStatus: uuid = " << uuid << ", newStatus = " << newStatus; // extParams.insert("blastStatus", QJsonArray({newStatus})); } void BlastOpePage::destroyFiringWidget(const QString &uuid, int row) { firingWidget *widget = firingWidgetByUuid.value(uuid); if (widget) { widget->close(); // 关闭窗口 widget->deleteLater(); // 释放内存 widget->disconnect(); // 断开信号连接 firingWidgetByUuid.remove(uuid); // 从映射中移除 } } // 槽函数,当 item 状态改变时触发 void BlastOpePage::onItemCheckboxChanged(QStandardItem *item) { if (item->column() == 0) { // 仅处理第一列的勾选状态改变 if (item->checkState() == Qt::Checked) { // Find the first row for this pcSn group that contains the UUID data int currentRow = item->row(); QStandardItem *uuidItem = model->item(currentRow, headers.size()); if (uuidItem) { QString uuidData = uuidItem->data(Qt::UserRole).toString(); QStringList uuids = uuidData.split(",", Qt::SkipEmptyParts); if (!uuids.isEmpty()) { // Get the pcSn for this group QStandardItem *pcSnItem = model->item(currentRow, ColIndexBlasterDev); if (pcSnItem) { QString pcSn = pcSnItem->text(); uuidMap[currentRow] = pcSn; // Store pcSn using the first row of the group } } } } else if (item->checkState() == Qt::Unchecked) { uuidMap.remove(item->row()); } } } void BlastOpePage::on_btnSelect_clicked() { // 禁用表格第一列的选项 for (int row = 0; row < model->rowCount(); ++row) { QStandardItem *item = model->item(row, 0); if (item) { Qt::ItemFlags flags = item->flags(); flags &= ~Qt::ItemIsEnabled; item->setFlags(flags); } } for (auto it = uuidMap.begin(); it != uuidMap.end(); ++it) { int row = it.key(); QString pcSn = it.value(); // This is now pcSn instead of uuid firingWidget *widgetSelect = new firingWidget(row, true, pcSn); QModelIndex index = model->index(row, ColIndexOpBtn); // Use ColIndexOpBtn instead of ColIndexBlasterDev if (index.isValid()) { QWidget *widgetButton = ui->tableView->indexWidget(index); if (widgetButton) { QPushButton *button = widgetButton->findChild(); if (button) { button->setText(stopBlastButtonTxt); button->setDisabled(true); } } } // 信号连接 connect(widgetSelect, &firingWidget::updatefiringStage, this, &BlastOpePage::onFiringStageUpdated); connect(widgetSelect, &firingWidget::batchFiringSignal, this, &BlastOpePage::setBatchBlastTrigger); connect(widgetSelect, &firingWidget::updateProjectStatus, this, &BlastOpePage::handlerUpdateProjectStatus); connect(widgetSelect, &firingWidget::closeFiring, this, &BlastOpePage::destroyBatchFiringWidget); widgetSelect->setAttribute(Qt::WA_DeleteOnClose); uuidWidgetSMap.insert(pcSn, widgetSelect); widgetSelect->startBlasting(); if (isShowTriggeringWidget) { widgetSelect->show(); } } } // 完成充电 void BlastOpePage::setBatchBlastTrigger(QString pcSn) { selectedUuids.insert(pcSn); // Now storing pcSn instead of UUID bool isSame = checkUuidsSame(); if (isSame) { bool successSelect; serialTool = SerialTool::getInstance(nullptr, &successSelect); connect(serialTool, &SerialTool::triggerButtonPressed, this, &BlastOpePage::showCountDownForBatchBlast, Qt::SingleShotConnection); if (serialTool) { QByteArray data = "\r\nENABLE_BUTTON\r\n"; bool success = serialTool->sendData(data); if (success) { Logger::getInstance().info("enable batch trigger button message send."); } else { Logger::getInstance().warn("enable batch trigger button message send failed."); } connect( serialTool, &SerialTool::triggerButtonEnabled, this, [this]() { for (const auto &row : uuidMap.keys()) { onFiringStageUpdated(FiringStages::PendingTriggerButtonClick, row); } }, Qt::SingleShotConnection); } else { qDebug() << "serialTool Not fond."; QMessageBox::critical(nullptr, "错误", "trigger button devices not found"); } } else { Logger::getInstance().error( QString("The pcSns in selectedUuids and uuidMap are different. pcSn: %1").arg(pcSn)); } } // 检查 selectedUuids 和 uuidMap 中的 pcSn 是否相同 bool BlastOpePage::checkUuidsSame() { QSet mapPcSns; for (const auto &value : uuidMap) { mapPcSns.insert(value); } return selectedUuids == mapPcSns; } void BlastOpePage::showCountDownForBatchBlast() { QByteArray data = "\r\nDISABLE_BUTTON\r\n"; bool success = serialTool->sendData(data); if (success) { qDebug() << "Data sent successfully"; } else { qDebug() << "Failed to send data"; } serialTool->releaseInstance(); CountdownWidget *countdownWidgetSelect = new CountdownWidget(this); countdownWidgetSelect->resize(200, 200); int x = (this->width() - countdownWidgetSelect->width()) / 2; int y = (this->height() - countdownWidgetSelect->height()) / 2; countdownWidgetSelect->move(x, y); countdownWidgetSelect->show(); connect(countdownWidgetSelect, &CountdownWidget::countdownFinished, this, &BlastOpePage::triggerBatchFiringBlast, Qt::SingleShotConnection); } void BlastOpePage::triggerBatchFiringBlast() { for (auto it = uuidWidgetSMap.begin(); it != uuidWidgetSMap.end(); ++it) { QString pcSn = it.key(); firingWidget *widget = it.value(); QString topic = "hxgc/" + pcSn + "/BlastTask"; QString message = "起爆"; widget->onCountdownFinished(topic, message); } } void BlastOpePage::destroyBatchFiringWidget(const QString &pcSn, int row) { firingWidget *widget = uuidWidgetSMap.value(pcSn); if (widget) { widget->close(); widget->deleteLater(); uuidWidgetSMap.remove(pcSn); } // reset the table's row by row index for (int row = 0; row < model->rowCount(); ++row) { // 恢复 checkbox 可选 QStandardItem *item = model->item(row, 0); if (item) { Qt::ItemFlags flags = item->flags(); flags |= Qt::ItemIsEnabled; item->setFlags(flags); } } }