#include "WeldSeamTCPProtocol.h" #include "IYTCPServer.h" #include "VrLog.h" #include #include #include #include #include #include #include #include namespace { bool ParseIndexedCommand(const QString& command, const QString& name, int& cameraIndex) { const QString prefix = name + QLatin1Char('_'); if (!command.startsWith(prefix)) { return false; } bool valid = false; cameraIndex = command.mid(prefix.size()).toInt(&valid); return valid && cameraIndex > 0; } } // namespace class WeldSeamTCPProtocol::Impl { public: explicit Impl(WeldSeamTCPProtocol* owner) : q(owner) {} bool Start(uint16_t port) { if (!VrCreatYTCPServer(&server) || !server) return false; if (!server->Init(port)) { delete server; server = nullptr; return false; } server->SetEventCallback([this](const TCPClient* client, TCPServerEventType type) { OnEvent(client, type); }); if (!server->Start([this](const TCPClient* client, const char* data, unsigned int size) { OnData(client, data, size); })) { server->Close(); delete server; server = nullptr; return false; } running = true; LOG_INFO("WeldSeam TCP server listening on %u\n", port); return true; } void Stop() { running = false; connected = false; if (server) { server->Stop(); server->Close(); delete server; server = nullptr; } std::lock_guard lock(clientMutex); clients.clear(); buffers.clear(); } void Send(const QString& message) { if (!running || !server) return; QByteArray packet = message.toUtf8(); if (!packet.endsWith("\r\n")) packet.append("\r\n"); server->SendAllData(packet.constData(), static_cast(packet.size())); } private: void OnEvent(const TCPClient* client, TCPServerEventType type) { bool notify = false; bool isConnected = connected.load(); if (type == TCP_EVENT_CLIENT_CONNECTED) { std::lock_guard lock(clientMutex); clients.insert(client); const bool wasConnected = connected.exchange(true); isConnected = true; notify = !wasConnected; } else if (type == TCP_EVENT_CLIENT_DISCONNECTED || type == TCP_EVENT_CLIENT_EXCEPTION) { std::lock_guard lock(clientMutex); buffers.erase(client); clients.erase(client); isConnected = !clients.empty(); const bool wasConnected = connected.exchange(isConnected); notify = wasConnected != isConnected; } if (notify) { QMetaObject::invokeMethod(q, [this, isConnected]() { emit q->ConnectionChanged(isConnected); }, Qt::QueuedConnection); } } void OnData(const TCPClient* client, const char* data, unsigned int size) { if (!data || size == 0) return; std::vector commands; { std::lock_guard lock(clientMutex); QByteArray& buffer = buffers[client]; buffer.append(data, static_cast(size)); while (true) { const int end = buffer.indexOf("\r\n"); if (end < 0) break; const QString command = QString::fromUtf8(buffer.left(end)).trimmed(); buffer.remove(0, end + 2); if (!command.isEmpty()) commands.push_back(command); } } for (const auto& command : commands) Process(command); } void Process(const QString& command) { const QString normalized = command.trimmed().toUpper(); QMetaObject::invokeMethod(q, [this, command]() { emit q->DataReceived(command); }, Qt::QueuedConnection); int cameraIndex = 0; if (ParseIndexedCommand(normalized, QStringLiteral("START_SCAN"), cameraIndex)) { QMetaObject::invokeMethod(q, [this, cameraIndex]() { emit q->StartScanRequested(cameraIndex); }, Qt::QueuedConnection); } else if (normalized == QStringLiteral("STOP_SCAN")) { QMetaObject::invokeMethod(q, [this]() { emit q->StopScanRequested(); }, Qt::QueuedConnection); } else if (ParseIndexedCommand(normalized, QStringLiteral("GET_RET"), cameraIndex)) { QMetaObject::invokeMethod(q, [this, cameraIndex]() { emit q->GetResultRequested(cameraIndex); }, Qt::QueuedConnection); } else if (normalized.startsWith(QStringLiteral("START_SCAN")) || normalized.startsWith(QStringLiteral("STOP_SCAN")) || normalized.startsWith(QStringLiteral("GET_RET"))) { q->SendMessage(QStringLiteral("ERROR_INVALID_FORMAT")); } else { q->SendMessage(QStringLiteral("ERROR_UNKNOWN_COMMAND")); } } public: WeldSeamTCPProtocol* q = nullptr; IYTCPServer* server = nullptr; std::atomic running{false}; std::atomic connected{false}; std::set clients; std::map buffers; std::mutex clientMutex; }; WeldSeamTCPProtocol::WeldSeamTCPProtocol(QObject* parent) : QObject(parent), m_impl(std::make_unique(this)) { } WeldSeamTCPProtocol::~WeldSeamTCPProtocol() { StopServer(); } bool WeldSeamTCPProtocol::StartServer(uint16_t port) { return m_impl->Start(port); } void WeldSeamTCPProtocol::StopServer() { m_impl->Stop(); } bool WeldSeamTCPProtocol::IsConnected() const { return m_impl->connected.load(); } void WeldSeamTCPProtocol::SendMessage(const QString& message) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, [this, message]() { m_impl->Send(message); }, Qt::QueuedConnection); return; } m_impl->Send(message); }