Browse Source

update:解决二维码和刷卡页面切换导致的充电流程问题;

Bigpang_1 1 tháng trước cách đây
mục cha
commit
a6eed76f0d

+ 4 - 4
TchargeManageSys.pro

@@ -14,10 +14,10 @@ LIBS += -lqrencode
 CONFIG += c++17
 
 # 明确不包含xcb
-!contains(QT_CONFIG, no-xcb) {
-    message("XCB support is disabled")
-    DEFINES += QT_NO_XCB
-}
+# !contains(QT_CONFIG, no-xcb) {
+#     message("XCB support is disabled")
+#     DEFINES += QT_NO_XCB
+# }
 
 # 输出到 bin 目录
 CONFIG(debug, debug|release) {

+ 1 - 1
src/utils/PriceCalculator.cpp

@@ -65,7 +65,7 @@ PriceCalculator* PriceCalculator::instance()
 void PriceCalculator::setPriceInfo(const PRICE_INFO& priceInfo)
 {
     m_priceInfo = priceInfo;
-    qDebug() << "Price information updated, type: " << priceInfo.type;
+    // qDebug() << "Price information updated, type: " << priceInfo.type;
 }
 
 double PriceCalculator::calculateCost(const QDateTime& startTime, const QDateTime& endTime, double electricity)

+ 133 - 48
src/utils/TcpServerThread.cpp

@@ -76,6 +76,7 @@ void TcpServerThread::startWorkerThread()
         QMetaObject::invokeMethod(m_workerThread, [this, &initComplete, &loop]() {
             // 在工作线程中创建QTcpServer
             m_server = new QTcpServer();
+            // m_server->setSocketOption(QAbstractSocket::KeepAliveOption, 1); // 启用TCP KeepAlive
 
             // 连接服务器信号
             connect(m_server, &QTcpServer::newConnection, this, &TcpServerThread::handleNewConnection);
@@ -88,16 +89,18 @@ void TcpServerThread::startWorkerThread()
 
                 connect(timer, &QTimer::timeout, this, [this, index]() {
                     emit heartbeatTimeout(index);
-                    auto recvStatus = m_dataReceived.values();
-                    if (!recvStatus.contains(true)) {
-                        LOG_WARNING(QString("No data received for gun %1 within timeout period").arg(index));
-                        // 在调用removeClient前先检查m_client是否有效
-                        if (m_client && m_isConnected) {
-                            removeClient();
+                    // 检查连接是否有效
+                    if (m_client && m_client->state() == QAbstractSocket::ConnectedState) {
+                        auto recvStatus = m_dataReceived.values();
+                        if (!recvStatus.contains(true)) {
+                            LOG_WARNING(QString("No data received for gun %1 within timeout period").arg(index));
+                            if (m_client && m_isConnected) {
+                                removeClient();
+                            }
+                        } else {
+                            // 重置数据接收状态
+                            m_dataReceived[index] = false;
                         }
-                    } else {
-                        // 重置数据接收状态
-                        m_dataReceived[index] = false;
                     }
                 });
             }
@@ -440,8 +443,9 @@ void TcpServerThread::parseClientMsg(const QByteArray &data)
         return;
     }
 
+    LOG_DEBUG(QString("CMD : %1, raw data: %2").arg(QString::number(msg_head.cmd, 16), QString::fromLocal8Bit(data.toHex(' ').toUpper())));
+
     m_isAuthReply = false;
-    LOG_DEBUG(QString("TCP recv data: " + data.toHex(' ').toUpper()));
     // 处理不同类型的消息
     switch (msg_head.cmd) {
     case CHARGE_GUN_BASIC_INFO_CMD:
@@ -481,7 +485,7 @@ void TcpServerThread::parseChargeGunBasicInfo(const QByteArray &data)
     // 解析消息头以获取充电枪ID
     m_dataReceived[basic_info.GUNID] = true;
 
-    // qDebug() << QString("Gun id: %1").arg(QString::number(basic_info.GUNID, 16));
+    LOG_DEBUG(QString("Gun    ID: %1").arg(QString::number(basic_info.GUNID, 16)));
 
     QStringList error_str_list  = QStringList();
     QStringList state_str_list  = QStringList();
@@ -507,7 +511,13 @@ void TcpServerThread::parseChargeGunBasicInfo(const QByteArray &data)
 void TcpServerThread::parseChargeGunChargeInfo(const QByteArray &data)
 {
     charge_gun_charge_info_t charge_info = GeneralInterface::decodeChargeInfo(data);
-    // sendHeatReply(CHARGE_GUN_CHARGE_INFO_CMD, charge_info.GUNID);
+    LOG_DEBUG(QString("GUN ID : %1").arg(QString::number(charge_info.GUNID)));
+    LOG_DEBUG(QString("SOC : %1").arg(QString::number(charge_info.SOC)));
+    LOG_DEBUG(QString("GUNLine_temp0 : %1").arg(QString::number(charge_info.GUNLine_temp0)));
+    LOG_DEBUG(QString("GUNLine_temp1 : %1").arg(QString::number(charge_info.GUNLine_temp1)));
+    LOG_DEBUG(QString("powerKW : %1").arg(QString::number(charge_info.powerKW)));
+    LOG_DEBUG(QString("voltage : %1").arg(QString::number(charge_info.voltage)));
+    LOG_DEBUG(QString("current : %1").arg(QString::number(charge_info.current)));
     refreshChargingInfo(charge_info);
 
     sendHeatReply(CHARGE_GUN_CHARGE_INFO_CMD, charge_info.GUNID);
@@ -550,6 +560,28 @@ void TcpServerThread::parseChargeGunStop(const QByteArray &data)
 }
 
 /**
+ * @brief TcpServerThread::resetHeartbeatTimer 重置心跳定时器
+ * @param gun_id 枪号,-1表示重置所有枪的定时器
+ */
+void TcpServerThread::resetHeartbeatTimer(int gun_id)
+{
+    if (gun_id == -1) {
+        // 重置所有枪的定时器
+        for (auto timer : m_heartbeatTimer.values()) {
+            if (timer) {
+                QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection);
+            }
+        }
+    } else if (m_heartbeatTimer.contains(gun_id)) {
+        // 重置指定枪的定时器
+        auto timer = m_heartbeatTimer.value(gun_id);
+        if (timer) {
+            QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection);
+        }
+    }
+}
+
+/**
  * @brief TcpServerThread::parseHeartbeatReply  解析心跳回复
  * @param data   心跳回复数据
  */
@@ -562,7 +594,10 @@ void TcpServerThread::parseHeartbeatReply(const QByteArray &data)
     sendData(HEARTBEAT_REPLY_CMD, reply_data, m_trans_id);
 
     // 重置心跳定时器
-    // resetHeartbeatTimer();
+    resetHeartbeatTimer();
+
+    // 记录心跳接收日志
+    LOG_INFO("Heartbeat reply received successfully");
 }
 
 /**
@@ -595,7 +630,7 @@ void TcpServerThread::parseGunError(const uint32_t &gun_error, const uint32_t &g
     else if (gun_id == CHARGE_GUN_DEFAULT_2) key = Global::Keys::GUN_ERROR_2;
 
     if (gun_error != 0) {
-        // qDebug() << QString("Gun error: %1").arg(error_map.value(gun_error));
+        LOG_DEBUG(QString("Gun error: %1").arg(error_map.value(gun_error)));
     }
     GLOBALS->setValue(key, QVariant::fromValue(error_map));
 }
@@ -612,7 +647,7 @@ void TcpServerThread::parseGunState(const uint32_t &gun_state, const uint32_t &g
     if (gun_id == CHARGE_GUN_DEFAULT_1) key = Global::Keys::GUN_STATE_1;
     else if (gun_id == CHARGE_GUN_DEFAULT_2) key = Global::Keys::GUN_STATE_2;
 
-    // qDebug() << QString("Gun state: %1/%2").arg(QString::number(gun_state, 16), state_str);
+    LOG_DEBUG(QString("Gun state: %1 / %2").arg(QString::number(gun_state, 16), state_str));
 
     QMap<uint32_t, QString> state_map;
     state_map.insert(gun_state, state_str);
@@ -652,7 +687,7 @@ void TcpServerThread::parseGunType(const uint32_t &gun_type, const uint32_t &gun
     if (gun_id == CHARGE_GUN_DEFAULT_1) key = Global::Keys::GUN_TYPE_1;
     else if (gun_id == CHARGE_GUN_DEFAULT_2) key = Global::Keys::GUN_TYPE_2;
 
-    // qDebug() << QString("Gun type: %1/%2").arg(QString::number(gun_type, 16), type_map.value(gun_type));
+    LOG_DEBUG(QString("Gun  type: %1 / %2").arg(QString::number(gun_type, 16), type_map.value(gun_type)));
     GLOBALS->setValue(key, QVariant::fromValue(type_map));
 }
 
@@ -860,6 +895,12 @@ void TcpServerThread::handleNewConnection()
     }
 
     // 启动心跳定时器
+    resetHeartbeatTimer();
+
+    // 发送连接成功日志
+    LOG_INFO(QString("New client connected: %1").arg(getClientInfo()));
+
+    // 启动心跳定时器
     for (auto timer : m_heartbeatTimer) {
         QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection,
             Q_ARG(int, m_tcu_cfg.time_out.tcp_heartbeat * 1000));
@@ -869,7 +910,7 @@ void TcpServerThread::handleNewConnection()
     QMetaObject::invokeMethod(this, [this, newSocket]() {
         m_client = newSocket;
         m_isConnected = true;
-        qDebug() << "update client : " << m_client;
+        // qDebug() << "update client : " << m_client;
         LOG_INFO(QString("New client connection : %1").arg(getClientInfo()));
         emit clientConnected();
     }, Qt::QueuedConnection);
@@ -893,33 +934,88 @@ void TcpServerThread::handleReadyRead()
         return;
     }
 
-    m_recvData.append(m_client->readAll());
+    // 读取所有可用数据
+    QByteArray newData = m_client->readAll();
+    if (!newData.isEmpty()) {
+        LOG_DEBUG(QString("TCP recv data: %1").arg(QString::fromLocal8Bit(newData.toHex(' ').toUpper())));
+        m_recvData.append(newData);
+    }
+
+    // 记录处理开始时间,避免死循环
+    qint64 startTime = QDateTime::currentMSecsSinceEpoch();
+    const int MAX_PROCESSING_TIME = 100; // 最大处理时间(ms)
+    const int MAX_BUFFER_SIZE = 1024 * 1024; // 最大缓冲区大小(1MB)
+
     while (true) {
-        // 1. 判断是否有长度头
-        if (m_recvData.size() < MESSAGE_HEAD_LENGTH)
+        // 检查处理时间是否过长
+        if (QDateTime::currentMSecsSinceEpoch() - startTime > MAX_PROCESSING_TIME) {
+            LOG_WARNING("TCP data processing time exceeded limit, breaking loop");
             break;
+        }
 
-        // 2. 取出长度头
-        // 处理报文帧头
-        MSG_HEAD msg_head;
-        msg_head.trans_id   = GeneralInterface::toUInt16(m_recvData.mid(0, 2));
-        msg_head.proto_id   = GeneralInterface::toUInt16(m_recvData.mid(2, 2));
-        msg_head.length     = GeneralInterface::toUInt16(m_recvData.mid(4, 2));
-        msg_head.unit_id    = GeneralInterface::toUInt8(m_recvData.mid(6, 1));
-        msg_head.cmd        = GeneralInterface::toUInt8(m_recvData.mid(7, 1));
-
-        // 3. 判断是否有完整消息
-        if (m_recvData.size() < MESSAGE_HEAD_LENGTH + msg_head.length)
+        // 检查缓冲区是否过大
+        if (m_recvData.size() > MAX_BUFFER_SIZE) {
+            LOG_WARNING("TCP receive buffer too large, clearing buffer");
+            m_recvData.clear();
+            break;
+        }
+
+        // 1. 判断是否有长度头
+        if (m_recvData.size() < MESSAGE_HEAD_LENGTH) {
             break;
+        }
+
+        try {
+            // 2. 取出长度头并验证
+            MSG_HEAD msg_head;
+            msg_head.trans_id   = GeneralInterface::toUInt16(m_recvData.mid(0, 2));
+            msg_head.proto_id   = GeneralInterface::toUInt16(m_recvData.mid(2, 2));
+            msg_head.length     = GeneralInterface::toUInt16(m_recvData.mid(4, 2));
+            msg_head.unit_id    = GeneralInterface::toUInt8(m_recvData.mid(6, 1));
+            msg_head.cmd        = GeneralInterface::toUInt8(m_recvData.mid(7, 1));
+
+            // 更新数据接收状态,标记所有枪的数据接收状态为true
+            auto gun_num = ConfigManager::instance()->ccu_cfg().GUN_Numb;
+            for (int index = 1; index <= gun_num; index++) {
+                m_dataReceived[index] = true;
+            }
 
-        // 4. 取出完整消息
-        QByteArray msg = m_recvData.mid(0, MESSAGE_HEAD_LENGTH+msg_head.length);
+            // 验证长度是否合理,防止缓冲区溢出攻击
+            if (msg_head.length > MAX_BUFFER_SIZE / 2) {
+                LOG_ERROR(QString("Invalid message length %1, clearing buffer").arg(msg_head.length));
+                m_recvData.clear();
+                break;
+            }
+
+            // 3. 判断是否有完整消息
+            if (m_recvData.size() < MESSAGE_HEAD_LENGTH + msg_head.length) {
+                break;
+            }
 
-        // 5. 处理消息
-        parseClientMsg(msg);
+            // 4. 取出完整消息
+            QByteArray msg = m_recvData.mid(0, MESSAGE_HEAD_LENGTH + msg_head.length);
 
-        // 6. 移除已处理部分
-        m_recvData.remove(0, MESSAGE_HEAD_LENGTH + msg_head.length);
+            // 5. 处理消息
+            parseClientMsg(msg);
+
+            // 6. 移除已处理部分
+            m_recvData.remove(0, MESSAGE_HEAD_LENGTH + msg_head.length);
+        } catch (const std::exception& e) {
+            // 捕获可能的异常
+            LOG_ERROR(QString("Exception in TCP data processing: %1, clearing buffer").arg(e.what()));
+            m_recvData.clear();
+            break;
+        } catch (...) {
+            // 捕获未知异常
+            LOG_ERROR("Unknown exception in TCP data processing, clearing buffer");
+            m_recvData.clear();
+            break;
+        }
+
+        // 如果没有更多数据可处理,退出循环
+        if (m_recvData.isEmpty()) {
+            break;
+        }
     }
 }
 
@@ -983,14 +1079,3 @@ void TcpServerThread::removeClient()
         }
     }
 }
-
-/**
- * @brief TcpServerThread::resetHeartbeatTimer  重置心跳定时器
- * @param gun_id     充电枪ID
- */
-void TcpServerThread::resetHeartbeatTimer(const int &gun_id)
-{
-    if (m_heartbeatTimer.contains(gun_id)) {
-        QMetaObject::invokeMethod(m_heartbeatTimer[gun_id], "start", Qt::QueuedConnection);
-    }
-}

+ 1 - 1
src/utils/TcpServerThread.h

@@ -134,7 +134,7 @@ private slots:
 
 private:
     void removeClient();
-    void resetHeartbeatTimer(const int& gun_id);
+    void resetHeartbeatTimer(int gun_id = -1);
 
 private:
     QTcpServer *m_server;

+ 25 - 8
src/widgets/workspace/home/FormAuthentication.cpp

@@ -178,6 +178,7 @@ void FormAuthentication::restartTimer(bool isStop)
     }
     m_timer->start();
     m_gun.is_stop_charging = isStop;
