浏览代码

feat: add sort to the firing job

YaoH 1 天之前
父节点
当前提交
1e4611a192

+ 4 - 0
CMakeLists.txt

@@ -60,6 +60,8 @@ set(SOURCES
     blastJob/blastoperationfactory.cpp
     blastJob/blastopepage.cpp
     blastJob/faceverification.cpp
+    blastJob/sequenceselectorwidget.cpp
+    blastJob/dragdroptablewidget.cpp
     address/addressfactory.cpp
     address/addresspage.cpp
     pagefactory.cpp
@@ -115,6 +117,8 @@ set(HEADERS
     blastJob/blastoperationfactory.h
     blastJob/blastopepage.h
     blastJob/faceverification.h
+    blastJob/sequenceselectorwidget.h
+    blastJob/dragdroptablewidget.h
     address/addressfactory.h
     address/addresspage.h
     pagefactory.h

+ 142 - 96
blastJob/blastopepage.cpp

@@ -6,10 +6,10 @@
 
 #include "../components/custommessagebox.h"
 #include "../components/loadingwidget.h"
-#include "../registryManager/registrymanager.h"
 #include "../utils/global.h"
 #include "../utils/logger.h"
 #include "countdownwidget.h"
+#include "sequenceselectorwidget.h"
 #include "ui_blastopepage.h"
 
 const int ColIndexBlastStatus = 6;
@@ -21,7 +21,8 @@ BlastOpePage::BlastOpePage(QWidget *parent)
     : QWidget(parent),
       ui(new Ui::BlastOpePage),
       dao(DatabaseManager::getInstance().getDatabase()),
-      m_faceVerification(nullptr) {
+      m_faceVerification(nullptr),
+      m_sequenceSelector(nullptr) {
     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
 
     bool isAllSafetyInspectorConfirmed = true;
@@ -94,25 +95,15 @@ BlastOpePage::~BlastOpePage() {
     if (m_faceVerification) {
         delete m_faceVerification;
     }
-}
 
-void BlastOpePage::initPagination() {
-    // // pageWidget = new PageWidget;
-    // //connect(pageWidget, &PageWidget::currentPageChanged, this, &BlastOpePage::PageChanged);
-    // connect(pageWidget->getComboBox(), QOverload<int>::of(&QComboBox::currentIndexChanged), this,
-    //         &BlastOpePage::onComboBoxIndexChanged);
-    // ui->verticalLayout_4->addWidget(pageWidget);
-    m_pageSize = 100;
-    m_currentPage = 1;
-    RefreshData();
+    if (m_sequenceSelector) {
+        delete m_sequenceSelector;
+    }
 }
 
-void BlastOpePage::RefreshData() { loadDataAndDrawTable(m_currentPage, m_pageSize); }
-
-void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
-    LoadingWidget::showLoading(this, "正在加载数据...");
+void BlastOpePage::initPagination() {
     QJsonObject extParams{{"blastStatus", QJsonArray({"2"})}};
-    QJsonObject result = backendAPIManager::getHProjects(currentPage, pageSize, extParams);
+    QJsonObject result = backendAPIManager::getHProjects(1, 200, extParams);
 
     QList<QSharedPointer<HProject>> projectList =
         dao.getHProjectsFromJsonArray(result["data"].toObject()["list"].toArray());
@@ -125,9 +116,19 @@ void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
         groupedProjects[key].append(project);
     }
 
-    totalCount = result["data"].toObject()["count"].toInt();
-    // pageWidget->setMaxPage(ceil(static_cast<double>(totalCount) / pageSize));
+    int totalCount = result["data"].toObject()["count"].toInt();
+    if (totalCount > 200) {
+        QMessageBox::warning(
+            this, "警告",
+            QString("目前可以操作的数量是200。当前查询结果总数:%1, 如需调整请联系开发人员进行调整").arg(totalCount));
+    }
+    m_groupedProjects = groupedProjects;
+
+    drawTable();
+}
 
+void BlastOpePage::drawTable() {
+    LoadingWidget::showLoading(this, "正在加载数据...");
     // Clear previous data
     pcSnToFirstRowMap.clear();
     progressBars.clear();
@@ -156,11 +157,11 @@ void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
     model->setHorizontalHeaderLabels(headerLabels);
 
     // Create rows with sub-rows for each project, grouped by pcSn
-    QStringList pcSnKeys = groupedProjects.keys();
+    QStringList pcSnKeys = m_groupedProjects.keys();
     int currentRow = 0;
 
     for (const QString &pcSn : pcSnKeys) {
-        QList<QSharedPointer<HProject>> projectsForPcSn = groupedProjects[pcSn];
+        QList<QSharedPointer<HProject>> projectsForPcSn = m_groupedProjects[pcSn];
         pcSnToFirstRowMap[pcSn] = currentRow;  // Store the first row index for this pcSn
 
         // Calculate aggregated blast status for this pcSn group
@@ -284,7 +285,7 @@ void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
 
     // Create progress bars and operation buttons only for the first row of each pcSn group
     for (const QString &pcSn : pcSnKeys) {
-        QList<QSharedPointer<HProject>> projectsForPcSn = groupedProjects[pcSn];
+        QList<QSharedPointer<HProject>> projectsForPcSn = m_groupedProjects[pcSn];
         int firstRowIndex = pcSnToFirstRowMap[pcSn];
         int groupRowSpan = projectsForPcSn.size();
 
@@ -322,20 +323,20 @@ void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
         // 创建操作按钮 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; }");
+        QPushButton *singleTriggerBtn = new QPushButton(widget);
+        singleTriggerBtn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+        singleTriggerBtn->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);
+                singleTriggerBtn->setText(startBlastButtonTxt);
+                singleTriggerBtn->setEnabled(true);
             }
         }
         QHBoxLayout *layout = new QHBoxLayout(widget);
-        layout->addWidget(button);
+        layout->addWidget(singleTriggerBtn);
         layout->setAlignment(Qt::AlignCenter);
         layout->setContentsMargins(0, 0, 0, 0);
         widget->setLayout(layout);
@@ -343,36 +344,25 @@ void BlastOpePage::loadDataAndDrawTable(int currentPage, int pageSize) {
         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); });
+            if (groupRowSpan > 1) {
+                ui->tableView->setSpan(firstRowIndex, operationCol, groupRowSpan, 1);
+            }
+            connect(singleTriggerBtn, &QPushButton::clicked, [this, firstRowIndex, singleTriggerBtn]() {
+                handleSingleBlastButtonClicked(firstRowIndex, singleTriggerBtn);
+            });
         }
 
