#include "TCPServerProtocol.h" #include "VrLog.h" #include #include namespace { constexpr int kMaxClientBufferBytes = 64 * 1024; } TCPServerProtocol::TCPServerProtocol() : m_pTCPServer(nullptr) , m_bServerRunning(false) , m_nPort(7800) , m_connectionStatus(STATUS_DISCONNECTED) { } TCPServerProtocol::~TCPServerProtocol() { Deinitialize(); } int TCPServerProtocol::Initialize(uint16_t port) { LOG_DEBUG("Initializing TCP server protocol on port %d\n", port); m_nPort = port; // 创建TCP服务器实例 if (!VrCreatYTCPServer(&m_pTCPServer)) { LOG_ERROR("Failed to create TCP server instance\n"); return -1; } // 初始化TCP服务器 if (!m_pTCPServer->Init(port)) { LOG_ERROR("Failed to initialize TCP server on port %d\n", port); delete m_pTCPServer; m_pTCPServer = nullptr; return -2; } // 设置事件回调 m_pTCPServer->SetEventCallback([this](const TCPClient* pClient, TCPServerEventType eventType) { this->OnTCPEvent(pClient, eventType); }); // Arm state before Start() creates its worker thread. A client can connect // immediately, so Initialize must not overwrite that event afterwards. m_connectionStatus.store(STATUS_DISCONNECTED); m_bServerRunning.store(true); // 启动TCP服务器 if (!m_pTCPServer->Start([this](const TCPClient* pClient, const char* pData, const unsigned int nLen) { this->OnTCPDataReceived(pClient, pData, nLen); })) { LOG_ERROR("Failed to start TCP server\n"); m_bServerRunning.store(false); IYTCPServer* failedServer = nullptr; { std::lock_guard sendLock(m_sendMutex); failedServer = m_pTCPServer; m_pTCPServer = nullptr; } if (failedServer) { failedServer->Close(); delete failedServer; } return -3; } LOG_DEBUG("TCP server protocol initialized successfully on port %d\n", port); return 0; } void TCPServerProtocol::Deinitialize() { m_bServerRunning.store(false); // Detach the pointer while excluding result sends. New sends recheck the // flag under this mutex and return without touching the server pointer. // Do not hold the mutex while Stop() joins callbacks. IYTCPServer* server = nullptr; { std::lock_guard sendLock(m_sendMutex); server = m_pTCPServer; m_pTCPServer = nullptr; } if (server) { LOG_DEBUG("Stopping TCP server protocol\n"); server->Stop(); server->Close(); delete server; LOG_DEBUG("TCP server protocol stopped\n"); } m_connectionStatus.store(ConnectionStatus::STATUS_DISCONNECTED); { std::lock_guard buffersLock(m_clientBuffersMutex); m_connectedClients.clear(); m_clientBuffers.clear(); } } int TCPServerProtocol::SendTextFrame(const QByteArray& frame, const TCPClient* pClient) { if (!m_bServerRunning.load()) { LOG_ERROR("TCP server is not running\n"); return -1; } QByteArray payload = frame; while (payload.endsWith('\n') || payload.endsWith('\r')) { payload.chop(1); } payload.append("\r\n"); std::lock_guard sendLock(m_sendMutex); if (!m_pTCPServer || !m_bServerRunning.load()) { LOG_ERROR("TCP server is not running\n"); return -1; } const bool success = pClient ? m_pTCPServer->SendData(pClient, payload.constData(), payload.size()) : m_pTCPServer->SendAllData(payload.constData(), payload.size()); if (!success) { LOG_ERROR("Failed to send TCP text frame\n"); return -2; } LOG_DEBUG("Sent TCP text frame, size: %d bytes\n", payload.size()); return 0; } TCPServerProtocol::TCPStatus TCPServerProtocol::GetConnectionStatus() const { return m_connectionStatus.load(); } void TCPServerProtocol::SetConnectionCallback(const ConnectionCallback& callback) { m_connectionCallback = callback; } void TCPServerProtocol::SetDetectionTriggerCallback(const DetectionTriggerCallback& callback) { m_detectionTriggerCallback = callback; } void TCPServerProtocol::SetBinDetectionTriggerCallback(const BinDetectionTriggerCallback& callback) { m_binDetectionTriggerCallback = callback; } bool TCPServerProtocol::IsRunning() const { return m_bServerRunning.load(); } void TCPServerProtocol::OnTCPEvent(const TCPClient* pClient, TCPServerEventType eventType) { if (!m_bServerRunning.load()) { return; } // CYTCPServer may report events from multiple worker tasks. Serialize the // aggregate-state transition and callback so an older event cannot publish // its result after a newer one. std::lock_guard eventLock(m_connectionEventMutex); switch (eventType) { case TCP_EVENT_CLIENT_CONNECTED: LOG_DEBUG("TCP client connected: %p\n", pClient); { bool hasConnectedClient = false; { std::lock_guard buffersLock(m_clientBuffersMutex); if (pClient) { m_connectedClients.insert(pClient); // Receive may race ahead of the connected callback. Do // not overwrite a buffer created lazily by receive. m_clientBuffers.emplace(pClient, QByteArray()); } hasConnectedClient = !m_connectedClients.empty(); } const TCPStatus newStatus = hasConnectedClient ? STATUS_CONNECTED : STATUS_DISCONNECTED; if (m_connectionStatus.exchange(newStatus) != newStatus && m_connectionCallback) { m_connectionCallback(hasConnectedClient); } } break; case TCP_EVENT_CLIENT_DISCONNECTED: LOG_DEBUG("TCP client disconnected: %p\n", pClient); { bool hasConnectedClient = false; { std::lock_guard buffersLock(m_clientBuffersMutex); m_connectedClients.erase(pClient); m_clientBuffers.erase(pClient); hasConnectedClient = !m_connectedClients.empty(); } const TCPStatus newStatus = hasConnectedClient ? STATUS_CONNECTED : STATUS_DISCONNECTED; if (m_connectionStatus.exchange(newStatus) != newStatus && m_connectionCallback) { m_connectionCallback(hasConnectedClient); } } break; case TCP_EVENT_CLIENT_EXCEPTION: LOG_WARNING("TCP client exception: %p\n", pClient); { bool hasConnectedClient = false; { std::lock_guard buffersLock(m_clientBuffersMutex); m_connectedClients.erase(pClient); m_clientBuffers.erase(pClient); hasConnectedClient = !m_connectedClients.empty(); } const TCPStatus newStatus = hasConnectedClient ? STATUS_CONNECTED : STATUS_DISCONNECTED; if (m_connectionStatus.exchange(newStatus) != newStatus && m_connectionCallback) { m_connectionCallback(hasConnectedClient); } } break; } } void TCPServerProtocol::OnTCPDataReceived(const TCPClient* pClient, const char* pData, unsigned int nLen) { if (!m_bServerRunning.load()) { return; } if (!pClient || !pData || nLen == 0) { LOG_WARNING("Received empty data from client %p\n", pClient); return; } LOG_DEBUG("Received TCP data from client %p, size: %u bytes\n", pClient, nLen); bool bufferOverflow = false; std::vector frames; { std::lock_guard buffersLock(m_clientBuffersMutex); QByteArray& buffer = m_clientBuffers[pClient]; if (nLen > static_cast(kMaxClientBufferBytes) || buffer.size() > kMaxClientBufferBytes - static_cast(nLen)) { buffer.clear(); bufferOverflow = true; } else { buffer.append(pData, static_cast(nLen)); while (true) { int idx = buffer.indexOf("\r\n"); int terminatorLen = 2; if (idx < 0) { idx = buffer.indexOf('\n'); terminatorLen = 1; } if (idx < 0) { break; } QByteArray frame = buffer.left(idx).trimmed(); buffer.remove(0, idx + terminatorLen); if (!frame.isEmpty()) { frames.push_back(frame); } } } } if (bufferOverflow) { LOG_WARNING("TCP receive buffer exceeded 64 KiB for client %p\n", pClient); SendErrorResponse(pClient, -6, "命令长度超过64KiB", 0); return; } for (const QByteArray& frame : frames) { ParseTextCommand(pClient, frame); } } void TCPServerProtocol::ParseTextCommand(const TCPClient* pClient, const QByteArray& frame) { const QString command = QString::fromUtf8(frame).trimmed(); const QStringList tokens = command.split('_', QString::SkipEmptyParts); LOG_DEBUG("Received TCP text command: %s, tokens=%d\n", command.toStdString().c_str(), tokens.size()); if (tokens.size() != 1) { LOG_WARNING("Invalid TCP text command, expected one command token: %s\n", command.toStdString().c_str()); SendErrorResponse(pClient, -3, "命令格式错误", QDateTime::currentMSecsSinceEpoch()); return; } const QString typeToken = tokens[0].toUpper(); const bool isWorkpieceDetection = typeToken.startsWith('S'); const bool isBinDetection = typeToken.startsWith('T'); if (!isWorkpieceDetection && !isBinDetection) { LOG_WARNING("Invalid TCP command type: %s\n", typeToken.toStdString().c_str()); SendErrorResponse(pClient, -3, "命令类型错误", QDateTime::currentMSecsSinceEpoch()); return; } bool ok = false; int cameraIndex = typeToken.mid(1).toInt(&ok); if (!ok || cameraIndex < 1) { cameraIndex = 1; } const qint64 timestamp = QDateTime::currentMSecsSinceEpoch(); LOG_INFO("TCP trigger: type=%s, camera=%d\n", isWorkpieceDetection ? "S" : "T", cameraIndex); if (isWorkpieceDetection) { if (!m_detectionTriggerCallback) { SendErrorResponse(pClient, -4, "工件检测服务未准备就绪", timestamp); return; } if (!m_detectionTriggerCallback(true, cameraIndex, timestamp)) { SendErrorResponse(pClient, -5, "工件检测启动失败", timestamp); } return; } if (!m_binDetectionTriggerCallback) { SendErrorResponse(pClient, -4, "料框检测服务未准备就绪", timestamp); return; } if (!m_binDetectionTriggerCallback(cameraIndex, timestamp)) { SendErrorResponse(pClient, -5, "料框检测启动失败", timestamp); } } void TCPServerProtocol::SendErrorResponse(const TCPClient* pClient, int code, const QString& message, qint64 timestamp) { (void)timestamp; const QString sanitizedMessage = QString(message).replace('_', '-').replace('\n', ' '); SendTextFrame(QString("Error_%1_%2").arg(code).arg(sanitizedMessage).toUtf8(), pClient); }