Forráskód Böngészése

功能:归还钥匙和锁

xj 3 hónapja
szülő
commit
654cd6375e

+ 9 - 3
src/httpclient/HttpGetWorkNodeDetail.cpp

@@ -100,6 +100,7 @@ void HttpGetWorkNodeDetail::clearWorkNodeFormModel()
 
 void HttpGetWorkNodeDetail::run()
 {
+
     httpRequestGetWorkNodeDetail();
 }
 
@@ -111,6 +112,7 @@ void HttpGetWorkNodeDetail::httpRequestGetWorkNodeDetail()
 
     QString url = Config()->workNodeDetail;
     jsonRoot.insert("nodeId", this->m_nodeId);
+    qDebug() << "[HttpGetWorkNodeDetail] " << url << this->m_nodeId;
 
     QJsonDocument jsonDoc(jsonRoot);
     QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
@@ -148,8 +150,7 @@ void HttpGetWorkNodeDetail::slotHttpResponseGetWorkNodeDetail(QByteArray data)
     }
 
     QJsonObject rootObj = jsonDoc.object();
-    if(rootObj.contains("code"))
-    {
+    if(rootObj.contains("code")) {
         int codeValue = rootObj.value("code").toInt();
         if(codeValue == 200 || codeValue == 0) {
             if(rootObj.contains("data")) {
@@ -185,7 +186,6 @@ void HttpGetWorkNodeDetail::slotHttpResponseGetWorkNodeDetail(QByteArray data)
                 }
                 if (vDataObj.contains("points") && vDataObj.value("points").isArray()) {
                     WorkNodeFormModel::instance()->setPointsList(vDataObj.value("points").toArray());
-                    qDebug() << WorkNodeFormModel::instance()->pointsList();
                 }
                 // 页面分类:审核、录入信息
                 WorkNodeFormModel::instance()->setModelType(vDataObj.value("type").toString());
@@ -193,6 +193,12 @@ void HttpGetWorkNodeDetail::slotHttpResponseGetWorkNodeDetail(QByteArray data)
                 if (vDataObj.contains("nodeProgress")) {
                     WorkNodeFormModel::instance()->setNodeProgress(vDataObj.value("nodeProgress").toString());
                 }
+                if (vDataObj.contains("workerUserName")) {
+                    WorkNodeFormModel::instance()->setWorkerName(vDataObj.value("initiatorName").toString());
+                }
+                if (vDataObj.contains("orderNo")) {
+                    WorkNodeFormModel::instance()->setOrderNo(vDataObj.value("orderNo").toString());
+                }
 
                 QJsonObject formData = parseJsonString(vDataObj.value("formData").toString());
                 WorkNodeFormModel::instance()->setFormJsonInfo(vDataObj.value("formData").toString());

+ 17 - 0
src/httpclient/WorkNodeFormModel.h

@@ -149,6 +149,20 @@ public:
     void setNodeProgress(const QString& node) {
         m_nodeProgress = node;
     }
+
+    QString orderNo() const {
+        return m_orderNo;
+    }
+    void setOrderNo(const QString& order) {
+        m_orderNo = order;
+    }
+
+    QString workerName() const {
+        return m_workerName;
+    }
+    void setWorkerName(const QString& name) {
+        m_workerName = name;
+    }
 signals:
     void modelTypeChanged();
     void formJsonInfoChanged();
@@ -175,6 +189,9 @@ private:
     QString m_formName;
     QString m_description;
     QString m_nodeProgress;
+
+    QString m_workerName;
+    QString m_orderNo;
 };
 
 #endif // WORKNODEFORMMODEL_H

+ 59 - 6
src/interactive/InteractiveCAN.cpp

@@ -62,6 +62,7 @@ InteractiveCAN::InteractiveCAN()
     connect(m_canClient, &CANClient::lockBuckleWritten, this, &InteractiveCAN::slotUnlockLocks);
     connect(m_canClient, &CANClient::signalReturnKey, this, &InteractiveCAN::slotGetReturnCANDevices);
     connect(m_canClient, &CANClient::signalLockNfcDeviceDetected, this, &InteractiveCAN::slotDeviceDetected, Qt::QueuedConnection);
+    connect(m_canClient, &CANClient::signalPopDevices, this, &InteractiveCAN::slotGetPopedDevices);
 
     m_bleClient = new BLEClient(this);
 //    m_bleClient->start();
@@ -98,13 +99,18 @@ InteractiveCAN::InteractiveCAN()
             if (WorkNodeFormModel::instance()->modelType() == QString("isolation")) {
                 createJobTicket();
             }
-            else {
+            else if (WorkNodeFormModel::instance()->modelType() == QString("releaseIsolation")) {
                 createColockJobTicket();
             }
+            else {
+                // 归还设备
+                checkEKeyStatus();
+            }
         }
     });
     connect(m_bleClient, &BLEClient::signalSendJobTicketStatus, this, &InteractiveCAN::slotSendJobTicketStatus);
     connect(m_bleClient, &BLEClient::workTicketResultReceived, this, &InteractiveCAN::slotReceivedJobTicketResult);
+    connect(m_bleClient, &BLEClient::workTicketResultReceived, ReturnKeyLockManager::instance(), &ReturnKeyLockManager::slotReceivedJobTicket);
     connect(m_bleClient, &BLEClient::signalFailConnect, this, &InteractiveCAN::slotReConnectBLE);
 
     m_checkHasKeyTimer = new QTimer(this);
@@ -121,6 +127,7 @@ InteractiveCAN::InteractiveCAN()
     connect(m_canClient, &CANClient::nfcDeviceError, 
             ReturnKeyLockManager::instance(), &ReturnKeyLockManager::onDeviceError,
             Qt::QueuedConnection);
+    connect(this, &InteractiveCAN::signalJobTicketInfo, ReturnKeyLockManager::instance(), &ReturnKeyLockManager::slotGetJobTicketResult);
     // ====================================================
 }
 