-        // 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);
+        if (groupRowSpan > 1) {
+            ui->tableView->setSpan(firstRowIndex, ColIndexBlasterDev, groupRowSpan, 1);
+            ui->tableView->setSpan(firstRowIndex, 0, groupRowSpan, 1);
+            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::onComboBoxIndexChanged(int index) {}
 
 void BlastOpePage::updateProgressBar(int firingStage, int row) {
     // Find the progress bar index for this row's pcSn group
@@ -525,10 +515,10 @@ void BlastOpePage::handleSingleBlastButtonClicked(int row, QPushButton *button)
 
     // Get all UUIDs for this pcSn group
     QStandardItem *uuidItem = model->item(row, headers.size());
-    QStringList uuids;
+    QStringList projUuids;
     if (uuidItem) {
         QString uuidData = uuidItem->data(Qt::UserRole).toString();
-        uuids = uuidData.split(",", Qt::SkipEmptyParts);
+        projUuids = uuidData.split(",", Qt::SkipEmptyParts);
     }
 
     if (button->text() == startBlastButtonTxt) {
@@ -545,9 +535,9 @@ void BlastOpePage::handleSingleBlastButtonClicked(int row, QPushButton *button)
         // 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::updateFiringStage, this, &BlastOpePage::onFiringStageUpdated);
         connect(widget, &firingWidget::startCountdown, this, &BlastOpePage::showCountDownWidget);
-        connect(widget, &firingWidget::updateProjectStatus, this, &BlastOpePage::handlerUpdateProjectStatus);
+        connect(widget, &firingWidget::updateProjectsStatus, this, &BlastOpePage::handlerUpdateProjectStatus);
         connect(widget, &firingWidget::closeFiring, this, &BlastOpePage::destroyFiringWidget);
         if (isShowTriggeringWidget) {
             widget->show();
@@ -642,11 +632,22 @@ void BlastOpePage::handleUpdateOpButton(int stage, int row) {
     }
 }
 
-void BlastOpePage::handlerUpdateProjectStatus(QString uuid, const QString &newStatus) {
-    // dao.updateBlastStatusByUuid(uuid, newStatus);
+void BlastOpePage::handlerUpdateProjectStatus(QString blasterDevUuid, const QString &newStatus) {
     QJsonObject extParams;
-    qDebug() << "handlerUpdateProjectStatus: uuid = " << uuid << ", newStatus = " << newStatus;
-    // extParams.insert("blastStatus", QJsonArray({newStatus}));
+    extParams.insert("blastStatus", QJsonArray({newStatus}));
+
+    for (const auto &project : m_groupedProjects.value(blasterDevUuid)) {
+        QString errMsg = backendAPIManager::updateProject(project->getUuid(), extParams);
+        if (errMsg != "") {
+            Logger::getInstance().error(
+                QString("handlerUpdateProjectStatus: update project(%1) failed: %2").arg(blasterDevUuid, errMsg));
+            QMessageBox::critical(this, "错误",
+                                  QString("更新项目状态失败: %1\n错误信息: %2").arg(blasterDevUuid, errMsg));
+            return;
+        }
+        Logger::getInstance().info(
+            QString("handlerUpdateProjectStatus: update project %1 to status %2").arg(blasterDevUuid).arg(newStatus));
+    }
 }
 
 void BlastOpePage::destroyFiringWidget(const QString &uuid, int row) {
@@ -685,45 +686,12 @@ void BlastOpePage::onItemCheckboxChanged(QStandardItem *item) {
 }
 
 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);
-        }
+    if (uuidMap.isEmpty()) {
+        QMessageBox::warning(this, "警告", "请先选择要起爆的设备!");
+        return;
     }
 
-    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<QPushButton *>();
-                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();
-        }
-    }
+    showSequenceSelector();
 }
 
 // 完成充电
