aboutsummaryrefslogtreecommitdiffstats
path: root/mock-server/MockServer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mock-server/MockServer.cpp')
-rw-r--r--mock-server/MockServer.cpp122
1 files changed, 122 insertions, 0 deletions
diff --git a/mock-server/MockServer.cpp b/mock-server/MockServer.cpp
new file mode 100644
index 0000000..52db5a1
--- /dev/null
+++ b/mock-server/MockServer.cpp
@@ -0,0 +1,122 @@
+/**
+ * @file MockServer.cpp
+ * @brief Mock XPL2 server — listens on all three protocol ports.
+ */
+#include "MockServer.h"
+
+#include <QTcpSocket>
+
+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<QTcpServer *> (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<QTcpSocket *> (sender ());
+ while (sock->canReadLine ())
+ {
+ QByteArray line = sock->readLine ();
+ handleCommand (sock, line);
+ }
+}
+
+void
+MockServer::onClientDisconnected ()
+{
+ auto *sock = qobject_cast<QTcpSocket *> (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 ());
+}