aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--jetting-interface/CommandsPage.qml44
-rw-r--r--jetting-interface/Main.qml27
-rw-r--r--src/Xpl2Client.cpp136
-rw-r--r--src/Xpl2Client.h79
4 files changed, 269 insertions, 17 deletions
diff --git a/jetting-interface/CommandsPage.qml b/jetting-interface/CommandsPage.qml
index 5a084e6..5b466f0 100644
--- a/jetting-interface/CommandsPage.qml
+++ b/jetting-interface/CommandsPage.qml
@@ -163,12 +163,31 @@ ColumnLayout {
ColumnLayout {
anchors.fill: parent
- Button {
- text: "Get All PH Versions"
+ RowLayout {
+ Layout.fillWidth: true
+
+ Button {
+ text: "Get All PH Versions"
+
+ onClicked: {
+ for (let i = 0; i < commandsPage.phModel.count; ++i)
+ Xpl2Client.getPhVersion(commandsPage.phModel.get(i).phId);
+ }
+ }
+
+ // Working replacement for the broken Jetting All On/Off: the
+ // library self-probes versions then jets each available head by
+ // ID, even non-contiguous (see docs/DISCREPANCIES.md, D6).
+ Button {
+ text: "Jet On All Available"
+
+ onClicked: Xpl2Client.jettingAvailableOn()
+ }
- onClicked: {
- for (let i = 0; i < commandsPage.phModel.count; ++i)
- Xpl2Client.getPhVersion(commandsPage.phModel.get(i).phId);
+ Button {
+ text: "Jet Off All Available"
+
+ onClicked: Xpl2Client.jettingAvailableOff()
}
}
@@ -185,6 +204,7 @@ ColumnLayout {
required property int phId
required property string versionInfo
+ required property bool valid
width: ListView.view.width
@@ -200,6 +220,20 @@ ColumnLayout {
onClicked: Xpl2Client.getPhVersion(phDelegate.phId)
}
+ Button {
+ text: "Jet On"
+ enabled: phDelegate.valid
+
+ onClicked: Xpl2Client.phJettingOn(phDelegate.phId, "FFFFFFFFFFFF")
+ }
+
+ Button {
+ text: "Jet Off"
+ enabled: phDelegate.valid
+
+ onClicked: Xpl2Client.phJettingOff(phDelegate.phId)
+ }
+
Label {
Layout.fillWidth: true
elide: Text.ElideRight
diff --git a/jetting-interface/Main.qml b/jetting-interface/Main.qml
index cd69ed8..cd239bb 100644
--- a/jetting-interface/Main.qml
+++ b/jetting-interface/Main.qml
@@ -20,7 +20,8 @@ ApplicationWindow {
for (let i = 1; i <= root.demoPhCount; ++i)
phModel.append({
"phId": i,
- "versionInfo": ""
+ "versionInfo": "",
+ "valid": false
});
}
@@ -54,16 +55,24 @@ ApplicationWindow {
fpgaFirmwareVersion: string,
fpgaHardwareVersion: string,
bootloaderVersion: string) {
+ // A printhead is present only if it reports a non-zero version.
+ // Absent slots reply with all zeros; mcuHardwareVersion and
+ // mcuFirmwareVariant are "00" even on present heads, so they are
+ // excluded from the check (see docs/DISCREPANCIES.md).
+ let isZero = v => parseFloat(v) === 0 || v === "";
+ let valid = !(isZero(mcuFirmwareVersion) && isZero(fpgaFirmwareVersion)
+ && isZero(fpgaHardwareVersion) && isZero(bootloaderVersion));
+ let info = valid ? "MCU %1/%2 (%3) | FPGA %4/%5 | Boot %6".arg(
+ mcuFirmwareVersion).arg(
+ mcuHardwareVersion).arg(
+ mcuFirmwareVariant).arg(
+ fpgaFirmwareVersion).arg(
+ fpgaHardwareVersion).arg(
+ bootloaderVersion) : "(Printhead unavailable)";
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));
+ phModel.setProperty(i, "versionInfo", info);
+ phModel.setProperty(i, "valid", valid);
break;
}
}
diff --git a/src/Xpl2Client.cpp b/src/Xpl2Client.cpp
index 102970e..40118b1 100644
--- a/src/Xpl2Client.cpp
+++ b/src/Xpl2Client.cpp
@@ -6,6 +6,8 @@
#include <QDebug>
+#include <algorithm>
+
bool Xpl2Client::s_wireDebug = false;
/* ------------------------------------------------------------------ */
@@ -163,6 +165,10 @@ Xpl2Client::Xpl2Client (QObject *parent) : QObject (parent)
connect (&m_retryTimer, &QTimer::timeout, this,
&Xpl2Client::retryConnection);
+
+ m_jettingScanTimer.setSingleShot (true);
+ connect (&m_jettingScanTimer, &QTimer::timeout, this,
+ &Xpl2Client::onJettingScanTimeout);
}
/* ------------------------------------------------------------------ */
@@ -301,6 +307,12 @@ Xpl2Client::disconnectFromProxy ()
void
Xpl2Client::getJcVersion ()
{
+ qWarning ("%s getJcVersion() is unreliable on this controller (wrong "
+ "printhead count) — do not use. See docs/DISCREPANCIES.md (D3).",
+ qPrintable (logTag (&m_commandSocket)));
+ emit errorOccurred (
+ QStringLiteral ("getJcVersion() is unreliable on this controller (wrong "
+ "printhead count) — do not use it."));
sendCommand (&m_commandSocket, "GS_JC_VERSION");
}
@@ -317,6 +329,14 @@ Xpl2Client::getPhVersion (int printheadId)
void
Xpl2Client::jettingAllOn ()
{
+ qWarning (
+ "%s jettingAllOn() is broken on this controller (only drives heads "
+ "before the first ID gap) — use jettingAvailableOn(). See "
+ "docs/DISCREPANCIES.md (D6).",
+ qPrintable (logTag (&m_commandSocket)));
+ emit errorOccurred (QStringLiteral (
+ "jettingAllOn() is broken on this controller — use jettingAvailableOn() "
+ "instead."));
sendCommand (&m_commandSocket, "CN_JETTING_ALL_ON");
}
@@ -329,6 +349,13 @@ Xpl2Client::jettingOn (const QString &jettingMask)
void
Xpl2Client::jettingOff ()
{
+ qWarning ("%s jettingOff() mirrors the jettingAllOn() defect on this "
+ "controller — use jettingAvailableOff(). See "
+ "docs/DISCREPANCIES.md (D6).",
+ qPrintable (logTag (&m_commandSocket)));
+ emit errorOccurred (
+ QStringLiteral ("jettingOff() is unreliable on this controller — use "
+ "jettingAvailableOff() instead."));
sendCommand (&m_commandSocket, "CN_JETTING_OFF");
}
@@ -346,6 +373,106 @@ Xpl2Client::phJettingOff (int printheadId)
}
void
+Xpl2Client::jettingAvailableOn ()
+{
+ startJettingScan (true);
+}
+
+void
+Xpl2Client::jettingAvailableOff ()
+{
+ startJettingScan (false);
+}
+
+bool
+Xpl2Client::phVersionIsAvailable (const QString &mcuFirmwareVersion,
+ const QString &fpgaFirmwareVersion,
+ const QString &fpgaHardwareVersion,
+ const QString &bootloaderVersion)
+{
+ // Absent printheads reply with all-zero versions. mcuHardwareVersion and
+ // mcuFirmwareVariant read "00" even on present heads, so they are not part
+ // of the test (see docs/DISCREPANCIES.md).
+ return mcuFirmwareVersion.toDouble () != 0.0
+ || fpgaFirmwareVersion.toDouble () != 0.0
+ || fpgaHardwareVersion.toDouble () != 0.0
+ || bootloaderVersion.toDouble () != 0.0;
+}
+
+void
+Xpl2Client::startJettingScan (bool turnOn)
+{
+ if (!m_connected)
+ {
+ emit errorOccurred (
+ QStringLiteral ("Not connected for jettingAvailable%1")
+ .arg (turnOn ? QStringLiteral ("On") : QStringLiteral ("Off")));
+ return;
+ }
+ if (m_jettingScanActive)
+ {
+ qWarning ("%s jettingAvailable: a scan is already in progress, ignoring",
+ qPrintable (logTag (&m_commandSocket)));
+ return;
+ }
+
+ m_jettingScanActive = true;
+ m_jettingScanTurnOn = turnOn;
+ m_jettingScanAvailable.clear ();
+ m_jettingScanPending.clear ();
+ for (int id = 1; id <= MaxProbedPrintheadId; ++id)
+ {
+ m_jettingScanPending.insert (id);
+ getPhVersion (id);
+ }
+ m_jettingScanTimer.start (JettingScanTimeoutMs);
+ qDebug ("%s jettingAvailable%s: probing %d printheads",
+ qPrintable (logTag (&m_commandSocket)), turnOn ? "On" : "Off",
+ MaxProbedPrintheadId);
+}
+
+void
+Xpl2Client::finishJettingScan ()
+{
+ m_jettingScanTimer.stop ();
+ m_jettingScanActive = false;
+ m_jettingScanPending.clear ();
+ std::sort (m_jettingScanAvailable.begin (), m_jettingScanAvailable.end ());
+
+ if (m_connected)
+ for (int id : m_jettingScanAvailable)
+ {
+ if (m_jettingScanTurnOn)
+ phJettingOn (id, QString::fromLatin1 (AllNozzlesOnMask));
+ else
+ phJettingOff (id);
+ }
+ qDebug ("%s jettingAvailable%s: jetted %lld available printhead(s)",
+ qPrintable (logTag (&m_commandSocket)),
+ m_jettingScanTurnOn ? "On" : "Off",
+ static_cast<long long> (m_jettingScanAvailable.size ()));
+ emit jettingAvailableComplete (m_jettingScanTurnOn, m_jettingScanAvailable);
+}
+
+void
+Xpl2Client::onJettingScanTimeout ()
+{
+ if (!m_jettingScanActive)
+ return;
+ qWarning (
+ "%s jettingAvailable: timed out waiting for %lld version reply(ies);"
+ " proceeding with %lld available",
+ qPrintable (logTag (&m_commandSocket)),
+ static_cast<long long> (m_jettingScanPending.size ()),
+ static_cast<long long> (m_jettingScanAvailable.size ()));
+ emit errorOccurred (
+ QStringLiteral (
+ "jettingAvailable: %1 printhead(s) did not respond in time")
+ .arg (m_jettingScanPending.size ()));
+ finishJettingScan ();
+}
+
+void
Xpl2Client::jcIdLedOn ()
{
sendCommand (&m_commandSocket, "CN_JC_ID_LED_ON");
@@ -789,6 +916,15 @@ Xpl2Client::handleGsPhVersion (const QVariantList &params)
.arg (bootVer));
emit phVersionReceived (cid, phId, mcuFw, mcuHw, mcuFwVar, fpgaFw, fpgaHw,
bootVer);
+
+ // Feed an in-progress jettingAvailableOn()/Off() scan.
+ if (m_jettingScanActive && m_jettingScanPending.remove (phId))
+ {
+ if (phVersionIsAvailable (mcuFw, fpgaFw, fpgaHw, bootVer))
+ m_jettingScanAvailable.append (phId);
+ if (m_jettingScanPending.isEmpty ())
+ finishJettingScan ();
+ }
}
bool
diff --git a/src/Xpl2Client.h b/src/Xpl2Client.h
index 49fbec5..cce2a47 100644
--- a/src/Xpl2Client.h
+++ b/src/Xpl2Client.h
@@ -9,8 +9,10 @@
#include "Xpl2Protocol.h"
#include <QHash>
+#include <QList>
#include <QObject>
#include <QQmlEngine>
+#include <QSet>
#include <QTcpSocket>
#include <QTimer>
@@ -53,24 +55,64 @@ public:
Q_INVOKABLE void connectToProxy ();
/** Disconnect from the proxy. */
Q_INVOKABLE void disconnectFromProxy ();
- /** Get the just connected Jetting Controller ID and Software Version. */
+ /**
+ * Get the just connected Jetting Controller ID and Software Version.
+ *
+ * @deprecated Unreliable on real controllers: the printhead-count field is
+ * wrong (reports the heads before the first ID gap, not the true count). See
+ * docs/DISCREPANCIES.md (D3). Enumerate printheads via getPhVersion() and
+ * the jettingAvailable* helpers instead. Calling this emits errorOccurred().
+ */
Q_INVOKABLE void getJcVersion ();
/** Query the specified printhead's version info. */
Q_INVOKABLE void getPhVersion (int printheadId);
/* -- CN_ Control commands ----------------------------------------- */
- /** Switch jetting on for all printheads. */
+ /**
+ * Switch jetting on for all printheads (CN_JETTING_ALL_ON).
+ *
+ * @deprecated Broken on real controllers: only drives the printheads before
+ * the first gap in the ID sequence, even though it replies success. See
+ * docs/DISCREPANCIES.md (D6). Use jettingAvailableOn() instead. Calling this
+ * emits errorOccurred().
+ */
Q_INVOKABLE void jettingAllOn ();
/** Switch jetting on for all printheads with a per-nozzle mask (180 chars).
*/
Q_INVOKABLE void jettingOn (const QString &jettingMask);
- /** Switch jetting off for all printheads. */
+ /**
+ * Switch jetting off for all printheads (CN_JETTING_OFF).
+ *
+ * @deprecated Mirrors the jettingAllOn() defect on real controllers (only
+ * affects the heads before the first ID gap). See docs/DISCREPANCIES.md
+ * (D6). Use jettingAvailableOff() instead. Calling this emits
+ * errorOccurred().
+ */
Q_INVOKABLE void jettingOff ();
/** Switch jetting on for one printhead with a per-nozzle mask (12 chars). */
Q_INVOKABLE void phJettingOn (int printheadId, const QString &jettingMask);
/** Switch jetting off for one printhead. */
Q_INVOKABLE void phJettingOff (int printheadId);
+
+ /**
+ * Switch jetting ON for every *available* printhead — the working
+ * replacement for the broken jettingAllOn().
+ *
+ * Probes every printhead's version (getPhVersion for IDs 1..15), then sends
+ * a per-head CN_PH_JETTING_ON (all-nozzles mask) to each printhead that
+ * reports a valid, non-zero version. Handles non-contiguous printhead IDs
+ * correctly (see docs/DISCREPANCIES.md, D2/D6). Asynchronous: emits
+ * jettingAvailableComplete() once the per-head commands have been sent. A
+ * second call while a scan is in progress is ignored.
+ */
+ Q_INVOKABLE void jettingAvailableOn ();
+ /**
+ * Switch jetting OFF for every available printhead — the working replacement
+ * for the broken jettingOff(). Counterpart to jettingAvailableOn(); same
+ * probe-then-per-head behaviour with CN_PH_JETTING_OFF.
+ */
+ Q_INVOKABLE void jettingAvailableOff ();
/** Switch the JC identification LED on. */
Q_INVOKABLE void jcIdLedOn ();
/** Switch the JC identification LED off. */
@@ -203,6 +245,11 @@ signals:
void jettingOffResult (int controllerId, bool success);
void phJettingOnResult (int controllerId, int printheadId, bool success);
void phJettingOffResult (int controllerId, int printheadId, bool success);
+ /** Emitted when jettingAvailableOn()/Off() has finished probing versions and
+ * has sent the per-head jetting commands. @p printheadIds are the available
+ * heads that were jetted (sorted ascending). */
+ void jettingAvailableComplete (bool turnedOn,
+ const QList<int> &printheadIds);
void jcIdLedOnResult (int controllerId, bool success);
void jcIdLedOffResult (int controllerId, bool success);
void phIdLedOnResult (int controllerId, int printheadId, bool success);
@@ -298,6 +345,7 @@ private slots:
void onSocketMessageReady ();
void onSocketError (QAbstractSocket::SocketError error);
void retryConnection ();
+ void onJettingScanTimeout ();
private:
void setupSocket (QTcpSocket &socket);
@@ -343,6 +391,25 @@ private:
void updateConnectedState ();
QString logTag (const QTcpSocket *socket) const;
+ /* Working "jet all available" workaround for the broken CN_JETTING_ALL_ON /
+ CN_JETTING_OFF (see docs/DISCREPANCIES.md, D6): probe all printhead
+ versions, then jet every head that reports a valid version. */
+ void startJettingScan (bool turnOn);
+ void finishJettingScan ();
+ static bool phVersionIsAvailable (const QString &mcuFirmwareVersion,
+ const QString &fpgaFirmwareVersion,
+ const QString &fpgaHardwareVersion,
+ const QString &bootloaderVersion);
+
+ /* Highest printhead ID to probe (protocol allows up to 15: a 180-char
+ jetting mask is 12 chars x 15 heads). */
+ static constexpr int MaxProbedPrintheadId = 15;
+ /* Give up waiting for version replies after this long and jet whatever
+ responded. */
+ static constexpr int JettingScanTimeoutMs = 2000;
+ /* 12-char all-nozzles-on mask for one printhead. */
+ static constexpr char AllNozzlesOnMask[] = "FFFFFFFFFFFF";
+
QString m_host = QStringLiteral ("127.0.0.1");
quint16 m_commandPort = 9210;
QTcpSocket m_commandSocket;
@@ -355,4 +422,10 @@ private:
QString m_firmwareVersion;
QString m_hardwareVersion;
int m_printheadCount = 0;
+
+ QTimer m_jettingScanTimer;
+ bool m_jettingScanActive = false;
+ bool m_jettingScanTurnOn = false;
+ QSet<int> m_jettingScanPending;
+ QList<int> m_jettingScanAvailable;
};