@@ -821,3 +789,81 @@ void BlastOpePage::destroyBatchFiringWidget(const QString &pcSn, int row) {
         }
     }
 }
+
+void BlastOpePage::showSequenceSelector() {
+    // Get selected pcSn list from uuidMap
+    QStringList selectedPcSnList;
+    for (auto it = uuidMap.begin(); it != uuidMap.end(); ++it) {
+        QString pcSn = it.value();
+        if (!selectedPcSnList.contains(pcSn)) {
+            selectedPcSnList << pcSn;
+        }
+    }
+
+    if (selectedPcSnList.isEmpty()) {
+        QMessageBox::warning(this, "警告", "没有选择的设备!");
+        return;
+    }
+
+    m_sequenceSelector = new SequenceSelectorWidget(selectedPcSnList, this);
+    connect(m_sequenceSelector, &SequenceSelectorWidget::startFiring, this, &BlastOpePage::onSequenceSelected);
+    connect(m_sequenceSelector, &SequenceSelectorWidget::cancel, this, &BlastOpePage::onSequenceCanceled);
+    m_sequenceSelector->show();
+}
+
+void BlastOpePage::onSequenceSelected(const QStringList &orderedPcSnList) {
+    // 禁用表格第一列的选项
+    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);
+        }
+    }
+
+    // Start firing process with ordered sequence
+    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<QPushButton *>();
+                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::updateProjectsStatus, 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();
+        }
+    }
+
+    // Clean up sequence selector
+    if (m_sequenceSelector) {
+        m_sequenceSelector->deleteLater();
+        m_sequenceSelector = nullptr;
+    }
+}
+
+void BlastOpePage::onSequenceCanceled() {
+    // Clean up sequence selector
+    if (m_sequenceSelector) {
+        m_sequenceSelector->deleteLater();
+        m_sequenceSelector = nullptr;
+    }
+}

+ 7 - 8
blastJob/blastopepage.h

@@ -22,6 +22,7 @@
 #include "../serial/serialtool.h"
 #include "countdownwidget.h"
 #include "faceverification.h"
