From e9d8a8b052150f42ea00da2c07e3f78a9b7d2061 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Tue, 24 Mar 2026 17:25:03 +0100 Subject: Add JettingProxy relay, convert Xpl2Client to active connection model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(). --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 18 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index d0f089c..518acbc 100644 --- a/README.md +++ b/README.md @@ -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 -- cgit v1.2.3