From 9ac64169720fb2b9852589b74f7300bcfebcaf62 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Mon, 16 Mar 2026 11:04:21 +0100 Subject: GS_PH_VERSION command, per-printhead demo UI with --printheads CLI arg Add getPhVersion(printheadId) to Xpl2Client with phVersionReceived signal carrying all 8 response fields. Mock server echoes canned version data for any requested printhead ID. Demo app accepts --printheads N (default 10) to simulate N printheads. The UI shows a scrollable per-PH list with individual and bulk version query buttons, updating each row's version info on response. --- demo/Main.qml | 132 +++++++++++++++++++++++++++++++++++++-------- demo/main.cpp | 6 +++ mock-server/MockServer.cpp | 13 +++++ mock-server/MockServer.h | 1 + src/Xpl2Client.cpp | 42 +++++++++++++++ src/Xpl2Client.h | 10 ++++ 6 files changed, 183 insertions(+), 21 deletions(-) diff --git a/demo/Main.qml b/demo/Main.qml index db015e3..07e42b1 100644 --- a/demo/Main.qml +++ b/demo/Main.qml @@ -8,10 +8,26 @@ import Xpl2 ApplicationWindow { id: root - height: 600 + // Set from C++ via setInitialProperties (--printheads CLI arg, default 10). + required property int demoPhCount + + height: 700 title: "XPL2 Demo" visible: true - width: 800 + width: 900 + + Component.onCompleted: { + for (let i = 1; i <= root.demoPhCount; ++i) + phModel.append({ + "phId": i, + "versionInfo": "" + }); + } + + ListModel { + id: phModel + + } Connections { function onConnectedChanged() { @@ -23,6 +39,28 @@ ApplicationWindow { debugConsole.appendLog("ERROR: " + error); } + function onPhVersionReceived(controllerId: int, printheadId: int, + mcuFirmwareVersion: string, + mcuHardwareVersion: string, + mcuFirmwareVariant: string, + fpgaFirmwareVersion: string, + fpgaHardwareVersion: string, + bootloaderVersion: string) { + for (let i = 0; i < phModel.count; ++i) { + if (phModel.get(i).phId === printheadId) { + phModel.setProperty(i, "versionInfo", + "MCU %1/%2 (%3) | FPGA %4/%5 | Boot %6".arg( + mcuFirmwareVersion).arg( + mcuHardwareVersion).arg( + mcuFirmwareVariant).arg( + fpgaFirmwareVersion).arg( + fpgaHardwareVersion).arg( + bootloaderVersion)); + break; + } + } + } + function onStatusMessage(message: string) { debugConsole.appendLog(message); } @@ -74,38 +112,90 @@ ApplicationWindow { } } - // --- Protocol Commands --- + // --- JC Version --- GroupBox { Layout.fillWidth: true enabled: Xpl2Client.connected - title: "Protocol Commands" + title: "Jetting Controller" - ColumnLayout { + RowLayout { anchors.fill: parent - RowLayout { - Button { - text: "Get JC Version" + Button { + text: "Get JC Version" - onClicked: Xpl2Client.getJcVersion() - } + onClicked: Xpl2Client.getJcVersion() + } - Label { - text: Xpl2Client.controllerId > 0 - ? "Controller: %1 | FW: %2 | HW: %3 | PHs: %4".arg( - Xpl2Client.controllerId).arg( - Xpl2Client.firmwareVersion).arg( - Xpl2Client.hardwareVersion).arg( - Xpl2Client.printheadCount) : - "No version data" - } + Label { + text: Xpl2Client.controllerId > 0 + ? "Controller: %1 | FW: %2 | HW: %3 | PHs: %4".arg( + Xpl2Client.controllerId).arg( + Xpl2Client.firmwareVersion).arg( + Xpl2Client.hardwareVersion).arg( + Xpl2Client.printheadCount) : + "No version data" } } } - // --- Spacer --- - Item { + // --- Printheads --- + GroupBox { Layout.fillHeight: true + Layout.fillWidth: true + enabled: Xpl2Client.connected + title: "Printheads (%1)".arg(root.demoPhCount) + + ColumnLayout { + anchors.fill: parent + + Button { + text: "Get All PH Versions" + + onClicked: { + for (let i = 0; i < phModel.count; ++i) + Xpl2Client.getPhVersion(phModel.get(i).phId); + } + } + + ScrollView { + Layout.fillHeight: true + Layout.fillWidth: true + + ListView { + model: phModel + spacing: 4 + + delegate: RowLayout { + id: phDelegate + + required property int phId + required property string versionInfo + + width: ListView.view.width + + Label { + Layout.preferredWidth: 50 + font.bold: true + text: "PH %1".arg(phDelegate.phId) + } + + Button { + text: "Version" + + onClicked: Xpl2Client.getPhVersion( + phDelegate.phId) + } + + Label { + Layout.fillWidth: true + elide: Text.ElideRight + text: phDelegate.versionInfo || "—" + } + } + } + } + } } } diff --git a/demo/main.cpp b/demo/main.cpp index 3100254..e0651d9 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -20,13 +20,19 @@ main (int argc, char *argv[]) QCommandLineParser parser; parser.addOption ({ "wire-debug", "Log raw wire TX/RX to dev log" }); + parser.addOption ({ "printheads", + "Number of simulated printheads (default 10)", "N", + "10" }); parser.addHelpOption (); parser.process (app); if (parser.isSet ("wire-debug")) Xpl2Client::enableWireDebug (); + int phCount = qBound (1, parser.value ("printheads").toInt (), 99); + QQmlApplicationEngine engine; + engine.setInitialProperties ({ { "demoPhCount", phCount } }); QObject::connect ( &engine, &QQmlApplicationEngine::objectCreationFailed, &app, [] () { QCoreApplication::exit (1); }, Qt::QueuedConnection); diff --git a/mock-server/MockServer.cpp b/mock-server/MockServer.cpp index d58e58e..981bd1c 100644 --- a/mock-server/MockServer.cpp +++ b/mock-server/MockServer.cpp @@ -119,6 +119,8 @@ MockServer::handleCommand (QTcpSocket *client, const QByteArray &line) handleKaPing (client, params); else if (cmd == "GS_JC_VERSION") handleGsJcVersion (client); + else if (cmd == "GS_PH_VERSION") + handleGsPhVersion (client, params); else qWarning ("%s Unknown command: %s", qPrintable (logTag (client->localPort ())), cmd.constData ()); @@ -154,3 +156,14 @@ MockServer::handleGsJcVersion (QTcpSocket *client) qDebug ("%s RX GS_JC_VERSION", qPrintable (logTag (client->localPort ()))); sendReply (client, "GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n"); } + +void +MockServer::handleGsPhVersion (QTcpSocket *client, const QByteArray ¶ms) +{ + qDebug ("%s RX GS_PH_VERSION,%s", qPrintable (logTag (client->localPort ())), + params.constData ()); + /* Echo back canned version info for whatever printhead ID was requested. */ + sendReply (client, QByteArray ("GS_PH_VERSION,1,") + params + + ",\"3.10\",\"1.00\",\"Standard\"," + "\"2.05\",\"1.02\",\"0.9.1\"\n"); +} diff --git a/mock-server/MockServer.h b/mock-server/MockServer.h index 13bb2cd..ce76889 100644 --- a/mock-server/MockServer.h +++ b/mock-server/MockServer.h @@ -42,6 +42,7 @@ private: void sendReply (QTcpSocket *client, const QByteArray &data); void handleKaPing (QTcpSocket *client, const QByteArray ¶ms); void handleGsJcVersion (QTcpSocket *client); + void handleGsPhVersion (QTcpSocket *client, const QByteArray ¶ms); Port m_command; Port m_imaging; diff --git a/src/Xpl2Client.cpp b/src/Xpl2Client.cpp index ba6a1ec..54346e0 100644 --- a/src/Xpl2Client.cpp +++ b/src/Xpl2Client.cpp @@ -110,6 +110,12 @@ Xpl2Client::getJcVersion () sendCommand (m_commandSocket, "GS_JC_VERSION"); } +void +Xpl2Client::getPhVersion (int printheadId) +{ + sendCommand (m_commandSocket, "GS_PH_VERSION", { printheadId }); +} + /* ------------------------------------------------------------------ */ /* Send / dispatch */ /* ------------------------------------------------------------------ */ @@ -141,6 +147,8 @@ Xpl2Client::dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg) handleKaPing (m_commandSocket); else if (msg.command == "GS_JC_VERSION") handleGsJcVersion (msg.params); + else if (msg.command == "GS_PH_VERSION") + handleGsPhVersion (msg.params); else qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_commandSocket)), msg.command.constData ()); @@ -197,6 +205,40 @@ Xpl2Client::handleGsJcVersion (const QVariantList ¶ms) emit jcVersionReceived (); } +void +Xpl2Client::handleGsPhVersion (const QVariantList ¶ms) +{ + if (params.size () < 8) + { + qWarning () << "GS_PH_VERSION: expected 8 params, got" << params.size (); + return; + } + int cid = params[0].toInt (); + int phId = params[1].toInt (); + QString mcuFw = params[2].toString (); + QString mcuHw = params[3].toString (); + QString mcuFwVar = params[4].toString (); + QString fpgaFw = params[5].toString (); + QString fpgaHw = params[6].toString (); + 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 (mcuHw), qPrintable (mcuFwVar), qPrintable (fpgaFw), + qPrintable (fpgaHw), qPrintable (bootVer)); + emit statusMessage ( + QStringLiteral ( + "RX PH[%1] Version: mcuFw=%2 mcuHw=%3 fpgaFw=%4 fpgaHw=%5 boot=%6") + .arg (phId) + .arg (mcuFw) + .arg (mcuHw) + .arg (fpgaFw) + .arg (fpgaHw) + .arg (bootVer)); + emit phVersionReceived (cid, phId, mcuFw, mcuHw, mcuFwVar, fpgaFw, fpgaHw, + bootVer); +} + /* ------------------------------------------------------------------ */ /* Internal */ /* ------------------------------------------------------------------ */ diff --git a/src/Xpl2Client.h b/src/Xpl2Client.h index 41729fd..95ae03e 100644 --- a/src/Xpl2Client.h +++ b/src/Xpl2Client.h @@ -43,6 +43,8 @@ public: Q_INVOKABLE void connectToServer (); Q_INVOKABLE void disconnectFromServer (); Q_INVOKABLE void getJcVersion (); + /** Query the specified printhead's version info. */ + Q_INVOKABLE void getPhVersion (int printheadId); signals: void hostChanged (); @@ -50,6 +52,13 @@ signals: void errorOccurred (const QString &error); void statusMessage (const QString &message); void jcVersionReceived (); + void phVersionReceived (int controllerId, int printheadId, + const QString &mcuFirmwareVersion, + const QString &mcuHardwareVersion, + const QString &mcuFirmwareVariant, + const QString &fpgaFirmwareVersion, + const QString &fpgaHardwareVersion, + const QString &bootloaderVersion); private slots: void onSocketConnected (); @@ -65,6 +74,7 @@ private: void dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg); void handleKaPing (QTcpSocket &socket); void handleGsJcVersion (const QVariantList ¶ms); + void handleGsPhVersion (const QVariantList ¶ms); void updateConnectedState (); QString logTag (const QTcpSocket *socket) const; -- cgit v1.2.3