aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md167
1 files changed, 167 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f82cf68
--- /dev/null
+++ b/README.md
@@ -0,0 +1,167 @@
+# QtXpl2
+
+Qt 6 static library and QML module for communicating with **Alchemie Jetting Interface 2** (JI2) controllers over TCP. Implements the full XPL2 printhead remote protocol (56 command tokens) with a typed, signal-based API designed for Qt Quick applications.
+
+## Requirements
+
+- Qt 6.10.2 (Core, Network, Qml, Quick)
+- CMake 3.16+
+- Ninja (recommended)
+
+## Build
+
+```bash
+qt-cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
+cmake --build build --parallel
+```
+
+Produces three targets:
+
+| Target | Binary | Description |
+|---|---|---|
+| `QtXpl2` | `libQtXpl2.a` | Static library + QML module (`import Xpl2`) |
+| `QtXpl2Demo` | `bin/QtXpl2Demo` | Interactive demo app |
+| `Xpl2MockServer` | `bin/Xpl2MockServer` | Mock JI2 server for development |
+
+## Quick start
+
+```bash
+# Terminal 1 — start the mock server
+./build/bin/Xpl2MockServer
+
+# Terminal 2 — run the demo
+./build/bin/QtXpl2Demo
+```
+
+The demo connects to `127.0.0.1` by default. Click **Connect**, then explore the **Commands** and **Status** tabs.
+
+### 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 |
+
+## Architecture
+
+### Three-port TCP client
+
+The JI2 protocol uses three independent TCP sockets:
+
+| 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 |
+
+`Xpl2Client` manages all three connections as a QML singleton.
+
+### Protocol implementation
+
+All 56 protocol tokens are implemented across 6 categories:
+
+| Category | Count | Direction | Examples |
+|---|---|---|---|
+| GS_ (Status) | 2 | Request/reply | `getJcVersion()`, `getPhVersion()` |
+| CN_ (Control) | 17 | Request/reply | `jettingAllOn()`, `jcCalibration()`, `phIdLedOn()` |
+| CF_ (Config) | 20 | Request/reply | `jcSetter()`, `phGetJettingParams()`, `jcShutdown()` |
+| Imaging (m/n) | 8 | Request/reply | `imagingStart()`, `imageMaskEnd()`, `imageCount()` |
+| KA_ (Keepalive) | 1 | Automatic | Invisible to QML, handled internally |
+| EV_ (Events) | 8 | Server-push | `jcStatusReceived()`, `phErrorCode()`, `shuttingDown()` |
+
+### Data-driven dispatch
+
+Response handling uses a static dispatch table (`QHash<QByteArray, ResponseEntry>`) mapping each command token to its response shape, minimum parameter count, and signal emitter. Adding a new command is one table entry:
+
+```cpp
+{ "CN_JETTING_ALL_ON", { ResponseShape::JcSuccess, 2,
+ [](auto *s, const auto &p) {
+ emit s->jettingAllOnResult(p[0].toInt(), p[1].toInt() == 1);
+ } } },
+```
+
+## QML usage
+
+```qml
+import Xpl2
+
+// Xpl2Client is a singleton — no instantiation needed
+Button {
+ text: "Connect"
+ onClicked: {
+ Xpl2Client.host = "192.168.1.100"
+ Xpl2Client.connectToServer()
+ }
+}
+
+// Bind to properties
+Label {
+ text: "FW: " + Xpl2Client.firmwareVersion
+}
+
+// React to signals
+Connections {
+ target: Xpl2Client
+
+ function onJcStatusReceived(status) {
+ // status is an Xpl2JcStatus Q_GADGET
+ tempLabel.text = status.temperature.toFixed(1) + "°C"
+ cpuLabel.text = status.cpuPercentageBusy.toFixed(1) + "%"
+ }
+
+ function onPhErrorCode(controllerId, printheadId, errorCode, params) {
+ console.log("PH", printheadId, "error:", errorCode)
+ }
+}
+```
+
+### Status gadgets
+
+Periodic status messages are parsed into structured types with named properties:
+
+- **`Xpl2JcStatus`** — 21 fields (7 at level 1, 14 more at level 2): CPU%, voltages, temperature, humidity, indicators, firmware versions
+- **`Xpl2PhStatus`** — 59 fields (28 at level 1, 31 more at level 2): temperatures, voltages, trip flags, jetting params, gyro/accelerometer, purge state
+
+### Extended result enums
+
+Configuration commands that can fail on specific parameters return typed enums visible in QML:
+
+- **`JettingParamsResult`** — `Ok` (1), `Failed` (0), `DutyCycle` (-1) through `NozzleDriveDutyCycle` (-5)
+- **`SetterResult`** — `Ok` (1), `Failed` (0), `IncorrectNewValue` (-1)
+
+## Project structure
+
+```
+QtXpl2/
+ src/
+ Xpl2Client.h/cpp # QML singleton — typed API, dispatch table
+ Xpl2Protocol.h/cpp # Wire framing, enums (Q_NAMESPACE)
+ Xpl2JcStatus.h/cpp # Q_GADGET for JC status (Appendix A)
+ Xpl2PhStatus.h/cpp # Q_GADGET for PH status (Appendix B)
+ CMakeLists.txt
+ demo/
+ Main.qml # App shell: connection, tabs, console
+ CommandsPage.qml # GS/CN/CF/imaging buttons, printhead list
+ StatusPage.qml # Status messaging controls, live displays
+ DebugConsole.qml # Dark log panel
+ main.cpp
+ CMakeLists.txt
+ mock-server/
+ MockServer.h/cpp # Canned responses, periodic status emission
+ main.cpp
+ CMakeLists.txt
+ docs/
+ protocol.pdf # Alchemie JI2-JC protocol specification
+```
+
+## 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.
+
+## Conventions
+
+- C++17, GNU code style (`.clang-format`)
+- QML: `.qmlformat.ini`, `pragma ComponentBehavior: Bound`, no unqualified access
+- Doc-comments on declarations (headers), not definitions (.cpp)
+- No external dependencies beyond Qt 6.10.2
+- Prefer named functions/slots over lambdas