@@ -402,7 +409,7 @@ void InteractiveCAN::getJobTicketInfo(quint8 nodeId, bool isLeftKey)
 
 //        m_bleClient->startGetWorkTicketResult();
         if (m_bleClient) {
-            qDebug() << "[getJobTicketInfo]: ss" << m_bleClient->isConnected();
+            qDebug() << "[getJobTicketInfo]: " << m_bleClient->isConnected();
             if (m_bleClient->isConnected()) {
                 m_bleClient->startGetWorkTicketResult();
             }
@@ -589,10 +596,12 @@ void InteractiveCAN::slotHttpResponseGetWorkTicketByNodeId(QByteArray data)
     QJsonParseError error;
     QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
     if (error.error != QJsonParseError::NoError) {
+        emit signalJobTicketInfo(-3, QJsonObject());
         return;
     }
     qDebug() << "[slotHttpResponseGetWorkTicketByNodeId]" << jsonDoc;
     if (jsonDoc.isNull() || jsonDoc.isEmpty()) {
+        emit signalJobTicketInfo(-3, QJsonObject());
         return;
     }
     ;
@@ -605,6 +614,7 @@ void InteractiveCAN::slotHttpResponseGetWorkTicketByNodeId(QByteArray data)
                 if (vDataObj.isEmpty()) {
                     return;
                 }
+                emit signalJobTicketInfo(0, vDataObj);
                 if (vDataObj.contains("ticketContent")) {
                     QVariantMap pointInfo;
 
@@ -613,15 +623,16 @@ void InteractiveCAN::slotHttpResponseGetWorkTicketByNodeId(QByteArray data)
                     }
                     emit signalJobTicketResult(vDataObj.value("ticketContent").toString(), pointInfo);
                 }
+                return;
             }
         }
     }
+    emit signalJobTicketInfo(-3, QJsonObject());
 }
 
 void InteractiveCAN::slotUpdateColockStatus(const QString& cardNfc)
 {
     // TODO: 更新共锁人状态
-
     QJsonObject jsonRoot;
     QDateTime currentDateTime = QDateTime::currentDateTime();
     qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();
@@ -698,6 +709,18 @@ void InteractiveCAN::slotReConnectBLE()
     }
 }
 
+void InteractiveCAN::slotGetPopedDevices(const QString &nfcId, const QString &deviceType, int slotIndex)
+{
+    if (deviceType == "lock") {
+        m_popedDevices.locksNfc.append(nfcId);
+    }
+    else if (deviceType == "key") {
+        m_popedDevices.keyNfc = nfcId;
+    }
+    qDebug() << "[slotGetPopedDevices] " << m_popedDevices.locksNfc;
+    qDebug() << "[slotGetPopedDevices] " << m_popedDevices.keyNfc;
+}
+
 InteractiveCAN *InteractiveCAN::instance()
 {
     if (!pInstance) {
@@ -864,10 +887,10 @@ void InteractiveCAN::sendJobTicket()
     }
 }
 
-void InteractiveCAN::updateKeyAndLockStatus()
+void InteractiveCAN::updateKeyAndLockStatus(bool status)
 {
-    m_canClient->setWorkingKey(false);
-    m_canClient->setWorkingLocks(false);
+    m_canClient->setWorkingKey(status);
+    m_canClient->setWorkingLocks(status);
 }
 
 void InteractiveCAN::uploadColockInfo()
@@ -898,8 +921,38 @@ void InteractiveCAN::getWorkTicketByNodeId()
     httpRequestGetWorkTicketByNodeId();
 }
 
