aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demo/CMakeLists.txt3
-rw-r--r--demo/CommandsPage.qml183
-rw-r--r--demo/DebugConsole.qml34
-rw-r--r--demo/Main.qml270
-rw-r--r--demo/StatusPage.qml620
-rw-r--r--mock-server/MockServer.cpp106
-rw-r--r--mock-server/MockServer.h6
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Xpl2Client.cpp79
-rw-r--r--src/Xpl2Client.h23
-rw-r--r--src/Xpl2JcStatus.cpp70
-rw-r--r--src/Xpl2JcStatus.h73
-rw-r--r--src/Xpl2PhStatus.cpp149
-rw-r--r--src/Xpl2PhStatus.h155
14 files changed, 1563 insertions, 212 deletions
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 : "—"
+ }
+ }
+ }
+ }
+}
diff --git a/mock-server/MockServer.cpp b/mock-server/MockServer.cpp
index f2431d9..f8831da 100644
--- a/mock-server/MockServer.cpp
+++ b/mock-server/MockServer.cpp
@@ -22,6 +22,11 @@ MockServer::MockServer (QObject *parent) : QObject (parent)
connect (&m_pingTimer, &QTimer::timeout, this, &MockServer::sendKaPing);
m_pingTimer.start (1000);
+
+ connect (&m_jcStatusTimer, &QTimer::timeout, this,
+ &MockServer::sendJcStatusMsg);
+ connect (&m_phStatusTimer, &QTimer::timeout, this,
+ &MockServer::sendPhStatusMsg);
}
void
@@ -291,7 +296,23 @@ MockServer::handleCnStatusMessagingStart (QTcpSocket *client,
{
qDebug ("%s RX %s,%s", qPrintable (logTag (client->localPort ())),
cmd.constData (), params.constData ());
- /* Echo back: controllerId, level, interval, success. */
+
+ /* Parse level and interval from params: "level,interval" */
+ QList<QByteArray> parts = params.split (',');
+ int level = parts.size () > 0 ? parts[0].toInt () : 1;
+ int interval = parts.size () > 1 ? parts[1].toInt () : 1000;
+
+ if (cmd == "CN_JC_STATUS_MESSAGING_START")
+ {
+ m_jcStatusLevel = level;
+ m_jcStatusTimer.start (interval);
+ }
+ else
+ {
+ m_phStatusLevel = level;
+ m_phStatusTimer.start (interval);
+ }
+
sendReply (client, cmd + ",1," + params + ",1\n");
}
@@ -301,6 +322,12 @@ MockServer::handleCnStatusMessagingStop (QTcpSocket *client,
{
qDebug ("%s RX %s", qPrintable (logTag (client->localPort ())),
cmd.constData ());
+
+ if (cmd == "CN_JC_STATUS_MESSAGING_STOP")
+ m_jcStatusTimer.stop ();
+ else
+ m_phStatusTimer.stop ();
+
sendReply (client, cmd + ",1,1\n");
}
@@ -365,3 +392,80 @@ MockServer::handleCfPhGetter (QTcpSocket *client, const QByteArray &params)
params.constData ());
sendReply (client, "CF_PH_GETTER,1," + params + ",\"0\",1\n");
}
+
+void
+MockServer::sendJcStatusMsg ()
+{
+ static int tick = 0;
+ ++tick;
+ bool odd = (tick % 2) != 0;
+
+ /* Alternate between two value sets so the demo visually updates.
+ Level 1: cid, level, cpu, rail5v, canBus8v, temp, humidity, busCurrent,
+ onTimeSeconds */
+ QByteArray level = QByteArray::number (m_jcStatusLevel);
+ QByteArray base = "EV_STATUS_MSG_JC,1," + level;
+ if (odd)
+ base += ",42.5,4.98,7.95,35.2,55.0,0.12,3600";
+ else
+ base += ",58.3,5.02,8.05,37.8,48.5,0.15,3660";
+
+ if (m_jcStatusLevel >= 2)
+ {
+ if (odd)
+ base += ",\"192.168.1.100\",23.5,1,1,1,3,\"1.05\",\"2.00\""
+ ",0,0,0,0,0,0";
+ else
+ base += ",\"192.168.1.100\",24.1,1,1,1,3,\"1.05\",\"2.00\""
+ ",1,0,1,0,1,0";
+ }
+ base += '\n';
+
+ for (auto *client : m_clients)
+ if (client->state () == QAbstractSocket::ConnectedState
+ && client->localPort () == m_status.number)
+ sendReply (client, base);
+}
+
+void
+MockServer::sendPhStatusMsg ()
+{
+ static int tick = 0;
+ ++tick;
+ bool odd = (tick % 2) != 0;
+
+ /* Alternate between two value sets.
+ Level 1: cid, level, phId, temp, humidity, mcuTemp, pdsV, mdsV, sysV,
+ eFuseI, nozzleI, vdd, 13×trip bools, dutyCycle, pwmFreq, drive,
+ nozzleDriveFreq, nozzleDriveDutyCycle, onTimeSeconds */
+ QByteArray level = QByteArray::number (m_phStatusLevel);
+ QByteArray base = "EV_STATUS_MSG_PH,1," + level + ",1";
+ if (odd)
+ base += ",38.5,45.0,40.2,36.0,24.0,3.3,0.05,0.02,3.3"
+ ",0,0,0,0,0,0,0,0,0,0,0,0,0"
+ ",50.0,10000.0,3,8000.0,50.0,1800";
+ else
+ base += ",41.2,42.0,43.1,35.5,23.8,3.28,0.06,0.03,3.31"
+ ",0,0,0,0,0,0,0,0,0,0,0,0,0"
+ ",55.0,10500.0,4,8200.0,52.0,1860";
+
+ if (m_phStatusLevel >= 2)
+ {
+ if (odd)
+ base += ",0,\"MCU001\",\"FLASH001\",12345"
+ ",\"1.00\",\"3.10\",\"Standard\",\"1.00\",\"2.05\",\"0.9.1\""
+ ",100.0,45.0,20.0,36.0,30.0,18.0,2.0,\"1.00\""
+ ",0,0,0,0,0,0,0,0,0,0,0,0,0,0";
+ else
+ base += ",0,\"MCU001\",\"FLASH001\",12345"
+ ",\"1.00\",\"3.10\",\"Standard\",\"1.00\",\"2.05\",\"0.9.1\""
+ ",100.0,45.0,20.0,35.5,30.0,18.0,2.0,\"1.00\""
+ ",1,-1,2,0,0,0,0,0,0,0,0,0,0,0";
+ }
+ base += '\n';
+
+ for (auto *client : m_clients)
+ if (client->state () == QAbstractSocket::ConnectedState
+ && client->localPort () == m_status.number)
+ sendReply (client, base);
+}
diff --git a/mock-server/MockServer.h b/mock-server/MockServer.h
index b14dda5..3086057 100644
--- a/mock-server/MockServer.h
+++ b/mock-server/MockServer.h
@@ -25,6 +25,8 @@ private slots:
void onClientDisconnected ();
/* Send KA_PING keepalive to all connected clients. */
void sendKaPing ();
+ void sendJcStatusMsg ();
+ void sendPhStatusMsg ();
private:
struct Port
@@ -73,5 +75,9 @@ private:
Port m_status;
QList<QTcpSocket *> m_clients;
QTimer m_pingTimer;
+ QTimer m_jcStatusTimer;
+ QTimer m_phStatusTimer;
+ int m_jcStatusLevel = 1;
+ int m_phStatusLevel = 1;
static bool s_wireDebug;
};
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4d57f8a..25572a2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,6 +9,10 @@ qt_add_qml_module(
SOURCES
Xpl2Protocol.h
Xpl2Protocol.cpp
+ Xpl2JcStatus.h
+ Xpl2JcStatus.cpp
+ Xpl2PhStatus.h
+ Xpl2PhStatus.cpp
Xpl2Client.h
Xpl2Client.cpp
OUTPUT_DIRECTORY
diff --git a/src/Xpl2Client.cpp b/src/Xpl2Client.cpp
index d6eb28a..ac7079f 100644
--- a/src/Xpl2Client.cpp
+++ b/src/Xpl2Client.cpp
@@ -132,6 +132,26 @@ Xpl2Client::s_responseTable = {
/* CF_ PH getter: cid, phId, savedValue, getterId, currentValue, successFlag */
{ "CF_PH_GETTER", { ResponseShape::PhGetter, 6,
[](auto *s, const auto &p) { emit s->phGetterResult (p[0].toInt (), p[1].toInt (), p[2].toInt () != 0, p[3].toInt (), p[4].toString (), p[5].toInt () == 1); } } },
+
+ /* EV_ events */
+ { "EV_PH_CONNECTION_CHANGED", { ResponseShape::PhConnectionChanged, 3,
+ [](auto *s, const auto &p) { emit s->phConnectionChanged (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } },
+ { "EV_JC_ERROR_CODE", { ResponseShape::ErrorCode, 3,
+ [](auto *s, const auto &p) {
+ QStringList ep; for (int i = 3; i < p.size (); ++i) ep << p[i].toString ();
+ emit s->jcErrorCode (p[0].toInt (), p[1].toInt (), ep); } } },
+ { "EV_IMG_ERROR_CODE", { ResponseShape::ErrorCode, 3,
+ [](auto *s, const auto &p) {
+ QStringList ep; for (int i = 3; i < p.size (); ++i) ep << p[i].toString ();
+ emit s->imgErrorCode (p[0].toInt (), p[1].toInt (), ep); } } },
+ { "EV_STATUS_ERROR_CODE", { ResponseShape::ErrorCode, 3,
+ [](auto *s, const auto &p) {
+ QStringList ep; for (int i = 3; i < p.size (); ++i) ep << p[i].toString ();
+ emit s->statusErrorCode (p[0].toInt (), p[1].toInt (), ep); } } },
+ { "EV_PH_ERROR_CODE", { ResponseShape::PhErrorCode, 4,
+ [](auto *s, const auto &p) {
+ QStringList ep; for (int i = 4; i < p.size (); ++i) ep << p[i].toString ();
+ emit s->phErrorCode (p[0].toInt (), p[1].toInt (), p[2].toInt (), ep); } } },
};
// clang-format on
@@ -567,6 +587,8 @@ Xpl2Client::dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg)
{
if (msg.command == "KA_PING")
handleKaPing (m_commandSocket);
+ else if (msg.command == "EV_SHUTTING_DOWN")
+ emit shuttingDown ();
else if (msg.command == "GS_JC_VERSION")
handleGsJcVersion (msg.params);
else if (msg.command == "GS_PH_VERSION")
@@ -581,6 +603,8 @@ Xpl2Client::dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg)
{
if (msg.command == "KA_PING")
handleKaPing (m_imagingSocket);
+ else if (msg.command == "EV_SHUTTING_DOWN")
+ emit shuttingDown ();
else
qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_imagingSocket)),
msg.command.constData ());
@@ -591,6 +615,12 @@ Xpl2Client::dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg)
{
if (msg.command == "KA_PING")
handleKaPing (m_statusSocket);
+ else if (msg.command == "EV_SHUTTING_DOWN")
+ emit shuttingDown ();
+ else if (msg.command == "EV_STATUS_MSG_JC")
+ handleEvStatusMsgJc (msg.params);
+ else if (msg.command == "EV_STATUS_MSG_PH")
+ handleEvStatusMsgPh (msg.params);
else if (!dispatchResponse (msg.command, msg.params, &m_statusSocket))
qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_statusSocket)),
msg.command.constData ());
@@ -778,6 +808,26 @@ Xpl2Client::dispatchResponse (const QByteArray &command,
.arg (params[4].toString ())
.arg (params[5].toInt () == 1);
break;
+ case ResponseShape::PhConnectionChanged:
+ logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 connected=%4")
+ .arg (cmd)
+ .arg (cid)
+ .arg (params[1].toInt ())
+ .arg (params[2].toInt () == 1);
+ break;
+ case ResponseShape::ErrorCode:
+ logStr = QStringLiteral ("RX %1: controller=%2 error=%3")
+ .arg (cmd)
+ .arg (cid)
+ .arg (params[1].toInt ());
+ break;
+ case ResponseShape::PhErrorCode:
+ logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 error=%4")
+ .arg (cmd)
+ .arg (cid)
+ .arg (params[1].toInt ())
+ .arg (params[2].toInt ());
+ break;
}
qDebug ("%s %s", qPrintable (logTag (socket)), qPrintable (logStr));
@@ -786,6 +836,35 @@ Xpl2Client::dispatchResponse (const QByteArray &command,
return true;
}
+void
+Xpl2Client::handleEvStatusMsgJc (const QVariantList &params)
+{
+ Xpl2JcStatus status = Xpl2JcStatus::fromParams (params);
+ qDebug ("%s EV_STATUS_MSG_JC controller=%d level=%d temp=%.1f cpu=%.1f%%",
+ qPrintable (logTag (&m_statusSocket)), status.controllerId,
+ status.statusLevel, status.temperature, status.cpuPercentageBusy);
+ emit statusMessage (
+ QStringLiteral ("RX EV_STATUS_MSG_JC: controller=%1 level=%2")
+ .arg (status.controllerId)
+ .arg (status.statusLevel));
+ emit jcStatusReceived (status);
+}
+
+void
+Xpl2Client::handleEvStatusMsgPh (const QVariantList &params)
+{
+ Xpl2PhStatus status = Xpl2PhStatus::fromParams (params);
+ qDebug ("%s EV_STATUS_MSG_PH controller=%d level=%d ph=%d temp=%.1f",
+ qPrintable (logTag (&m_statusSocket)), status.controllerId,
+ status.statusLevel, status.printheadId, status.temperature);
+ emit statusMessage (
+ QStringLiteral ("RX EV_STATUS_MSG_PH: controller=%1 level=%2 ph=%3")
+ .arg (status.controllerId)
+ .arg (status.statusLevel)
+ .arg (status.printheadId));
+ emit phStatusReceived (status);
+}
+
/* ------------------------------------------------------------------ */
/* Internal */
/* ------------------------------------------------------------------ */
diff --git a/src/Xpl2Client.h b/src/Xpl2Client.h
index 78eca0a..724f926 100644
--- a/src/Xpl2Client.h
+++ b/src/Xpl2Client.h
@@ -4,6 +4,8 @@
*/
#pragma once
+#include "Xpl2JcStatus.h"
+#include "Xpl2PhStatus.h"
#include "Xpl2Protocol.h"
#include <QHash>
@@ -248,6 +250,20 @@ signals:
void jcSaveAllPrintheadSettingsResult (int controllerId, bool success);
void phSaveSettingsResult (int controllerId, int printheadId, bool success);
+ /* EV_ event signals */
+ void shuttingDown ();
+ void phConnectionChanged (int controllerId, int printheadId, bool connected);
+ void jcErrorCode (int controllerId, int errorCode,
+ const QStringList &params);
+ void phErrorCode (int controllerId, int printheadId, int errorCode,
+ const QStringList &params);
+ void imgErrorCode (int controllerId, int errorCode,
+ const QStringList &params);
+ void statusErrorCode (int controllerId, int errorCode,
+ const QStringList &params);
+ void jcStatusReceived (const Xpl2JcStatus &status);
+ void phStatusReceived (const Xpl2PhStatus &status);
+
private slots:
void onSocketConnected ();
void onSocketDisconnected ();
@@ -276,7 +292,10 @@ private:
JcSetter,
PhSetter,
JcGetter,
- PhGetter
+ PhGetter,
+ PhConnectionChanged,
+ ErrorCode,
+ PhErrorCode
};
struct ResponseEntry
@@ -290,6 +309,8 @@ private:
bool dispatchResponse (const QByteArray &command, const QVariantList &params,
const QTcpSocket *socket);
+ void handleEvStatusMsgJc (const QVariantList &params);
+ void handleEvStatusMsgPh (const QVariantList &params);
void updateConnectedState ();
QString logTag (const QTcpSocket *socket) const;
diff --git a/src/Xpl2JcStatus.cpp b/src/Xpl2JcStatus.cpp
new file mode 100644
index 0000000..b800eb9
--- /dev/null
+++ b/src/Xpl2JcStatus.cpp
@@ -0,0 +1,70 @@
+/**
+ * @file Xpl2JcStatus.cpp
+ * @brief Structured JC status message (EV_STATUS_MSG_JC, Appendix A).
+ */
+#include "Xpl2JcStatus.h"
+
+#include <QVariant>
+
+Xpl2JcStatus
+Xpl2JcStatus::fromParams (const QVariantList &params)
+{
+ Xpl2JcStatus s;
+ int n = params.size ();
+ if (n < 2)
+ return s;
+
+ s.controllerId = params[0].toInt ();
+ s.statusLevel = params[1].toInt ();
+
+ /* Level 1: fields at indices 2-8 */
+ if (n > 2)
+ s.cpuPercentageBusy = params[2].toFloat ();
+ if (n > 3)
+ s.rail5V = params[3].toFloat ();
+ if (n > 4)
+ s.railCanBus8V = params[4].toFloat ();
+ if (n > 5)
+ s.temperature = params[5].toFloat ();
+ if (n > 6)
+ s.humidity = params[6].toFloat ();
+ if (n > 7)
+ s.busCurrent = params[7].toFloat ();
+ if (n > 8)
+ s.onTimeSeconds = params[8].toInt ();
+
+ /* Level 2: fields at indices 9-22 */
+ if (s.statusLevel < 2 || n <= 9)
+ return s;
+
+ if (n > 9)
+ s.ipAddress = params[9].toString ();
+ if (n > 10)
+ s.eFuseVoltage = params[10].toFloat ();
+ if (n > 11)
+ s.eFuseBusEnabled = params[11].toInt () != 0;
+ if (n > 12)
+ s.busPowerEnabled = params[12].toInt () != 0;
+ if (n > 13)
+ s.busPowerOk = params[13].toInt () != 0;
+ if (n > 14)
+ s.switchValue = params[14].toInt ();
+ if (n > 15)
+ s.firmwareVersion = params[15].toString ();
+ if (n > 16)
+ s.hardwareVersion = params[16].toString ();
+ if (n > 17)
+ s.indicator0 = params[17].toInt () != 0;
+ if (n > 18)
+ s.indicator1 = params[18].toInt () != 0;
+ if (n > 19)
+ s.indicator2 = params[19].toInt () != 0;
+ if (n > 20)
+ s.indicator3 = params[20].toInt () != 0;
+ if (n > 21)
+ s.indicator4 = params[21].toInt () != 0;
+ if (n > 22)
+ s.indicator5 = params[22].toInt () != 0;
+
+ return s;
+}
diff --git a/src/Xpl2JcStatus.h b/src/Xpl2JcStatus.h
new file mode 100644
index 0000000..00e2a64
--- /dev/null
+++ b/src/Xpl2JcStatus.h
@@ -0,0 +1,73 @@
+/**
+ * @file Xpl2JcStatus.h
+ * @brief Structured JC status message (EV_STATUS_MSG_JC, Appendix A).
+ */
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+class Xpl2JcStatus
+{
+ Q_GADGET
+
+ Q_PROPERTY (int controllerId MEMBER controllerId)
+ Q_PROPERTY (int statusLevel MEMBER statusLevel)
+
+ /* Level 1 fields (3-9) */
+ Q_PROPERTY (float cpuPercentageBusy MEMBER cpuPercentageBusy)
+ Q_PROPERTY (float rail5V MEMBER rail5V)
+ Q_PROPERTY (float railCanBus8V MEMBER railCanBus8V)
+ Q_PROPERTY (float temperature MEMBER temperature)
+ Q_PROPERTY (float humidity MEMBER humidity)
+ Q_PROPERTY (float busCurrent MEMBER busCurrent)
+ Q_PROPERTY (int onTimeSeconds MEMBER onTimeSeconds)
+
+ /* Level 2 fields (10-23) */
+ Q_PROPERTY (QString ipAddress MEMBER ipAddress)
+ Q_PROPERTY (float eFuseVoltage MEMBER eFuseVoltage)
+ Q_PROPERTY (bool eFuseBusEnabled MEMBER eFuseBusEnabled)
+ Q_PROPERTY (bool busPowerEnabled MEMBER busPowerEnabled)
+ Q_PROPERTY (bool busPowerOk MEMBER busPowerOk)
+ Q_PROPERTY (int switchValue MEMBER switchValue)
+ Q_PROPERTY (QString firmwareVersion MEMBER firmwareVersion)
+ Q_PROPERTY (QString hardwareVersion MEMBER hardwareVersion)
+ Q_PROPERTY (bool indicator0 MEMBER indicator0)
+ Q_PROPERTY (bool indicator1 MEMBER indicator1)
+ Q_PROPERTY (bool indicator2 MEMBER indicator2)
+ Q_PROPERTY (bool indicator3 MEMBER indicator3)
+ Q_PROPERTY (bool indicator4 MEMBER indicator4)
+ Q_PROPERTY (bool indicator5 MEMBER indicator5)
+
+public:
+ int controllerId = 0;
+ int statusLevel = 0;
+
+ /* Level 1 */
+ float cpuPercentageBusy = 0;
+ float rail5V = 0;
+ float railCanBus8V = 0;
+ float temperature = 0;
+ float humidity = 0;
+ float busCurrent = 0;
+ int onTimeSeconds = 0;
+
+ /* Level 2 */
+ QString ipAddress;
+ float eFuseVoltage = 0;
+ bool eFuseBusEnabled = false;
+ bool busPowerEnabled = false;
+ bool busPowerOk = false;
+ int switchValue = 0;
+ QString firmwareVersion;
+ QString hardwareVersion;
+ bool indicator0 = false;
+ bool indicator1 = false;
+ bool indicator2 = false;
+ bool indicator3 = false;
+ bool indicator4 = false;
+ bool indicator5 = false;
+
+ /** Parse from EV_STATUS_MSG_JC params (after command token). */
+ static Xpl2JcStatus fromParams (const QVariantList &params);
+};
diff --git a/src/Xpl2PhStatus.cpp b/src/Xpl2PhStatus.cpp
new file mode 100644
index 0000000..8413db4
--- /dev/null
+++ b/src/Xpl2PhStatus.cpp
@@ -0,0 +1,149 @@
+/**
+ * @file Xpl2PhStatus.cpp
+ * @brief Structured PH status message (EV_STATUS_MSG_PH, Appendix B).
+ */
+#include "Xpl2PhStatus.h"
+
+#include <QVariant>
+
+Xpl2PhStatus
+Xpl2PhStatus::fromParams (const QVariantList &params)
+{
+ Xpl2PhStatus s;
+ int n = params.size ();
+ if (n < 3)
+ return s;
+
+ s.controllerId = params[0].toInt ();
+ s.statusLevel = params[1].toInt ();
+ s.printheadId = params[2].toInt ();
+
+ /* Level 1: fields at indices 3-30 */
+ if (n > 3)
+ s.temperature = params[3].toFloat ();
+ if (n > 4)
+ s.humidity = params[4].toFloat ();
+ if (n > 5)
+ s.mcuTemperature = params[5].toFloat ();
+ if (n > 6)
+ s.pdsVoltage = params[6].toFloat ();
+ if (n > 7)
+ s.mdsVoltage = params[7].toFloat ();
+ if (n > 8)
+ s.systemVoltage = params[8].toFloat ();
+ if (n > 9)
+ s.eFuseCurrent = params[9].toFloat ();
+ if (n > 10)
+ s.nozzleCurrent = params[10].toFloat ();
+ if (n > 11)
+ s.vdd = params[11].toFloat ();
+ if (n > 12)
+ s.temperatureTrip = params[12].toInt () != 0;
+ if (n > 13)
+ s.pdsOverVoltageTrip = params[13].toInt () != 0;
+ if (n > 14)
+ s.pdsUnderVoltageTrip = params[14].toInt () != 0;
+ if (n > 15)
+ s.pdsSupplyErrorTrip = params[15].toInt () != 0;
+ if (n > 16)
+ s.mdsOverVoltageTrip = params[16].toInt () != 0;
+ if (n > 17)
+ s.mdsUnderVoltageTrip = params[17].toInt () != 0;
+ if (n > 18)
+ s.supplyOverVoltageTrip = params[18].toInt () != 0;
+ if (n > 19)
+ s.supplyUnderVoltageTrip = params[19].toInt () != 0;
+ if (n > 20)
+ s.eFuseOverCurrentTrip = params[20].toInt () != 0;
+ if (n > 21)
+ s.eFuseInputVoltageErrorTrip = params[21].toInt () != 0;
+ if (n > 22)
+ s.eFuseFaultTrip = params[22].toInt () != 0;
+ if (n > 23)
+ s.flashFaultyTrip = params[23].toInt () != 0;
+ if (n > 24)
+ s.flashChecksumErrorTrip = params[24].toInt () != 0;
+ if (n > 25)
+ s.dutyCycle = params[25].toFloat ();
+ if (n > 26)
+ s.pwmFrequency = params[26].toFloat ();
+ if (n > 27)
+ s.drive = params[27].toInt ();
+ if (n > 28)
+ s.nozzleDriveFrequency = params[28].toFloat ();
+ if (n > 29)
+ s.nozzleDriveDutyCycle = params[29].toFloat ();
+ if (n > 30)
+ s.onTimeSeconds = params[30].toInt ();
+
+ /* Level 2: fields at indices 31-61 */
+ if (s.statusLevel < 2 || n <= 31)
+ return s;
+
+ if (n > 31)
+ s.accelerometerId = params[31].toInt ();
+ if (n > 32)
+ s.mcuId = params[32].toString ();
+ if (n > 33)
+ s.flashMemoryId = params[33].toString ();
+ if (n > 34)
+ s.temperatureSensorSerialNumber = params[34].toInt ();
+ if (n > 35)
+ s.mcuHardwareVersion = params[35].toString ();
+ if (n > 36)
+ s.mcuFirmwareVersion = params[36].toString ();
+ if (n > 37)
+ s.mcuFirmwareVariant = params[37].toString ();
+ if (n > 38)
+ s.fpgaHardwareVersion = params[38].toString ();
+ if (n > 39)
+ s.fpgaFirmwareVersion = params[39].toString ();
+ if (n > 40)
+ s.bootloaderVersion = params[40].toString ();
+ if (n > 41)
+ s.maxAllowedTemperature = params[41].toFloat ();
+ if (n > 42)
+ s.pdsVoltageMax = params[42].toFloat ();
+ if (n > 43)
+ s.pdsVoltageMin = params[43].toFloat ();
+ if (n > 44)
+ s.pdsVoltageSetting = params[44].toFloat ();
+ if (n > 45)
+ s.mdsVoltageMax = params[45].toFloat ();
+ if (n > 46)
+ s.mdsVoltageMin = params[46].toFloat ();
+ if (n > 47)
+ s.eFuseCurrentMax = params[47].toFloat ();
+ if (n > 48)
+ s.measuredHardwareVersion = params[48].toString ();
+ if (n > 49)
+ s.gyroX = params[49].toInt ();
+ if (n > 50)
+ s.gyroY = params[50].toInt ();
+ if (n > 51)
+ s.gyroZ = params[51].toInt ();
+ if (n > 52)
+ s.accelerationX = params[52].toInt ();
+ if (n > 53)
+ s.accelerationY = params[53].toInt ();
+ if (n > 54)
+ s.accelerationZ = params[54].toInt ();
+ if (n > 55)
+ s.purge = params[55].toInt ();
+ if (n > 56)
+ s.purgeState = params[56].toInt ();
+ if (n > 57)
+ s.purgeDelay = params[57].toInt ();
+ if (n > 58)
+ s.purgeCounter = params[58].toInt ();
+ if (n > 59)
+ s.cleaningStartPeriod = params[59].toInt ();
+ if (n > 60)
+ s.cleaningEndPeriod = params[60].toInt ();
+ if (n > 61)
+ s.cleaningStepPeriod = params[61].toInt ();
+ if (n > 62)
+ s.cleaningPeriod = params[62].toInt ();
+
+ return s;
+}
diff --git a/src/Xpl2PhStatus.h b/src/Xpl2PhStatus.h
new file mode 100644
index 0000000..75a04a0
--- /dev/null
+++ b/src/Xpl2PhStatus.h
@@ -0,0 +1,155 @@
+/**
+ * @file Xpl2PhStatus.h
+ * @brief Structured PH status message (EV_STATUS_MSG_PH, Appendix B).
+ */
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+class Xpl2PhStatus
+{
+ Q_GADGET
+
+ Q_PROPERTY (int controllerId MEMBER controllerId)
+ Q_PROPERTY (int statusLevel MEMBER statusLevel)
+ Q_PROPERTY (int printheadId MEMBER printheadId)
+
+ /* Level 1 fields (4-31) */
+ Q_PROPERTY (float temperature MEMBER temperature)
+ Q_PROPERTY (float humidity MEMBER humidity)
+ Q_PROPERTY (float mcuTemperature MEMBER mcuTemperature)
+ Q_PROPERTY (float pdsVoltage MEMBER pdsVoltage)
+ Q_PROPERTY (float mdsVoltage MEMBER mdsVoltage)
+ Q_PROPERTY (float systemVoltage MEMBER systemVoltage)
+ Q_PROPERTY (float eFuseCurrent MEMBER eFuseCurrent)
+ Q_PROPERTY (float nozzleCurrent MEMBER nozzleCurrent)
+ Q_PROPERTY (float vdd MEMBER vdd)
+ Q_PROPERTY (bool temperatureTrip MEMBER temperatureTrip)
+ Q_PROPERTY (bool pdsOverVoltageTrip MEMBER pdsOverVoltageTrip)
+ Q_PROPERTY (bool pdsUnderVoltageTrip MEMBER pdsUnderVoltageTrip)
+ Q_PROPERTY (bool pdsSupplyErrorTrip MEMBER pdsSupplyErrorTrip)
+ Q_PROPERTY (bool mdsOverVoltageTrip MEMBER mdsOverVoltageTrip)
+ Q_PROPERTY (bool mdsUnderVoltageTrip MEMBER mdsUnderVoltageTrip)
+ Q_PROPERTY (bool supplyOverVoltageTrip MEMBER supplyOverVoltageTrip)
+ Q_PROPERTY (bool supplyUnderVoltageTrip MEMBER supplyUnderVoltageTrip)
+ Q_PROPERTY (bool eFuseOverCurrentTrip MEMBER eFuseOverCurrentTrip)
+ Q_PROPERTY (
+ bool eFuseInputVoltageErrorTrip MEMBER eFuseInputVoltageErrorTrip)
+ Q_PROPERTY (bool eFuseFaultTrip MEMBER eFuseFaultTrip)
+ Q_PROPERTY (bool flashFaultyTrip MEMBER flashFaultyTrip)
+ Q_PROPERTY (bool flashChecksumErrorTrip MEMBER flashChecksumErrorTrip)
+ Q_PROPERTY (float dutyCycle MEMBER dutyCycle)
+ Q_PROPERTY (float pwmFrequency MEMBER pwmFrequency)
+ Q_PROPERTY (int drive MEMBER drive)
+ Q_PROPERTY (float nozzleDriveFrequency MEMBER nozzleDriveFrequency)
+ Q_PROPERTY (float nozzleDriveDutyCycle MEMBER nozzleDriveDutyCycle)
+ Q_PROPERTY (int onTimeSeconds MEMBER onTimeSeconds)
+
+ /* Level 2 fields (32-62) */
+ Q_PROPERTY (int accelerometerId MEMBER accelerometerId)
+ Q_PROPERTY (QString mcuId MEMBER mcuId)
+ Q_PROPERTY (QString flashMemoryId MEMBER flashMemoryId)
+ Q_PROPERTY (
+ int temperatureSensorSerialNumber MEMBER temperatureSensorSerialNumber)
+ Q_PROPERTY (QString mcuHardwareVersion MEMBER mcuHardwareVersion)
+ Q_PROPERTY (QString mcuFirmwareVersion MEMBER mcuFirmwareVersion)
+ Q_PROPERTY (QString mcuFirmwareVariant MEMBER mcuFirmwareVariant)
+ Q_PROPERTY (QString fpgaHardwareVersion MEMBER fpgaHardwareVersion)
+ Q_PROPERTY (QString fpgaFirmwareVersion MEMBER fpgaFirmwareVersion)
+ Q_PROPERTY (QString bootloaderVersion MEMBER bootloaderVersion)
+ Q_PROPERTY (float maxAllowedTemperature MEMBER maxAllowedTemperature)
+ Q_PROPERTY (float pdsVoltageMax MEMBER pdsVoltageMax)
+ Q_PROPERTY (float pdsVoltageMin MEMBER pdsVoltageMin)
+ Q_PROPERTY (float pdsVoltageSetting MEMBER pdsVoltageSetting)
+ Q_PROPERTY (float mdsVoltageMax MEMBER mdsVoltageMax)
+ Q_PROPERTY (float mdsVoltageMin MEMBER mdsVoltageMin)
+ Q_PROPERTY (float eFuseCurrentMax MEMBER eFuseCurrentMax)
+ Q_PROPERTY (QString measuredHardwareVersion MEMBER measuredHardwareVersion)
+ Q_PROPERTY (int gyroX MEMBER gyroX)
+ Q_PROPERTY (int gyroY MEMBER gyroY)
+ Q_PROPERTY (int gyroZ MEMBER gyroZ)
+ Q_PROPERTY (int accelerationX MEMBER accelerationX)
+ Q_PROPERTY (int accelerationY MEMBER accelerationY)
+ Q_PROPERTY (int accelerationZ MEMBER accelerationZ)
+ Q_PROPERTY (int purge MEMBER purge)
+ Q_PROPERTY (int purgeState MEMBER purgeState)
+ Q_PROPERTY (int purgeDelay MEMBER purgeDelay)
+ Q_PROPERTY (int purgeCounter MEMBER purgeCounter)
+ Q_PROPERTY (int cleaningStartPeriod MEMBER cleaningStartPeriod)
+ Q_PROPERTY (int cleaningEndPeriod MEMBER cleaningEndPeriod)
+ Q_PROPERTY (int cleaningStepPeriod MEMBER cleaningStepPeriod)
+ Q_PROPERTY (int cleaningPeriod MEMBER cleaningPeriod)
+
+public:
+ int controllerId = 0;
+ int statusLevel = 0;
+ int printheadId = 0;
+
+ /* Level 1 */
+ float temperature = 0;
+ float humidity = 0;
+ float mcuTemperature = 0;
+ float pdsVoltage = 0;
+ float mdsVoltage = 0;
+ float systemVoltage = 0;
+ float eFuseCurrent = 0;
+ float nozzleCurrent = 0;
+ float vdd = 0;
+ bool temperatureTrip = false;
+ bool pdsOverVoltageTrip = false;
+ bool pdsUnderVoltageTrip = false;
+ bool pdsSupplyErrorTrip = false;
+ bool mdsOverVoltageTrip = false;
+ bool mdsUnderVoltageTrip = false;
+ bool supplyOverVoltageTrip = false;
+ bool supplyUnderVoltageTrip = false;
+ bool eFuseOverCurrentTrip = false;
+ bool eFuseInputVoltageErrorTrip = false;
+ bool eFuseFaultTrip = false;
+ bool flashFaultyTrip = false;
+ bool flashChecksumErrorTrip = false;
+ float dutyCycle = 0;
+ float pwmFrequency = 0;
+ int drive = 0;
+ float nozzleDriveFrequency = 0;
+ float nozzleDriveDutyCycle = 0;
+ int onTimeSeconds = 0;
+
+ /* Level 2 */
+ int accelerometerId = 0;
+ QString mcuId;
+ QString flashMemoryId;
+ int temperatureSensorSerialNumber = 0;
+ QString mcuHardwareVersion;
+ QString mcuFirmwareVersion;
+ QString mcuFirmwareVariant;
+ QString fpgaHardwareVersion;
+ QString fpgaFirmwareVersion;
+ QString bootloaderVersion;
+ float maxAllowedTemperature = 0;
+ float pdsVoltageMax = 0;
+ float pdsVoltageMin = 0;
+ float pdsVoltageSetting = 0;
+ float mdsVoltageMax = 0;
+ float mdsVoltageMin = 0;
+ float eFuseCurrentMax = 0;
+ QString measuredHardwareVersion;
+ int gyroX = 0;
+ int gyroY = 0;
+ int gyroZ = 0;
+ int accelerationX = 0;
+ int accelerationY = 0;
+ int accelerationZ = 0;
+ int purge = 0;
+ int purgeState = 0;
+ int purgeDelay = 0;
+ int purgeCounter = 0;
+ int cleaningStartPeriod = 0;
+ int cleaningEndPeriod = 0;
+ int cleaningStepPeriod = 0;
+ int cleaningPeriod = 0;
+
+ /** Parse from EV_STATUS_MSG_PH params (after command token). */
+ static Xpl2PhStatus fromParams (const QVariantList &params);
+};