/** * @file MockServer.cpp * @brief Mock XPL2 server — listens on all three protocol ports. */ #include "MockServer.h" #include MockServer::MockServer (QObject *parent) : QObject (parent) { setupPort (m_command, "Command", 9110); setupPort (m_imaging, "Imaging", 9111); setupPort (m_status, "Status", 9112); connect (&m_pingTimer, &QTimer::timeout, this, &MockServer::sendKaPing); m_pingTimer.start (1000); } void MockServer::setupPort (Port &port, const char *name, quint16 number) { port.name = name; port.number = number; connect (&port.server, &QTcpServer::newConnection, this, &MockServer::onNewConnection); if (!port.server.listen (QHostAddress::Any, number)) qCritical ("Failed to listen on %s port %d: %s", name, number, qPrintable (port.server.errorString ())); else qInfo ("Listening on %s port %d", name, number); } const char * MockServer::portName (quint16 localPort) const { if (localPort == m_command.number) return m_command.name; if (localPort == m_imaging.number) return m_imaging.name; if (localPort == m_status.number) return m_status.name; return "Unknown"; } void MockServer::onNewConnection () { auto *server = qobject_cast (sender ()); while (auto *sock = server->nextPendingConnection ()) { quint16 lp = sock->localPort (); qInfo ("[%s:%d] client connected", portName (lp), lp); m_clients.append (sock); connect (sock, &QTcpSocket::readyRead, this, &MockServer::onClientMessageReady); connect (sock, &QTcpSocket::disconnected, this, &MockServer::onClientDisconnected); } } void MockServer::onClientMessageReady () { auto *sock = qobject_cast (sender ()); while (sock->canReadLine ()) { QByteArray line = sock->readLine (); handleCommand (sock, line); } } void MockServer::onClientDisconnected () { auto *sock = qobject_cast (sender ()); quint16 lp = sock->localPort (); qInfo ("[%s:%d] client disconnected", portName (lp), lp); m_clients.removeOne (sock); sock->deleteLater (); } void MockServer::sendKaPing () { for (auto *client : m_clients) { if (client->state () == QAbstractSocket::ConnectedState) client->write ("KA_PING\n"); } } void MockServer::handleCommand (QTcpSocket *client, const QByteArray &line) { QByteArray trimmed = line.trimmed (); if (trimmed.isEmpty ()) return; quint16 lp = client->localPort (); /* Split on first comma to get command token. */ int comma = trimmed.indexOf (','); QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed; if (cmd == "KA_PING") { qDebug ("[%s:%d] KA_PING ACK received", portName (lp), lp); return; } if (cmd == "GS_JC_VERSION") { qInfo ("[%s:%d] -> GS_JC_VERSION reply", portName (lp), lp); client->write ("GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n"); return; } qWarning ("[%s:%d] Unknown command: %s", portName (lp), lp, trimmed.constData ()); }