#include "TearingTcpProtocol.h" #include "VrLog.h" #include "Version.h" #include #include #include #include TearingTcpProtocol::TearingTcpProtocol(QObject *parent) : QObject(parent) , m_tcpServer(nullptr) , m_heartbeatTimer(new QTimer(this)) , m_clientCheckTimer(new QTimer(this)) , m_heartbeatInterval(30) , m_clientTimeout(90) , m_tcpPort(0) , m_historyMaxLength(0) , m_historyMaxId(0) , m_historyMaxWidth(0) , m_hasLastDetectResult(false) { // 连接心跳定时器 connect(m_heartbeatTimer, &QTimer::timeout, this, &TearingTcpProtocol::onHeartbeatTimeout); // 连接客户端超时检查定时器 connect(m_clientCheckTimer, &QTimer::timeout, this, &TearingTcpProtocol::onClientTimeoutCheck); } TearingTcpProtocol::~TearingTcpProtocol() { stop(); if (m_tcpServer) { delete m_tcpServer; m_tcpServer = nullptr; } } void TearingTcpProtocol::start(int heartbeatInterval) { m_heartbeatInterval = heartbeatInterval; m_clientTimeout = heartbeatInterval * 3; // 超时时间为3倍心跳间隔 // 创建TCP服务器 if (!m_tcpServer && m_tcpPort > 0) { if (!VrCreatYTCPServer(&m_tcpServer)) { LOG_ERROR("Failed to create TCP server\n"); m_tcpServer = nullptr; return; } // 初始化TCP服务器 if (!m_tcpServer->Init(m_tcpPort, true)) { LOG_ERROR("Failed to initialize TCP server on port %d\n", m_tcpPort); delete m_tcpServer; m_tcpServer = nullptr; return; } // 启动TCP服务器,设置数据接收回调函数 if (!m_tcpServer->Start([this](const TCPClient* pClient, const char* pData, const unsigned int nLen) { this->handleReceivedData(pClient, pData, nLen); }, false)) { LOG_ERROR("Failed to start TCP server on port %d\n", m_tcpPort); m_tcpServer->Close(); delete m_tcpServer; m_tcpServer = nullptr; return; } LOG_INFO("TCP server started successfully on port %d\n", m_tcpPort); } // 启动心跳定时器(暂不启动,等有客户端连接后再考虑) // m_heartbeatTimer->start(m_heartbeatInterval * 1000); // 启动客户端超时检查定时器 m_clientCheckTimer->start(10000); // 每10秒检查一次 LOG_INFO("TearingTcpProtocol started, heartbeat interval: %d seconds\n", m_heartbeatInterval); } void TearingTcpProtocol::stop() { m_heartbeatTimer->stop(); m_clientCheckTimer->stop(); m_clientBuffers.clear(); m_clientLastActive.clear(); m_clientRealtimeEnabled.clear(); m_clientPointers.clear(); // 关闭并释放TCP服务器 if (m_tcpServer) { m_tcpServer->Stop(); m_tcpServer->Close(); delete m_tcpServer; m_tcpServer = nullptr; } LOG_INFO("TearingTcpProtocol stopped\n"); } void TearingTcpProtocol::sendDetectResult(const std::vector& results, const QImage& visImage, bool alarm) { if (!m_tcpServer) { return; } std::lock_guard sendLock(m_detectSendMutex); // 计算撕裂个数和本次检测的最大撕裂长度 int currentMaxLength = 0; int currentMaxId = 0; int currentMaxWidth = 0; std::vector currentTearObjects; currentTearObjects.reserve(results.size()); for (const auto& result : results) { int tearLength = static_cast(std::round(std::abs(result.roi.right - result.roi.left))); int tearWidth = static_cast(std::round(std::abs(result.tearWidth))); if (tearLength > currentMaxLength) { currentMaxLength = tearLength; currentMaxId = result.tearID; currentMaxWidth = tearWidth; } QJsonObject tearObj; tearObj["id"] = result.tearID; tearObj["status"] = static_cast(result.tearStatus); tearObj["length"] = tearLength; tearObj["width"] = tearWidth; tearObj["depth"] = result.tearDepth; currentTearObjects.push_back(tearObj); } int historyMaxLength = 0; int historyMaxId = 0; int historyMaxWidth = 0; QJsonArray tearsArray; { std::lock_guard lock(m_historyMutex); // 更新历史最大值(从开流开始的最大值) if (currentMaxLength > m_historyMaxLength) { m_historyMaxLength = currentMaxLength; m_historyMaxId = currentMaxId; m_historyMaxWidth = currentMaxWidth; } historyMaxLength = m_historyMaxLength; historyMaxId = m_historyMaxId; historyMaxWidth = m_historyMaxWidth; for (const auto& tearObj : currentTearObjects) { m_historyTears[tearObj.value("id").toInt()] = tearObj; } std::vector historyTearObjects; historyTearObjects.reserve(m_historyTears.size()); for (auto it = m_historyTears.cbegin(); it != m_historyTears.cend(); ++it) { historyTearObjects.push_back(it.value()); } std::sort(historyTearObjects.begin(), historyTearObjects.end(), [](const QJsonObject& lhs, const QJsonObject& rhs) { const int lhsLength = lhs.value("length").toInt(); const int rhsLength = rhs.value("length").toInt(); if (lhsLength != rhsLength) { return lhsLength > rhsLength; } return lhs.value("id").toInt() < rhs.value("id").toInt(); }); if (historyTearObjects.size() > 5) { historyTearObjects.resize(5); } m_historyTears.clear(); for (const auto& tearObj : historyTearObjects) { m_historyTears.insert(tearObj.value("id").toInt(), tearObj); tearsArray.append(tearObj); } } const int count = tearsArray.size(); // Timestamp and image are deliberately excluded: only tearing information // and the accumulated maximum values trigger a DETECT_RESULT report. QJsonObject signatureObj; signatureObj["count"] = count; signatureObj["maxLength"] = historyMaxLength; signatureObj["maxWidth"] = historyMaxWidth; signatureObj["maxId"] = historyMaxId; signatureObj["tears"] = tearsArray; const QByteArray signature = QJsonDocument(signatureObj).toJson(QJsonDocument::Compact); { std::lock_guard lock(m_historyMutex); const bool hasCurrentInfo = count > 0 || historyMaxLength > 0 || historyMaxWidth > 0 || historyMaxId > 0; if (!hasCurrentInfo && !m_hasLastDetectResult) { return; } if (m_hasLastDetectResult && signature == m_lastDetectSignature) { return; } } // 构造JSON消息(使用历史最大值) QJsonObject jsonObj; addProtocolVersion(jsonObj); jsonObj["msgType"] = "DETECT_RESULT"; jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch(); jsonObj["alarm"] = alarm; jsonObj["count"] = count; jsonObj["max"] = historyMaxLength; // 保留1.4版本字段 jsonObj["maxLength"] = historyMaxLength; jsonObj["maxWidth"] = historyMaxWidth; jsonObj["maxId"] = historyMaxId; // 使用历史最大撕裂ID jsonObj["tears"] = tearsArray; // 将图像转换为Base64 if (!visImage.isNull()) { jsonObj["visimg"] = imageToBase64(visImage); } else { jsonObj["visimg"] = ""; } // 转换为JSON字符串 QJsonDocument doc(jsonObj); QByteArray jsonData = doc.toJson(QJsonDocument::Compact); // 构造数据帧 QByteArray frame = buildFrame(jsonData); // DETECT_RESULT is the baseline result stream and is broadcast to every // connected client when its tearing summary changes, independent of SET_REALTIME. bool success = m_tcpServer->SendAllData(frame.constData(), frame.size()); if (success) { std::lock_guard lock(m_historyMutex); m_lastDetectSignature = signature; m_hasLastDetectResult = true; LOG_DEBUG("Sent DETECT_RESULT to all clients, count=%d, historyMax=%d, historyMaxId=%d\n", count, historyMaxLength, historyMaxId); } else { LOG_WARNING("Failed to send DETECT_RESULT to clients\n"); } } void TearingTcpProtocol::sendRealtimeResult(const std::vector& results, const QImage& visImage) { if (!m_tcpServer) { return; } // 检查是否有客户端开启了实时传输 bool hasRealtimeClient = false; for (auto it = m_clientRealtimeEnabled.begin(); it != m_clientRealtimeEnabled.end(); ++it) { if (it.value()) { hasRealtimeClient = true; break; } } if (!hasRealtimeClient) { return; // 没有客户端开启实时传输,直接返回 } // 计算撕裂个数 int count = static_cast(results.size()); // 构造tears数组 QJsonArray tearsArray; for (const auto& result : results) { QJsonObject tearObj; tearObj["id"] = result.tearID; tearObj["status"] = static_cast(result.tearStatus); // X方向长度统一使用ROI的left/right跨度 int tearLength = static_cast(std::round(std::abs(result.roi.right - result.roi.left))); tearObj["length"] = tearLength; // 撕裂宽度 tearObj["width"] = static_cast(std::round(std::abs(result.tearWidth))); // 撕裂深度 tearObj["depth"] = static_cast(result.tearDepth); tearsArray.append(tearObj); } // 构造JSON消息 QJsonObject jsonObj; addProtocolVersion(jsonObj); jsonObj["msgType"] = "REALTIME_RESULT"; jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch(); jsonObj["count"] = count; jsonObj["tears"] = tearsArray; // 将图像转换为Base64 if (!visImage.isNull()) { jsonObj["visimg"] = imageToBase64(visImage); } else { jsonObj["visimg"] = ""; } // 转换为JSON字符串 QJsonDocument doc(jsonObj); QByteArray jsonData = doc.toJson(QJsonDocument::Compact); // 构造数据帧 QByteArray frame = buildFrame(jsonData); // 只发送给开启了实时传输的客户端 int sentCount = 0; for (auto it = m_clientRealtimeEnabled.begin(); it != m_clientRealtimeEnabled.end(); ++it) { if (it.value()) { QString clientId = it.key(); // 通过clientId找到对应的TCPClient指针 if (m_clientPointers.contains(clientId)) { const TCPClient* pClient = m_clientPointers[clientId]; bool success = m_tcpServer->SendData(pClient, frame.constData(), frame.size()); if (success) { sentCount++; } else { LOG_WARNING("Failed to send REALTIME_RESULT to client %s\n", clientId.toStdString().c_str()); } } } } if (sentCount > 0) { LOG_DEBUG("Sent REALTIME_RESULT to %d clients, tears count=%d\n", sentCount, count); } } bool TearingTcpProtocol::sendPointCloudFrame(const QByteArray& payload) { if (!m_tcpServer || payload.isEmpty()) { return false; } QByteArray packet; QDataStream packetStream(&packet, QIODevice::WriteOnly); packetStream.setByteOrder(QDataStream::BigEndian); packetStream << quint8(0x05) << quint32(payload.size()); packetStream.writeRawData(payload.constData(), payload.size()); packet.append("___END___\r\n"); return m_tcpServer->SendAllData(packet.constData(), packet.size()); } void TearingTcpProtocol::handleReceivedData(const TCPClient* pClient, const char* pData, unsigned int nLen) { if (!pClient || !pData || nLen == 0) { return; } QString clientId = generateClientId(pClient); // 更新客户端最后活跃时间 m_clientLastActive[clientId] = QDateTime::currentMSecsSinceEpoch(); // 保存客户端指针(用于后续发送数据) if (!m_clientPointers.contains(clientId)) { m_clientPointers[clientId] = pClient; } // 将数据添加到客户端缓冲区 QByteArray& buffer = m_clientBuffers[clientId]; buffer.append(pData, nLen); // 解析数据帧 QList jsonDataList; int parsedCount = parseFrames(buffer, jsonDataList); if (parsedCount > 0) { LOG_DEBUG("Parsed %d frames from client %s\n", parsedCount, clientId.toStdString().c_str()); // 处理每个JSON消息 for (const QByteArray& jsonData : jsonDataList) { handleJsonMessage(pClient, jsonData); } } } void TearingTcpProtocol::setSpeedCallback(const SpeedCallback& callback) { m_speedCallback = callback; } void TearingTcpProtocol::setControlCallback(const ControlCallback& callback) { m_controlCallback = callback; } void TearingTcpProtocol::setResetAlarmCallback(const SimpleCommandCallback& callback) { m_resetAlarmCallback = callback; } void TearingTcpProtocol::setRestDetectCallback(const SimpleCommandCallback& callback) { m_restDetectCallback = callback; } void TearingTcpProtocol::setGetConfigCallback(const GetConfigCallback& callback) { m_getConfigCallback = callback; } void TearingTcpProtocol::setSetConfigCallback(const SetConfigCallback& callback) { m_setConfigCallback = callback; } void TearingTcpProtocol::setTcpPort(quint16 port) { m_tcpPort = port; } void TearingTcpProtocol::clearHistoryMaxData() { { std::lock_guard lock(m_historyMutex); m_historyMaxLength = 0; m_historyMaxId = 0; m_historyMaxWidth = 0; m_historyTears.clear(); m_lastDetectSignature.clear(); m_hasLastDetectResult = false; } LOG_INFO("Cleared history max data (开流清除)\n"); } int TearingTcpProtocol::getHistoryMaxLength() const { std::lock_guard lock(m_historyMutex); return m_historyMaxLength; } int TearingTcpProtocol::getHistoryMaxId() const { std::lock_guard lock(m_historyMutex); return m_historyMaxId; } void TearingTcpProtocol::onHeartbeatTimeout() { // 可以在这里主动发送心跳给客户端(如果需要) // 目前协议设计是客户端发送心跳,服务器应答 } void TearingTcpProtocol::onClientTimeoutCheck() { qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); qint64 timeoutMs = m_clientTimeout * 1000; QList timeoutClients; for (auto it = m_clientLastActive.begin(); it != m_clientLastActive.end(); ++it) { if (currentTime - it.value() > timeoutMs) { timeoutClients.append(it.key()); } } // 清理超时客户端的数据 for (const QString& clientId : timeoutClients) { LOG_WARNING("Client %s timeout, cleaning up\n", clientId.toStdString().c_str()); m_clientBuffers.remove(clientId); m_clientLastActive.remove(clientId); m_clientRealtimeEnabled.remove(clientId); m_clientPointers.remove(clientId); } } QByteArray TearingTcpProtocol::buildFrame(const QByteArray& jsonData) { QByteArray frame; quint32 dataLength = static_cast(jsonData.size()); // 写入帧头 frame.append(FRAME_HEADER, FRAME_HEADER_SIZE); // 写入数据长度(8位字符串格式) char lengthStr[9]; // 8位数字 + '\0' #ifdef _WIN32 sprintf_s(lengthStr, "%08u", dataLength); #else sprintf(lengthStr, "%08u", dataLength); #endif frame.append(lengthStr, FRAME_LENGTH_SIZE); // 写入JSON数据 frame.append(jsonData); // 写入帧尾 frame.append(FRAME_TAIL, FRAME_TAIL_SIZE); return frame; } int TearingTcpProtocol::parseFrames(const QByteArray& data, QList& outJsonData) { int parsedCount = 0; QByteArray& buffer = const_cast(data); while (buffer.size() >= FRAME_HEADER_SIZE + FRAME_LENGTH_SIZE) { // 查找帧头 int headerPos = buffer.indexOf(FRAME_HEADER); if (headerPos == -1) { // 没有找到帧头,清空缓冲区 buffer.clear(); break; } // 如果帧头不在开始位置,丢弃之前的数据 if (headerPos > 0) { buffer.remove(0, headerPos); } // 检查是否有足够的数据读取长度字段 if (buffer.size() < FRAME_HEADER_SIZE + FRAME_LENGTH_SIZE) { break; } // 读取数据长度(8位字符串格式) QByteArray lengthBytes = buffer.mid(FRAME_HEADER_SIZE, FRAME_LENGTH_SIZE); QString lengthStr = QString::fromLatin1(lengthBytes); bool ok; quint32 dataLength = lengthStr.toUInt(&ok); if (!ok) { LOG_ERROR("Failed to parse frame data length: %s, discarding buffer\n", lengthStr.toStdString().c_str()); buffer.remove(0, FRAME_HEADER_SIZE); continue; } // 检查数据长度是否合理(防止错误数据) if (dataLength > 10 * 1024 * 1024) { // 限制最大10MB LOG_ERROR("Invalid frame data length: %u, discarding buffer\n", dataLength); buffer.remove(0, FRAME_HEADER_SIZE); continue; } // 计算完整帧的大小 int frameSize = FRAME_HEADER_SIZE + FRAME_LENGTH_SIZE + dataLength + FRAME_TAIL_SIZE; // 检查是否接收到完整的帧 if (buffer.size() < frameSize) { // 数据不完整,等待更多数据 break; } // 验证帧尾 int tailPos = FRAME_HEADER_SIZE + FRAME_LENGTH_SIZE + dataLength; QByteArray tail = buffer.mid(tailPos, FRAME_TAIL_SIZE); if (tail != QByteArray(FRAME_TAIL, FRAME_TAIL_SIZE)) { LOG_ERROR("Invalid frame tail, discarding frame\n"); buffer.remove(0, FRAME_HEADER_SIZE); continue; } // 提取JSON数据 QByteArray jsonData = buffer.mid(FRAME_HEADER_SIZE + FRAME_LENGTH_SIZE, dataLength); outJsonData.append(jsonData); parsedCount++; // 移除已处理的帧 buffer.remove(0, frameSize); } return parsedCount; } TcpMessageType TearingTcpProtocol::parseMessageType(const QString& msgTypeStr) { if (msgTypeStr == "DETECT_RESULT") return TcpMessageType::DETECT_RESULT; if (msgTypeStr == "REALTIME_RESULT") return TcpMessageType::REALTIME_RESULT; if (msgTypeStr == "SET_SPEED") return TcpMessageType::SET_SPEED; if (msgTypeStr == "SET_CONTROL") return TcpMessageType::SET_CONTROL; if (msgTypeStr == "SET_REALTIME") return TcpMessageType::SET_REALTIME; if (msgTypeStr == "RESET_ALARM") return TcpMessageType::RESET_ALARM; if (msgTypeStr == "REST_DETECT") return TcpMessageType::REST_DETECT; if (msgTypeStr == "GET_CONFIG") return TcpMessageType::GET_CONFIG; if (msgTypeStr == "SET_CONFIG") return TcpMessageType::SET_CONFIG; if (msgTypeStr == "GET_PROTOCOL_INFO") return TcpMessageType::GET_PROTOCOL_INFO; if (msgTypeStr == "CMD_RESPONSE") return TcpMessageType::CMD_RESPONSE; if (msgTypeStr == "HEARTBEAT") return TcpMessageType::HEARTBEAT; if (msgTypeStr == "HEARTBEAT_ACK") return TcpMessageType::HEARTBEAT_ACK; return TcpMessageType::UNKNOWN; } void TearingTcpProtocol::handleJsonMessage(const TCPClient* pClient, const QByteArray& jsonData) { // 解析JSON QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError); if (parseError.error != QJsonParseError::NoError) { LOG_ERROR("Failed to parse JSON: %s\n", parseError.errorString().toStdString().c_str()); return; } if (!doc.isObject()) { LOG_ERROR("JSON is not an object\n"); return; } QJsonObject jsonObj = doc.object(); const QString protocolVersion = jsonObj["protocolVersion"].toString(); if (!protocolVersion.isEmpty() && !isProtocolVersionSupported(protocolVersion)) { sendCommandResponse(pClient, jsonObj["msgType"].toString(), false, 1001, QString("Unsupported protocol version: %1").arg(protocolVersion)); return; } // 获取消息类型 QString msgTypeStr = jsonObj["msgType"].toString(); TcpMessageType msgType = parseMessageType(msgTypeStr); // 根据消息类型处理 switch (msgType) { case TcpMessageType::SET_SPEED: handleSetSpeed(pClient, jsonObj); break; case TcpMessageType::SET_CONTROL: handleSetControl(pClient, jsonObj); break; case TcpMessageType::SET_REALTIME: handleSetRealtime(pClient, jsonObj); break; case TcpMessageType::RESET_ALARM: handleSimpleCommand(pClient, "RESET_ALARM", m_resetAlarmCallback); break; case TcpMessageType::REST_DETECT: handleSimpleCommand(pClient, "REST_DETECT", m_restDetectCallback); break; case TcpMessageType::GET_CONFIG: handleGetConfig(pClient, jsonObj); break; case TcpMessageType::SET_CONFIG: handleSetConfig(pClient, jsonObj); break; case TcpMessageType::GET_PROTOCOL_INFO: handleGetProtocolInfo(pClient); break; case TcpMessageType::HEARTBEAT: handleHeartbeat(pClient, jsonObj); break; default: LOG_WARNING("Unknown message type: %s\n", msgTypeStr.toStdString().c_str()); break; } } void TearingTcpProtocol::handleSetSpeed(const TCPClient* pClient, const QJsonObject& jsonObj) { int speed = jsonObj["speed"].toInt(); LOG_INFO("Received SET_SPEED command: speed=%d mm/s\n", speed); // 参数验证 bool result = true; int errorCode = 0; QString errorMsg; if (speed < 0 || speed > 5000) { result = false; errorCode = 1; errorMsg = "Speed out of range (0-5000 mm/s)"; LOG_ERROR("Invalid speed value: %d\n", speed); } else { // 调用速度设置回调 if (m_speedCallback) { errorCode = m_speedCallback(speed); result = (errorCode == 0); if (!result) { errorMsg = "Speed callback execution failed"; LOG_ERROR("Speed callback execution failed for speed: %d, error code: %d\n", speed, errorCode); } } } // 发送应答 sendCommandResponse(pClient, "SET_SPEED", result, errorCode, errorMsg); } void TearingTcpProtocol::handleSetControl(const TCPClient* pClient, const QJsonObject& jsonObj) { bool control = jsonObj["control"].toBool(); LOG_INFO("Received SET_CONTROL command: control=%s\n", control ? "start" : "stop"); // 调用控制回调 bool result = true; int errorCode = 0; QString errorMsg; if (m_controlCallback) { errorCode = m_controlCallback(control); result = (errorCode == 0); if (!result) { errorMsg = "Control callback execution failed"; LOG_ERROR("Control callback execution failed for control: %s, error code: %d\n", control ? "start" : "stop", errorCode); } } // 发送应答 sendCommandResponse(pClient, "SET_CONTROL", result, errorCode, errorMsg); } void TearingTcpProtocol::handleHeartbeat(const TCPClient* pClient, const QJsonObject& jsonObj) { LOG_DEBUG("Received HEARTBEAT from client\n"); // 发送心跳应答 sendHeartbeatAck(pClient); } void TearingTcpProtocol::handleSetRealtime(const TCPClient* pClient, const QJsonObject& jsonObj) { bool enable = jsonObj["enable"].toBool(); QString clientId = generateClientId(pClient); LOG_INFO("Received SET_REALTIME command from %s: enable=%s\n", clientId.toStdString().c_str(), enable ? "true" : "false"); // 更新客户端的实时传输开关状态 m_clientRealtimeEnabled[clientId] = enable; // 发送应答 sendCommandResponse(pClient, "SET_REALTIME", true, 0, ""); } void TearingTcpProtocol::handleSimpleCommand(const TCPClient* pClient, const QString& command, const SimpleCommandCallback& callback) { if (!callback) { sendCommandResponse(pClient, command, false, 2, "Command callback is not available"); return; } int errorCode = callback(); sendCommandResponse(pClient, command, errorCode == 0, errorCode, errorCode == 0 ? "" : "Command execution failed"); } void TearingTcpProtocol::handleGetConfig(const TCPClient* pClient, const QJsonObject& jsonObj) { if (!m_getConfigCallback) { sendCommandResponse(pClient, "GET_CONFIG", false, 2, "Config callback is not available"); return; } sendConfigResponse(pClient, jsonObj["requestId"].toString(), m_getConfigCallback()); } void TearingTcpProtocol::handleSetConfig(const TCPClient* pClient, const QJsonObject& jsonObj) { if (!m_setConfigCallback) { sendCommandResponse(pClient, "SET_CONFIG", false, 2, "Config callback is not available"); return; } if (!jsonObj.contains("config") || !jsonObj["config"].isObject()) { sendCommandResponse(pClient, "SET_CONFIG", false, 1, "Missing config object"); return; } QString errorMsg; bool reconnectRequired = false; int errorCode = m_setConfigCallback(jsonObj["config"].toObject(), errorMsg, reconnectRequired); if (errorCode != 0) { sendCommandResponse(pClient, "SET_CONFIG", false, errorCode, errorMsg); return; } QJsonObject currentConfig = m_getConfigCallback ? m_getConfigCallback() : jsonObj["config"].toObject(); sendConfigResponse(pClient, jsonObj["requestId"].toString(), currentConfig, reconnectRequired); } void TearingTcpProtocol::handleGetProtocolInfo(const TCPClient* pClient) { if (!m_tcpServer || !pClient) { return; } QJsonArray capabilities; capabilities.append("DETECT_RESULT_V2"); capabilities.append("RESET_ALARM"); capabilities.append("REST_DETECT"); capabilities.append("GET_CONFIG"); capabilities.append("SET_CONFIG"); capabilities.append("REALTIME_RESULT"); capabilities.append("DEPTH_PSEUDOCOLOR"); capabilities.append("POINT_CLOUD_STREAMING"); QJsonObject jsonObj; addProtocolVersion(jsonObj); jsonObj["msgType"] = "PROTOCOL_INFO"; jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch(); jsonObj["protocolName"] = TEARING_TCP_PROTOCOL_NAME; jsonObj["minCompatibleVersion"] = TEARING_TCP_MIN_COMPATIBLE_VERSION; jsonObj["softwareVersion"] = QString("%1.%2").arg(BELTTEARING_VERSION_STRING).arg(BELTTEARING_VERSION_BUILD); jsonObj["capabilities"] = capabilities; QByteArray frame = buildFrame(QJsonDocument(jsonObj).toJson(QJsonDocument::Compact)); m_tcpServer->SendData(pClient, frame.constData(), frame.size()); } void TearingTcpProtocol::sendCommandResponse(const TCPClient* pClient, const QString& cmdType, bool result, int errorCode, const QString& errorMsg) { if (!m_tcpServer || !pClient) { return; } // 构造应答JSON QJsonObject jsonObj; addProtocolVersion(jsonObj); jsonObj["msgType"] = "CMD_RESPONSE"; jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch(); jsonObj["cmdType"] = cmdType; jsonObj["result"] = result; jsonObj["errorCode"] = errorCode; jsonObj["errorMsg"] = errorMsg; // 转换为JSON字符串 QJsonDocument doc(jsonObj); QByteArray jsonData = doc.toJson(QJsonDocument::Compact); // 构造数据帧 QByteArray frame = buildFrame(jsonData); // 发送应答 bool success = m_tcpServer->SendData(pClient, frame.constData(), frame.size()); if (success) { LOG_DEBUG("Sent CMD_RESPONSE for %s, result=%s\n", cmdType.toStdString().c_str(), result ? "success" : "failure"); } else { LOG_ERROR("Failed to send CMD_RESPONSE for %s\n", cmdType.toStdString().c_str()); } } void TearingTcpProtocol::sendHeartbeatAck(const TCPClient* pClient) { if (!m_tcpServer || !pClient) { return; } // 构造心跳应答JSON QJsonObject jsonObj; addProtocolVersion(jsonObj); jsonObj["msgType"] = "HEARTBEAT_ACK"; jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch(); // 转换为JSON字符串 QJsonDocument doc(jsonObj); QByteArray jsonData = doc.toJson(QJsonDocument::Compact); // 构造数据帧 QByteArray frame = buildFrame(jsonData); // 发送应答 m_tcpServer->SendData(pClient, frame.constData(), frame.size()); } void TearingTcpProtocol::sendConfigResponse(const TCPClient* pClient, const QString& requestId, const QJsonObject& config, bool reconnectRequired) { if (!m_tcpServer || !pClient) { return; } QJsonObject jsonObj; addProtocolVersion(jsonObj); jsonObj["msgType"] = "CONFIG_RESPONSE"; jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch(); jsonObj["requestId"] = requestId; jsonObj["result"] = true; jsonObj["reconnectRequired"] = reconnectRequired; jsonObj["config"] = config; QByteArray frame = buildFrame(QJsonDocument(jsonObj).toJson(QJsonDocument::Compact)); m_tcpServer->SendData(pClient, frame.constData(), frame.size()); } bool TearingTcpProtocol::isProtocolVersionSupported(const QString& version) const { bool clientMajorOk = false; bool clientMinorOk = false; bool minimumMajorOk = false; bool minimumMinorOk = false; int clientMajor = version.section('.', 0, 0).toInt(&clientMajorOk); int clientMinor = version.section('.', 1, 1).toInt(&clientMinorOk); const QString minimumVersion = QString::fromLatin1(TEARING_TCP_MIN_COMPATIBLE_VERSION); int minimumMajor = minimumVersion.section('.', 0, 0).toInt(&minimumMajorOk); int minimumMinor = minimumVersion.section('.', 1, 1).toInt(&minimumMinorOk); return clientMajorOk && clientMinorOk && minimumMajorOk && minimumMinorOk && clientMajor == minimumMajor && clientMinor >= minimumMinor; } void TearingTcpProtocol::addProtocolVersion(QJsonObject& jsonObj) const { jsonObj["protocolVersion"] = TEARING_TCP_PROTOCOL_VERSION; } QString TearingTcpProtocol::imageToBase64(const QImage& image) { if (image.isNull()) { return ""; } QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); // 较高质量可以保留伪彩层次和报警框边缘 image.save(&buffer, "JPEG", 80); // 转换为Base64,并添加Data URI头部 QString base64Data = QString(byteArray.toBase64()); return QString("data:image/jpeg;base64,%1").arg(base64Data); } QString TearingTcpProtocol::generateClientId(const TCPClient* pClient) { return QString("Client_%1").arg(pClient->m_nFD); }