+    sendSignals();
 }
 
 void FormAuthentication::stopTimer()
@@ -224,14 +225,19 @@ void FormAuthentication::startAuth()
 
     if (!m_gun.is_reading_card) {
         m_gun.is_reading_card = true;
-        if (m_tcu_cfg.auth_type_enable.nfc) {
-            m_gun.charging_info.isStopCharging = m_gun.is_stop_charging;
-            emit signalStartNFCReading(m_gun.charging_info);
-        }
+        sendSignals();
+    }
+}
 
-        if (m_tcu_cfg.auth_type_enable.qr) {
-            refreshQrIcon();
-        }
+void FormAuthentication::sendSignals()
+{
+    if (m_tcu_cfg.auth_type_enable.nfc) {
+        m_gun.charging_info.isStopCharging = m_gun.is_stop_charging;
+        emit signalStartNFCReading(m_gun.charging_info);
+    }
+
+    if (m_tcu_cfg.auth_type_enable.qr) {
+        refreshQrIcon();
     }
 }
 
@@ -281,7 +287,18 @@ void FormAuthentication::onNFCReadCard(const charging_control_info_t& data)
 void FormAuthentication::showEvent(QShowEvent *event)
 {
     QWidget::showEvent(event);
-    if (m_timer->isActive()) return;
+    // qDebug() << "auth shot," << "auth.nfc enable : " << m_tcu_cfg.auth_type_enable.nfc
+    //          << (m_tcu_cfg.auth_type_enable.qr != ConfigManager::instance()->tcu_cfg().auth_type_enable.qr ||
+    //              m_tcu_cfg.auth_type_enable.nfc != ConfigManager::instance()->tcu_cfg().auth_type_enable.nfc ||
+    //              m_tcu_cfg.auth_type_enable.pos != ConfigManager::instance()->tcu_cfg().auth_type_enable.pos);
+    if (m_tcu_cfg.auth_type_enable.qr != ConfigManager::instance()->tcu_cfg().auth_type_enable.qr ||
+        m_tcu_cfg.auth_type_enable.nfc != ConfigManager::instance()->tcu_cfg().auth_type_enable.nfc ||
+        m_tcu_cfg.auth_type_enable.pos != ConfigManager::instance()->tcu_cfg().auth_type_enable.pos) {
+        restartTimer();
+    } else if (m_timer->isActive()) {
+        return;
+    }
+
     m_tcu_cfg = ConfigManager::instance()->tcu_cfg();
     m_gun.cur_time = m_tcu_cfg.time_out.auth_form_wait_time;
     m_gun.is_reading_card = false;

+ 2 - 1
src/widgets/workspace/home/FormAuthentication.h

@@ -80,10 +80,11 @@ private:
     void refreshQrIcon();
 
     // 定时器相关
-    void restartTimer(bool isStop);
+    void restartTimer(bool isStop = false);
     void updateTimerDisplay();
 
     void startAuth();
+    void sendSignals();
 
 private:
     Ui::FormAuthentication* ui;

+ 16 - 9
src/widgets/workspace/home/FormGunDetail.cpp

@@ -204,15 +204,21 @@ void FormGunDetail::setupGlobalsConnections()
         });
     }
 
