diff options
| author | Thomas Vanbesien <tvanbesi@proton.me> | 2026-03-24 17:25:03 +0100 |
|---|---|---|
| committer | Thomas Vanbesien <tvanbesi@proton.me> | 2026-03-24 17:29:52 +0100 |
| commit | e9d8a8b052150f42ea00da2c07e3f78a9b7d2061 (patch) | |
| tree | 2124701ae0991ed854c1a94e58d64558b6f78b48 | |
| parent | 8bcf948b76c9564cb38d3611228ccaf73890a548 (diff) | |
| download | QtXpl2-e9d8a8b052150f42ea00da2c07e3f78a9b7d2061.tar.gz QtXpl2-e9d8a8b052150f42ea00da2c07e3f78a9b7d2061.zip | |
Insert a transparent TCP proxy between the controller and N clients:
- JettingProxy listens on 9110-9112 (controller) and 9210-9212 (clients)
- Broadcasts controller frames to all clients, forwards client→controller
- Independent KA_PING handling on both sides
Convert Xpl2Client from passive QTcpServer listener to active QTcpSocket
outbound connections with auto-retry. New QML API: host/commandPort
properties, connectToProxy()/disconnectFromProxy() replacing
startListening()/stopListening().
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | README.md | 62 | ||||
| -rw-r--r-- | jetting-interface/Main.qml | 39 | ||||
| -rw-r--r-- | jetting-proxy/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | jetting-proxy/JettingProxy.cpp | 266 | ||||
| -rw-r--r-- | jetting-proxy/JettingProxy.h | 54 | ||||
| -rw-r--r-- | jetting-proxy/main.cpp | 37 | ||||
| -rw-r--r-- | src/Xpl2Client.cpp | 351 | ||||
| -rw-r--r-- | src/Xpl2Client.h | 45 |
9 files changed, 629 insertions, 232 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a81bd5f..7c8fe3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,5 +31,6 @@ add_subdirectory(src) if(PROJECT_IS_TOP_LEVEL) add_subdirectory(jetting-interface) + add_subdirectory(jetting-proxy) add_subdirectory(mock-jetting-controller) endif() @@ -15,46 +15,63 @@ qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug cmake --build build --parallel ``` -Produces three targets: +Produces four targets: | Target | Binary | Description | |---|---|---| | `QtXpl2` | `libQtXpl2.a` | Static library + QML module (`import Xpl2`) | +| `JettingProxy` | `bin/JettingProxy` | TCP relay between one controller and N clients | | `JettingInterfaceDemo` | `bin/JettingInterfaceDemo` | Interactive demo app | | `MockJettingController` | `bin/MockJettingController` | Mock JI2 server for development | ## Quick start ```bash -# Terminal 1 — start the mock server +# Terminal 1 — start the proxy +./build/bin/JettingProxy + +# Terminal 2 — start the mock server (connects to proxy on 9110-9112) ./build/bin/MockJettingController -# Terminal 2 — run the demo +# Terminal 3 — run the demo (connects to proxy on 9210-9212) ./build/bin/JettingInterfaceDemo ``` -The demo connects to `127.0.0.1` by default. Click **Connect**, then explore the **Commands** and **Status** tabs. +Click **Connect** in the demo, then explore the **Commands** and **Status** tabs. Multiple demo instances can connect simultaneously. ### CLI flags -| Flag | Description | -|---|---| -| `--printheads N` | Number of printheads in the demo UI (default 10) | -| `--wire-debug` | Log raw wire bytes for both client and mock server | +| Binary | Flag | Description | +|---|---|---| +| `JettingInterfaceDemo` | `--printheads N` | Number of printheads in the demo UI (default 10) | +| `JettingProxy` | `--controller-port P` | Base port for controller side (default 9110) | +| `JettingProxy` | `--client-port P` | Base port for client side (default 9210) | +| All | `--wire-debug` | Log raw wire bytes / forwarded frames | ## Architecture -### Three-port TCP client +### Proxy architecture -The JI2 protocol uses three independent TCP sockets: +A `JettingProxy` relay sits between the controller and N client instances: -| Port | Name | Purpose | -|---|---|---| -| 9110 | Command | GS_, CN_, CF_ commands and responses | -| 9111 | Imaging | m0-m6 image masks, m2/m4 start/stop, n replies | -| 9112 | Status | KA_PING keepalive, EV_ events, status messaging | +``` +MockController ──(9110/9111/9112)──→ JettingProxy ←──(9210/9211/9212)── Xpl2Client #1 + ←──(9210/9211/9212)── Xpl2Client #2 +``` + +The proxy broadcasts controller frames to all clients and forwards client frames to the controller. KA_PING is handled independently on both sides (never forwarded). + +### Three-port TCP protocol -`Xpl2Client` manages all three connections as a QML singleton. +The JI2 protocol uses three independent TCP channels: + +| Controller port | Client port | Name | Purpose | +|---|---|---|---| +| 9110 | 9210 | Command | GS_, CN_, CF_ commands and responses | +| 9111 | 9211 | Imaging | m0-m6 image masks, m2/m4 start/stop, n replies | +| 9112 | 9212 | Status | EV_ events, status messaging | + +`Xpl2Client` connects out to the proxy on the client ports as a QML singleton. ### Protocol implementation @@ -90,7 +107,8 @@ Button { text: "Connect" onClicked: { Xpl2Client.host = "192.168.1.100" - Xpl2Client.connectToServer() + Xpl2Client.commandPort = 9210 + Xpl2Client.connectToProxy() } } @@ -146,6 +164,10 @@ QtXpl2/ DebugConsole.qml # Dark log panel main.cpp CMakeLists.txt + jetting-proxy/ + JettingProxy.h/cpp # Transparent TCP relay (1 controller ↔ N clients) + main.cpp + CMakeLists.txt mock-jetting-controller/ MockServer.h/cpp # Canned responses, periodic status emission main.cpp @@ -154,9 +176,13 @@ QtXpl2/ protocol.pdf # Alchemie JI2-JC protocol specification ``` +## Jetting proxy + +The proxy (`JettingProxy`) is a transparent TCP relay that enables multiple clients to share a single controller connection. It operates at the raw frame level — it reads LF-terminated lines and forwards them without parsing the protocol. KA_PING is handled independently on both sides to prevent N clients from each replying to the controller. + ## Mock server -The mock server responds to all 56 protocol tokens with canned data. When status messaging is started (`CN_JC_STATUS_MESSAGING_START` / `CN_PH_STATUS_MESSAGING_START`), it emits periodic `EV_STATUS_MSG_JC` / `EV_STATUS_MSG_PH` events at the requested interval, alternating between two value sets so updates are visually apparent in the demo. +The mock server responds to all 56 protocol tokens with canned data. It connects to the proxy (or directly to a listening client) on ports 9110-9112. When status messaging is started (`CN_JC_STATUS_MESSAGING_START` / `CN_PH_STATUS_MESSAGING_START`), it emits periodic `EV_STATUS_MSG_JC` / `EV_STATUS_MSG_PH` events at the requested interval, alternating between two value sets so updates are visually apparent in the demo. ## Conventions diff --git a/jetting-interface/Main.qml b/jetting-interface/Main.qml index 1c7b553..cd69ed8 100644 --- a/jetting-interface/Main.qml +++ b/jetting-interface/Main.qml @@ -31,9 +31,8 @@ ApplicationWindow { Connections { function onConnectedChanged() { - debugConsole.appendLog(Xpl2Client.connected - ? "Controller connected" : - "Controller disconnected"); + debugConsole.appendLog(Xpl2Client.connected ? "Connected to proxy" : + "Disconnected from proxy"); } function onErrorOccurred(error: string) { @@ -44,12 +43,6 @@ ApplicationWindow { statusPage.lastJcStatus = status; } - function onListeningChanged() { - debugConsole.appendLog(Xpl2Client.listening - ? "Listening on ports 9110/9111/9112" : - "Stopped listening"); - } - function onPhStatusReceived(status) { statusPage.lastPhStatus = status; } @@ -101,9 +94,25 @@ ApplicationWindow { RowLayout { anchors.fill: parent + TextField { + Layout.preferredWidth: 140 + placeholderText: "Host" + text: Xpl2Client.host + + onEditingFinished: Xpl2Client.host = text + } + + TextField { + Layout.preferredWidth: 70 + placeholderText: "Port" + text: Xpl2Client.commandPort + + onEditingFinished: Xpl2Client.commandPort = parseInt(text) + } + Label { - text: Xpl2Client.connected ? "Controller connected" : - "Waiting for controller…" + text: Xpl2Client.connected ? "Connected to proxy" : + "Disconnected" } Item { @@ -111,13 +120,13 @@ ApplicationWindow { } Button { - text: Xpl2Client.listening ? "Stop" : "Listen" + text: Xpl2Client.connected ? "Disconnect" : "Connect" onClicked: { - if (Xpl2Client.listening) - Xpl2Client.stopListening(); + if (Xpl2Client.connected) + Xpl2Client.disconnectFromProxy(); else - Xpl2Client.startListening(); + Xpl2Client.connectToProxy(); } } } diff --git a/jetting-proxy/CMakeLists.txt b/jetting-proxy/CMakeLists.txt new file mode 100644 index 0000000..6e3c055 --- /dev/null +++ b/jetting-proxy/CMakeLists.txt @@ -0,0 +1,6 @@ +qt_add_executable(JettingProxy main.cpp JettingProxy.h JettingProxy.cpp) + +set_target_properties(JettingProxy PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${PROJECT_BINARY_DIR}/bin") + +target_link_libraries(JettingProxy PRIVATE Qt6::Core Qt6::Network) diff --git a/jetting-proxy/JettingProxy.cpp b/jetting-proxy/JettingProxy.cpp new file mode 100644 index 0000000..30fe7df --- /dev/null +++ b/jetting-proxy/JettingProxy.cpp @@ -0,0 +1,266 @@ +/** + * @file JettingProxy.cpp + * @brief Transparent TCP relay between one controller and N clients. + */ +#include "JettingProxy.h" + +bool JettingProxy::s_wireDebug = false; + +void +JettingProxy::enableWireDebug () +{ + s_wireDebug = true; +} + +JettingProxy::JettingProxy (quint16 controllerPortBase, quint16 clientPortBase, + QObject *parent) + : QObject (parent) +{ + setupChannel (m_command, "Command", controllerPortBase, clientPortBase); + setupChannel (m_imaging, "Imaging", controllerPortBase + 1, + clientPortBase + 1); + setupChannel (m_status, "Status", controllerPortBase + 2, + clientPortBase + 2); + + connect (&m_tickTimer, &QTimer::timeout, this, &JettingProxy::tick); + m_tickTimer.start (1000); +} + +void +JettingProxy::setupChannel (Channel &channel, const char *name, + quint16 controllerPort, quint16 clientPort) +{ + channel.name = name; + + connect (&channel.controllerServer, &QTcpServer::newConnection, this, + &JettingProxy::onControllerConnected); + connect (&channel.clientServer, &QTcpServer::newConnection, this, + &JettingProxy::onClientConnected); + + if (!channel.controllerServer.listen (QHostAddress::Any, controllerPort)) + { + qCritical ("%s Failed to listen for controller on port %d: %s", + qPrintable (logTag (channel)), controllerPort, + qPrintable (channel.controllerServer.errorString ())); + return; + } + if (!channel.clientServer.listen (QHostAddress::Any, clientPort)) + { + qCritical ("%s Failed to listen for clients on port %d: %s", + qPrintable (logTag (channel)), clientPort, + qPrintable (channel.clientServer.errorString ())); + return; + } + + qInfo ("%s Listening — controller port %d, client port %d", + qPrintable (logTag (channel)), controllerPort, clientPort); +} + +/* ------------------------------------------------------------------ */ +/* Channel lookup helpers */ +/* ------------------------------------------------------------------ */ + +JettingProxy::Channel * +JettingProxy::channelForControllerServer (QTcpServer *server) +{ + if (server == &m_command.controllerServer) + return &m_command; + if (server == &m_imaging.controllerServer) + return &m_imaging; + if (server == &m_status.controllerServer) + return &m_status; + return nullptr; +} + +JettingProxy::Channel * +JettingProxy::channelForControllerSocket (QTcpSocket *socket) +{ + if (socket == m_command.controllerSocket) + return &m_command; + if (socket == m_imaging.controllerSocket) + return &m_imaging; + if (socket == m_status.controllerSocket) + return &m_status; + return nullptr; +} + +JettingProxy::Channel * +JettingProxy::channelForClientSocket (QTcpSocket *socket) +{ + for (auto *ch : { &m_command, &m_imaging, &m_status }) + if (ch->clientSockets.contains (socket)) + return ch; + return nullptr; +} + +QString +JettingProxy::logTag (const Channel &channel) const +{ + return QStringLiteral ("[%1]").arg (channel.name).leftJustified (11); +} + +/* ------------------------------------------------------------------ */ +/* Controller slots */ +/* ------------------------------------------------------------------ */ + +void +JettingProxy::onControllerConnected () +{ + auto *server = qobject_cast<QTcpServer *> (sender ()); + Channel *ch = channelForControllerServer (server); + + while (auto *pending = server->nextPendingConnection ()) + { + if (ch->controllerSocket) + { + qWarning ("%s Rejected extra controller connection", + qPrintable (logTag (*ch))); + pending->deleteLater (); + continue; + } + ch->controllerSocket = pending; + connect (pending, &QTcpSocket::readyRead, this, + &JettingProxy::onControllerDataReady); + connect (pending, &QTcpSocket::disconnected, this, + &JettingProxy::onControllerDisconnected); + qInfo ("%s Controller connected", qPrintable (logTag (*ch))); + } +} + +void +JettingProxy::onControllerDisconnected () +{ + auto *socket = qobject_cast<QTcpSocket *> (sender ()); + Channel *ch = channelForControllerSocket (socket); + if (ch) + { + qInfo ("%s Controller disconnected", qPrintable (logTag (*ch))); + ch->controllerSocket = nullptr; + } + socket->deleteLater (); +} + +void +JettingProxy::onControllerDataReady () +{ + auto *socket = qobject_cast<QTcpSocket *> (sender ()); + Channel *ch = channelForControllerSocket (socket); + if (!ch) + return; + + while (socket->canReadLine ()) + { + QByteArray line = socket->readLine (); + QByteArray trimmed = line.trimmed (); + + /* Extract command token (everything before the first comma). */ + int comma = trimmed.indexOf (','); + QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed; + + if (cmd == "KA_PING") + { + /* Reply directly — never forward keepalive to clients. */ + socket->write ("KA_PING,1\n"); + continue; + } + + if (s_wireDebug) + qDebug ("%s Controller → clients: %s", qPrintable (logTag (*ch)), + trimmed.constData ()); + + /* Broadcast to all connected clients on this channel. */ + for (auto *client : ch->clientSockets) + client->write (line); + } +} + +/* ------------------------------------------------------------------ */ +/* Client slots */ +/* ------------------------------------------------------------------ */ + +void +JettingProxy::onClientConnected () +{ + auto *server = qobject_cast<QTcpServer *> (sender ()); + Channel *ch = nullptr; + if (server == &m_command.clientServer) + ch = &m_command; + else if (server == &m_imaging.clientServer) + ch = &m_imaging; + else + ch = &m_status; + + while (auto *pending = server->nextPendingConnection ()) + { + ch->clientSockets.append (pending); + connect (pending, &QTcpSocket::readyRead, this, + &JettingProxy::onClientDataReady); + connect (pending, &QTcpSocket::disconnected, this, + &JettingProxy::onClientDisconnected); + qInfo ("%s Client #%lld connected", qPrintable (logTag (*ch)), + static_cast<long long> (ch->clientSockets.size ())); + } +} + +void +JettingProxy::onClientDisconnected () +{ + auto *socket = qobject_cast<QTcpSocket *> (sender ()); + Channel *ch = channelForClientSocket (socket); + if (ch) + { + ch->clientSockets.removeOne (socket); + qInfo ("%s Client disconnected (%lld remaining)", + qPrintable (logTag (*ch)), + static_cast<long long> (ch->clientSockets.size ())); + } + socket->deleteLater (); +} + +void +JettingProxy::onClientDataReady () +{ + auto *socket = qobject_cast<QTcpSocket *> (sender ()); + Channel *ch = channelForClientSocket (socket); + if (!ch) + return; + + while (socket->canReadLine ()) + { + QByteArray line = socket->readLine (); + QByteArray trimmed = line.trimmed (); + + /* Extract command token. */ + int comma = trimmed.indexOf (','); + QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed; + + /* Absorb keepalive replies — never forward to controller. */ + if (cmd == "KA_PING") + continue; + + if (s_wireDebug) + qDebug ("%s Client → controller: %s", qPrintable (logTag (*ch)), + trimmed.constData ()); + + /* Forward to controller. */ + if (ch->controllerSocket + && ch->controllerSocket->state () == QAbstractSocket::ConnectedState) + ch->controllerSocket->write (line); + else + qWarning ("%s No controller — dropped: %s", qPrintable (logTag (*ch)), + trimmed.constData ()); + } +} + +/* ------------------------------------------------------------------ */ +/* Tick */ +/* ------------------------------------------------------------------ */ + +void +JettingProxy::tick () +{ + for (auto *ch : { &m_command, &m_imaging, &m_status }) + for (auto *client : ch->clientSockets) + if (client->state () == QAbstractSocket::ConnectedState) + client->write ("KA_PING\n"); +} diff --git a/jetting-proxy/JettingProxy.h b/jetting-proxy/JettingProxy.h new file mode 100644 index 0000000..ec8c26d --- /dev/null +++ b/jetting-proxy/JettingProxy.h @@ -0,0 +1,54 @@ +/** + * @file JettingProxy.h + * @brief Transparent TCP relay between one controller and N clients. + */ +#pragma once + +#include <QList> +#include <QObject> +#include <QTcpServer> +#include <QTcpSocket> +#include <QTimer> + +class JettingProxy : public QObject +{ + Q_OBJECT + +public: + explicit JettingProxy (quint16 controllerPortBase, quint16 clientPortBase, + QObject *parent = nullptr); + static void enableWireDebug (); + +private slots: + void onControllerConnected (); + void onControllerDisconnected (); + void onControllerDataReady (); + void onClientConnected (); + void onClientDisconnected (); + void onClientDataReady (); + /* Send KA_PING to all connected clients on all channels. */ + void tick (); + +private: + struct Channel + { + const char *name = nullptr; + QTcpServer controllerServer; + QTcpSocket *controllerSocket = nullptr; + QTcpServer clientServer; + QList<QTcpSocket *> clientSockets; + }; + + void setupChannel (Channel &channel, const char *name, + quint16 controllerPort, quint16 clientPort); + Channel *channelForControllerServer (QTcpServer *server); + Channel *channelForControllerSocket (QTcpSocket *socket); + Channel *channelForClientSocket (QTcpSocket *socket); + QString logTag (const Channel &channel) const; + + Channel m_command; + Channel m_imaging; + Channel m_status; + QTimer m_tickTimer; + static bool s_wireDebug; +}; diff --git a/jetting-proxy/main.cpp b/jetting-proxy/main.cpp new file mode 100644 index 0000000..0e14054 --- /dev/null +++ b/jetting-proxy/main.cpp @@ -0,0 +1,37 @@ +/** + * @file main.cpp + * @brief Jetting proxy — relay between one controller and N clients. + */ +#include "JettingProxy.h" + +#include <QCommandLineParser> +#include <QCoreApplication> + +int +main (int argc, char *argv[]) +{ + qSetMessagePattern ("Proxy [%{time HH:mm:ss.zzz}] %{message}"); + + QCoreApplication app (argc, argv); + + QCommandLineParser parser; + parser.addOption ({ "wire-debug", "Log forwarded frames to dev log" }); + parser.addOption ({ "controller-port", + "Base port for controller side (default 9110)", "port", + "9110" }); + parser.addOption ({ "client-port", + "Base port for client side (default 9210)", "port", + "9210" }); + parser.addHelpOption (); + parser.process (app); + + if (parser.isSet ("wire-debug")) + JettingProxy::enableWireDebug (); + + quint16 controllerPort = parser.value ("controller-port").toUShort (); + quint16 clientPort = parser.value ("client-port").toUShort (); + + new JettingProxy (controllerPort, clientPort, &app); + + return app.exec (); +} diff --git a/src/Xpl2Client.cpp b/src/Xpl2Client.cpp index 4fa502e..2a972e0 100644 --- a/src/Xpl2Client.cpp +++ b/src/Xpl2Client.cpp @@ -5,7 +5,6 @@ #include "Xpl2Client.h" #include <QDebug> -#include <QTcpSocket> bool Xpl2Client::s_wireDebug = false; @@ -158,19 +157,47 @@ Xpl2Client::s_responseTable = { Xpl2Client::Xpl2Client (QObject *parent) : QObject (parent) { - setupServer (m_commandServer, m_commandPort); - setupServer (m_imagingServer, m_imagingPort); - setupServer (m_statusServer, m_statusPort); + setupSocket (m_commandSocket); + setupSocket (m_imagingSocket); + setupSocket (m_statusSocket); + + connect (&m_retryTimer, &QTimer::timeout, this, + &Xpl2Client::retryConnection); } /* ------------------------------------------------------------------ */ /* Properties */ /* ------------------------------------------------------------------ */ -bool -Xpl2Client::isListening () const +QString +Xpl2Client::host () const +{ + return m_host; +} + +void +Xpl2Client::setHost (const QString &host) +{ + if (m_host == host) + return; + m_host = host; + emit hostChanged (); +} + +int +Xpl2Client::commandPort () const { - return m_listening; + return m_commandPort; +} + +void +Xpl2Client::setCommandPort (int port) +{ + quint16 p = static_cast<quint16> (port); + if (m_commandPort == p) + return; + m_commandPort = p; + emit commandPortChanged (); } bool @@ -214,90 +241,73 @@ Xpl2Client::enableWireDebug () /* ------------------------------------------------------------------ */ void -Xpl2Client::setupServer (QTcpServer &server, quint16 port) +Xpl2Client::setupSocket (QTcpSocket &socket) { - Q_UNUSED (port) - connect (&server, &QTcpServer::newConnection, this, - &Xpl2Client::onNewConnection); + 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); } void -Xpl2Client::startListening () +Xpl2Client::connectToProxy () { - if (m_listening) + if (m_connected) { - emit statusMessage (QStringLiteral ("Already listening")); + emit statusMessage (QStringLiteral ("Already connected")); return; } - 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…") + + quint16 imagingPort = m_commandPort + 1; + quint16 statusPort = m_commandPort + 2; + + for (auto *s : { &m_commandSocket, &m_imagingSocket, &m_statusSocket }) + if (s->state () == QAbstractSocket::UnconnectedState) + { + if (s == &m_commandSocket) + s->connectToHost (m_host, m_commandPort); + else if (s == &m_imagingSocket) + s->connectToHost (m_host, imagingPort); + else + s->connectToHost (m_host, statusPort); + } + + if (!m_retryTimer.isActive ()) + m_retryTimer.start (2000); + + emit statusMessage (QStringLiteral ("Connecting to %1:%2/%3/%4…") + .arg (m_host) .arg (m_commandPort) - .arg (m_imagingPort) - .arg (m_statusPort)); + .arg (imagingPort) + .arg (statusPort)); } void -Xpl2Client::stopListening () +Xpl2Client::disconnectFromProxy () { - m_commandServer.close (); - m_imagingServer.close (); - m_statusServer.close (); + m_retryTimer.stop (); - auto cleanup = [] (QTcpSocket *&sock) - { - if (sock) - { - sock->disconnectFromHost (); - sock->deleteLater (); - sock = nullptr; - } - }; - cleanup (m_commandSocket); - cleanup (m_imagingSocket); - cleanup (m_statusSocket); + for (auto *s : { &m_commandSocket, &m_imagingSocket, &m_statusSocket }) + if (s->state () != QAbstractSocket::UnconnectedState) + s->disconnectFromHost (); - if (m_listening) - { - m_listening = false; - emit listeningChanged (); - } updateConnectedState (); } -QTcpSocket *& -Xpl2Client::socketForServer (QTcpServer *server) -{ - if (server == &m_commandServer) - return m_commandSocket; - if (server == &m_imagingServer) - return m_imagingSocket; - return m_statusSocket; -} - void Xpl2Client::getJcVersion () { - sendCommand (m_commandSocket, "GS_JC_VERSION"); + sendCommand (&m_commandSocket, "GS_JC_VERSION"); } void Xpl2Client::getPhVersion (int printheadId) { - sendCommand (m_commandSocket, "GS_PH_VERSION", { printheadId }); + sendCommand (&m_commandSocket, "GS_PH_VERSION", { printheadId }); } /* ------------------------------------------------------------------ */ @@ -307,131 +317,132 @@ Xpl2Client::getPhVersion (int printheadId) void Xpl2Client::jettingAllOn () { - sendCommand (m_commandSocket, "CN_JETTING_ALL_ON"); + sendCommand (&m_commandSocket, "CN_JETTING_ALL_ON"); } void Xpl2Client::jettingOn (const QString &jettingMask) { - sendCommand (m_commandSocket, "CN_JETTING_ON", { jettingMask }); + sendCommand (&m_commandSocket, "CN_JETTING_ON", { jettingMask }); } void Xpl2Client::jettingOff () { - sendCommand (m_commandSocket, "CN_JETTING_OFF"); + sendCommand (&m_commandSocket, "CN_JETTING_OFF"); } void Xpl2Client::phJettingOn (int printheadId, const QString &jettingMask) { - sendCommand (m_commandSocket, "CN_PH_JETTING_ON", + sendCommand (&m_commandSocket, "CN_PH_JETTING_ON", { printheadId, jettingMask }); } void Xpl2Client::phJettingOff (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_JETTING_OFF", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_JETTING_OFF", { printheadId }); } void Xpl2Client::jcIdLedOn () { - sendCommand (m_commandSocket, "CN_JC_ID_LED_ON"); + sendCommand (&m_commandSocket, "CN_JC_ID_LED_ON"); } void Xpl2Client::jcIdLedOff () { - sendCommand (m_commandSocket, "CN_JC_ID_LED_OFF"); + sendCommand (&m_commandSocket, "CN_JC_ID_LED_OFF"); } void Xpl2Client::phIdLedOn (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_ID_LED_ON", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_ID_LED_ON", { printheadId }); } void Xpl2Client::phIdLedOff (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_ID_LED_OFF", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_ID_LED_OFF", { printheadId }); } void Xpl2Client::jcCalibration () { - sendCommand (m_commandSocket, "CN_JC_CALIBRATION"); + sendCommand (&m_commandSocket, "CN_JC_CALIBRATION"); } void Xpl2Client::phCalibration (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_CALIBRATION", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_CALIBRATION", { printheadId }); } void Xpl2Client::phCalibrationData (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_CALIBRATION_DATA", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_CALIBRATION_DATA", { printheadId }); } void Xpl2Client::phCalibrationRawData (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_CALIBRATION_RAW_DATA", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_CALIBRATION_RAW_DATA", + { printheadId }); } void Xpl2Client::phCalibratedBaseFrequency (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_CALIBRATED_BASE_FREQUENCY", + sendCommand (&m_commandSocket, "CN_PH_CALIBRATED_BASE_FREQUENCY", { printheadId }); } void Xpl2Client::jcStatusMessagingStart (int statusLevel, int sendIntervalMs) { - sendCommand (m_statusSocket, "CN_JC_STATUS_MESSAGING_START", + sendCommand (&m_statusSocket, "CN_JC_STATUS_MESSAGING_START", { statusLevel, sendIntervalMs }); } void Xpl2Client::jcStatusMessagingStop () { - sendCommand (m_statusSocket, "CN_JC_STATUS_MESSAGING_STOP"); + sendCommand (&m_statusSocket, "CN_JC_STATUS_MESSAGING_STOP"); } void Xpl2Client::phStatusMessagingStart (int statusLevel, int sendIntervalMs) { - sendCommand (m_statusSocket, "CN_PH_STATUS_MESSAGING_START", + sendCommand (&m_statusSocket, "CN_PH_STATUS_MESSAGING_START", { statusLevel, sendIntervalMs }); } void Xpl2Client::phStatusMessagingStop () { - sendCommand (m_statusSocket, "CN_PH_STATUS_MESSAGING_STOP"); + sendCommand (&m_statusSocket, "CN_PH_STATUS_MESSAGING_STOP"); } void Xpl2Client::jcResetFaultCodes () { - sendCommand (m_commandSocket, "CN_JC_RESET_FAULT_CODES"); + sendCommand (&m_commandSocket, "CN_JC_RESET_FAULT_CODES"); } void Xpl2Client::phResetFaultCodes (int printheadId) { - sendCommand (m_commandSocket, "CN_PH_RESET_FAULT_CODES", { printheadId }); + sendCommand (&m_commandSocket, "CN_PH_RESET_FAULT_CODES", { printheadId }); } void Xpl2Client::phNozzlesDisabled (int printheadId, const QString &mask) { - sendCommand (m_commandSocket, "CN_PH_NOZZLES_DISABLED", + sendCommand (&m_commandSocket, "CN_PH_NOZZLES_DISABLED", { printheadId, mask }); } @@ -442,13 +453,13 @@ Xpl2Client::phNozzlesDisabled (int printheadId, const QString &mask) void Xpl2Client::phSetId (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_SET_ID", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_SET_ID", { printheadId }); } void Xpl2Client::phDeassignId () { - sendCommand (m_commandSocket, "CF_PH_DEASSIGN_ID"); + sendCommand (&m_commandSocket, "CF_PH_DEASSIGN_ID"); } void @@ -456,7 +467,7 @@ Xpl2Client::jcSetJettingParams (int dutyCycle, int pwmFreq, int drive, int nozzleDriveFreq, int nozzleDriveDutyCycle) { sendCommand ( - m_commandSocket, "CF_JC_SET_JETTING_PARAMS", + &m_commandSocket, "CF_JC_SET_JETTING_PARAMS", { dutyCycle, pwmFreq, drive, nozzleDriveFreq, nozzleDriveDutyCycle }); } @@ -465,7 +476,7 @@ Xpl2Client::phSetJettingParams (int printheadId, int dutyCycle, int pwmFreq, int drive, int nozzleDriveFreq, int nozzleDriveDutyCycle) { - sendCommand (m_commandSocket, "CF_PH_SET_JETTING_PARAMS", + sendCommand (&m_commandSocket, "CF_PH_SET_JETTING_PARAMS", { printheadId, dutyCycle, pwmFreq, drive, nozzleDriveFreq, nozzleDriveDutyCycle }); } @@ -473,50 +484,50 @@ Xpl2Client::phSetJettingParams (int printheadId, int dutyCycle, int pwmFreq, void Xpl2Client::phGetJettingParams (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_GET_JETTING_PARAMS", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_GET_JETTING_PARAMS", { printheadId }); } void Xpl2Client::jcSaveCalibration () { - sendCommand (m_commandSocket, "CF_JC_SAVE_CALIBRATION"); + sendCommand (&m_commandSocket, "CF_JC_SAVE_CALIBRATION"); } void Xpl2Client::phSaveCalibration (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_SAVE_CALIBRATION", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_SAVE_CALIBRATION", { printheadId }); } void Xpl2Client::jcResetCalibration () { - sendCommand (m_commandSocket, "CF_JC_RESET_CALIBRATION"); + sendCommand (&m_commandSocket, "CF_JC_RESET_CALIBRATION"); } void Xpl2Client::phResetCalibration (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_RESET_CALIBRATION", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_RESET_CALIBRATION", { printheadId }); } void Xpl2Client::jcSetPurgeSettings (int purgeIntervalMs, int purgeTimeMs) { - sendCommand (m_commandSocket, "CF_JC_SET_PURGE_SETTINGS", + sendCommand (&m_commandSocket, "CF_JC_SET_PURGE_SETTINGS", { purgeIntervalMs, purgeTimeMs }); } void Xpl2Client::jcSwitchOffPurge () { - sendCommand (m_commandSocket, "CF_JC_SWITCH_OFF_PURGE"); + sendCommand (&m_commandSocket, "CF_JC_SWITCH_OFF_PURGE"); } void Xpl2Client::jcSetter (bool saveNewValue, int setterId, const QString &newValue) { - sendCommand (m_commandSocket, "CF_JC_SETTER", + sendCommand (&m_commandSocket, "CF_JC_SETTER", { saveNewValue, setterId, newValue }); } @@ -524,75 +535,75 @@ void Xpl2Client::phSetter (int printheadId, bool saveNewValue, int setterId, const QString &newValue) { - sendCommand (m_commandSocket, "CF_PH_SETTER", + sendCommand (&m_commandSocket, "CF_PH_SETTER", { printheadId, saveNewValue, setterId, newValue }); } void Xpl2Client::jcGetter (bool getSavedValue, int getterId) { - sendCommand (m_commandSocket, "CF_JC_GETTER", { getSavedValue, getterId }); + sendCommand (&m_commandSocket, "CF_JC_GETTER", { getSavedValue, getterId }); } void Xpl2Client::phGetter (int printheadId, bool getSavedValue, int getterId) { - sendCommand (m_commandSocket, "CF_PH_GETTER", + sendCommand (&m_commandSocket, "CF_PH_GETTER", { printheadId, getSavedValue, getterId }); } void Xpl2Client::jcResetSettingsAllPrintheads () { - sendCommand (m_commandSocket, "CF_JC_RESET_SETTINGS_ALL_PRINTHEADS"); + sendCommand (&m_commandSocket, "CF_JC_RESET_SETTINGS_ALL_PRINTHEADS"); } void Xpl2Client::phResetAllSettings (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_RESET_ALL_SETTINGS", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_RESET_ALL_SETTINGS", { printheadId }); } void Xpl2Client::jcRebootAllPrintheads () { - sendCommand (m_commandSocket, "CF_JC_REBOOT_ALL_PRINTHEADS"); + sendCommand (&m_commandSocket, "CF_JC_REBOOT_ALL_PRINTHEADS"); } void Xpl2Client::phReboot (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_REBOOT", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_REBOOT", { printheadId }); } void Xpl2Client::jcResetControllerSoftware () { - sendCommand (m_commandSocket, "CF_JC_RESET_CONTROLLER_SOFTWARE"); + sendCommand (&m_commandSocket, "CF_JC_RESET_CONTROLLER_SOFTWARE"); } void Xpl2Client::jcRestart () { - sendCommand (m_commandSocket, "CF_JC_RESTART"); + sendCommand (&m_commandSocket, "CF_JC_RESTART"); } void Xpl2Client::jcShutdown () { - sendCommand (m_commandSocket, "CF_JC_SHUTDOWN"); + sendCommand (&m_commandSocket, "CF_JC_SHUTDOWN"); } void Xpl2Client::jcSaveAllPrintheadSettings () { - sendCommand (m_commandSocket, "CF_JC_SAVE_ALL_PRINTHEAD_SETTINGS"); + sendCommand (&m_commandSocket, "CF_JC_SAVE_ALL_PRINTHEAD_SETTINGS"); } void Xpl2Client::phSaveSettings (int printheadId) { - sendCommand (m_commandSocket, "CF_PH_SAVE_SETTINGS", { printheadId }); + sendCommand (&m_commandSocket, "CF_PH_SAVE_SETTINGS", { printheadId }); } /* ------------------------------------------------------------------ */ @@ -602,43 +613,43 @@ Xpl2Client::phSaveSettings (int printheadId) void Xpl2Client::imagingStart (double speed) { - sendCommand (m_imagingSocket, "m2", { speed }); + sendCommand (&m_imagingSocket, "m2", { speed }); } void Xpl2Client::imagingStop () { - sendCommand (m_imagingSocket, "m4"); + sendCommand (&m_imagingSocket, "m4"); } void Xpl2Client::imageMaskStart (const QString &mask) { - sendCommand (m_imagingSocket, "m0", { mask }); + sendCommand (&m_imagingSocket, "m0", { mask }); } void Xpl2Client::imageMaskEnd (const QString &tail) { - sendCommand (m_imagingSocket, "m1", { tail }); + sendCommand (&m_imagingSocket, "m1", { tail }); } void Xpl2Client::dutyCycleMaskStart (const QString &mask) { - sendCommand (m_imagingSocket, "m5", { mask }); + sendCommand (&m_imagingSocket, "m5", { mask }); } void Xpl2Client::dutyCycleMaskEnd (const QString &tail) { - sendCommand (m_imagingSocket, "m6", { tail }); + sendCommand (&m_imagingSocket, "m6", { tail }); } void Xpl2Client::imageCount () { - sendCommand (m_imagingSocket, "m3"); + sendCommand (&m_imagingSocket, "m3"); } /* ------------------------------------------------------------------ */ @@ -669,15 +680,15 @@ void Xpl2Client::dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") - handleKaPing (m_commandSocket); + handleKaPing (&m_commandSocket); else if (msg.command == "EV_SHUTTING_DOWN") emit shuttingDown (); else if (msg.command == "GS_JC_VERSION") 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 ()); } @@ -685,7 +696,7 @@ void Xpl2Client::dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") - handleKaPing (m_imagingSocket); + handleKaPing (&m_imagingSocket); else if (msg.command == "EV_SHUTTING_DOWN") emit shuttingDown (); else if (msg.command == "n") @@ -693,7 +704,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); @@ -701,13 +712,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 ()); } @@ -715,15 +726,15 @@ void Xpl2Client::dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") - handleKaPing (m_statusSocket); + handleKaPing (&m_statusSocket); else if (msg.command == "EV_SHUTTING_DOWN") emit shuttingDown (); else if (msg.command == "EV_STATUS_MSG_JC") 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 ()); } @@ -746,7 +757,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 ( @@ -776,7 +787,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 ( @@ -942,7 +953,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") @@ -956,7 +967,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") @@ -974,12 +985,9 @@ void Xpl2Client::updateConnectedState () { bool allConnected - = m_commandSocket - && m_commandSocket->state () == QAbstractSocket::ConnectedState - && m_imagingSocket - && m_imagingSocket->state () == QAbstractSocket::ConnectedState - && m_statusSocket - && m_statusSocket->state () == QAbstractSocket::ConnectedState; + = m_commandSocket.state () == QAbstractSocket::ConnectedState + && m_imagingSocket.state () == QAbstractSocket::ConnectedState + && m_statusSocket.state () == QAbstractSocket::ConnectedState; if (m_connected == allConnected) return; m_connected = allConnected; @@ -991,20 +999,20 @@ 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; + port = m_commandPort + 1; } - else if (socket == m_statusSocket) + else if (socket == &m_statusSocket) { name = "Status"; - port = m_statusPort; + port = m_commandPort + 2; } return QStringLiteral ("[%1:%2]").arg (name).arg (port).leftJustified (15); @@ -1015,47 +1023,23 @@ Xpl2Client::logTag (const QTcpSocket *socket) const /* ------------------------------------------------------------------ */ void -Xpl2Client::onNewConnection () +Xpl2Client::onSocketConnected () { - 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 (); - } + auto *socket = qobject_cast<QTcpSocket *> (sender ()); + qInfo ("%s Connected to proxy", qPrintable (logTag (socket))); + updateConnectedState (); + if (m_connected) + m_retryTimer.stop (); } void Xpl2Client::onSocketDisconnected () { auto *socket = qobject_cast<QTcpSocket *> (sender ()); - 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 (); + qInfo ("%s Disconnected from proxy", qPrintable (logTag (socket))); updateConnectedState (); + if (!m_retryTimer.isActive ()) + m_retryTimer.start (2000); } void @@ -1074,9 +1058,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); @@ -1093,3 +1077,14 @@ Xpl2Client::onSocketError (QAbstractSocket::SocketError error) .arg (logTag (socket)) .arg (socket->errorString ())); } + +void +Xpl2Client::retryConnection () +{ + if (m_connected) + { + m_retryTimer.stop (); + return; + } + connectToProxy (); +} diff --git a/src/Xpl2Client.h b/src/Xpl2Client.h index 44edb65..ef29ddb 100644 --- a/src/Xpl2Client.h +++ b/src/Xpl2Client.h @@ -11,19 +11,20 @@ #include <QHash> #include <QObject> #include <QQmlEngine> -#include <QTcpServer> +#include <QTcpSocket> +#include <QTimer> #include <functional> -class QTcpSocket; - class Xpl2Client : public QObject { Q_OBJECT QML_ELEMENT QML_SINGLETON - Q_PROPERTY (bool listening READ isListening NOTIFY listeningChanged) + Q_PROPERTY (QString host READ host WRITE setHost NOTIFY hostChanged) + Q_PROPERTY (int commandPort READ commandPort WRITE setCommandPort NOTIFY + commandPortChanged) Q_PROPERTY (bool connected READ isConnected NOTIFY connectedChanged) Q_PROPERTY (int controllerId READ controllerId NOTIFY jcVersionReceived) Q_PROPERTY ( @@ -35,7 +36,10 @@ class Xpl2Client : public QObject public: explicit Xpl2Client (QObject *parent = nullptr); - bool isListening () const; + QString host () const; + void setHost (const QString &host); + int commandPort () const; + void setCommandPort (int port); bool isConnected () const; int controllerId () const; @@ -45,8 +49,10 @@ public: static void enableWireDebug (); - Q_INVOKABLE void startListening (); - Q_INVOKABLE void stopListening (); + /** Connect to the proxy on host:commandPort (+1, +2). */ + Q_INVOKABLE void connectToProxy (); + /** Disconnect from the proxy. */ + Q_INVOKABLE void disconnectFromProxy (); /** Get the just connected Jetting Controller ID and Software Version. */ Q_INVOKABLE void getJcVersion (); /** Query the specified printhead's version info. */ @@ -177,7 +183,8 @@ public: Q_INVOKABLE void imageCount (); signals: - void listeningChanged (); + void hostChanged (); + void commandPortChanged (); void connectedChanged (); void errorOccurred (const QString &error); void statusMessage (const QString &message); @@ -286,14 +293,14 @@ signals: void imagingStopResult (bool success); private slots: - void onNewConnection (); + void onSocketConnected (); void onSocketDisconnected (); void onSocketMessageReady (); void onSocketError (QAbstractSocket::SocketError error); + void retryConnection (); private: - void setupServer (QTcpServer &server, quint16 port); - QTcpSocket *&socketForServer (QTcpServer *server); + void setupSocket (QTcpSocket &socket); void sendCommand (QTcpSocket *socket, const QByteArray &command, const QVariantList ¶ms = {}); void dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg); @@ -337,16 +344,12 @@ private: void updateConnectedState (); QString logTag (const QTcpSocket *socket) const; - 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; + QString m_host = QStringLiteral ("127.0.0.1"); + quint16 m_commandPort = 9210; + QTcpSocket m_commandSocket; + QTcpSocket m_imagingSocket; + QTcpSocket m_statusSocket; + QTimer m_retryTimer; bool m_connected = false; static bool s_wireDebug; int m_controllerId = 0; |
