|
|
@@ -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);
|
|
|
- }
|
|
|
-}
|