-    // connect(GLOBALS,    &Globals::tcuConfigValueChanged,        this,   [this](const TCU_CFG& tcu_cfg) {
-    //     m_tcu_cfg = tcu_cfg;
-    //     charge_prepare_enum form = static_cast<charge_prepare_enum>(m_tcu_cfg.current_prepare_form);
-    //     if (TcpServerThread::instance()->isClientConnected()) {
-    //         showForm(form);
-    //     }
-    //     showForm(form);
-    //     refreshGunLinTempVisible();
-    // });
+    connect(GLOBALS,    &Globals::tcuConfigValueChanged,        this,   [this](const TCU_CFG& tcu_cfg) {
+        // m_tcu_cfg = tcu_cfg;
+        // charge_prepare_enum form = static_cast<charge_prepare_enum>(m_tcu_cfg.current_prepare_form);
+        // if (TcpServerThread::instance()->isClientConnected()) {
+        //     showForm(form);
+        // }
+        // showForm(form);
+        // refreshGunLinTempVisible();
+        if (m_tcu_cfg.auth_type_enable.qr != tcu_cfg.auth_type_enable.qr ||
+            m_tcu_cfg.auth_type_enable.nfc != tcu_cfg.auth_type_enable.nfc ||
+            m_tcu_cfg.auth_type_enable.pos != tcu_cfg.auth_type_enable.pos) {
+            m_gun.is_auth_form = false;
+            showForm(AUTHENTICATION);
+        }
+    });
 
     connect(GLOBALS,    &Globals::chargeInfoValueChanged,       this,   [this](const CHARGING_INFO& info) {
         if (info.GUNID != static_cast<uint32_t>(m_gun.gun_id)) return;
@@ -618,6 +624,7 @@ void FormGunDetail::refreshPlugIn()
 {
     m_gun.is_stop_charging = false;
     auto ret = m_gun.charging_state >> STOP_DEAL & 1;
+    // qDebug() << (m_gun.auth_result == false || m_gun.currentForm == CHARGE_PREPARE || ret == 1 || m_gun.is_auth_form);
     if (m_gun.auth_result == false || m_gun.currentForm == CHARGE_PREPARE || ret == 1 || m_gun.is_auth_form) {
         return;
     }

+ 1 - 1
src/widgets/workspace/home/FormPlugIn.cpp

@@ -77,7 +77,7 @@ void FormPlugIn::globalsValueChanged(const QString &key, const QVariant &value)
 
 void FormPlugIn::displayChargeGunDetail(const CHARGING_INFO& info)
 {
-    qDebug() << info.GUNID;
+    // qDebug() << info.GUNID;
 }
 
 void FormPlugIn::displayGunBasicInfo(const BASIC_INFO& info)