188 lines
6.1 KiB
C++
188 lines
6.1 KiB
C++
#include "WeldSeamTCPProtocol.h"
|
|
|
|
#include "IYTCPServer.h"
|
|
#include "VrLog.h"
|
|
|
|
#include <QByteArray>
|
|
#include <QMetaObject>
|
|
#include <QThread>
|
|
#include <atomic>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
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<std::mutex> 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<unsigned int>(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<std::mutex> 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<std::mutex> 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<QString> commands;
|
|
{
|
|
std::lock_guard<std::mutex> lock(clientMutex);
|
|
QByteArray& buffer = buffers[client];
|
|
buffer.append(data, static_cast<int>(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<bool> running{false};
|
|
std::atomic<bool> connected{false};
|
|
std::set<const TCPClient*> clients;
|
|
std::map<const TCPClient*, QByteArray> buffers;
|
|
std::mutex clientMutex;
|
|
};
|
|
|
|
WeldSeamTCPProtocol::WeldSeamTCPProtocol(QObject* parent)
|
|
: QObject(parent), m_impl(std::make_unique<Impl>(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);
|
|
}
|