diff options
| author | Thomas Vanbesien <tvanbesi@proton.me> | 2026-03-23 16:39:33 +0100 |
|---|---|---|
| committer | Thomas Vanbesien <tvanbesi@proton.me> | 2026-03-23 16:39:33 +0100 |
| commit | 61debe99a269bf7e87f6ba2f8d2a376e619fcf12 (patch) | |
| tree | 41cf942329760f8a5f16d98ea202124fcae8a0f3 /src/Xpl2Client.cpp | |
| parent | 46e96b3318900b561928be304f4a9e252f2785a4 (diff) | |
| download | QtXpl2-61debe99a269bf7e87f6ba2f8d2a376e619fcf12.tar.gz QtXpl2-61debe99a269bf7e87f6ba2f8d2a376e619fcf12.zip | |
Invert TCP connection model: client listens, server connects
The XPL2 protocol specifies that the JI2 controller initiates connections
to the client, not the other way around. Xpl2Client now listens on 3 ports
via QTcpServer and accepts inbound connections; MockServer connects out
with auto-retry on the 1s KA_PING tick.
QML API: startListening/stopListening, listening+connected properties,
host property removed. Mock server gains --host CLI arg.
Diffstat (limited to 'src/Xpl2Client.cpp')
| -rw-r--r-- | src/Xpl2Client.cpp | 204 |
1 files changed, 132 insertions, 72 deletions
diff --git a/src/Xpl2Client.cpp b/src/Xpl2Client.cpp index d3a6da2..4fa502e 100644 --- a/src/Xpl2Client.cpp +++ b/src/Xpl2Client.cpp @@ -5,6 +5,7 @@ #include "Xpl2Client.h" #include <QDebug> +#include <QTcpSocket> bool Xpl2Client::s_wireDebug = false; @@ -157,36 +158,19 @@ Xpl2Client::s_responseTable = { Xpl2Client::Xpl2Client (QObject *parent) : QObject (parent) { - for (auto *socket : { &m_commandSocket, &m_imagingSocket, &m_statusSocket }) - { - connect (socket, &QTcpSocket::connected, this, - &Xpl2Client::onSocketConnected); - connect (socket, &QTcpSocket::disconnected, this, - &Xpl2Client::onSocketDisconnected); - connect (socket, &QTcpSocket::readyRead, this, - &Xpl2Client::onSocketMessageReady); - connect (socket, &QAbstractSocket::errorOccurred, this, - &Xpl2Client::onSocketError); - } + setupServer (m_commandServer, m_commandPort); + setupServer (m_imagingServer, m_imagingPort); + setupServer (m_statusServer, m_statusPort); } /* ------------------------------------------------------------------ */ /* Properties */ /* ------------------------------------------------------------------ */ -QString -Xpl2Client::host () const -{ - return m_host; -} - -void -Xpl2Client::setHost (const QString &host) +bool +Xpl2Client::isListening () const { - if (m_host == host) - return; - m_host = host; - emit hostChanged (); + return m_listening; } bool @@ -230,25 +214,78 @@ Xpl2Client::enableWireDebug () /* ------------------------------------------------------------------ */ void -Xpl2Client::connectToServer () +Xpl2Client::setupServer (QTcpServer &server, quint16 port) +{ + Q_UNUSED (port) + connect (&server, &QTcpServer::newConnection, this, + &Xpl2Client::onNewConnection); +} + +void +Xpl2Client::startListening () { - if (m_connected) + if (m_listening) { - emit statusMessage (QStringLiteral ("Already connected")); + emit statusMessage (QStringLiteral ("Already listening")); return; } - emit statusMessage (QStringLiteral ("Connecting to %1…").arg (m_host)); - m_commandSocket.connectToHost (m_host, m_commandPort); - m_imagingSocket.connectToHost (m_host, m_imagingPort); - m_statusSocket.connectToHost (m_host, m_statusPort); + if (!m_commandServer.listen (QHostAddress::Any, m_commandPort) + || !m_imagingServer.listen (QHostAddress::Any, m_imagingPort) + || !m_statusServer.listen (QHostAddress::Any, m_statusPort)) + { + emit errorOccurred (QStringLiteral ("Failed to listen: %1 / %2 / %3") + .arg (m_commandServer.errorString ()) + .arg (m_imagingServer.errorString ()) + .arg (m_statusServer.errorString ())); + m_commandServer.close (); + m_imagingServer.close (); + m_statusServer.close (); + return; + } + m_listening = true; + emit listeningChanged (); + emit statusMessage (QStringLiteral ("Listening on ports %1/%2/%3…") + .arg (m_commandPort) + .arg (m_imagingPort) + .arg (m_statusPort)); } void -Xpl2Client::disconnectFromServer () +Xpl2Client::stopListening () +{ + m_commandServer.close (); + m_imagingServer.close (); + m_statusServer.close (); + + auto cleanup = [] (QTcpSocket *&sock) + { + if (sock) + { + sock->disconnectFromHost (); + sock->deleteLater (); + sock = nullptr; + } + }; + cleanup (m_commandSocket); + cleanup (m_imagingSocket); + cleanup (m_statusSocket); + + if (m_listening) + { + m_listening = false; + emit listeningChanged (); + } + updateConnectedState (); +} + +QTcpSocket *& +Xpl2Client::socketForServer (QTcpServer *server) { - m_commandSocket.disconnectFromHost (); - m_imagingSocket.disconnectFromHost (); - m_statusSocket.disconnectFromHost (); + if (server == &m_commandServer) + return m_commandSocket; + if (server == &m_imagingServer) + return m_imagingSocket; + return m_statusSocket; } void @@ -609,22 +646,22 @@ Xpl2Client::imageCount () /* ------------------------------------------------------------------ */ void -Xpl2Client::sendCommand (QTcpSocket &socket, const QByteArray &command, +Xpl2Client::sendCommand (QTcpSocket *socket, const QByteArray &command, const QVariantList ¶ms) { - if (socket.state () != QAbstractSocket::ConnectedState) + if (!socket || socket->state () != QAbstractSocket::ConnectedState) { emit errorOccurred (QStringLiteral ("Not connected for %1") .arg (QString::fromUtf8 (command))); return; } QByteArray data = Xpl2Protocol::buildMessage (command, params); - socket.write (data); + socket->write (data); QByteArray wire; if (s_wireDebug) wire = " >> " + data.trimmed (); - qDebug ("%s TX %s%s", qPrintable (logTag (&socket)), command.constData (), + qDebug ("%s TX %s%s", qPrintable (logTag (socket)), command.constData (), wire.constData ()); } @@ -639,8 +676,8 @@ Xpl2Client::dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg) handleGsJcVersion (msg.params); else if (msg.command == "GS_PH_VERSION") handleGsPhVersion (msg.params); - else if (!dispatchResponse (msg.command, msg.params, &m_commandSocket)) - qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_commandSocket)), + else if (!dispatchResponse (msg.command, msg.params, m_commandSocket)) + qWarning ("%s Unknown command: %s", qPrintable (logTag (m_commandSocket)), msg.command.constData ()); } @@ -656,7 +693,7 @@ Xpl2Client::dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg) int lines = 0; if (!msg.params.isEmpty ()) lines = msg.params[0].toString ().toInt (nullptr, 16); - qDebug ("%s n imageLines=%d", qPrintable (logTag (&m_imagingSocket)), + qDebug ("%s n imageLines=%d", qPrintable (logTag (m_imagingSocket)), lines); emit statusMessage (QStringLiteral ("RX n: imageLines=%1").arg (lines)); emit imagingReply (lines); @@ -664,13 +701,13 @@ Xpl2Client::dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg) else if (msg.command == "m4") { bool ok = !msg.params.isEmpty () && msg.params[0].toInt () == 1; - qDebug ("%s m4 success=%d", qPrintable (logTag (&m_imagingSocket)), + qDebug ("%s m4 success=%d", qPrintable (logTag (m_imagingSocket)), ok); emit statusMessage (QStringLiteral ("RX m4: success=%1").arg (ok)); emit imagingStopResult (ok); } - else if (!dispatchResponse (msg.command, msg.params, &m_imagingSocket)) - qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_imagingSocket)), + else if (!dispatchResponse (msg.command, msg.params, m_imagingSocket)) + qWarning ("%s Unknown command: %s", qPrintable (logTag (m_imagingSocket)), msg.command.constData ()); } @@ -685,13 +722,13 @@ Xpl2Client::dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg) handleEvStatusMsgJc (msg.params); else if (msg.command == "EV_STATUS_MSG_PH") handleEvStatusMsgPh (msg.params); - else if (!dispatchResponse (msg.command, msg.params, &m_statusSocket)) - qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_statusSocket)), + else if (!dispatchResponse (msg.command, msg.params, m_statusSocket)) + qWarning ("%s Unknown command: %s", qPrintable (logTag (m_statusSocket)), msg.command.constData ()); } void -Xpl2Client::handleKaPing (QTcpSocket &socket) +Xpl2Client::handleKaPing (QTcpSocket *socket) { sendCommand (socket, "KA_PING", { 1 }); } @@ -709,7 +746,7 @@ Xpl2Client::handleGsJcVersion (const QVariantList ¶ms) m_hardwareVersion = params[2].toString (); m_printheadCount = params[3].toInt (); qDebug ("%s controller=%d fw=%s hw=%s phCount=%d", - qPrintable (logTag (&m_commandSocket)), m_controllerId, + qPrintable (logTag (m_commandSocket)), m_controllerId, qPrintable (m_firmwareVersion), qPrintable (m_hardwareVersion), m_printheadCount); emit statusMessage ( @@ -739,7 +776,7 @@ Xpl2Client::handleGsPhVersion (const QVariantList ¶ms) QString bootVer = params[7].toString (); qDebug ("%s PH[%d] mcuFw=%s mcuHw=%s mcuFwVar=%s fpgaFw=%s fpgaHw=%s " "boot=%s", - qPrintable (logTag (&m_commandSocket)), phId, qPrintable (mcuFw), + qPrintable (logTag (m_commandSocket)), phId, qPrintable (mcuFw), qPrintable (mcuHw), qPrintable (mcuFwVar), qPrintable (fpgaFw), qPrintable (fpgaHw), qPrintable (bootVer)); emit statusMessage ( @@ -905,7 +942,7 @@ Xpl2Client::handleEvStatusMsgJc (const QVariantList ¶ms) { Xpl2JcStatus status = Xpl2JcStatus::fromParams (params); qDebug ("%s EV_STATUS_MSG_JC controller=%d level=%d temp=%.1f cpu=%.1f%%", - qPrintable (logTag (&m_statusSocket)), status.controllerId, + qPrintable (logTag (m_statusSocket)), status.controllerId, status.statusLevel, status.temperature, status.cpuPercentageBusy); emit statusMessage ( QStringLiteral ("RX EV_STATUS_MSG_JC: controller=%1 level=%2") @@ -919,7 +956,7 @@ Xpl2Client::handleEvStatusMsgPh (const QVariantList ¶ms) { Xpl2PhStatus status = Xpl2PhStatus::fromParams (params); qDebug ("%s EV_STATUS_MSG_PH controller=%d level=%d ph=%d temp=%.1f", - qPrintable (logTag (&m_statusSocket)), status.controllerId, + qPrintable (logTag (m_statusSocket)), status.controllerId, status.statusLevel, status.printheadId, status.temperature); emit statusMessage ( QStringLiteral ("RX EV_STATUS_MSG_PH: controller=%1 level=%2 ph=%3") @@ -937,9 +974,12 @@ void Xpl2Client::updateConnectedState () { bool allConnected - = m_commandSocket.state () == QAbstractSocket::ConnectedState - && m_imagingSocket.state () == QAbstractSocket::ConnectedState - && m_statusSocket.state () == QAbstractSocket::ConnectedState; + = m_commandSocket + && m_commandSocket->state () == QAbstractSocket::ConnectedState + && m_imagingSocket + && m_imagingSocket->state () == QAbstractSocket::ConnectedState + && m_statusSocket + && m_statusSocket->state () == QAbstractSocket::ConnectedState; if (m_connected == allConnected) return; m_connected = allConnected; @@ -951,17 +991,17 @@ Xpl2Client::logTag (const QTcpSocket *socket) const { const char *name = "Unknown"; quint16 port = 0; - if (socket == &m_commandSocket) + if (socket == m_commandSocket) { name = "Command"; port = m_commandPort; } - else if (socket == &m_imagingSocket) + else if (socket == m_imagingSocket) { name = "Imaging"; port = m_imagingPort; } - else if (socket == &m_statusSocket) + else if (socket == m_statusSocket) { name = "Status"; port = m_statusPort; @@ -975,26 +1015,46 @@ Xpl2Client::logTag (const QTcpSocket *socket) const /* ------------------------------------------------------------------ */ void -Xpl2Client::onSocketConnected () +Xpl2Client::onNewConnection () { - auto *socket = qobject_cast<QTcpSocket *> (sender ()); - qInfo ("%s Connected", qPrintable (logTag (socket))); - updateConnectedState (); + auto *server = qobject_cast<QTcpServer *> (sender ()); + QTcpSocket *&slot = socketForServer (server); + + while (auto *pending = server->nextPendingConnection ()) + { + if (slot) + { + qWarning ("%s Rejected extra connection", + qPrintable (logTag (slot))); + pending->deleteLater (); + continue; + } + slot = pending; + connect (slot, &QTcpSocket::readyRead, this, + &Xpl2Client::onSocketMessageReady); + connect (slot, &QTcpSocket::disconnected, this, + &Xpl2Client::onSocketDisconnected); + connect (slot, &QAbstractSocket::errorOccurred, this, + &Xpl2Client::onSocketError); + qInfo ("%s Controller connected", qPrintable (logTag (slot))); + updateConnectedState (); + } } void Xpl2Client::onSocketDisconnected () { auto *socket = qobject_cast<QTcpSocket *> (sender ()); - if (!socket) - { - /* Identify by elimination — which socket just left ConnectedState? */ - for (auto *s : { &m_commandSocket, &m_imagingSocket, &m_statusSocket }) - if (s->state () != QAbstractSocket::ConnectedState) - qInfo ("%s Disconnected", qPrintable (logTag (s))); - } - else - qInfo ("%s Disconnected", qPrintable (logTag (socket))); + qInfo ("%s Controller disconnected", qPrintable (logTag (socket))); + + if (socket == m_commandSocket) + m_commandSocket = nullptr; + else if (socket == m_imagingSocket) + m_imagingSocket = nullptr; + else if (socket == m_statusSocket) + m_statusSocket = nullptr; + + socket->deleteLater (); updateConnectedState (); } @@ -1014,9 +1074,9 @@ Xpl2Client::onSocketMessageReady () qDebug ("%s RX %s%s", qPrintable (logTag (socket)), msg.command.constData (), wire.constData ()); - if (socket == &m_commandSocket) + if (socket == m_commandSocket) dispatchCommandMessage (msg); - else if (socket == &m_imagingSocket) + else if (socket == m_imagingSocket) dispatchImagingMessage (msg); else dispatchStatusMessage (msg); |
