From fa095de6cc8ec5a5b5c14091f2b3179ad7071876 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Mon, 16 Mar 2026 16:19:49 +0100 Subject: EV_ events with Q_GADGET status types, mock periodic emission, tabbed demo UI --- demo/CMakeLists.txt | 3 + demo/CommandsPage.qml | 183 +++++++++++++++ demo/DebugConsole.qml | 34 +++ demo/Main.qml | 270 +++++----------------- demo/StatusPage.qml | 620 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 900 insertions(+), 210 deletions(-) create mode 100644 demo/CommandsPage.qml create mode 100644 demo/DebugConsole.qml create mode 100644 demo/StatusPage.qml (limited to 'demo') diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index bdee67f..aa8fd49 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -8,6 +8,9 @@ qt_add_qml_module( 1.0 QML_FILES Main.qml + CommandsPage.qml + StatusPage.qml + DebugConsole.qml # IMPORT_PATH sets QT_QML_IMPORT_PATH on the target, which qmlls reads to # resolve imports like "import Xpl2" in .qmlls.ini. IMPORT_PATH diff --git a/demo/CommandsPage.qml b/demo/CommandsPage.qml new file mode 100644 index 0000000..2e4437b --- /dev/null +++ b/demo/CommandsPage.qml @@ -0,0 +1,183 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Xpl2 + +ColumnLayout { + id: commandsPage + + required property int demoPhCount + required property ListModel phModel + + spacing: 12 + + // --- JC Version --- + GroupBox { + Layout.fillWidth: true + enabled: Xpl2Client.connected + title: "Jetting Controller" + + RowLayout { + anchors.fill: parent + + Button { + text: "Get JC Version" + + 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" + } + } + } + + // --- Control --- + GroupBox { + Layout.fillWidth: true + enabled: Xpl2Client.connected + title: "Control" + + GridLayout { + anchors.fill: parent + columns: 4 + + Button { + text: "Jetting All On" + + onClicked: Xpl2Client.jettingAllOn() + } + + Button { + text: "Jetting Off" + + onClicked: Xpl2Client.jettingOff() + } + + Button { + text: "JC LED On" + + onClicked: Xpl2Client.jcIdLedOn() + } + + Button { + text: "JC LED Off" + + onClicked: Xpl2Client.jcIdLedOff() + } + + Button { + text: "JC Calibration" + + onClicked: Xpl2Client.jcCalibration() + } + + Button { + text: "Reset Fault Codes" + + onClicked: Xpl2Client.jcResetFaultCodes() + } + } + } + + // --- Configuration --- + GroupBox { + Layout.fillWidth: true + enabled: Xpl2Client.connected + title: "Configuration" + + GridLayout { + anchors.fill: parent + columns: 4 + + Button { + text: "Save All Settings" + + onClicked: Xpl2Client.jcSaveAllPrintheadSettings() + } + + Button { + text: "Reboot All PHs" + + onClicked: Xpl2Client.jcRebootAllPrintheads() + } + + Button { + text: "Restart JC" + + onClicked: Xpl2Client.jcRestart() + } + + Button { + text: "Shutdown JC" + + onClicked: Xpl2Client.jcShutdown() + } + } + } + + // --- Printheads --- + GroupBox { + Layout.fillHeight: true + Layout.fillWidth: true + enabled: Xpl2Client.connected + title: "Printheads (%1)".arg(commandsPage.demoPhCount) + + ColumnLayout { + anchors.fill: parent + + Button { + text: "Get All PH Versions" + + onClicked: { + for (let i = 0; i < commandsPage.phModel.count; ++i) + Xpl2Client.getPhVersion(commandsPage.phModel.get(i).phId); + } + } + + ScrollView { + Layout.fillHeight: true + Layout.fillWidth: true + + ListView { + model: commandsPage.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/DebugConsole.qml b/demo/DebugConsole.qml new file mode 100644 index 0000000..9a5a9ff --- /dev/null +++ b/demo/DebugConsole.qml @@ -0,0 +1,34 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls + +Rectangle { + id: debugRoot + + function appendLog(msg: string): void { + let ts = new Date().toLocaleTimeString(Qt.locale(), "HH:mm:ss.zzz"); + debugLog.text += "[" + ts + "] " + msg + "\n"; + debugLog.cursorPosition = debugLog.text.length; + } + + border.color: "#444" + color: "#1e1e1e" + radius: 4 + + ScrollView { + anchors.fill: parent + anchors.margins: 4 + + TextArea { + id: debugLog + + background: null + color: "#cccccc" + font.family: "monospace" + font.pointSize: 9 + readOnly: true + wrapMode: TextEdit.Wrap + } + } +} diff --git a/demo/Main.qml b/demo/Main.qml index 394d1ba..9b9223f 100644 --- a/demo/Main.qml +++ b/demo/Main.qml @@ -39,6 +39,14 @@ ApplicationWindow { debugConsole.appendLog("ERROR: " + error); } + function onJcStatusReceived(status) { + statusPage.lastJcStatus = status; + } + + function onPhStatusReceived(status) { + statusPage.lastPhStatus = status; + } + function onPhVersionReceived(controllerId: int, printheadId: int, mcuFirmwareVersion: string, mcuHardwareVersion: string, @@ -61,6 +69,10 @@ ApplicationWindow { } } + function onShuttingDown() { + debugConsole.appendLog("SERVER SHUTTING DOWN"); + } + function onStatusMessage(message: string) { debugConsole.appendLog(message); } @@ -72,249 +84,87 @@ ApplicationWindow { anchors.fill: parent spacing: 0 - ColumnLayout { - Layout.fillHeight: true + // --- Connection (always visible) --- + GroupBox { + Layout.bottomMargin: 0 Layout.fillWidth: true Layout.margins: 16 - spacing: 12 - - // --- Connection --- - GroupBox { - Layout.fillWidth: true - title: "Connection" - - RowLayout { - anchors.fill: parent - - Label { - text: "Host:" - } - - TextField { - id: hostField - - Layout.fillWidth: true - text: Xpl2Client.host - - onEditingFinished: Xpl2Client.host = text - } + title: "Connection" - Button { - text: Xpl2Client.connected ? "Disconnect" : "Connect" + RowLayout { + anchors.fill: parent - onClicked: { - if (Xpl2Client.connected) - Xpl2Client.disconnectFromServer(); - else - Xpl2Client.connectToServer(); - } - } + Label { + text: "Host:" } - } - // --- JC Version --- - GroupBox { - Layout.fillWidth: true - enabled: Xpl2Client.connected - title: "Jetting Controller" + TextField { + id: hostField - RowLayout { - anchors.fill: parent + Layout.fillWidth: true + text: Xpl2Client.host - Button { - text: "Get JC Version" - - 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" - } + onEditingFinished: Xpl2Client.host = text } - } - - // --- Control --- - GroupBox { - Layout.fillWidth: true - enabled: Xpl2Client.connected - title: "Control" - - GridLayout { - anchors.fill: parent - columns: 4 - Button { - text: "Jetting All On" + Button { + text: Xpl2Client.connected ? "Disconnect" : "Connect" - onClicked: Xpl2Client.jettingAllOn() - } - - Button { - text: "Jetting Off" - - onClicked: Xpl2Client.jettingOff() - } - - Button { - text: "JC LED On" - - onClicked: Xpl2Client.jcIdLedOn() - } - - Button { - text: "JC LED Off" - - onClicked: Xpl2Client.jcIdLedOff() - } - - Button { - text: "JC Calibration" - - onClicked: Xpl2Client.jcCalibration() - } - - Button { - text: "Reset Fault Codes" - - onClicked: Xpl2Client.jcResetFaultCodes() + onClicked: { + if (Xpl2Client.connected) + Xpl2Client.disconnectFromServer(); + else + Xpl2Client.connectToServer(); } } } + } - // --- Configuration --- - GroupBox { - Layout.fillWidth: true - enabled: Xpl2Client.connected - title: "Configuration" - - GridLayout { - anchors.fill: parent - columns: 4 - - Button { - text: "Save All Settings" - - onClicked: Xpl2Client.jcSaveAllPrintheadSettings() - } - - Button { - text: "Reboot All PHs" - - onClicked: Xpl2Client.jcRebootAllPrintheads() - } - - Button { - text: "Restart JC" - - onClicked: Xpl2Client.jcRestart() - } + // --- Tab bar --- + TabBar { + id: tabBar - Button { - text: "Shutdown JC" + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - onClicked: Xpl2Client.jcShutdown() - } - } + TabButton { + text: "Commands" } - // --- 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 + TabButton { + text: "Status" + } + } - required property int phId - required property string versionInfo + // --- Swipe view --- + SwipeView { + id: swipeView - width: ListView.view.width + Layout.fillHeight: true + Layout.fillWidth: true + Layout.margins: 16 + currentIndex: tabBar.currentIndex - Label { - Layout.preferredWidth: 50 - font.bold: true - text: "PH %1".arg(phDelegate.phId) - } + onCurrentIndexChanged: tabBar.currentIndex = currentIndex - Button { - text: "Version" + CommandsPage { + demoPhCount: root.demoPhCount + phModel: phModel + } - onClicked: Xpl2Client.getPhVersion( - phDelegate.phId) - } + StatusPage { + id: statusPage - Label { - Layout.fillWidth: true - elide: Text.ElideRight - text: phDelegate.versionInfo || "—" - } - } - } - } - } } } // --- Debug Console --- - Rectangle { + DebugConsole { id: debugConsole - function appendLog(msg: string): void { - let ts = new Date().toLocaleTimeString(Qt.locale(), - "HH:mm:ss.zzz"); - debugLog.text += "[" + ts + "] " + msg + "\n"; - debugLog.cursorPosition = debugLog.text.length; - } - Layout.fillWidth: true Layout.preferredHeight: 160 - border.color: "#444" - color: "#1e1e1e" - radius: 4 - - ScrollView { - anchors.fill: parent - anchors.margins: 4 - - TextArea { - id: debugLog - - background: null - color: "#cccccc" - font.family: "monospace" - font.pointSize: 9 - readOnly: true - wrapMode: TextEdit.Wrap - } - } } } } diff --git a/demo/StatusPage.qml b/demo/StatusPage.qml new file mode 100644 index 0000000..6a85bcc --- /dev/null +++ b/demo/StatusPage.qml @@ -0,0 +1,620 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Xpl2 + +ColumnLayout { + id: statusPage + + property var lastJcStatus: null + property var lastPhStatus: null + + spacing: 12 + + // --- Status Messaging Controls --- + GroupBox { + Layout.fillWidth: true + enabled: Xpl2Client.connected + title: "Status Messaging" + + GridLayout { + anchors.fill: parent + columns: 2 + + Label { + text: "Level:" + } + + ComboBox { + id: statusLevelCombo + + Layout.fillWidth: true + model: ["1 — Basic", "2 — Extended"] + } + + Label { + text: "Interval (ms):" + } + + SpinBox { + id: statusIntervalSpin + + Layout.fillWidth: true + from: 100 + stepSize: 100 + to: 10000 + value: 1000 + } + + Button { + text: "Start JC Status" + + onClicked: Xpl2Client.jcStatusMessagingStart( + statusLevelCombo.currentIndex + 1, + statusIntervalSpin.value) + } + + Button { + text: "Stop JC Status" + + onClicked: Xpl2Client.jcStatusMessagingStop() + } + + Button { + text: "Start PH Status" + + onClicked: Xpl2Client.phStatusMessagingStart( + statusLevelCombo.currentIndex + 1, + statusIntervalSpin.value) + } + + Button { + text: "Stop PH Status" + + onClicked: Xpl2Client.phStatusMessagingStop() + } + } + } + + // --- JC Status Display --- + GroupBox { + Layout.fillWidth: true + title: "JC Status" + + RowLayout { + anchors.fill: parent + + GridLayout { + Layout.fillWidth: true + columns: 4 + + Label { + font.bold: true + text: "CPU:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.cpuPercentageBusy.toFixed( + 1) + "%" : "—" + } + + Label { + font.bold: true + text: "Temp:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.temperature.toFixed(1) + + "°C" : "—" + } + + Label { + font.bold: true + text: "Rail 5V:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.rail5V.toFixed(2) + "V" : + "—" + } + + Label { + font.bold: true + text: "CAN 8V:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.railCanBus8V.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "Humidity:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.humidity.toFixed(1) + "%" : + "—" + } + + Label { + font.bold: true + text: "Bus I:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.busCurrent.toFixed(3) + "A" : + "—" + } + + Label { + font.bold: true + text: "Uptime:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.onTimeSeconds + "s" : "—" + } + } + + // --- Extended (Level 2) --- + GridLayout { + Layout.fillWidth: true + columns: 4 + visible: statusPage.lastJcStatus !== null + && statusPage.lastJcStatus.statusLevel >= 2 + + Label { + font.bold: true + text: "IP:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.ipAddress : "—" + } + + Label { + font.bold: true + text: "eFuse V:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.eFuseVoltage.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "eFuse Bus:" + } + + Label { + text: statusPage.lastJcStatus ? ( + statusPage.lastJcStatus.eFuseBusEnabled + ? "On" : "Off") : "—" + } + + Label { + font.bold: true + text: "Bus Power:" + } + + Label { + text: statusPage.lastJcStatus ? ( + statusPage.lastJcStatus.busPowerEnabled + ? "On" : "Off") : "—" + } + + Label { + font.bold: true + text: "Bus OK:" + } + + Label { + text: statusPage.lastJcStatus ? ( + statusPage.lastJcStatus.busPowerOk + ? "Yes" : "No") : "—" + } + + Label { + font.bold: true + text: "Switch:" + } + + Label { + text: statusPage.lastJcStatus ? String( + statusPage.lastJcStatus.switchValue) : + "—" + } + + Label { + font.bold: true + text: "FW:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.firmwareVersion : "—" + } + + Label { + font.bold: true + text: "HW:" + } + + Label { + text: statusPage.lastJcStatus + ? statusPage.lastJcStatus.hardwareVersion : "—" + } + + Label { + font.bold: true + text: "Indicators:" + } + + Label { + Layout.columnSpan: 3 + text: statusPage.lastJcStatus + ? [statusPage.lastJcStatus.indicator0, + statusPage.lastJcStatus.indicator1, + statusPage.lastJcStatus.indicator2, + statusPage.lastJcStatus.indicator3, + statusPage.lastJcStatus.indicator4, + statusPage.lastJcStatus.indicator5].map(function ( + v) { + return v ? "1" : "0"; + }).join(" ") : "—" + } + } + } + } + + // --- PH Status Display --- + GroupBox { + Layout.fillHeight: true + Layout.fillWidth: true + title: "PH Status" + + RowLayout { + anchors.fill: parent + + GridLayout { + Layout.fillWidth: true + columns: 4 + + Label { + font.bold: true + text: "PH:" + } + + Label { + text: statusPage.lastPhStatus ? String( + statusPage.lastPhStatus.printheadId) : + "—" + } + + Label { + font.bold: true + text: "Temp:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.temperature.toFixed(1) + + "°C" : "—" + } + + Label { + font.bold: true + text: "MCU Temp:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mcuTemperature.toFixed(1) + + "°C" : "—" + } + + Label { + font.bold: true + text: "Humidity:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.humidity.toFixed(1) + "%" : + "—" + } + + Label { + font.bold: true + text: "PDS V:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.pdsVoltage.toFixed(2) + "V" : + "—" + } + + Label { + font.bold: true + text: "MDS V:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mdsVoltage.toFixed(2) + "V" : + "—" + } + + Label { + font.bold: true + text: "Sys V:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.systemVoltage.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "VDD:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.vdd.toFixed(2) + "V" : "—" + } + + Label { + font.bold: true + text: "eFuse I:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.eFuseCurrent.toFixed(3) + + "A" : "—" + } + + Label { + font.bold: true + text: "Nozzle I:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.nozzleCurrent.toFixed(3) + + "A" : "—" + } + + Label { + font.bold: true + text: "Duty Cycle:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.dutyCycle.toFixed(1) + "%" : + "—" + } + + Label { + font.bold: true + text: "Drive:" + } + + Label { + text: statusPage.lastPhStatus ? String( + statusPage.lastPhStatus.drive) : + "—" + } + + Label { + font.bold: true + text: "Uptime:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.onTimeSeconds + "s" : "—" + } + } + + // --- Extended (Level 2) --- + GridLayout { + Layout.fillWidth: true + columns: 4 + visible: statusPage.lastPhStatus !== null + && statusPage.lastPhStatus.statusLevel >= 2 + + Label { + font.bold: true + text: "MCU FW:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mcuFirmwareVersion : "—" + } + + Label { + font.bold: true + text: "MCU HW:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mcuHardwareVersion : "—" + } + + Label { + font.bold: true + text: "MCU Variant:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mcuFirmwareVariant : "—" + } + + Label { + font.bold: true + text: "FPGA FW:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.fpgaFirmwareVersion : "—" + } + + Label { + font.bold: true + text: "FPGA HW:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.fpgaHardwareVersion : "—" + } + + Label { + font.bold: true + text: "Boot:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.bootloaderVersion : "—" + } + + Label { + font.bold: true + text: "Max Temp:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.maxAllowedTemperature.toFixed( + 1) + "°C" : "—" + } + + Label { + font.bold: true + text: "eFuse Max:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.eFuseCurrentMax.toFixed(2) + + "A" : "—" + } + + Label { + font.bold: true + text: "PDS V Max:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.pdsVoltageMax.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "PDS V Min:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.pdsVoltageMin.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "MDS V Max:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mdsVoltageMax.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "MDS V Min:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.mdsVoltageMin.toFixed(2) + + "V" : "—" + } + + Label { + font.bold: true + text: "Meas. HW:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.measuredHardwareVersion : + "—" + } + + Label { + font.bold: true + text: "Accel ID:" + } + + Label { + text: statusPage.lastPhStatus ? String( + statusPage.lastPhStatus.accelerometerId) : + "—" + } + + Label { + font.bold: true + text: "Gyro:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.gyroX + ", " + + statusPage.lastPhStatus.gyroY + ", " + + statusPage.lastPhStatus.gyroZ : "—" + } + + Label { + font.bold: true + text: "Accel:" + } + + Label { + text: statusPage.lastPhStatus + ? statusPage.lastPhStatus.accelerationX + ", " + + statusPage.lastPhStatus.accelerationY + ", " + + statusPage.lastPhStatus.accelerationZ : "—" + } + } + } + } +} -- cgit v1.2.3