+#include "sequenceselectorwidget.h"
 
 // 自定义结构体
 struct ProgressBarTriple {
@@ -43,7 +44,6 @@ class BlastOpePage : public QWidget {
     explicit BlastOpePage(QWidget *parent = nullptr);
     ~BlastOpePage();
    private slots:
-    void PageChanged(int page);
     void onComboBoxIndexChanged(int index);
     void updateProgressBar(int value, int row);
     void onFiringStageUpdated(int status, int row);
@@ -57,16 +57,18 @@ class BlastOpePage : public QWidget {
     void showCountDownForBatchBlast();
     void triggerBatchFiringBlast();
     void destroyBatchFiringWidget(const QString &uuid, int row);
+    void onSequenceSelected(const QStringList &orderedPcSnList);
+    void onSequenceCanceled();
 
    private:
-    void RefreshData();
-    void loadDataAndDrawTable(int currentPage, int pageSize);
+    void drawTable();
     void initFaceVerification();
     void onFaceVerificationSuccess();
     void onFaceVerificationFailed(const QString &message);
     void initPagination();
     void handleSingleBlastButtonClicked(int row, QPushButton *button);
     bool checkUuidsSame();
+    void showSequenceSelector();
 
    private:
     Ui::BlastOpePage *ui;
@@ -74,8 +76,6 @@ class BlastOpePage : public QWidget {
     QStandardItemModel *model;
     QMap<QString, firingWidget *> firingWidgetByUuid;
     FaceVerification *m_faceVerification;
-    // PageWidget *pageWidget;
-    QJsonArray dataArray;
     HProjectDao dao;
     QVector<ProgressBarTriple> progressBars;  // 用于保存每行的两个进度条指针
     QMap<int, QString> uuidMap;               // 用于存储行号和对应的 uuid
@@ -84,12 +84,11 @@ class BlastOpePage : public QWidget {
     SerialTool *serialTool;
     QSet<QString> selectedUuids;
     QMetaObject::Connection connectionItem;
-    int m_pageSize;     // 每页显示的记录数量
-    int m_currentPage;  // 当前页面
-    int totalCount;
+    QMap<QString, QList<QSharedPointer<HProject>>> m_groupedProjects;
     QString stopBlastButtonTxt = "取消起爆";
     QString startBlastButtonTxt = "开启起爆";
     QString reStartButtonTxt = "返回授权";
+    SequenceSelectorWidget *m_sequenceSelector;
 };
 
 #endif  // BLASTOPEPAGE_H

+ 64 - 0
blastJob/dragdroptablewidget.cpp

@@ -0,0 +1,64 @@
+#include "dragdroptablewidget.h"
+
+#include <QDebug>
+#include <QHeaderView>
+#include <QMimeData>
+
+DragDropTableWidget::DragDropTableWidget(QWidget *parent) : QTableWidget(parent), m_currentDragRow(-1) {
+    setAcceptDrops(true);
+    setDragEnabled(true);
+    setDropIndicatorShown(true);
+    setSelectionBehavior(QAbstractItemView::SelectRows);
+    setDragDropMode(QAbstractItemView::DragDrop);  // Changed from InternalMove
+    setDragDropOverwriteMode(false);
+    setDefaultDropAction(Qt::MoveAction);
+    verticalHeader()->setVisible(false);
+    horizontalHeader()->setStretchLastSection(true);
+
+    // Track selection changes to know which row is being dragged
+    connect(this, &QTableWidget::itemSelectionChanged, this, [this]() {
+        m_currentDragRow = currentRow();
+        qDebug() << "Selection changed, current row:" << m_currentDragRow;
+    });
+}
+
+void DragDropTableWidget::dragEnterEvent(QDragEnterEvent *event) {
+    qDebug() << "DragDropTableWidget::dragEnterEvent";
+    if (event->source() == this && event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) {
+        event->setAccepted(true);
+    } else {
+        event->ignore();
+    }
+}
+
+void DragDropTableWidget::dragMoveEvent(QDragMoveEvent *event) {
+    qDebug() << "DragDropTableWidget::dragMoveEvent";
+    if (event->source() == this && event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) {
+        event->setAccepted(true);
+    } else {
+        event->ignore();
+    }
+}
+
+void DragDropTableWidget::dropEvent(QDropEvent *event) {
+    qDebug() << "DragDropTableWidget::dropEvent source=" << event->source() << " m_currentDragRow=" << m_currentDragRow;
+
+    // Prevent default handling by the widget
+    event->setAccepted(true);
+
+    if (event->source() != this) {
+        qDebug() << "Drop event ignored: source is not this table";
+        return;
+    }
+
+    int toRow = indexAt(event->pos()).row();
+    if (toRow == -1) {
+        toRow = rowCount();
+    }
+
+    qDebug() << "Drop at row:" << toRow;
+
+    if (m_currentDragRow >= 0 && m_currentDragRow != toRow) {
+        emit rowDropped(m_currentDragRow, toRow);
+    }
+}

+ 25 - 0
blastJob/dragdroptablewidget.h

@@ -0,0 +1,25 @@
+#ifndef DRAGDROPTABLEWIDGET_H
+#define DRAGDROPTABLEWIDGET_H
+
+#include <QDropEvent>
+#include <QTableWidget>
+
+class DragDropTableWidget : public QTableWidget {
+    Q_OBJECT
+
+   public:
+    explicit DragDropTableWidget(QWidget *parent = nullptr);
+
+   signals:
+    void rowDropped(int fromRow, int toRow);
+
+   protected:
+    void dragEnterEvent(QDragEnterEvent *event) override;
+    void dragMoveEvent(QDragMoveEvent *event) override;
+    void dropEvent(QDropEvent *event) override;
+
+   private:
+    int m_currentDragRow;
+};
+
+#endif  // DRAGDROPTABLEWIDGET_H

+ 183 - 0
blastJob/sequenceselectorwidget.cpp

@@ -0,0 +1,183 @@
+#include "sequenceselectorwidget.h"
+
+#include <QApplication>
+#include <QDebug>
+#include <QHeaderView>
+#include <QLabel>
+#include <QMessageBox>
+#include <QScreen>
+
+#include "dragdroptablewidget.h"
+
+SequenceSelectorWidget::SequenceSelectorWidget(const QStringList &pcSnList, QWidget *parent)
+    : QDialog(parent), m_pcSnList(pcSnList) {
+    setupUI();
+    populateTable();
+    centerOnScreen();
+}
+
+SequenceSelectorWidget::~SequenceSelectorWidget() {}
+
+void SequenceSelectorWidget::setupUI() {
+    setWindowTitle("起爆顺序设置");
+    setFixedSize(440, 550);
+    setModal(true);
+
+    // Create main layout with margins
+    QVBoxLayout *mainLayout = new QVBoxLayout(this);
+    mainLayout->setContentsMargins(20, 20, 20, 20);
+    mainLayout->setSpacing(15);
+
+    // Create our custom table widget
+    m_tableWidget = new DragDropTableWidget(this);
+    m_tableWidget->setShowGrid(true);
+
+    // Set up the table - keep all original logic
+    m_tableWidget->setColumnCount(2);
+    QStringList headers;
+    headers << "序号" << "井下起爆器";
+    m_tableWidget->setHorizontalHeaderLabels(headers);
+
+    // Additional properties specific to our needs
+    m_tableWidget->setColumnWidth(0, 80);
+
+    // Add table to main layout
+    mainLayout->addWidget(m_tableWidget);
+
+    // Create button layout
+    QHBoxLayout *buttonLayout = new QHBoxLayout();
+    m_confirmButton = new QPushButton("确认", this);
+    m_cancelButton = new QPushButton("取消", this);
+
+    m_confirmButton->setStyleSheet(
+        "QPushButton {"
+        "    background-color: #007ACC;"
+        "    color: white;"
+        "    border: none;"
+        "    padding: 8px 16px;"
+        "    border-radius: 4px;"
+        "    font-size: 14px;"
+        "}"
+        "QPushButton:hover {"
+        "    background-color: #005A9E;"
+        "}"
+        "QPushButton:pressed {"
+        "    background-color: #004578;"
+        "}");
+
+    m_cancelButton->setStyleSheet(
+        "QPushButton {"
+        "    background-color: #6C757D;"
+        "    color: white;"
+        "    border: none;"
+        "    padding: 8px 16px;"
+        "    border-radius: 4px;"
+        "    font-size: 14px;"
+        "}"
+        "QPushButton:hover {"
+        "    background-color: #545B62;"
+        "}"
+        "QPushButton:pressed {"
+        "    background-color: #3D4449;"
+        "}");
+
+    buttonLayout->addStretch();
+    buttonLayout->addWidget(m_confirmButton);
+    buttonLayout->addWidget(m_cancelButton);
+
+    // Add button layout to main layout
+    mainLayout->addLayout(buttonLayout);
+
+    // Connect signals
+    connect(m_confirmButton, &QPushButton::clicked, this, &SequenceSelectorWidget::onConfirmClicked);
+    connect(m_cancelButton, &QPushButton::clicked, this, &SequenceSelectorWidget::onCancelClicked);
+
+    // Connect our custom drag-drop signal
+    connect(m_tableWidget, &DragDropTableWidget::rowDropped, this, &SequenceSelectorWidget::handleRowDropped);
+}
+
+void SequenceSelectorWidget::populateTable() {
+    m_tableWidget->clearContents();
+    m_tableWidget->setRowCount(m_pcSnList.size());
+
+    qDebug() << "draw" << m_pcSnList;
+
+    for (int i = 0; i < m_pcSnList.size(); ++i) {
+        // Sequence number
+        QTableWidgetItem *sequenceItem = new QTableWidgetItem(QString::number(i + 1));
+        sequenceItem->setTextAlignment(Qt::AlignCenter);
+        sequenceItem->setFlags(sequenceItem->flags() & ~Qt::ItemIsEditable);
+        m_tableWidget->setItem(i, 0, sequenceItem);
+
+        // PcSn
+        QTableWidgetItem *pcSnItem = new QTableWidgetItem(m_pcSnList[i]);
+        pcSnItem->setTextAlignment(Qt::AlignCenter);
+        pcSnItem->setFlags(pcSnItem->flags() & ~Qt::ItemIsEditable);
+        m_tableWidget->setItem(i, 1, pcSnItem);
+    }
+}
+
+QStringList SequenceSelectorWidget::getOrderedPcSnList() const {
+    QStringList orderedList;
+    for (int i = 0; i < m_tableWidget->rowCount(); ++i) {
+        QTableWidgetItem *item = m_tableWidget->item(i, 1);
+        if (item) {
+            orderedList << item->text();
+        }
+    }
+    return orderedList;
+}
+
+void SequenceSelectorWidget::onConfirmClicked() {
+    QStringList orderedPcSnList = getOrderedPcSnList();
+    emit startFiring(orderedPcSnList);
+    accept();
+}
+
+void SequenceSelectorWidget::onCancelClicked() {
+    emit cancel();
+    reject();
+}
+
+void SequenceSelectorWidget::centerOnScreen() {
+    QScreen *screen = QApplication::primaryScreen();
+    if (screen) {
+        QRect screenGeometry = screen->geometry();
+        int x = (screenGeometry.width() - width()) / 2;
+        int y = (screenGeometry.height() - height()) / 2;
+        move(x, y);
+    }
+}
+
+void SequenceSelectorWidget::handleRowDropped(int fromRow, int toRow) {
+    qDebug() << "Row dropped from" << fromRow << "to" << toRow;
+
+    if (fromRow == toRow) {
+        return;
+    }
+    // 获取当前所有数据
+    QStringList pcSnList;
+    for (int i = 0; i < m_tableWidget->rowCount(); ++i) {
+        QTableWidgetItem *item = m_tableWidget->item(i, 1);
+        if (item) {
+            pcSnList << item->text();
+        }
+    }
+
+    if (toRow >= pcSnList.size()) {
+        toRow = pcSnList.size() - 1;  // 确保不越界
+    }
+
+    // Store the dragged row data
+    QString draggedSn = m_tableWidget->item(fromRow, 1)->text();
+
+    pcSnList.removeAt(fromRow);
+    pcSnList.insert(toRow, draggedSn);
+
+    m_pcSnList = pcSnList;
+    qDebug() << "Updated PcSnList:" << m_pcSnList;
+    populateTable();
+
+    // Make sure the table maintains selection on the moved row
+    // m_tableWidget->selectRow(toRow);
+}

+ 52 - 0
blastJob/sequenceselectorwidget.h

@@ -0,0 +1,52 @@
+#ifndef SEQUENCESELECTORWIDGET_H
+#define SEQUENCESELECTORWIDGET_H
+
+#include <QDialog>
+#include <QDrag>
+#include <QDragEnterEvent>
+#include <QDropEvent>
+#include <QHBoxLayout>
+#include <QHeaderView>
+#include <QMimeData>
+#include <QPushButton>
+#include <QStringList>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "dragdroptablewidget.h"
+
+class SequenceSelectorWidget : public QDialog {
+    Q_OBJECT
+
+   public:
+    explicit SequenceSelectorWidget(const QStringList &pcSnList, QWidget *parent = nullptr);
+    ~SequenceSelectorWidget();
+
+    QStringList getOrderedPcSnList() const;
+
+   signals:
+    void startFiring(const QStringList &orderedPcSnList);
+    void cancel();
+
+    // Removed drag and drop event handlers as they are handled by DragDropTableWidget
+
+   private slots:
+    void onConfirmClicked();
+    void onCancelClicked();
+
+   private:
+    void setupUI();
+    void populateTable();
+    void centerOnScreen();
+
+   private slots:
+    void handleRowDropped(int fromRow, int toRow);
+
+   private:
+    DragDropTableWidget *m_tableWidget;
+    QPushButton *m_confirmButton;
+    QPushButton *m_cancelButton;
+    QStringList m_pcSnList;
+};
+
+#endif  // SEQUENCESELECTORWIDGET_H

+ 14 - 15
fireWidget/firingwidget.cpp

@@ -79,7 +79,6 @@ void firingWidget::startBlasting() {
 }
 
 // 定间隔检查有没有收到爆破器返回信息
-// TODO: all triggers shared one timer
 void firingWidget::checkBlasterConnection() {
     m_lastMsgTime = QDateTime::currentDateTime();
 
@@ -95,7 +94,7 @@ void firingWidget::checkBlasterConnection() {
             m_connectionCheckTimer->stop();
             QMessageBox msgBox;
             msgBox.setWindowTitle("起爆检测测试错误");
-            msgBox.setText("超时未收到爆破器消息,是否继续等待");
+            msgBox.setText(QString("超时未收到j井下起爆装置(%1)消息,是否继续等待").arg(m_blasterDevUuid));
             QPushButton *yesButton = msgBox.addButton("继续等待", QMessageBox::YesRole);
             QPushButton *noButton = msgBox.addButton("强制取消", QMessageBox::NoRole);
             yesButton->setStyleSheet(
@@ -150,7 +149,7 @@ void firingWidget::handleProjectFiringMqttMessage(const QMqttMessage &message) {
                             break;
                         case FiringStages::QuickTesting:
                             navProgress->setState(FiringStages::QuickTesting);
-                            emit updatefiringStage(FiringStages::QuickTesting, m_row);
+                            emit updateFiringStage(FiringStages::QuickTesting, m_row);
                             ui->pushButton_2->setText("立即测试");
                             ui->pushButton_2->setEnabled(false);
                             break;
@@ -182,11 +181,11 @@ void firingWidget::handleProjectFiringMqttMessage(const QMqttMessage &message) {
                             ui->pushButton_2->setText("充电");
                             navProgress->setState(FiringStages::QuickTestFinished);
 
-                            emit updatefiringStage(FiringStages::QuickTestFinished, m_row);
+                            emit updateFiringStage(FiringStages::QuickTestFinished, m_row);
                             break;
                         case FiringStages::NetCharging:
                             ui->pushButton_2->setEnabled(false);
-                            emit updatefiringStage(FiringStages::NetCharging, m_row);
+                            emit updateFiringStage(FiringStages::NetCharging, m_row);
                             ui->pushButton_2->setText("充电");
                             navProgress->setState(FiringStages::NetCharging);
                             break;
@@ -194,18 +193,18 @@ void firingWidget::handleProjectFiringMqttMessage(const QMqttMessage &message) {
                             ui->pushButton_2->setEnabled(true);
                             ui->pushButton_2->setText("起爆");
                             navProgress->setState(FiringStages::NetChargingFinished);
-                            emit updatefiringStage(FiringStages::NetChargingFinished, m_row);
+                            emit updateFiringStage(FiringStages::NetChargingFinished, m_row);
                             break;
                         case FiringStages::Blasting:
                             ui->pushButton_2->setEnabled(false);
-                            emit updatefiringStage(FiringStages::Blasting, m_row);
+                            emit updateFiringStage(FiringStages::Blasting, m_row);
                             ui->pushButton_2->setText("起爆");
                             navProgress->setState(FiringStages::Blasting);
                             break;
                         case FiringStages::BlastFinished:
                             ui->pushButton_2->setEnabled(false);
-                            emit updatefiringStage(FiringStages::BlastFinished, m_row);
-                            emit updateProjectStatus(m_blasterDevUuid, BlastStatus::Blasted);
+                            emit updateFiringStage(FiringStages::BlastFinished, m_row);
+                            emit updateProjectsStatus(m_blasterDevUuid, BlastStatus::Blasted);
                             ui->pushButton_2->setText("已完成起爆");
                             navProgress->setState(FiringStages::BlastFinished);
                             emit closeFiring(m_blasterDevUuid, m_row);
@@ -215,8 +214,8 @@ void firingWidget::handleProjectFiringMqttMessage(const QMqttMessage &message) {
                             ui->pushButton_2->setEnabled(false);
                             ui->pushButton_2->setText("已确认取消");
 
-                            emit updatefiringStage(FiringStages::CancelConfirmed, m_row);
-                            emit updateProjectStatus(m_blasterDevUuid, BlastStatus::Created);
+                            emit updateFiringStage(FiringStages::CancelConfirmed, m_row);
+                            emit updateProjectsStatus(m_blasterDevUuid, BlastStatus::Created);
                             emit closeFiring(m_blasterDevUuid, m_row);
                             break;
                         default:
@@ -264,7 +263,7 @@ void firingWidget::stageChangeEngine(int newStage) {
             message = "起爆测试";
             buttonText = "立即测试";
             sendMqttMessage(m_blasterSubTopic, message.toUtf8());
-            emit updatefiringStage(FiringStages::Starting, m_row);
+            emit updateFiringStage(FiringStages::Starting, m_row);
             break;
         case FiringStages::QuickTestFinished:
             message = "开始充电";
@@ -294,8 +293,8 @@ void firingWidget::stageChangeEngine(int newStage) {
         case FiringStages::ForceCanceled:
             // 强制取消
             sendCancelFiringMsg();
-            emit updateProjectStatus(m_blasterDevUuid, BlastStatus::Created);
-            emit updatefiringStage(FiringStages::ForceCanceled, m_row);
+            emit updateProjectsStatus(m_blasterDevUuid, BlastStatus::Created);
+            emit updateFiringStage(FiringStages::ForceCanceled, m_row);
             emit closeFiring(m_blasterDevUuid, m_row);
             break;
         default:
@@ -315,7 +314,7 @@ void firingWidget::stageChangeEngine(int newStage) {
                     qDebug() << "Failed to send data";
                 }
                 connection = connect(serialTool, &SerialTool::triggerButtonEnabled,
-                                     [this]() { emit updatefiringStage(10, m_row); });
+                                     [this]() { emit updateFiringStage(10, m_row); });
                 connectionPress = connect(serialTool, &SerialTool::triggerButtonPressed, [this, message]() {
                     this->onButtonPressedReceived(m_blasterSubTopic, message);
                 });

+ 2 - 2
fireWidget/firingwidget.h

@@ -58,9 +58,9 @@ class firingWidget : public QWidget {
    signals:
     void changeStage(int newStage);
     void projSafeCheckSuccess(QString projectUuid);
-    void updatefiringStage(int status, int row);
+    void updateFiringStage(int status, int row);
     void batchFiringSignal(QString uuid);
-    void updateProjectStatus(QString uuid, QString status);
+    void updateProjectsStatus(QString uuid, QString status);
     void safeChecked(const QJsonObject &jsonObj);
     void startCountdown(QString uuid, const QString &topic, const QString &message);
     void closeFiring(QString uuid, int row);

+ 3 - 0
utils/global.h

@@ -57,6 +57,7 @@ const QString Created = "1";
 const QString Registered = "2";
 const QString Blasted = "3";
 const QString ErrorOccurred = "4";
+const QString LostConnection = "5";
 }  // namespace BlastStatus
 
 namespace BlastStatusNames {
@@ -64,6 +65,7 @@ const QString Created = "未注册";
 const QString Registered = "已注册";
 const QString Blasted = "爆破完成";
 const QString ErrorOccurred = "爆破异常";
+const QString LostConnection = "起爆器失连";
 }  // namespace BlastStatusNames
 
 namespace ErrorBlastStatus {
@@ -92,6 +94,7 @@ inline QString getErrorMessage(int code) {
     return (it != map.end()) ? it->second : "Unknown error";
 }
 }  // namespace ErrorBlastStatus
+//
 
 // namespace FiringStages
 namespace FiringStages {