aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-03-23 16:39:33 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-03-23 16:39:33 +0100
commit61debe99a269bf7e87f6ba2f8d2a376e619fcf12 (patch)
tree41cf942329760f8a5f16d98ea202124fcae8a0f3
parent46e96b3318900b561928be304f4a9e252f2785a4 (diff)
downloadQtXpl2-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.
-rw-r--r--demo/Main.qml29
-rw-r--r--mock-server/MockServer.cpp356
-rw-r--r--mock-server/MockServer.h77
-rw-r--r--mock-server/main.cpp4
-rw-r--r--src/Xpl2Client.cpp204
-rw-r--r--src/Xpl2Client.h35
6 files changed, 397 insertions, 308 deletions
diff --git a/demo/Main.qml b/demo/Main.qml
index 9b9223f..6aca201 100644
--- a/demo/Main.qml
+++ b/demo/Main.qml
@@ -31,8 +31,9 @@ ApplicationWindow {
Connections {
function onConnectedChanged() {
- debugConsole.appendLog(Xpl2Client.connected ? "Connected" :
- "Disconnected");
+ debugConsole.appendLog(Xpl2Client.connected
+ ? "Controller connected" :
+ "Controller disconnected");
}
function onErrorOccurred(error: string) {
@@ -43,6 +44,12 @@ ApplicationWindow {
statusPage.lastJcStatus = status;
}
+ function onListeningChanged() {
+ debugConsole.appendLog(Xpl2Client.listening
+ ? "Listening on ports 9110/9111/9112" :
+ "Stopped listening");
+ }
+
function onPhStatusReceived(status) {
statusPage.lastPhStatus = status;
}
@@ -95,26 +102,22 @@ ApplicationWindow {
anchors.fill: parent
Label {
- text: "Host:"
+ text: Xpl2Client.connected ? "Controller connected" :
+ "Waiting for controller…"
}
- TextField {
- id: hostField
-
+ Item {
Layout.fillWidth: true
- text: Xpl2Client.host
-
- onEditingFinished: Xpl2Client.host = text
}
Button {
- text: Xpl2Client.connected ? "Disconnect" : "Connect"
+ text: Xpl2Client.listening ? "Stop" : "Listen"
onClicked: {
- if (Xpl2Client.connected)
- Xpl2Client.disconnectFromServer();
+ if (Xpl2Client.listening)
+ Xpl2Client.stopListening();
else
- Xpl2Client.connectToServer();
+ Xpl2Client.startListening();
}
}
}
diff --git a/mock-server/MockServer.cpp b/mock-server/MockServer.cpp
index 87279b4..08eb116 100644
--- a/mock-server/MockServer.cpp
+++ b/mock-server/MockServer.cpp
@@ -1,11 +1,10 @@
/**
* @file MockServer.cpp
- * @brief Mock XPL2 server — listens on all three protocol ports.
+ * @brief Mock XPL2 server — connects out to the client on three protocol
+ * ports.
*/
#include "MockServer.h"
-#include <QTcpSocket>
-
bool MockServer::s_wireDebug = false;
void
@@ -14,19 +13,22 @@ MockServer::enableWireDebug ()
s_wireDebug = true;
}
-MockServer::MockServer (QObject *parent) : QObject (parent)
+MockServer::MockServer (const QString &host, QObject *parent)
+ : QObject (parent), m_host (host)
{
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);
+ connect (&m_tickTimer, &QTimer::timeout, this, &MockServer::tick);
+ m_tickTimer.start (1000);
connect (&m_jcStatusTimer, &QTimer::timeout, this,
&MockServer::sendJcStatusMsg);
connect (&m_phStatusTimer, &QTimer::timeout, this,
&MockServer::sendPhStatusMsg);
+
+ connectAll ();
}
void
@@ -35,81 +37,102 @@ MockServer::setupPort (Port &port, const char *name, quint16 number)
port.name = name;
port.number = number;
- connect (&port.server, &QTcpServer::newConnection, this,
- &MockServer::onNewConnection);
+ connect (&port.socket, &QTcpSocket::connected, this,
+ &MockServer::onSocketConnected);
+ connect (&port.socket, &QTcpSocket::disconnected, this,
+ &MockServer::onSocketDisconnected);
+ connect (&port.socket, &QTcpSocket::readyRead, this,
+ &MockServer::onSocketMessageReady);
+ connect (&port.socket, &QAbstractSocket::errorOccurred, this,
+ [this, &port] (QAbstractSocket::SocketError)
+ {
+ qDebug ("%s Connection error: %s",
+ qPrintable (logTag (&port.socket)),
+ qPrintable (port.socket.errorString ()));
+ });
+}
- 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)));
+void
+MockServer::connectAll ()
+{
+ for (auto *port : { &m_command, &m_imaging, &m_status })
+ if (port->socket.state () == QAbstractSocket::UnconnectedState)
+ port->socket.connectToHost (m_host, port->number);
}
QString
-MockServer::logTag (quint16 localPort) const
+MockServer::logTag (const QTcpSocket *socket) 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;
+ quint16 number = 0;
+ if (socket == &m_command.socket)
+ {
+ name = m_command.name;
+ number = m_command.number;
+ }
+ else if (socket == &m_imaging.socket)
+ {
+ name = m_imaging.name;
+ number = m_imaging.number;
+ }
+ else if (socket == &m_status.socket)
+ {
+ name = m_status.name;
+ number = m_status.number;
+ }
/* Fixed-width tag: "[Command:9110]" = 14 chars, left-padded to 15. */
- return QStringLiteral ("[%1:%2]").arg (name).arg (localPort).leftJustified (
- 15);
+ return QStringLiteral ("[%1:%2]").arg (name).arg (number).leftJustified (15);
}
void
-MockServer::onNewConnection ()
+MockServer::onSocketConnected ()
{
- auto *server = qobject_cast<QTcpServer *> (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);
- }
+ auto *socket = qobject_cast<QTcpSocket *> (sender ());
+ qInfo ("%s Connected to client", qPrintable (logTag (socket)));
}
void
-MockServer::onClientMessageReady ()
+MockServer::onSocketDisconnected ()
{
- auto *sock = qobject_cast<QTcpSocket *> (sender ());
- while (sock->canReadLine ())
- {
- QByteArray line = sock->readLine ();
- handleCommand (sock, line);
- }
+ auto *socket = qobject_cast<QTcpSocket *> (sender ());
+ qInfo ("%s Disconnected from client", qPrintable (logTag (socket)));
}
void
-MockServer::onClientDisconnected ()
+MockServer::onSocketMessageReady ()
{
- auto *sock = qobject_cast<QTcpSocket *> (sender ());
- quint16 lp = sock->localPort ();
- qInfo ("%s Client disconnected", qPrintable (logTag (lp)));
- m_clients.removeOne (sock);
- sock->deleteLater ();
+ auto *socket = qobject_cast<QTcpSocket *> (sender ());
+ Port *port = nullptr;
+ if (socket == &m_command.socket)
+ port = &m_command;
+ else if (socket == &m_imaging.socket)
+ port = &m_imaging;
+ else
+ port = &m_status;
+
+ while (socket->canReadLine ())
+ {
+ QByteArray line = socket->readLine ();
+ handleCommand (*port, line);
+ }
}
void
-MockServer::sendKaPing ()
+MockServer::tick ()
{
- for (auto *client : m_clients)
+ /* Send KA_PING on connected sockets, retry connection on disconnected. */
+ for (auto *port : { &m_command, &m_imaging, &m_status })
{
- if (client->state () == QAbstractSocket::ConnectedState)
- sendReply (client, "KA_PING\n");
+ if (port->socket.state () == QAbstractSocket::ConnectedState)
+ sendReply (port->socket, "KA_PING\n");
+ else if (port->socket.state () == QAbstractSocket::UnconnectedState)
+ port->socket.connectToHost (m_host, port->number);
}
}
void
-MockServer::handleCommand (QTcpSocket *client, const QByteArray &line)
+MockServer::handleCommand (Port &port, const QByteArray &line)
{
QByteArray trimmed = line.trimmed ();
if (trimmed.isEmpty ())
@@ -121,37 +144,37 @@ MockServer::handleCommand (QTcpSocket *client, const QByteArray &line)
QByteArray params = (comma >= 0) ? trimmed.mid (comma + 1) : QByteArray ();
if (cmd == "KA_PING")
- handleKaPing (client, params);
+ handleKaPing (port, params);
else if (cmd == "GS_JC_VERSION")
- handleGsJcVersion (client);
+ handleGsJcVersion (port.socket);
else if (cmd == "GS_PH_VERSION")
- handleGsPhVersion (client, params);
+ handleGsPhVersion (port.socket, params);
/* CN_ JC success shape (no params needed) */
else if (cmd == "CN_JETTING_ALL_ON" || cmd == "CN_JETTING_ON"
|| cmd == "CN_JETTING_OFF" || cmd == "CN_JC_ID_LED_ON"
|| cmd == "CN_JC_ID_LED_OFF" || cmd == "CN_JC_CALIBRATION"
|| cmd == "CN_JC_RESET_FAULT_CODES")
- handleCnJcSuccess (client, cmd);
+ handleCnJcSuccess (port.socket, cmd);
/* CN_ PH success shape (params carry printheadId) */
else if (cmd == "CN_PH_JETTING_ON" || cmd == "CN_PH_JETTING_OFF"
|| cmd == "CN_PH_ID_LED_ON" || cmd == "CN_PH_ID_LED_OFF"
|| cmd == "CN_PH_CALIBRATION" || cmd == "CN_PH_RESET_FAULT_CODES"
|| cmd == "CN_PH_NOZZLES_DISABLED")
- handleCnPhSuccess (client, cmd, params);
+ handleCnPhSuccess (port.socket, cmd, params);
/* CN_ custom-shape commands */
else if (cmd == "CN_PH_CALIBRATION_DATA")
- handleCnPhCalibrationData (client, params);
+ handleCnPhCalibrationData (port.socket, params);
else if (cmd == "CN_PH_CALIBRATION_RAW_DATA")
- handleCnPhCalibrationRawData (client, params);
+ handleCnPhCalibrationRawData (port.socket, params);
else if (cmd == "CN_PH_CALIBRATED_BASE_FREQUENCY")
- handleCnPhCalibratedBaseFrequency (client, params);
+ handleCnPhCalibratedBaseFrequency (port.socket, params);
/* CN_ status messaging (arrives on status port) */
else if (cmd == "CN_JC_STATUS_MESSAGING_START"
|| cmd == "CN_PH_STATUS_MESSAGING_START")
- handleCnStatusMessagingStart (client, cmd, params);
+ handleCnStatusMessagingStart (port.socket, cmd, params);
else if (cmd == "CN_JC_STATUS_MESSAGING_STOP"
|| cmd == "CN_PH_STATUS_MESSAGING_STOP")
- handleCnStatusMessagingStop (client, cmd);
+ handleCnStatusMessagingStop (port.socket, cmd);
/* CF_ JC success shape */
else if (cmd == "CF_PH_DEASSIGN_ID" || cmd == "CF_JC_SAVE_CALIBRATION"
|| cmd == "CF_JC_RESET_CALIBRATION"
@@ -161,152 +184,151 @@ MockServer::handleCommand (QTcpSocket *client, const QByteArray &line)
|| cmd == "CF_JC_RESET_CONTROLLER_SOFTWARE"
|| cmd == "CF_JC_RESTART" || cmd == "CF_JC_SHUTDOWN"
|| cmd == "CF_JC_SAVE_ALL_PRINTHEAD_SETTINGS")
- handleCnJcSuccess (client, cmd);
+ handleCnJcSuccess (port.socket, cmd);
/* CF_ PH success shape */
else if (cmd == "CF_PH_SET_ID" || cmd == "CF_PH_SAVE_CALIBRATION"
|| cmd == "CF_PH_RESET_CALIBRATION"
|| cmd == "CF_PH_RESET_ALL_SETTINGS" || cmd == "CF_PH_REBOOT"
|| cmd == "CF_PH_SAVE_SETTINGS")
- handleCnPhSuccess (client, cmd, params);
+ handleCnPhSuccess (port.socket, cmd, params);
/* CF_ custom-shape commands */
else if (cmd == "CF_JC_SET_PURGE_SETTINGS")
- handleCfJcSetPurgeSettings (client, params);
+ handleCfJcSetPurgeSettings (port.socket, params);
else if (cmd == "CF_JC_SET_JETTING_PARAMS")
- handleCfJcSetJettingParams (client, params);
+ handleCfJcSetJettingParams (port.socket, params);
else if (cmd == "CF_PH_SET_JETTING_PARAMS")
- handleCfPhJettingParams (client, cmd, params);
+ handleCfPhJettingParams (port.socket, cmd, params);
else if (cmd == "CF_PH_GET_JETTING_PARAMS")
- handleCfPhJettingParams (client, cmd, params);
+ handleCfPhJettingParams (port.socket, cmd, params);
else if (cmd == "CF_JC_SETTER")
- handleCfJcSetter (client, params);
+ handleCfJcSetter (port.socket, params);
else if (cmd == "CF_PH_SETTER")
- handleCfPhSetter (client, params);
+ handleCfPhSetter (port.socket, params);
else if (cmd == "CF_JC_GETTER")
- handleCfJcGetter (client, params);
+ handleCfJcGetter (port.socket, params);
else if (cmd == "CF_PH_GETTER")
- handleCfPhGetter (client, params);
+ handleCfPhGetter (port.socket, params);
/* Imaging commands (port 2) */
else if (cmd == "m2")
- handleImagingStart (client, params);
+ handleImagingStart (port.socket, params);
else if (cmd == "m4")
- handleImagingStop (client);
+ handleImagingStop (port.socket);
else if (cmd == "m0" || cmd == "m5")
- handleImagingMaskStart (client, cmd, params);
+ handleImagingMaskStart (port.socket, cmd, params);
else if (cmd == "m1" || cmd == "m6")
- handleImagingMaskEnd (client, cmd, params);
+ handleImagingMaskEnd (port.socket, cmd, params);
else if (cmd == "m3")
- handleImageCount (client);
+ handleImageCount (port.socket);
else
- qWarning ("%s Unknown command: %s",
- qPrintable (logTag (client->localPort ())), cmd.constData ());
+ qWarning ("%s Unknown command: %s", qPrintable (logTag (&port.socket)),
+ cmd.constData ());
}
void
-MockServer::sendReply (QTcpSocket *client, const QByteArray &data)
+MockServer::sendReply (QTcpSocket &socket, const QByteArray &data)
{
- client->write (data);
+ socket.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 ());
+ qDebug ("%s TX %s%s", qPrintable (logTag (&socket)), cmd.constData (),
+ wire.constData ());
}
void
-MockServer::handleKaPing (QTcpSocket *client, const QByteArray &params)
+MockServer::handleKaPing (Port &port, const QByteArray &params)
{
QByteArray wire;
if (s_wireDebug)
wire = " << KA_PING," + params;
- qDebug ("%s RX KA_PING ACK%s", qPrintable (logTag (client->localPort ())),
+ qDebug ("%s RX KA_PING ACK%s", qPrintable (logTag (&port.socket)),
wire.constData ());
}
void
-MockServer::handleGsJcVersion (QTcpSocket *client)
+MockServer::handleGsJcVersion (QTcpSocket &socket)
{
- qDebug ("%s RX GS_JC_VERSION", qPrintable (logTag (client->localPort ())));
- sendReply (client, "GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n");
+ qDebug ("%s RX GS_JC_VERSION", qPrintable (logTag (&socket)));
+ sendReply (socket, "GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n");
}
void
-MockServer::handleGsPhVersion (QTcpSocket *client, const QByteArray &params)
+MockServer::handleGsPhVersion (QTcpSocket &socket, const QByteArray &params)
{
- qDebug ("%s RX GS_PH_VERSION,%s", qPrintable (logTag (client->localPort ())),
+ qDebug ("%s RX GS_PH_VERSION,%s", qPrintable (logTag (&socket)),
params.constData ());
/* Echo back canned version info for whatever printhead ID was requested. */
- sendReply (client, QByteArray ("GS_PH_VERSION,1,") + params
+ sendReply (socket, QByteArray ("GS_PH_VERSION,1,") + params
+ ",\"3.10\",\"1.00\",\"Standard\","
"\"2.05\",\"1.02\",\"0.9.1\"\n");
}
void
-MockServer::handleCnJcSuccess (QTcpSocket *client, const QByteArray &cmd)
+MockServer::handleCnJcSuccess (QTcpSocket &socket, const QByteArray &cmd)
{
- qDebug ("%s RX %s", qPrintable (logTag (client->localPort ())),
- cmd.constData ());
- sendReply (client, cmd + ",1,1\n");
+ qDebug ("%s RX %s", qPrintable (logTag (&socket)), cmd.constData ());
+ sendReply (socket, cmd + ",1,1\n");
}
void
-MockServer::handleCnPhSuccess (QTcpSocket *client, const QByteArray &cmd,
+MockServer::handleCnPhSuccess (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params)
{
- qDebug ("%s RX %s,%s", qPrintable (logTag (client->localPort ())),
- cmd.constData (), params.constData ());
+ qDebug ("%s RX %s,%s", qPrintable (logTag (&socket)), cmd.constData (),
+ params.constData ());
/* Extract printhead ID (first field before any comma). */
int comma = params.indexOf (',');
QByteArray phId = (comma >= 0) ? params.left (comma) : params;
- sendReply (client, cmd + ",1," + phId + ",1\n");
+ sendReply (socket, cmd + ",1," + phId + ",1\n");
}
void
-MockServer::handleCnPhCalibrationData (QTcpSocket *client,
+MockServer::handleCnPhCalibrationData (QTcpSocket &socket,
const QByteArray &params)
{
- qDebug ("%s RX CN_PH_CALIBRATION_DATA,%s",
- qPrintable (logTag (client->localPort ())), params.constData ());
+ qDebug ("%s RX CN_PH_CALIBRATION_DATA,%s", qPrintable (logTag (&socket)),
+ params.constData ());
/* Reply: controllerId, phId, then 48 floats (-1 = uncalibrated). */
QByteArray reply = "CN_PH_CALIBRATION_DATA,1," + params;
for (int i = 0; i < 48; ++i)
reply += ",-1";
reply += '\n';
- sendReply (client, reply);
+ sendReply (socket, reply);
}
void
-MockServer::handleCnPhCalibrationRawData (QTcpSocket *client,
+MockServer::handleCnPhCalibrationRawData (QTcpSocket &socket,
const QByteArray &params)
{
- qDebug ("%s RX CN_PH_CALIBRATION_RAW_DATA,%s",
- qPrintable (logTag (client->localPort ())), params.constData ());
+ qDebug ("%s RX CN_PH_CALIBRATION_RAW_DATA,%s", qPrintable (logTag (&socket)),
+ params.constData ());
QByteArray reply = "CN_PH_CALIBRATION_RAW_DATA,1," + params;
for (int i = 0; i < 48; ++i)
reply += ",-1";
reply += '\n';
- sendReply (client, reply);
+ sendReply (socket, reply);
}
void
-MockServer::handleCnPhCalibratedBaseFrequency (QTcpSocket *client,
+MockServer::handleCnPhCalibratedBaseFrequency (QTcpSocket &socket,
const QByteArray &params)
{
qDebug ("%s RX CN_PH_CALIBRATED_BASE_FREQUENCY,%s",
- qPrintable (logTag (client->localPort ())), params.constData ());
- sendReply (client,
+ qPrintable (logTag (&socket)), params.constData ());
+ sendReply (socket,
"CN_PH_CALIBRATED_BASE_FREQUENCY,1," + params + ",10.0,10.0\n");
}
void
-MockServer::handleCnStatusMessagingStart (QTcpSocket *client,
+MockServer::handleCnStatusMessagingStart (QTcpSocket &socket,
const QByteArray &cmd,
const QByteArray &params)
{
- qDebug ("%s RX %s,%s", qPrintable (logTag (client->localPort ())),
- cmd.constData (), params.constData ());
+ qDebug ("%s RX %s,%s", qPrintable (logTag (&socket)), cmd.constData (),
+ params.constData ());
/* Parse level and interval from params: "level,interval" */
QList<QByteArray> parts = params.split (',');
@@ -324,89 +346,91 @@ MockServer::handleCnStatusMessagingStart (QTcpSocket *client,
m_phStatusTimer.start (interval);
}
- sendReply (client, cmd + ",1," + params + ",1\n");
+ sendReply (socket, cmd + ",1," + params + ",1\n");
}
void
-MockServer::handleCnStatusMessagingStop (QTcpSocket *client,
+MockServer::handleCnStatusMessagingStop (QTcpSocket &socket,
const QByteArray &cmd)
{
- qDebug ("%s RX %s", qPrintable (logTag (client->localPort ())),
- cmd.constData ());
+ qDebug ("%s RX %s", qPrintable (logTag (&socket)), cmd.constData ());
if (cmd == "CN_JC_STATUS_MESSAGING_STOP")
m_jcStatusTimer.stop ();
else
m_phStatusTimer.stop ();
- sendReply (client, cmd + ",1,1\n");
+ sendReply (socket, cmd + ",1,1\n");
}
void
-MockServer::handleCfJcSetPurgeSettings (QTcpSocket *client,
+MockServer::handleCfJcSetPurgeSettings (QTcpSocket &socket,
const QByteArray &params)
{
- qDebug ("%s RX CF_JC_SET_PURGE_SETTINGS,%s",
- qPrintable (logTag (client->localPort ())), params.constData ());
- sendReply (client, "CF_JC_SET_PURGE_SETTINGS,1," + params + ",1\n");
+ qDebug ("%s RX CF_JC_SET_PURGE_SETTINGS,%s", qPrintable (logTag (&socket)),
+ params.constData ());
+ sendReply (socket, "CF_JC_SET_PURGE_SETTINGS,1," + params + ",1\n");
}
void
-MockServer::handleCfJcSetJettingParams (QTcpSocket *client,
+MockServer::handleCfJcSetJettingParams (QTcpSocket &socket,
const QByteArray &params)
{
- qDebug ("%s RX CF_JC_SET_JETTING_PARAMS,%s",
- qPrintable (logTag (client->localPort ())), params.constData ());
- sendReply (client, "CF_JC_SET_JETTING_PARAMS,1," + params + ",1\n");
+ qDebug ("%s RX CF_JC_SET_JETTING_PARAMS,%s", qPrintable (logTag (&socket)),
+ params.constData ());
+ sendReply (socket, "CF_JC_SET_JETTING_PARAMS,1," + params + ",1\n");
}
void
-MockServer::handleCfPhJettingParams (QTcpSocket *client, const QByteArray &cmd,
+MockServer::handleCfPhJettingParams (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params)
{
- qDebug ("%s RX %s,%s", qPrintable (logTag (client->localPort ())),
- cmd.constData (), params.constData ());
+ qDebug ("%s RX %s,%s", qPrintable (logTag (&socket)), cmd.constData (),
+ params.constData ());
if (cmd == "CF_PH_GET_JETTING_PARAMS")
- sendReply (client, cmd + ",1," + params + ",0,0,0,0,0,1\n");
+ sendReply (socket, cmd + ",1," + params + ",0,0,0,0,0,1\n");
else
- sendReply (client, cmd + ",1," + params + ",1\n");
+ sendReply (socket, cmd + ",1," + params + ",1\n");
}
void
-MockServer::handleCfJcSetter (QTcpSocket *client, const QByteArray &params)
+MockServer::handleCfJcSetter (QTcpSocket &socket, const QByteArray &params)
{
- qDebug ("%s RX CF_JC_SETTER,%s", qPrintable (logTag (client->localPort ())),
+ qDebug ("%s RX CF_JC_SETTER,%s", qPrintable (logTag (&socket)),
params.constData ());
- sendReply (client, "CF_JC_SETTER,1," + params + ",1\n");
+ sendReply (socket, "CF_JC_SETTER,1," + params + ",1\n");
}
void
-MockServer::handleCfPhSetter (QTcpSocket *client, const QByteArray &params)
+MockServer::handleCfPhSetter (QTcpSocket &socket, const QByteArray &params)
{
- qDebug ("%s RX CF_PH_SETTER,%s", qPrintable (logTag (client->localPort ())),
+ qDebug ("%s RX CF_PH_SETTER,%s", qPrintable (logTag (&socket)),
params.constData ());
- sendReply (client, "CF_PH_SETTER,1," + params + ",1\n");
+ sendReply (socket, "CF_PH_SETTER,1," + params + ",1\n");
}
void
-MockServer::handleCfJcGetter (QTcpSocket *client, const QByteArray &params)
+MockServer::handleCfJcGetter (QTcpSocket &socket, const QByteArray &params)
{
- qDebug ("%s RX CF_JC_GETTER,%s", qPrintable (logTag (client->localPort ())),
+ qDebug ("%s RX CF_JC_GETTER,%s", qPrintable (logTag (&socket)),
params.constData ());
- sendReply (client, "CF_JC_GETTER,1," + params + ",\"0\",1\n");
+ sendReply (socket, "CF_JC_GETTER,1," + params + ",\"0\",1\n");
}
void
-MockServer::handleCfPhGetter (QTcpSocket *client, const QByteArray &params)
+MockServer::handleCfPhGetter (QTcpSocket &socket, const QByteArray &params)
{
- qDebug ("%s RX CF_PH_GETTER,%s", qPrintable (logTag (client->localPort ())),
+ qDebug ("%s RX CF_PH_GETTER,%s", qPrintable (logTag (&socket)),
params.constData ());
- sendReply (client, "CF_PH_GETTER,1," + params + ",\"0\",1\n");
+ sendReply (socket, "CF_PH_GETTER,1," + params + ",\"0\",1\n");
}
void
MockServer::sendJcStatusMsg ()
{
+ if (m_status.socket.state () != QAbstractSocket::ConnectedState)
+ return;
+
static int tick = 0;
++tick;
bool odd = (tick % 2) != 0;
@@ -432,15 +456,15 @@ MockServer::sendJcStatusMsg ()
}
base += '\n';
- for (auto *client : m_clients)
- if (client->state () == QAbstractSocket::ConnectedState
- && client->localPort () == m_status.number)
- sendReply (client, base);
+ sendReply (m_status.socket, base);
}
void
MockServer::sendPhStatusMsg ()
{
+ if (m_status.socket.state () != QAbstractSocket::ConnectedState)
+ return;
+
static int tick = 0;
++tick;
bool odd = (tick % 2) != 0;
@@ -475,47 +499,43 @@ MockServer::sendPhStatusMsg ()
}
base += '\n';
- for (auto *client : m_clients)
- if (client->state () == QAbstractSocket::ConnectedState
- && client->localPort () == m_status.number)
- sendReply (client, base);
+ sendReply (m_status.socket, base);
}
void
-MockServer::handleImagingStart (QTcpSocket *client, const QByteArray &params)
+MockServer::handleImagingStart (QTcpSocket &socket, const QByteArray &params)
{
- qDebug ("%s RX m2,%s", qPrintable (logTag (client->localPort ())),
- params.constData ());
- sendReply (client, "n,\"A\"\n");
+ qDebug ("%s RX m2,%s", qPrintable (logTag (&socket)), params.constData ());
+ sendReply (socket, "n,\"A\"\n");
}
void
-MockServer::handleImagingStop (QTcpSocket *client)
+MockServer::handleImagingStop (QTcpSocket &socket)
{
- qDebug ("%s RX m4", qPrintable (logTag (client->localPort ())));
- sendReply (client, "m4,1\n");
+ qDebug ("%s RX m4", qPrintable (logTag (&socket)));
+ sendReply (socket, "m4,1\n");
}
void
-MockServer::handleImagingMaskStart (QTcpSocket *client, const QByteArray &cmd,
+MockServer::handleImagingMaskStart (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params)
{
- qDebug ("%s RX %s,%s", qPrintable (logTag (client->localPort ())),
- cmd.constData (), params.constData ());
+ qDebug ("%s RX %s,%s", qPrintable (logTag (&socket)), cmd.constData (),
+ params.constData ());
}
void
-MockServer::handleImagingMaskEnd (QTcpSocket *client, const QByteArray &cmd,
+MockServer::handleImagingMaskEnd (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params)
{
- qDebug ("%s RX %s,%s", qPrintable (logTag (client->localPort ())),
- cmd.constData (), params.constData ());
- sendReply (client, "n,\"A\"\n");
+ qDebug ("%s RX %s,%s", qPrintable (logTag (&socket)), cmd.constData (),
+ params.constData ());
+ sendReply (socket, "n,\"A\"\n");
}
void
-MockServer::handleImageCount (QTcpSocket *client)
+MockServer::handleImageCount (QTcpSocket &socket)
{
- qDebug ("%s RX m3", qPrintable (logTag (client->localPort ())));
- sendReply (client, "n,\"0\"\n");
+ qDebug ("%s RX m3", qPrintable (logTag (&socket)));
+ sendReply (socket, "n,\"0\"\n");
}
diff --git a/mock-server/MockServer.h b/mock-server/MockServer.h
index 74b2731..242cb32 100644
--- a/mock-server/MockServer.h
+++ b/mock-server/MockServer.h
@@ -1,88 +1,87 @@
/**
* @file MockServer.h
- * @brief Mock XPL2 server — listens on all three protocol ports.
+ * @brief Mock XPL2 server — connects out to the client on three protocol
+ * ports.
*/
#pragma once
-#include <QList>
#include <QObject>
-#include <QTcpServer>
+#include <QTcpSocket>
#include <QTimer>
-class QTcpSocket;
-
class MockServer : public QObject
{
Q_OBJECT
public:
- explicit MockServer (QObject *parent = nullptr);
+ explicit MockServer (const QString &host, QObject *parent = nullptr);
static void enableWireDebug ();
private slots:
- void onNewConnection ();
- void onClientMessageReady ();
- void onClientDisconnected ();
- /* Send KA_PING keepalive to all connected clients. */
- void sendKaPing ();
+ void onSocketConnected ();
+ void onSocketDisconnected ();
+ void onSocketMessageReady ();
+ /* Send KA_PING on connected sockets, retry connection on disconnected. */
+ void tick ();
void sendJcStatusMsg ();
void sendPhStatusMsg ();
private:
struct Port
{
- QTcpServer server;
+ QTcpSocket socket;
const char *name = nullptr;
quint16 number = 0;
};
void setupPort (Port &port, const char *name, quint16 number);
+ void connectAll ();
/* Return a fixed-width "[Name:port]" tag for log lines. */
- QString logTag (quint16 localPort) const;
+ QString logTag (const QTcpSocket *socket) const;
/* Parse incoming line, log RX, and dispatch to the matching handler. */
- void handleCommand (QTcpSocket *client, const QByteArray &line);
- void sendReply (QTcpSocket *client, const QByteArray &data);
- void handleKaPing (QTcpSocket *client, const QByteArray &params);
- void handleGsJcVersion (QTcpSocket *client);
- void handleGsPhVersion (QTcpSocket *client, const QByteArray &params);
+ void handleCommand (Port &port, const QByteArray &line);
+ void sendReply (QTcpSocket &socket, const QByteArray &data);
+ void handleKaPing (Port &port, const QByteArray &params);
+ void handleGsJcVersion (QTcpSocket &socket);
+ void handleGsPhVersion (QTcpSocket &socket, const QByteArray &params);
/* CN_ control command handlers */
- void handleCnJcSuccess (QTcpSocket *client, const QByteArray &cmd);
- void handleCnPhSuccess (QTcpSocket *client, const QByteArray &cmd,
+ void handleCnJcSuccess (QTcpSocket &socket, const QByteArray &cmd);
+ void handleCnPhSuccess (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params);
- void handleCnPhCalibrationData (QTcpSocket *client,
+ void handleCnPhCalibrationData (QTcpSocket &socket,
const QByteArray &params);
- void handleCnPhCalibrationRawData (QTcpSocket *client,
+ void handleCnPhCalibrationRawData (QTcpSocket &socket,
const QByteArray &params);
- void handleCnPhCalibratedBaseFrequency (QTcpSocket *client,
+ void handleCnPhCalibratedBaseFrequency (QTcpSocket &socket,
const QByteArray &params);
- void handleCnStatusMessagingStart (QTcpSocket *client, const QByteArray &cmd,
+ void handleCnStatusMessagingStart (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params);
- void handleCnStatusMessagingStop (QTcpSocket *client, const QByteArray &cmd);
+ void handleCnStatusMessagingStop (QTcpSocket &socket, const QByteArray &cmd);
/* CF_ configuration command handlers */
- void handleCfJcSetPurgeSettings (QTcpSocket *client,
+ void handleCfJcSetPurgeSettings (QTcpSocket &socket,
const QByteArray &params);
- void handleCfJcSetJettingParams (QTcpSocket *client,
+ void handleCfJcSetJettingParams (QTcpSocket &socket,
const QByteArray &params);
- void handleCfPhJettingParams (QTcpSocket *client, const QByteArray &cmd,
+ void handleCfPhJettingParams (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params);
- void handleCfJcSetter (QTcpSocket *client, const QByteArray &params);
- void handleCfPhSetter (QTcpSocket *client, const QByteArray &params);
- void handleCfJcGetter (QTcpSocket *client, const QByteArray &params);
- void handleCfPhGetter (QTcpSocket *client, const QByteArray &params);
+ void handleCfJcSetter (QTcpSocket &socket, const QByteArray &params);
+ void handleCfPhSetter (QTcpSocket &socket, const QByteArray &params);
+ void handleCfJcGetter (QTcpSocket &socket, const QByteArray &params);
+ void handleCfPhGetter (QTcpSocket &socket, const QByteArray &params);
/* Imaging command handlers */
- void handleImagingStart (QTcpSocket *client, const QByteArray &params);
- void handleImagingStop (QTcpSocket *client);
- void handleImagingMaskStart (QTcpSocket *client, const QByteArray &cmd,
+ void handleImagingStart (QTcpSocket &socket, const QByteArray &params);
+ void handleImagingStop (QTcpSocket &socket);
+ void handleImagingMaskStart (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params);
- void handleImagingMaskEnd (QTcpSocket *client, const QByteArray &cmd,
+ void handleImagingMaskEnd (QTcpSocket &socket, const QByteArray &cmd,
const QByteArray &params);
- void handleImageCount (QTcpSocket *client);
+ void handleImageCount (QTcpSocket &socket);
+ QString m_host;
Port m_command;
Port m_imaging;
Port m_status;
- QList<QTcpSocket *> m_clients;
- QTimer m_pingTimer;
+ QTimer m_tickTimer;
QTimer m_jcStatusTimer;
QTimer m_phStatusTimer;
int m_jcStatusLevel = 1;
diff --git a/mock-server/main.cpp b/mock-server/main.cpp
index 5613462..9a55463 100644
--- a/mock-server/main.cpp
+++ b/mock-server/main.cpp
@@ -16,13 +16,15 @@ main (int argc, char *argv[])
QCommandLineParser parser;
parser.addOption ({ "wire-debug", "Log raw wire TX/RX to dev log" });
+ parser.addOption ({ "host", "Target host to connect to (default 127.0.0.1)",
+ "address", "127.0.0.1" });
parser.addHelpOption ();
parser.process (app);
if (parser.isSet ("wire-debug"))
MockServer::enableWireDebug ();
- new MockServer (&app);
+ new MockServer (parser.value ("host"), &app);
return app.exec ();
}
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 &params)
{
- 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 &params)
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 &params)
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 &params)
{
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 &params)
{
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);
diff --git a/src/Xpl2Client.h b/src/Xpl2Client.h
index 838c344..44edb65 100644
--- a/src/Xpl2Client.h
+++ b/src/Xpl2Client.h
@@ -11,17 +11,19 @@
#include <QHash>
#include <QObject>
#include <QQmlEngine>
-#include <QTcpSocket>
+#include <QTcpServer>
#include <functional>
+class QTcpSocket;
+
class Xpl2Client : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
- Q_PROPERTY (QString host READ host WRITE setHost NOTIFY hostChanged)
+ Q_PROPERTY (bool listening READ isListening NOTIFY listeningChanged)
Q_PROPERTY (bool connected READ isConnected NOTIFY connectedChanged)
Q_PROPERTY (int controllerId READ controllerId NOTIFY jcVersionReceived)
Q_PROPERTY (
@@ -33,9 +35,7 @@ class Xpl2Client : public QObject
public:
explicit Xpl2Client (QObject *parent = nullptr);
- QString host () const;
- void setHost (const QString &host);
-
+ bool isListening () const;
bool isConnected () const;
int controllerId () const;
@@ -45,8 +45,8 @@ public:
static void enableWireDebug ();
- Q_INVOKABLE void connectToServer ();
- Q_INVOKABLE void disconnectFromServer ();
+ Q_INVOKABLE void startListening ();
+ Q_INVOKABLE void stopListening ();
/** Get the just connected Jetting Controller ID and Software Version. */
Q_INVOKABLE void getJcVersion ();
/** Query the specified printhead's version info. */
@@ -177,7 +177,7 @@ public:
Q_INVOKABLE void imageCount ();
signals:
- void hostChanged ();
+ void listeningChanged ();
void connectedChanged ();
void errorOccurred (const QString &error);
void statusMessage (const QString &message);
@@ -286,18 +286,20 @@ signals:
void imagingStopResult (bool success);
private slots:
- void onSocketConnected ();
+ void onNewConnection ();
void onSocketDisconnected ();
void onSocketMessageReady ();
void onSocketError (QAbstractSocket::SocketError error);
private:
- void sendCommand (QTcpSocket &socket, const QByteArray &command,
+ void setupServer (QTcpServer &server, quint16 port);
+ QTcpSocket *&socketForServer (QTcpServer *server);
+ void sendCommand (QTcpSocket *socket, const QByteArray &command,
const QVariantList &params = {});
void dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg);
void dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg);
void dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg);
- void handleKaPing (QTcpSocket &socket);
+ void handleKaPing (QTcpSocket *socket);
void handleGsJcVersion (const QVariantList &params);
void handleGsPhVersion (const QVariantList &params);
enum class ResponseShape
@@ -335,13 +337,16 @@ private:
void updateConnectedState ();
QString logTag (const QTcpSocket *socket) const;
- QTcpSocket m_commandSocket;
- QTcpSocket m_imagingSocket;
- QTcpSocket m_statusSocket;
- QString m_host = QStringLiteral ("127.0.0.1");
+ QTcpServer m_commandServer;
+ QTcpServer m_imagingServer;
+ QTcpServer m_statusServer;
+ QTcpSocket *m_commandSocket = nullptr;
+ QTcpSocket *m_imagingSocket = nullptr;
+ QTcpSocket *m_statusSocket = nullptr;
quint16 m_commandPort = 9110;
quint16 m_imagingPort = 9111;
quint16 m_statusPort = 9112;
+ bool m_listening = false;
bool m_connected = false;
static bool s_wireDebug;
int m_controllerId = 0;