+void InteractiveCAN::connectBLEDevice(const QString &keyNfc)
+{
+    qDebug() << keyNfc;
+    if (!keyNfc.isEmpty()) {
+        m_keyNFC = keyNfc;
+        QJsonObject jsonRoot;
+        QDateTime currentDateTime = QDateTime::currentDateTime();
+        qint64 timestampSeconds = currentDateTime.toMSecsSinceEpoch();
+
+        QString url = Config()->keyMACByNFC;
+        jsonRoot.insert("nfc", keyNfc);
+
+        QJsonDocument jsonDoc(jsonRoot);
+        QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
+
+        emit signalGetRequestData(timestampSeconds, url, jsonData, GetInteractiveData()->m_token);
+    }
+}
+
+void InteractiveCAN::unlockDevices(const QString &nfc, const QString &deviceType)
+{
+    if (deviceType == "key") {
+        m_canClient->unlockEKey(nfc);
+    }
+    else if (deviceType == "lock") {
+        m_canClient->unlockLock(nfc);
+    }
+}
+
 void InteractiveCAN::slotUnlockEKey(const CANKeyBaseChargeStatus& status)
 {
+    if (ReturnKeyLockManager::instance()->isProcessing()) return;
     if (status.success) {
         if (m_okOpenKey) {
             QMutexLocker locker(&m_mutex);

+ 16 - 1
src/interactive/InteractiveCAN.h

@@ -15,6 +15,11 @@ typedef struct {
     int target = 0;
 } IsolationPointInfo;
 
+typedef struct {
+    QString keyNfc;
+    QList<QString> locksNfc;
+} DevicesInfoOfTask;
+
 class InteractiveCAN : public QObject
 {
     Q_OBJECT
@@ -75,11 +80,14 @@ public:
 
     Q_INVOKABLE void sendJobTicket();
     // 启动检索是否自动关闭钥匙扣和锁扣
-    Q_INVOKABLE void updateKeyAndLockStatus();
+    Q_INVOKABLE void updateKeyAndLockStatus(bool status = false);
 
     Q_INVOKABLE void uploadColockInfo();
 
     Q_INVOKABLE void getWorkTicketByNodeId();
+
+    void connectBLEDevice(const QString& keyNfc);
+    void unlockDevices(const QString& nfc, const QString& deviceType);
 public slots:
     void slotUnlockEKey(const CANKeyBaseChargeStatus& status);
     void slotUnlockLocks(bool success, const QList<QString>& lockRfids);
@@ -102,6 +110,7 @@ private:
     void httpRequestPostUploadPositionInfo(const QJsonDocument& doc);
     void httpRequestPostUpdatePointUnlock(const QJsonDocument& doc);
     void httpRequestPostUpdateBackLock(const QList<QString>& lockNfcs);
+public:
     void httpRequestGetWorkTicketByNodeId();
 public slots:
     void getJobTicketInfo(quint8 nodeId, bool isLeftKey);
@@ -121,6 +130,8 @@ public slots:
 
     void slotGetReturnCANDevices();
     void slotReConnectBLE();
+
+    void slotGetPopedDevices(const QString& nfcId, const QString& deviceType, int slotIndex);
 signals:
     void signalReturnDevicesInfo(const QByteArray& info);
     void signalOkSearchedBLE(bool success);
@@ -140,6 +151,7 @@ signals:
     void signalCheckedKey(quint8 nodeId, bool isLeftKey);
 
     void signalJobTicketResult(const QString& jsonResult, const QVariantMap& pointInfo);
+    void signalJobTicketInfo(int stat, const QJsonObject& dataObj);
 
     void signalPostRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token);
     void signalUploadJobTicketReturnStat(int stat, QString str);
@@ -179,6 +191,9 @@ private:
     bool m_okSendJobTicket = false;
     bool m_okUnlockKey = false;
     bool m_okUnlockLocks = false;
+
+    DevicesInfoOfTask m_currentTask;    // 当前任务所需设备
+    DevicesInfoOfTask m_popedDevices;   // 已取出的设备
 };
 
 #endif // INTERACTIVECAN_H

+ 1 - 1
src/interactive/InteractiveData.h

@@ -46,7 +46,7 @@ public:
     QSet<QString> m_roles;                  // 用户角色
     QSet<QString> m_permissions;            // 用户许可
 
-    QString m_token;
+    QString m_token = "ab3938d75cf84f02a2fb7fa62f9e4397";
 };
 
 inline InteractiveData* GetInteractiveData()

+ 170 - 18
src/interactive/ReturnKeyLockManager.cpp

@@ -2,6 +2,14 @@
 #include <QDebug>
 #include <QDateTime>
 #include <QRandomGenerator>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+
+#include "../usr/config.h"
+#include "../usr/BluetoothClient.h"
+#include "InteractiveCAN.h"
+#include "../httpclient/WorkNodeFormModel.h"
 
 ReturnKeyLockManager* ReturnKeyLockManager::m_instance = nullptr;
 
@@ -18,6 +26,11 @@ ReturnKeyLockManager::ReturnKeyLockManager(QObject* parent)
     , m_confirmCountdown(20)
 {
     m_currentReadingStatus = "正在读取锁/钥匙状态......";
+
+    m_workDetail = new HttpGetWorkNodeDetail(this);
+    m_httpClient = new HttpClient(this);
+    connect(m_workDetail, &HttpGetWorkNodeDetail::signalGetRequestData, m_httpClient, &HttpClient::slotGetRequestData);
+    connect(m_httpClient, &HttpClient::signalResponseGetWorkNodeDetail, m_workDetail, &HttpGetWorkNodeDetail::slotHttpResponseGetWorkNodeDetail);
     
     // 处理队列定时器
     m_processTimer = new QTimer(this);
@@ -102,6 +115,8 @@ void ReturnKeyLockManager::onNfcDetected(const QString& nfcId, const QString& de
         m_totalLocks++;
     }
     emit statisticsChanged();
+
+    setIsVisible(true);
     
     // 显示页面(如果还没显示)
     if (!m_isVisible) {
@@ -141,9 +156,12 @@ void ReturnKeyLockManager::onDeviceError(const QString& deviceType, int slotInde
     }
     m_nfcIdSet.insert(info.nfcId);
     
+    m_testDevices.append(info);
     // 添加到异常列表
     m_errorDevices.append(info);
     m_deviceMap[info.nfcId] = info;
+
+    setIsVisible(true);
     
     // 显示页面
     if (!m_isVisible) {
@@ -170,7 +188,6 @@ void ReturnKeyLockManager::onApiResponse(const QString& nfcId, bool success, con
         info.taskId = data.value("taskId", "").toString();
         info.taskName = data.value("taskName", "").toString();
         info.taskOrderNo = data.value("orderNo", "").toString();
-        info.taskWorkshop = data.value("workshop", "").toString();
         info.taskWorker = data.value("worker", "").toString();
         info.hasTask = !info.taskId.isEmpty();
         
@@ -216,6 +233,39 @@ void ReturnKeyLockManager::onApiResponse(const QString& nfcId, bool success, con
     emit dataUpdated();
 }
 
+void ReturnKeyLockManager::slotReceivedJobTicket(const BLERWorkTicketResult &result)
+{
+    if (result.resultDoc.isNull() || result.resultDoc.isEmpty()) return;
+    QJsonObject root = result.resultDoc.object();
+
+    if (!root.contains("data")) return;
+    QJsonArray dataList = root.value("data").toArray();
+    if (dataList.isEmpty()) return;
+
+    // 目前只考虑一把钥匙的情况
+    QJsonObject firstObj = dataList[0].toObject();
+    if (!firstObj.contains("taskCode")) return;
+
+    m_currTask.taskCode = firstObj.value("taskCode").toString();
+    if (!root.contains("lockList")) return;
+    QJsonArray lockList = root.value("lockList").toArray();
+    if (lockList.isEmpty()) return;
+
+    for (int i = 0; i < lockList.count(); i++) {
+        QJsonObject itemObj = lockList[i].toObject();
+        if (itemObj.contains("rfid")) {
+            m_currTask.lockNfcs.append(itemObj.value("rfid").toString());
+        }
+    }
+
+    // 根据nodeId获取任务详情
+    m_workDetail->setNodeId(m_currTask.taskCode);
+    m_workDetail->start();
+
+    InteractiveCAN::instance()->setTaskCode(m_currTask.taskCode.toInt());
+    InteractiveCAN::instance()->httpRequestGetWorkTicketByNodeId();
+}
+
 void ReturnKeyLockManager::processNextInQueue()
 {
     QMutexLocker locker(&m_mutex);
@@ -233,22 +283,32 @@ void ReturnKeyLockManager::processNextInQueue()
     m_currentReadingStatus = QString("正在读取%1状态...").arg(info.deviceType == "key" ? "钥匙" : "锁仓");
     emit currentReadingStatusChanged();
     
-    // TODO: 调用API查询设备状态
-    // 这里需要发出信号,由HttpClient处理
-    // emit signalQueryDeviceStatus(timestamp, url, jsonData, token);
+//    // TODO: 调用API查询设备状态
+//    // 这里需要发出信号,由HttpClient处理
+//    // emit signalQueryDeviceStatus(timestamp, url, jsonData, token);
     
-    // 临时:模拟API响应(实际使用时删除这段,改为真实API调用)
+//    // 临时:模拟API响应(实际使用时删除这段,改为真实API调用)
     QVariantMap mockData;
     mockData["returnStatus"] = "success";
-    mockData["statusMessage"] = "归还成功";
-    mockData["taskId"] = QString("task_%1").arg(QRandomGenerator::global()->bounded(3));
-    mockData["taskName"] = QString("作业任务%1").arg(QRandomGenerator::global()->bounded(3) + 1);
-    mockData["orderNo"] = QString("WO-2024-%1").arg(QRandomGenerator::global()->bounded(100), 3, 10, QChar('0'));
-    mockData["workshop"] = "车间A区";
-    mockData["worker"] = "张工";
+    mockData["statusMessage"] = "";
+//    mockData["taskId"] = QString("task_%1").arg(QRandomGenerator::global()->bounded(3));
+//    mockData["taskName"] = QString("作业任务%1").arg(QRandomGenerator::global()->bounded(3) + 1);
+//    mockData["orderNo"] = QString("WO-2024-%1").arg(QRandomGenerator::global()->bounded(100), 3, 10, QChar('0'));
+    mockData["taskId"] = "";
+    mockData["taskName"] = "";
+    mockData["orderNo"] = "";
+    mockData["worker"] = "";
+
+    m_testDevices.append(info);
     
     locker.unlock();
-    onApiResponse(info.nfcId, true, mockData);
+    if (info.deviceType == "key") {
+        m_currTask.keyNfc = info.nfcId;
+        InteractiveCAN::instance()->connectBLEDevice(info.nfcId);
+    }
+    else {
+        onApiResponse(info.nfcId, true, mockData);
+    }
 }
 
 void ReturnKeyLockManager::confirmReturn()
@@ -261,9 +321,13 @@ void ReturnKeyLockManager::confirmReturn()
 void ReturnKeyLockManager::retrieveFailedDevices()
 {
     qDebug() << "[ReturnKeyLockManager] 取出归还失败的设备";
+    InteractiveCAN::instance()->updateKeyAndLockStatus(true);
     
-    // TODO: 发出信号通知CAN解锁失败的设备
-    // 这里需要遍历失败的设备,调用CAN解锁
+//    for (auto iter = m_errorDevices.begin(); iter != m_errorDevices.end(); ++iter) {
+    for (auto iter = m_testDevices.begin(); iter != m_testDevices.end(); ++iter) {
+        qDebug() << iter->nfcId;
+        InteractiveCAN::instance()->unlockDevices(iter->nfcId, iter->deviceType);
+    }
     
     // 重置倒计时
     m_confirmCountdown = 20;
@@ -291,6 +355,7 @@ void ReturnKeyLockManager::closePage()
     
     m_isProcessing = false;
     m_currentReadingStatus = "正在读取锁/钥匙状态......";
+    InteractiveCAN::instance()->updateKeyAndLockStatus(false);
     
     emit statisticsChanged();
     emit isProcessingChanged();
@@ -301,7 +366,7 @@ void ReturnKeyLockManager::closePage()
 
 QVariantList ReturnKeyLockManager::getTaskGroups()
 {
-    QMutexLocker locker(&m_mutex);
+//    QMutexLocker locker(&m_mutex);
     QVariantList result;
     
     for (auto it = m_taskGroups.begin(); it != m_taskGroups.end(); ++it) {
@@ -312,7 +377,6 @@ QVariantList ReturnKeyLockManager::getTaskGroups()
             const ReturnDeviceInfo& first = it.value().first();
             group["taskName"] = first.taskName;
             group["orderNo"] = first.taskOrderNo;
-            group["workshop"] = first.taskWorkshop;
             group["worker"] = first.taskWorker;
         }
         
@@ -344,7 +408,7 @@ QVariantList ReturnKeyLockManager::getTaskGroups()
 
 QVariantList ReturnKeyLockManager::getErrorDevices()
 {
-    QMutexLocker locker(&m_mutex);
+//    QMutexLocker locker(&m_mutex);
     QVariantList result;
     
     for (const ReturnDeviceInfo& info : m_errorDevices) {
@@ -363,7 +427,7 @@ QVariantList ReturnKeyLockManager::getErrorDevices()
 
 QVariantList ReturnKeyLockManager::getNoTaskDevices()
 {
-    QMutexLocker locker(&m_mutex);
+//    QMutexLocker locker(&m_mutex);
     QVariantList result;
     
     for (const ReturnDeviceInfo& info : m_noTaskDevices) {
@@ -394,6 +458,93 @@ void ReturnKeyLockManager::testTrigger(const QString& deviceType, int slotIndex)
     onNfcDetected(testNfcId, deviceType, slotIndex);
 }
 
+void ReturnKeyLockManager::slotGetJobTicketResult(int stat, const QJsonObject& dataObj)
+{
+    qDebug() << "[slotGetJobTicketResult] " << stat << dataObj;
+    if (!dataObj.contains("ticketStatus")) return;
+
+    int ticketStatus = dataObj.value("ticketStatus").toInt();
+
+    QMutexLocker locker(&m_mutex);
+
+    QString nfcId = m_currTask.keyNfc;
+
+    ReturnDeviceInfo& info = m_deviceMap[nfcId];
+
+    if (stat != 0){
+        info.status = "failed";
+        info.statusMessage = "获取信息失败";
+        info.hasTask = false;
+
+        if (info.deviceType == "key") {
+            m_failedKeys++;
+        }
+        else {
+            m_failedLocks++;
+        }
+        return;
+    }
+    else {
+        // 0未开始 1待上锁 2进行中 3待解锁 4已解锁 5已结束 6已取消)
+        if (ticketStatus == 0 || ticketStatus == 5 || ticketStatus == 6) {
+            info.status = "success";
+            info.statusMessage = "归还成功";
+        }
+        else {
+            info.status = "failed";
+            info.statusMessage = "作业任务未完成";
+        }
+
+        info.taskId = m_currTask.taskCode;
+        info.taskName = WorkNodeFormModel::instance()->formName();
+        info.taskOrderNo = WorkNodeFormModel::instance()->orderNo();
+        info.taskWorker = WorkNodeFormModel::instance()->workerName();
+        info.hasTask = !info.taskId.isEmpty();
+
+        // 更新统计
+        if (info.status == "success") {
+            if (info.deviceType == "key") {
+                m_successKeys++;
+            }
+            else {
+                m_successLocks++;
+            }
+        }
+        else {
+            if (info.deviceType == "key") {
+                m_failedKeys++;
+            }
+            else {
+                m_failedLocks++;
+            }
+        }
+    }
+
+    // 分类到对应组
+    if (!info.hasTask) {
+        m_noTaskDevices.append(info);
+    }
+    else {
+        if (!m_taskGroups.contains(info.taskId)) {
+            m_taskGroups[info.taskId] = QList<ReturnDeviceInfo>();
+        }
+        m_taskGroups[info.taskId].append(info);
+        for (int i = 0; i < m_currTask.lockNfcs.count(); i++) {
+            QString lockNfc = m_currTask.lockNfcs[i];
+            if (!m_deviceMap.contains(nfcId)) continue;
+            for (auto iter = m_noTaskDevices.begin(); iter != m_noTaskDevices.end(); ++iter) {
+                if ((*iter).nfcId == lockNfc) {
+                    iter = m_noTaskDevices.erase(iter);
+                    break;
+                }
+            }
+        }
+    }
+
+    emit statisticsChanged();
+    emit dataUpdated();
+}
+
 void ReturnKeyLockManager::startConfirmCountdown()
 {
     m_confirmCountdown = 20;
@@ -425,3 +576,4 @@ QString ReturnKeyLockManager::getCurrentTimeString()
 {
     return QDateTime::currentDateTime().toString("hh:mm:ss");
 }
+

+ 24 - 6
src/interactive/ReturnKeyLockManager.h

@@ -1,7 +1,6 @@
 #ifndef RETURNKEYLOCKMANAGER_H
 #define RETURNKEYLOCKMANAGER_H
 
-#include <QObject>
 #include <QTimer>
 #include <QMutex>
 #include <QSet>
@@ -10,6 +9,11 @@
 #include <QVariantList>
 #include <QtQml/qqml.h>
 
+#include "../httpclient/HttpClient.h"
+#include "../httpclient/HttpGetWorkNodeDetail.h"
+
+struct BLERWorkTicketResult;
+
 /**
  * @brief 归还设备信息结构
  */
@@ -22,13 +26,18 @@ struct ReturnDeviceInfo {
     QString taskId;             // 所属作业任务ID
     QString taskName;           // 作业任务名称
     QString taskOrderNo;        // 工单号
-    QString taskWorkshop;       // 车间
     QString taskWorker;         // 负责人
     QString returnTime;         // 归还时间
     bool hasTask;               // 是否有关联作业任务
     bool isDeviceError;         // 是否设备异常(无法读取NFC)
 };
 
+typedef struct {
+    QString taskCode;
+    QString keyNfc;
+    QList<QString> lockNfcs;
+} CurrentKeyAndLocks;
+
 /**
  * @brief 归还钥匙和锁管理器
  * 
@@ -102,6 +111,8 @@ public slots:
      */
     void onApiResponse(const QString& nfcId, bool success, const QVariantMap& data);
 
+    void slotReceivedJobTicket(const BLERWorkTicketResult &result);
+
     /**
      * @brief 确认归还
      */
@@ -139,6 +150,10 @@ public slots:
      */
     Q_INVOKABLE void testTrigger(const QString& deviceType = "key", int slotIndex = 0);
 
+    void slotGetJobTicketResult(int stat, const QJsonObject& dataObj);
+public:
+    Q_INVOKABLE void startConfirmCountdown();
+    Q_INVOKABLE void stopConfirmCountdown();
 signals:
     void isProcessingChanged();
     void isVisibleChanged();
@@ -154,7 +169,6 @@ signals:
     
     // API请求信号(连接到HttpClient)
     void signalQueryDeviceStatus(quint64 id, const QString& url, const QByteArray& data, const QString& token);
-
 private slots:
     void processNextInQueue();
     void onConfirmCountdownTick();
@@ -163,10 +177,8 @@ private:
     explicit ReturnKeyLockManager(QObject* parent = nullptr);
     
     void updateStatistics();
-    void startConfirmCountdown();
-    void stopConfirmCountdown();
-    QString getCurrentTimeString();
 
+    QString getCurrentTimeString();
 private:
     static ReturnKeyLockManager* m_instance;
     
@@ -179,6 +191,7 @@ private:
     QMap<QString, QList<ReturnDeviceInfo>> m_taskGroups;
     QList<ReturnDeviceInfo> m_errorDevices;     // 设备异常列表
     QList<ReturnDeviceInfo> m_noTaskDevices;    // 无作业任务列表
+    QList<ReturnDeviceInfo> m_testDevices;
     
     bool m_isProcessing;
     bool m_isVisible;
@@ -196,6 +209,11 @@ private:
     int m_confirmCountdown;
     QTimer* m_confirmTimer;
     QTimer* m_processTimer;
+
+    // 记录当前搜索的钥匙
+    CurrentKeyAndLocks m_currTask;
+    HttpGetWorkNodeDetail* m_workDetail;
+    HttpClient* m_httpClient;
 };
 
 #endif // RETURNKEYLOCKMANAGER_H

+ 48 - 1
src/qml/components/JobTicketProcess.qml

@@ -387,6 +387,53 @@ Rectangle {
                 }
             }
 
+//            Row {
+//                anchors.horizontalCenter: parent.horizontalCenter
+//                anchors.top: __lockTipsContainer.bottom
+//                anchors.topMargin: 50
+//                spacing: 20
+//                visible: __lockView.width > 0 ? false : true
+
+//                MButton {
+//                    id: __getKey
+
+//                    width: 118
+//                    height: 52
+//                    buttonColor: "#1890FF"
+
+//                    text: qsTr("取钥匙")
+//                    textColor: "white"
+//                    iconCharacter: "\uf084"
+//                    iconColor: "white"
+//                }
+
+//                MButton {
+//                    id: __getLocks
+
+//                    width: 118
+//                    height: 52
+//                    buttonColor: "#1890FF"
+
+//                    text: qsTr("取锁")
+//                    textColor: "white"
+//                    iconCharacter: "\uf406"
+//                    iconColor: "white"
+//                }
+
+//                MButton {
+//                    id: __getKeyAndLocks
+
+//                    width: 118
+//                    height: 52
+//                    buttonColor: "#1890FF"
+
+//                    text: qsTr("取钥匙/锁")
+//                    textColor: "white"
+//                    iconCharacter: "\uf084"
+//                    iconColor: "white"
+//                }
+//            }
+
             Flickable {
                 id: __lockView
                 anchors.horizontalCenter: parent.horizontalCenter
@@ -426,7 +473,7 @@ Rectangle {
 
                             statusIconCharacter: lockStatusIcon
                             statusIconSize: 20
-                            statusIconColor: "white"
+                            statusIconColor: "green"
 
 //                            onClicked: {
 //                                control.lockModel.get(index).lockStatus = "已上锁";

+ 67 - 47
src/qml/components/ReturnKeyLockProcess.qml

@@ -10,7 +10,6 @@ Rectangle {
     width: parent ? parent.width : 1920
     height: parent ? parent.height : 1080
     color: "transparent"
-    visible: ReturnKeyLockManager.isVisible
 
     // 数据
     property var taskGroups: []
@@ -37,21 +36,16 @@ Rectangle {
     // 连接数据更新信号
     Connections {
         target: ReturnKeyLockManager
-        onDataUpdated: refreshData()
+        function onDataUpdated() {
+            refreshData();
+        }
     }
 
     // 背景遮罩
     Rectangle {
         anchors.fill: parent
-        color: "#FF0000"
+        color: "transparent"
         opacity: 0.8
-        
-        Text {
-            anchors.centerIn: parent
-            text: "背景遮罩测试 - 如果看到这个说明组件显示了"
-            color: "white"
-            font.pixelSize: 32
-        }
     }
 
     // 主容器
@@ -166,14 +160,14 @@ Rectangle {
                         }
                     }
 
-                    Text {
-                        anchors.right: parent.right
-                        anchors.rightMargin: 20
-                        anchors.verticalCenter: parent.verticalCenter
-                        text: Qt.formatTime(new Date(), "hh:mm:ss")
-                        color: "#888888"
-                        font.pixelSize: 14
-                    }
+//                    Text {
+//                        anchors.right: parent.right
+//                        anchors.rightMargin: 20
+//                        anchors.verticalCenter: parent.verticalCenter
+//                        text: Qt.formatTime(new Date(), "hh:mm:ss")
+//                        color: "#888888"
+//                        font.pixelSize: 14
+//                    }
                 }
 
                 // ========== 作业任务分组 ==========
@@ -208,19 +202,19 @@ Rectangle {
                                 }
 
                                 Text {
-                                    text: " " + (modelData.orderNo || "")
+                                    text: "作业编号: " + (modelData.orderNo || "")
                                     color: "#888888"
                                     font.pixelSize: 14
                                 }
 
-                                Text {
-                                    text: "📍 " + (modelData.workshop || "")
-                                    color: "#FF6B6B"
-                                    font.pixelSize: 14
-                                }
+//                                Text {
+//                                    text: "📍 " + (modelData.workshop || "")
+//                                    color: "#FF6B6B"
+//                                    font.pixelSize: 14
+//                                }
 
                                 Text {
-                                    text: "👤 " + (modelData.worker || "")
+                                    text: "发起人: " + (modelData.worker || "")
                                     color: "white"
                                     font.pixelSize: 14
                                 }
@@ -289,13 +283,13 @@ Rectangle {
                                         anchors.verticalCenter: parent.verticalCenter
                                     }
 
-                                    // 时间
-                                    Text {
-                                        text: modelData.returnTime || ""
-                                        color: "#666666"
-                                        font.pixelSize: 14
-                                        anchors.verticalCenter: parent.verticalCenter
-                                    }
+//                                    // 时间
+//                                    Text {
+//                                        text: modelData.returnTime || ""
+//                                        color: "#666666"
+//                                        font.pixelSize: 14
+//                                        anchors.verticalCenter: parent.verticalCenter
+//                                    }
                                 }
                             }
                         }
@@ -388,12 +382,12 @@ Rectangle {
                                     anchors.verticalCenter: parent.verticalCenter
                                 }
 
-                                Text {
-                                    text: modelData.returnTime || ""
-                                    color: "#666666"
-                                    font.pixelSize: 14
-                                    anchors.verticalCenter: parent.verticalCenter
-                                }
+//                                Text {
+//                                    text: modelData.returnTime || ""
+//                                    color: "#666666"
+//                                    font.pixelSize: 14
+//                                    anchors.verticalCenter: parent.verticalCenter
+//                                }
                             }
                         }
                     }
@@ -486,12 +480,12 @@ Rectangle {
                                     anchors.verticalCenter: parent.verticalCenter
                                 }
 
-                                Text {
-                                    text: modelData.returnTime || ""
-                                    color: "#666666"
-                                    font.pixelSize: 14
-                                    anchors.verticalCenter: parent.verticalCenter
-                                }
+//                                Text {
+//                                    text: modelData.returnTime || ""
+//                                    color: "#666666"
+//                                    font.pixelSize: 14
+//                                    anchors.verticalCenter: parent.verticalCenter
+//                                }
                             }
                         }
                     }
@@ -561,7 +555,8 @@ Rectangle {
                 color: "transparent"
                 border.color: "#FF4D4F"
                 border.width: 2
-                visible: (ReturnKeyLockManager.failedKeys + ReturnKeyLockManager.failedLocks) > 0
+//                visible: (ReturnKeyLockManager.failedKeys + ReturnKeyLockManager.failedLocks) > 0
+                visible: true
 
                 Text {
                     anchors.centerIn: parent
@@ -596,6 +591,7 @@ Rectangle {
                     }
 
                     Rectangle {
+                        id: __confirmCountdownArea
                         width: 45
                         height: 28
                         radius: 14
@@ -624,7 +620,31 @@ Rectangle {
     // 阻止点击穿透
     MouseArea {
         anchors.fill: parent
-        onClicked: { }
-        onPressed: { }
+        onClicked: {
+            mouse.accepted = false;
+        }
+        onPressed: {
+            if (ReturnKeyLockManager.isVisible) {
+                ReturnKeyLockManager.stopConfirmCountdown();
+                __confirmCountdownArea.visible = false;
+                __startConfirmCountdownTimer.start();
+            }
+            mouse.accepted = false;
+        }
+    }
+
+    Timer {
+        id: __startConfirmCountdownTimer
+        interval: 20 * 1000
+        repeat: true
+        running: false
+
+        onTriggered: {
+            if (ReturnKeyLockManager.isVisible) {
+                ReturnKeyLockManager.startConfirmCountdown();
+                __confirmCountdownArea.visible = true;
+                __startConfirmCountdownTimer.stop();
+            }
+        }
     }
 }

+ 20 - 17
src/qml/main.qml

@@ -575,23 +575,26 @@ ApplicationWindow {
         // anchors.horizontalCenter: parent.horizontalCenter
     }
 
-//    Loader {
-//        id: __returnKeyLockProcess
-//        visible: false
-
-//        anchors.fill: parent
-//        source: "components/ReturnKeyLockProcess.qml"
-//    }
-
-//    Connections {
-//        target: InteractiveCAN
-//        function onSignalReturnDevicesInfo(info) {
-//            if (!__returnKeyLockProcess.visible) {
-//                __returnKeyLockProcess.visible = true;
-//            }
-//            __returnKeyLockProcess.item.deviceInfo = info;
-//        }
-//    }
+    Loader {
+        id: __returnKeyLockProcess
+        visible: ReturnKeyLockManager.isVisible
+        z: 999
+
+        anchors.fill: parent
+        asynchronous: true
+    }
+
+    Connections {
+        target: ReturnKeyLockManager
+        function onIsVisibleChanged() {
+            if (ReturnKeyLockManager.isVisible) {
+                __returnKeyLockProcess.source = "components/ReturnKeyLockProcess.qml";
+            } else {
+                __returnKeyLockProcess.sourceComponent = null;
+                __returnKeyLockProcess.source = "";
+            }
+        }
+    }
     
     Timer {
         id: swipeTimer

+ 0 - 1
src/usr/BluetoothClient.cpp

@@ -366,7 +366,6 @@ void BLEClient::sendJobTicket(const QJsonDocument &doc)
 void BLEClient::startGetWorkTicketResult()
 {
     msleep(50);
-    qDebug() << "[startGetWorkTicketResult]";
     getWorkTicketResult();
     msleep(50);
 }

+ 124 - 22
src/usr/CANClient.cpp

@@ -183,12 +183,12 @@ void CANClient::run()
         }
         readKeyRFIDStatusAsync();
         msleep(CAN_SLEEP_TIME);
-        readLockRFIDStatusAsync();
-        msleep(CAN_SLEEP_TIME);
         readKeyBaseStatusAsync();
         msleep(CAN_SLEEP_TIME);
         readLockStatusAsync();
         msleep(CAN_SLEEP_TIME);
+        readLockRFIDStatusAsync();
+        msleep(CAN_SLEEP_TIME);
         readKeyBaseControlStatusAsync();
         msleep(CAN_SLEEP_TIME);
         readLockControlStatusAsync();
@@ -805,6 +805,43 @@ void CANClient::readKeyBaseStatusAsync()
 //                    << ",左充电:" << (status.leftCharging ? "充电中" : "未充电")
 //                    << "; 右钥匙:" << (status.rightHasKey ? "有" : "无")
 //                    << ",右充电:" << (status.rightCharging ? "充电中" : "未充电");
+            QMutexLocker locker(&m_insertDataMutex);
+
+            bool prevLeftHasKey = false, prevRightHasKey = false;
+            if (m_keyBaseStatus.find(nodeId) != m_keyBaseStatus.end()) {
+                prevLeftHasKey = m_keyBaseStatus[nodeId].leftHasKey;
+                prevRightHasKey = m_keyBaseStatus[nodeId].rightHasKey;
+            }
+
+            if (!m_keyBaseStatus.isEmpty()) {
+                if (!prevLeftHasKey && status.leftHasKey) {
+                    qInfo() << "[归还检测] 检测到左钥匙插入!";
+
+                    if (m_keyRFIDStatus.find(nodeId) != m_keyRFIDStatus.end() &&
+                        !m_keyRFIDStatus[nodeId].leftKeyRFID.isEmpty() &&
+                        m_keyRFIDStatus[nodeId].leftKeyRFID != "00000000") {
+                        qInfo() << "[归还检测] 左钥匙:NFC卡号:" << m_keyRFIDStatus[nodeId].leftKeyRFID;
+
+                        emit nfcDeviceDetected(m_keyRFIDStatus[nodeId].leftKeyRFID, "key", 0);
+                    } else {
+                        qWarning() << "[归还检测] 左钥匙存在但无法读取NFC";
+                        emit nfcDeviceError("key", 0);
+                    }
+                }
+                else if (prevLeftHasKey && !status.leftHasKey) {
+                    qInfo() << "[取出检测] 检测到左钥匙取出!";
+                    if (m_keyRFIDStatus.find(nodeId) != m_keyRFIDStatus.end() &&
+                        !m_keyRFIDStatus[nodeId].leftKeyRFID.isEmpty() &&
+                        m_keyRFIDStatus[nodeId].leftKeyRFID != "00000000") {
+                        qInfo() << "[取出检测] 左钥匙:NFC卡号:" << m_keyRFIDStatus[nodeId].leftKeyRFID;
+
+                        emit signalPopDevices(m_keyRFIDStatus[nodeId].leftKeyRFID, "key", 0);
+                    } else {
+                        qWarning() << "[取出检测] 左钥匙存在但无法读取NFC";
+                        emit nfcDeviceError("key", 0);
+                    }
+                }
+            }
         }
         else {
             qWarning() << "[KeyBase] 读取状态失败";
@@ -917,26 +954,42 @@ void CANClient::readLockStatusAsync()
             if (m_locksControlStatus.find(nodeId) != m_locksControlStatus.end()) {
                 prevLockHasKeyMap = m_locksControlStatus[nodeId].lockHasKeyMap;
             }
-            
-            // 检测每个锁位的插入(从无到有)
-            for (int i = 0; i < 5; i++) {
-                bool prevHasLock = prevLockHasKeyMap.contains(i) ? prevLockHasKeyMap[i] : false;
-                bool currHasLock = status.lockHasKeyMap.contains(i) ? status.lockHasKeyMap[i] : false;
-                
-                if (!prevHasLock && currHasLock) {
-                    qInfo() << "[归还检测] 检测到" << (i+1) << "号锁插入!";
-                    // 锁插入,获取NFC卡号
-                    if (m_locksRFIDStatus.find(nodeId) != m_locksRFIDStatus.end() &&
-                        m_locksRFIDStatus[nodeId].lockRFIDMap.contains(i) &&
-                        !m_locksRFIDStatus[nodeId].lockRFIDMap[i].isEmpty() &&
-                        m_locksRFIDStatus[nodeId].lockRFIDMap[i] != "00000000") {
-                        qInfo() << "[归还检测]" << (i+1) << "号锁NFC卡号:" << m_locksRFIDStatus[nodeId].lockRFIDMap[i];
-
-                        emit nfcDeviceDetected(m_locksRFIDStatus[nodeId].lockRFIDMap[i], "lock", i);
-                        emit signalLockNfcDeviceDetected(m_locksRFIDStatus[nodeId].lockRFIDMap[i]);
-                    } else {
-                        qWarning() << "[归还检测]" << (i+1) << "号锁存在但无法读取NFC";
-                        emit nfcDeviceError("lock", i);
+            if (!m_locksControlStatus.isEmpty()) {
+                // 检测每个锁位的插入(从无到有)
+                for (int i = 0; i < 5; i++) {
+                    bool prevHasLock = prevLockHasKeyMap.contains(i) ? prevLockHasKeyMap[i] : false;
+                    bool currHasLock = status.lockHasKeyMap.contains(i) ? status.lockHasKeyMap[i] : false;
+
+                    if (!prevHasLock && currHasLock) {
+                        qInfo() << "[归还检测] 检测到" << (i+1) << "号锁插入!";
+                        // 锁插入,获取NFC卡号
+                        if (m_locksRFIDStatus.find(nodeId) != m_locksRFIDStatus.end() &&
+                            m_locksRFIDStatus[nodeId].lockRFIDMap.contains(i) &&
+                            !m_locksRFIDStatus[nodeId].lockRFIDMap[i].isEmpty() &&
+                            m_locksRFIDStatus[nodeId].lockRFIDMap[i] != "00000000") {
+                            qInfo() << "[归还检测]" << (i+1) << "号锁NFC卡号:" << m_locksRFIDStatus[nodeId].lockRFIDMap[i];
+
+                            emit nfcDeviceDetected(m_locksRFIDStatus[nodeId].lockRFIDMap[i], "lock", i);
+                            emit signalLockNfcDeviceDetected(m_locksRFIDStatus[nodeId].lockRFIDMap[i]);
+                        } else {
+                            qWarning() << "[归还检测]" << (i+1) << "号锁存在但无法读取NFC";
+                            emit nfcDeviceError("lock", i);
+                        }
+                    }
+                    else if (prevHasLock && !currHasLock) {
+                        qInfo() << "[取出检测] 检测到"  << (i+1) << "号锁取出!";
+                        qDebug() << m_locksRFIDStatus[nodeId].lockRFIDMap[i];
+                        if (m_locksRFIDStatus.find(nodeId) != m_locksRFIDStatus.end() &&
+                            m_locksRFIDStatus[nodeId].lockRFIDMap.contains(i) &&
+                            !m_locksRFIDStatus[nodeId].lockRFIDMap[i].isEmpty() &&
+                            m_locksRFIDStatus[nodeId].lockRFIDMap[i] != "00000000") {
+                            qInfo() << "[取出检测]" << (i+1) << "号锁NFC卡号:" << m_locksRFIDStatus[nodeId].lockRFIDMap[i];
+
+                            emit signalPopDevices(m_locksRFIDStatus[nodeId].lockRFIDMap[i], "lock", i);
+                        } else {
+                            qWarning() << "[取出检测]" << (i+1) << "号锁存在但无法读取NFC";
+                            emit nfcDeviceError("lock", i);
+                        }
                     }
                 }
             }
@@ -1331,6 +1384,7 @@ void CANClient::readLockRFIDStatusAsync()
                 readSuccess = true;
 
                 QMutexLocker locker(&m_insertDataMutex);
+
                 if (m_locksRFIDStatus.find(nodeId) == m_locksRFIDStatus.end()) {
                     m_locksRFIDStatus.insert(nodeId, lockStatus);
                 }
@@ -1609,6 +1663,54 @@ bool CANClient::lockingEKey(quint8 nodeId, bool leftKey, bool rightKey)
     return true;
 }
 
+void CANClient::unlockEKey(const QString &keyNFC)
+{
+    CANKeyBaseChargeStatus chargeStatus;
+
+    for (auto iter = m_keyRFIDStatus.begin(); iter != m_keyRFIDStatus.end(); ++iter) {
+//        int nodeId = iter.key();
+        if (keyNFC == iter->leftKeyRFID) {
+            chargeStatus.leftLocked = false;
+            chargeStatus.leftWorking = true;
+            chargeStatus.leftChargeEnable = false;
+            chargeStatus.leftChargeState = ChargeState::Uncharged;
+
+            writeKeyBaseChargeControlAsync(chargeStatus);
+            msleep(CAN_SLEEP_TIME);
+            return;
+        }
+        else if (keyNFC == iter->rightKeyRFID) {
+            chargeStatus.rightLocked = false;
+            chargeStatus.rightWorking = true;
+            chargeStatus.rightChargeEnable = false;
+            chargeStatus.rightChargeState = ChargeState::Uncharged;
+
+            writeKeyBaseChargeControlAsync(chargeStatus);
+            msleep(CAN_SLEEP_TIME);
+            return;
+        }
+    }
+}
+
+void CANClient::unlockLock(const QString &lockNFC)
+{
+    CANLocksControl lockStatus;
+    // 暂时只处理一个5路锁
+    for (auto iter = m_locksRFIDStatus.begin(); iter != m_locksRFIDStatus.end(); ++iter) {
+        for (auto hasKeyIter = iter->lockRFIDMap.begin(); hasKeyIter != iter->lockRFIDMap.end(); ++hasKeyIter) {
+            if (hasKeyIter.value() == lockNFC) {
+                lockStatus.lockNums.append(hasKeyIter.key());
+                lockStatus.lockLockedMap.insert(hasKeyIter.key(), false);
+                lockStatus.lockWorkMap.insert(hasKeyIter.key(), true);
+
+                 writeLockBuckleAsync(lockStatus);
+                 msleep(50);
+                 return;
+            }
+        }
+    }
+}
+
 void CANClient::writeKeyBaseChargeControlAsync(CANKeyBaseChargeStatus& chargeStatus)
 {
     quint8 nodeId = 0;

+ 4 - 0
src/usr/CANClient.h

@@ -217,6 +217,7 @@ signals:
     // ==========================================
     
     void signalReturnKey();
+    void signalPopDevices(const QString& nfcId, const QString& deviceType, int slotIndex);
 private:
     // 处理设备主动上报的插入帧(归还流程)
     void handleDeviceInsertionFrame(const QCanBusFrame& frame);
@@ -299,6 +300,9 @@ public:
     bool unlockLocks(const QList<int>& lockNums);
 
     bool lockingEKey(quint8 nodeId, bool leftKey, bool rightKey);
+
+    void unlockEKey(const QString& keyNFC);
+    void unlockLock(const QString& lockNFC);
 public:
     static CANReturnKeyDevices returnDevicesInfo;
 private:

+ 2 - 2
src/usr/config.h

@@ -73,8 +73,8 @@ public:
 
     QString m_systemMACAddr;
 
-    QString httpHost = "120.27.232.27:9292";
-//    QString httpHost = "192.168.0.10:48080";
+//    QString httpHost = "120.27.232.27:9292";
+    QString httpHost = "192.168.0.10:48080";
     QString tenant_id = "149";
 
     QString userInfoUrl = "/admin-api/system/user/profile/get";                                             // 用户中心