aboutsummaryrefslogtreecommitdiffstats
path: root/jetting-interface
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-03-23 16:48:32 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-03-23 16:48:32 +0100
commit8bcf948b76c9564cb38d3611228ccaf73890a548 (patch)
tree5d7bc5aec767ff7911c067d1bc137f6905919c91 /jetting-interface
parent61debe99a269bf7e87f6ba2f8d2a376e619fcf12 (diff)
downloadQtXpl2-8bcf948b76c9564cb38d3611228ccaf73890a548.tar.gz
QtXpl2-8bcf948b76c9564cb38d3611228ccaf73890a548.zip
Rename demo/ → jetting-interface/, mock-server/ → mock-jetting-controller/
Executables: QtXpl2Demo → JettingInterfaceDemo, Xpl2MockServer → MockJettingController. Dev log prefixes: "Demo" → "JI", "MockServer" → "MockJC". Window title → "Jetting Interface".
Diffstat (limited to 'jetting-interface')
-rw-r--r--jetting-interface/CMakeLists.txt25
-rw-r--r--jetting-interface/CommandsPage.qml213
-rw-r--r--jetting-interface/DebugConsole.qml34
-rw-r--r--jetting-interface/Main.qml173
-rw-r--r--jetting-interface/StatusPage.qml620
-rw-r--r--jetting-interface/main.cpp42
6 files changed, 1107 insertions, 0 deletions
diff --git a/jetting-interface/CMakeLists.txt b/jetting-interface/CMakeLists.txt
new file mode 100644
index 0000000..4d46ba6
--- /dev/null
+++ b/jetting-interface/CMakeLists.txt
@@ -0,0 +1,25 @@
+qt_add_executable(JettingInterfaceDemo main.cpp)
+
+qt_add_qml_module(
+ JettingInterfaceDemo
+ URI
+ JettingInterfaceDemo
+ VERSION
+ 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
+ "${PROJECT_BINARY_DIR}/qml")
+
+set_target_properties(
+ JettingInterfaceDemo PROPERTIES RUNTIME_OUTPUT_DIRECTORY
+ "${PROJECT_BINARY_DIR}/bin")
+
+target_link_libraries(JettingInterfaceDemo PRIVATE Qt6::Quick QtXpl2plugin)
+target_include_directories(JettingInterfaceDemo
+ PRIVATE "${PROJECT_SOURCE_DIR}/src")
diff --git a/jetting-interface/CommandsPage.qml b/jetting-interface/CommandsPage.qml
new file mode 100644
index 0000000..5a084e6
--- /dev/null
+++ b/jetting-interface/CommandsPage.qml
@@ -0,0 +1,213 @@
+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()
+ }
+ }
+ }
+
+ // --- Imaging ---
+ GroupBox {
+ Layout.fillWidth: true
+ enabled: Xpl2Client.connected
+ title: "Imaging"
+
+ GridLayout {
+ anchors.fill: parent
+ columns: 4
+
+ Button {
+ text: "Start Imaging"
+
+ onClicked: Xpl2Client.imagingStart(1.0)
+ }
+
+ Button {
+ text: "Stop Imaging"
+
+ onClicked: Xpl2Client.imagingStop()
+ }
+
+ Button {
+ text: "Image Count"
+
+ onClicked: Xpl2Client.imageCount()
+ }
+ }
+ }
+
+ // --- 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/jetting-interface/DebugConsole.qml b/jetting-interface/DebugConsole.qml
new file mode 100644
index 0000000..9a5a9ff
--- /dev/null
+++ b/jetting-interface/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/jetting-interface/Main.qml b/jetting-interface/Main.qml
new file mode 100644
index 0000000..1c7b553
--- /dev/null
+++ b/jetting-interface/Main.qml
@@ -0,0 +1,173 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Xpl2
+
+ApplicationWindow {
+ id: root
+
+ // Set from C++ via setInitialProperties (--printheads CLI arg, default 10).
+ required property int demoPhCount
+
+ height: 1200
+ title: "Jetting Interface"
+ visible: true
+ width: 900
+
+ Component.onCompleted: {
+ for (let i = 1; i <= root.demoPhCount; ++i)
+ phModel.append({
+ "phId": i,
+ "versionInfo": ""
+ });
+ }
+
+ ListModel {
+ id: phModel
+
+ }
+
+ Connections {
+ function onConnectedChanged() {
+ debugConsole.appendLog(Xpl2Client.connected
+ ? "Controller connected" :
+ "Controller disconnected");
+ }
+
+ function onErrorOccurred(error: string) {
+ debugConsole.appendLog("ERROR: " + error);
+ }
+
+ function onJcStatusReceived(status) {
+ statusPage.lastJcStatus = status;
+ }
+
+ function onListeningChanged() {
+ debugConsole.appendLog(Xpl2Client.listening
+ ? "Listening on ports 9110/9111/9112" :
+ "Stopped listening");
+ }
+
+ function onPhStatusReceived(status) {
+ statusPage.lastPhStatus = status;
+ }
+
+ 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 onShuttingDown() {
+ debugConsole.appendLog("SERVER SHUTTING DOWN");
+ }
+
+ function onStatusMessage(message: string) {
+ debugConsole.appendLog(message);
+ }
+
+ target: Xpl2Client
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 0
+
+ // --- Connection (always visible) ---
+ GroupBox {
+ Layout.bottomMargin: 0
+ Layout.fillWidth: true
+ Layout.margins: 16
+ title: "Connection"
+
+ RowLayout {
+ anchors.fill: parent
+
+ Label {
+ text: Xpl2Client.connected ? "Controller connected" :
+ "Waiting for controller…"
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ Button {
+ text: Xpl2Client.listening ? "Stop" : "Listen"
+
+ onClicked: {
+ if (Xpl2Client.listening)
+ Xpl2Client.stopListening();
+ else
+ Xpl2Client.startListening();
+ }
+ }
+ }
+ }
+
+ // --- Tab bar ---
+ TabBar {
+ id: tabBar
+
+ Layout.fillWidth: true
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+
+ TabButton {
+ text: "Commands"
+ }
+
+ TabButton {
+ text: "Status"
+ }
+ }
+
+ // --- Swipe view ---
+ SwipeView {
+ id: swipeView
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.margins: 16
+ currentIndex: tabBar.currentIndex
+
+ onCurrentIndexChanged: tabBar.currentIndex = currentIndex
+
+ CommandsPage {
+ demoPhCount: root.demoPhCount
+ phModel: phModel
+ }
+
+ StatusPage {
+ id: statusPage
+
+ }
+ }
+
+ // --- Debug Console ---
+ DebugConsole {
+ id: debugConsole
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: 160
+ }
+ }
+}
diff --git a/jetting-interface/StatusPage.qml b/jetting-interface/StatusPage.qml
new file mode 100644
index 0000000..6a85bcc
--- /dev/null
+++ b/jetting-interface/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 : "—"
+ }
+ }
+ }
+ }
+}
diff --git a/jetting-interface/main.cpp b/jetting-interface/main.cpp
new file mode 100644
index 0000000..b88bd37
--- /dev/null
+++ b/jetting-interface/main.cpp
@@ -0,0 +1,42 @@
+/**
+ * @file main.cpp
+ * @brief Entry point for the Jetting Interface demo application.
+ */
+#include "Xpl2Client.h"
+
+#include <QCommandLineParser>
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+#include <QtQml/QQmlExtensionPlugin>
+Q_IMPORT_QML_PLUGIN (Xpl2Plugin)
+
+int
+main (int argc, char *argv[])
+{
+ qSetMessagePattern ("JI [%{time HH:mm:ss.zzz}] %{message}");
+
+ QGuiApplication app (argc, 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);
+
+ engine.loadFromModule ("JettingInterfaceDemo", "Main");
+ return app.exec ();
+}