aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-06-16 17:03:26 +0200
committerThomas Vanbesien <tvanbesi@proton.me>2026-06-16 17:03:26 +0200
commite90b28d8ccb9eedc19c23ebeb0129308a74e2865 (patch)
tree0cfdd646986c8ea8b2ecf325ac96fa909db5069a /src
parentbf525d35301dcf0c612598f4394e4357b8378746 (diff)
downloadQtXpl2-reality-check.tar.gz
QtXpl2-reality-check.zip
fix: add workaround for turning all printheads on/offreality-check
Diffstat (limited to 'src')
-rw-r--r--src/Xpl2Client.cpp136
-rw-r--r--src/Xpl2Client.h79
2 files changed, 212 insertions, 3 deletions
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;
};