/** * @file MockServer.cpp * @brief Mock XPL2 server — listens on all three protocol ports. */ #include "MockServer.h" #include bool MockServer::s_wireDebug = false; void MockServer::enableWireDebug () { s_wireDebug = true; } 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 ("%s Failed to listen: %s", qPrintable (logTag (number)), qPrintable (port.server.errorString ())); else qInfo ("%s Listening", qPrintable (logTag (number))); } QString MockServer::logTag (quint16 localPort) const { const char *name = "Unknown"; if (localPort == m_command.number) name = m_command.name; else if (localPort == m_imaging.number) name = m_imaging.name; else if (localPort == m_status.number) name = m_status.name; /* Fixed-width tag: "[Command:9110]" = 14 chars, left-padded to 15. */ return QStringLiteral ("[%1:%2]").arg (name).arg (localPort).leftJustified ( 15); } void MockServer::onNewConnection () { auto *server = qobject_cast (sender ()); while (auto *sock = server->nextPendingConnection ()) { quint16 lp = sock->localPort (); qInfo ("%s Client connected", qPrintable (logTag (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 Client disconnected", qPrintable (logTag (lp))); m_clients.removeOne (sock); sock->deleteLater (); } void MockServer::sendKaPing () { for (auto *client : m_clients) { if (client->state () == QAbstractSocket::ConnectedState) sendReply (client, "KA_PING\n"); } } void MockServer::handleCommand (QTcpSocket *client, const QByteArray &line) { QByteArray trimmed = line.trimmed (); if (trimmed.isEmpty ()) return; /* Split on first comma to get command token and remaining params. */ int comma = trimmed.indexOf (','); QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed; QByteArray params = (comma >= 0) ? trimmed.mid (comma + 1) : QByteArray (); if (cmd == "KA_PING") handleKaPing (client, params); else if (cmd == "GS_JC_VERSION") handleGsJcVersion (client); else if (cmd == "GS_PH_VERSION") handleGsPhVersion (client, params); else qWarning ("%s Unknown command: %s", qPrintable (logTag (client->localPort ())), cmd.constData ()); } void MockServer::sendReply (QTcpSocket *client, const QByteArray &data) { client->write (data); QByteArray trimmed = data.trimmed (); int comma = trimmed.indexOf (','); QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed; QByteArray wire; if (s_wireDebug) wire = " >> " + trimmed; qDebug ("%s TX %s%s", qPrintable (logTag (client->localPort ())), cmd.constData (), wire.constData ()); } void MockServer::handleKaPing (QTcpSocket *client, const QByteArray ¶ms) { QByteArray wire; if (s_wireDebug) wire = " << KA_PING," + params; qDebug ("%s RX KA_PING ACK%s", qPrintable (logTag (client->localPort ())), wire.constData ()); } void MockServer::handleGsJcVersion (QTcpSocket *client) { qDebug ("%s RX GS_JC_VERSION", qPrintable (logTag (client->localPort ()))); sendReply (client, "GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n"); } void MockServer::handleGsPhVersion (QTcpSocket *client, const QByteArray ¶ms) { qDebug ("%s RX GS_PH_VERSION,%s", qPrintable (logTag (client->localPort ())), params.constData ()); /* Echo back canned version info for whatever printhead ID was requested. */ sendReply (client, QByteArray ("GS_PH_VERSION,1,") + params + ",\"3.10\",\"1.00\",\"Standard\"," "\"2.05\",\"1.02\",\"0.9.1\"\n"); }