/** * @file Xpl2Client.cpp * @brief TCP client for the Alchemie XPL2 printhead protocol. */ #include "Xpl2Client.h" #include bool Xpl2Client::s_wireDebug = false; /* ------------------------------------------------------------------ */ /* Response dispatch table */ /* ------------------------------------------------------------------ */ // clang-format off const QHash Xpl2Client::s_responseTable = { /* CN_ JC success shape: controllerId, successFlag */ { "CN_JETTING_ALL_ON", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jettingAllOnResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JETTING_ON", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jettingOnResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JETTING_OFF", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jettingOffResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JC_ID_LED_ON", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcIdLedOnResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JC_ID_LED_OFF", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcIdLedOffResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JC_CALIBRATION", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcCalibrationResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JC_RESET_FAULT_CODES", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcResetFaultCodesResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_JC_STATUS_MESSAGING_STOP", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcStatusMessagingStopResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CN_PH_STATUS_MESSAGING_STOP", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->phStatusMessagingStopResult (p[0].toInt (), p[1].toInt () == 1); } } }, /* CN_ PH success shape: controllerId, printheadId, successFlag */ { "CN_PH_JETTING_ON", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phJettingOnResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CN_PH_JETTING_OFF", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phJettingOffResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CN_PH_ID_LED_ON", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phIdLedOnResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CN_PH_ID_LED_OFF", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phIdLedOffResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CN_PH_CALIBRATION", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phCalibrationResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CN_PH_RESET_FAULT_CODES", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phResetFaultCodesResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CN_PH_NOZZLES_DISABLED", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phNozzlesDisabledResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, /* CN_ calibration data: controllerId, phId, 48 floats */ { "CN_PH_CALIBRATION_DATA", { ResponseShape::CalData, 50, [](auto *s, const auto &p) { emit s->phCalibrationDataReceived (p[0].toInt (), p[1].toInt (), p.mid (2, 48)); } } }, { "CN_PH_CALIBRATION_RAW_DATA", { ResponseShape::CalData, 50, [](auto *s, const auto &p) { emit s->phCalibrationRawDataReceived (p[0].toInt (), p[1].toInt (), p.mid (2, 48)); } } }, /* CN_ base frequency: controllerId, phId, baseFreq, activeBaseFreq */ { "CN_PH_CALIBRATED_BASE_FREQUENCY", { ResponseShape::BaseFreq, 4, [](auto *s, const auto &p) { emit s->phCalibratedBaseFrequencyReceived (p[0].toInt (), p[1].toInt (), p[2].toDouble (), p[3].toDouble ()); } } }, /* CN_ status messaging start: controllerId, level, interval, successFlag */ { "CN_JC_STATUS_MESSAGING_START", { ResponseShape::StatusStart, 4, [](auto *s, const auto &p) { emit s->jcStatusMessagingStartResult (p[0].toInt (), p[1].toInt (), p[2].toInt (), p[3].toInt () == 1); } } }, { "CN_PH_STATUS_MESSAGING_START", { ResponseShape::StatusStart, 4, [](auto *s, const auto &p) { emit s->phStatusMessagingStartResult (p[0].toInt (), p[1].toInt (), p[2].toInt (), p[3].toInt () == 1); } } }, /* CF_ JC success shape: controllerId, successFlag */ { "CF_PH_DEASSIGN_ID", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->phDeassignIdResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_SAVE_CALIBRATION", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcSaveCalibrationResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_RESET_CALIBRATION", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcResetCalibrationResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_SWITCH_OFF_PURGE", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcSwitchOffPurgeResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_RESET_SETTINGS_ALL_PRINTHEADS", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcResetSettingsAllPrintheadsResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_REBOOT_ALL_PRINTHEADS", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcRebootAllPrintheadsResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_RESET_CONTROLLER_SOFTWARE", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcResetControllerSoftwareResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_RESTART", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcRestartResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_SHUTDOWN", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcShutdownResult (p[0].toInt (), p[1].toInt () == 1); } } }, { "CF_JC_SAVE_ALL_PRINTHEAD_SETTINGS", { ResponseShape::JcSuccess, 2, [](auto *s, const auto &p) { emit s->jcSaveAllPrintheadSettingsResult (p[0].toInt (), p[1].toInt () == 1); } } }, /* CF_ PH success shape: controllerId, phId, successFlag */ { "CF_PH_SET_ID", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phSetIdResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CF_PH_SAVE_CALIBRATION", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phSaveCalibrationResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CF_PH_RESET_CALIBRATION", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phResetCalibrationResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CF_PH_RESET_ALL_SETTINGS", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phResetAllSettingsResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CF_PH_REBOOT", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phRebootResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, { "CF_PH_SAVE_SETTINGS", { ResponseShape::PhSuccess, 3, [](auto *s, const auto &p) { emit s->phSaveSettingsResult (p[0].toInt (), p[1].toInt (), p[2].toInt () == 1); } } }, /* CF_ purge settings: controllerId, purgeInterval, purgeTime, successFlag */ { "CF_JC_SET_PURGE_SETTINGS", { ResponseShape::PurgeSettings, 4, [](auto *s, const auto &p) { emit s->jcSetPurgeSettingsResult (p[0].toInt (), p[1].toInt (), p[2].toInt (), p[3].toInt () == 1); } } }, /* CF_ JC jetting params: cid, dutyCycle, pwmFreq, drive, nozzleDriveFreq, nozzleDriveDutyCycle, successFlag */ { "CF_JC_SET_JETTING_PARAMS", { ResponseShape::JcJettingParams, 7, [](auto *s, const auto &p) { emit s->jcSetJettingParamsResult (p[0].toInt (), p[1].toInt (), p[2].toInt (), p[3].toInt (), p[4].toInt (), p[5].toInt (), static_cast (p[6].toInt ())); } } }, /* CF_ PH jetting params: cid, phId, dutyCycle, pwmFreq, drive, nozzleDriveFreq, nozzleDriveDutyCycle, successFlag */ { "CF_PH_SET_JETTING_PARAMS", { ResponseShape::PhJettingParams, 8, [](auto *s, const auto &p) { emit s->phSetJettingParamsResult (p[0].toInt (), p[1].toInt (), p[2].toInt (), p[3].toInt (), p[4].toInt (), p[5].toInt (), p[6].toInt (), static_cast (p[7].toInt ())); } } }, { "CF_PH_GET_JETTING_PARAMS", { ResponseShape::PhJettingParams, 8, [](auto *s, const auto &p) { emit s->phGetJettingParamsResult (p[0].toInt (), p[1].toInt (), p[2].toInt (), p[3].toInt (), p[4].toInt (), p[5].toInt (), p[6].toInt (), static_cast (p[7].toInt ())); } } }, /* CF_ JC setter: cid, saveNewValue, setterId, newValue, successFlag */ { "CF_JC_SETTER", { ResponseShape::JcSetter, 5, [](auto *s, const auto &p) { emit s->jcSetterResult (p[0].toInt (), p[1].toInt () != 0, p[2].toInt (), p[3].toString (), static_cast (p[4].toInt ())); } } }, /* CF_ PH setter: cid, phId, saveNewValue, setterId, newValue, successFlag */ { "CF_PH_SETTER", { ResponseShape::PhSetter, 6, [](auto *s, const auto &p) { emit s->phSetterResult (p[0].toInt (), p[1].toInt (), p[2].toInt () != 0, p[3].toInt (), p[4].toString (), static_cast (p[5].toInt ())); } } }, /* CF_ JC getter: cid, savedValue, getterId, currentValue, successFlag */ { "CF_JC_GETTER", { ResponseShape::JcGetter, 5, [](auto *s, const auto &p) { emit s->jcGetterResult (p[0].toInt (), p[1].toInt () != 0, p[2].toInt (), p[3].toString (), p[4].toInt () == 1); } } }, /* 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); } } }, }; // clang-format on Xpl2Client::Xpl2Client (QObject *parent) : QObject (parent) { for (auto *socket : { &m_commandSocket, &m_imagingSocket, &m_statusSocket }) { connect (socket, &QTcpSocket::connected, this, &Xpl2Client::onSocketConnected); connect (socket, &QTcpSocket::disconnected, this, &Xpl2Client::onSocketDisconnected); connect (socket, &QTcpSocket::readyRead, this, &Xpl2Client::onSocketMessageReady); connect (socket, &QAbstractSocket::errorOccurred, this, &Xpl2Client::onSocketError); } } /* ------------------------------------------------------------------ */ /* Properties */ /* ------------------------------------------------------------------ */ QString Xpl2Client::host () const { return m_host; } void Xpl2Client::setHost (const QString &host) { if (m_host == host) return; m_host = host; emit hostChanged (); } bool Xpl2Client::isConnected () const { return m_connected; } int Xpl2Client::controllerId () const { return m_controllerId; } QString Xpl2Client::firmwareVersion () const { return m_firmwareVersion; } QString Xpl2Client::hardwareVersion () const { return m_hardwareVersion; } int Xpl2Client::printheadCount () const { return m_printheadCount; } void Xpl2Client::enableWireDebug () { s_wireDebug = true; } /* ------------------------------------------------------------------ */ /* Connection */ /* ------------------------------------------------------------------ */ void Xpl2Client::connectToServer () { if (m_connected) { emit statusMessage (QStringLiteral ("Already connected")); return; } emit statusMessage (QStringLiteral ("Connecting to %1…").arg (m_host)); m_commandSocket.connectToHost (m_host, m_commandPort); m_imagingSocket.connectToHost (m_host, m_imagingPort); m_statusSocket.connectToHost (m_host, m_statusPort); } void Xpl2Client::disconnectFromServer () { m_commandSocket.disconnectFromHost (); m_imagingSocket.disconnectFromHost (); m_statusSocket.disconnectFromHost (); } void Xpl2Client::getJcVersion () { sendCommand (m_commandSocket, "GS_JC_VERSION"); } void Xpl2Client::getPhVersion (int printheadId) { sendCommand (m_commandSocket, "GS_PH_VERSION", { printheadId }); } /* ------------------------------------------------------------------ */ /* CN_ Control commands */ /* ------------------------------------------------------------------ */ void Xpl2Client::jettingAllOn () { sendCommand (m_commandSocket, "CN_JETTING_ALL_ON"); } void Xpl2Client::jettingOn (const QString &jettingMask) { sendCommand (m_commandSocket, "CN_JETTING_ON", { jettingMask }); } void Xpl2Client::jettingOff () { sendCommand (m_commandSocket, "CN_JETTING_OFF"); } void Xpl2Client::phJettingOn (int printheadId, const QString &jettingMask) { sendCommand (m_commandSocket, "CN_PH_JETTING_ON", { printheadId, jettingMask }); } void Xpl2Client::phJettingOff (int printheadId) { sendCommand (m_commandSocket, "CN_PH_JETTING_OFF", { printheadId }); } void Xpl2Client::jcIdLedOn () { sendCommand (m_commandSocket, "CN_JC_ID_LED_ON"); } void Xpl2Client::jcIdLedOff () { sendCommand (m_commandSocket, "CN_JC_ID_LED_OFF"); } void Xpl2Client::phIdLedOn (int printheadId) { sendCommand (m_commandSocket, "CN_PH_ID_LED_ON", { printheadId }); } void Xpl2Client::phIdLedOff (int printheadId) { sendCommand (m_commandSocket, "CN_PH_ID_LED_OFF", { printheadId }); } void Xpl2Client::jcCalibration () { sendCommand (m_commandSocket, "CN_JC_CALIBRATION"); } void Xpl2Client::phCalibration (int printheadId) { sendCommand (m_commandSocket, "CN_PH_CALIBRATION", { printheadId }); } void Xpl2Client::phCalibrationData (int printheadId) { sendCommand (m_commandSocket, "CN_PH_CALIBRATION_DATA", { printheadId }); } void Xpl2Client::phCalibrationRawData (int printheadId) { sendCommand (m_commandSocket, "CN_PH_CALIBRATION_RAW_DATA", { printheadId }); } void Xpl2Client::phCalibratedBaseFrequency (int printheadId) { sendCommand (m_commandSocket, "CN_PH_CALIBRATED_BASE_FREQUENCY", { printheadId }); } void Xpl2Client::jcStatusMessagingStart (int statusLevel, int sendIntervalMs) { sendCommand (m_statusSocket, "CN_JC_STATUS_MESSAGING_START", { statusLevel, sendIntervalMs }); } void Xpl2Client::jcStatusMessagingStop () { sendCommand (m_statusSocket, "CN_JC_STATUS_MESSAGING_STOP"); } void Xpl2Client::phStatusMessagingStart (int statusLevel, int sendIntervalMs) { sendCommand (m_statusSocket, "CN_PH_STATUS_MESSAGING_START", { statusLevel, sendIntervalMs }); } void Xpl2Client::phStatusMessagingStop () { sendCommand (m_statusSocket, "CN_PH_STATUS_MESSAGING_STOP"); } void Xpl2Client::jcResetFaultCodes () { sendCommand (m_commandSocket, "CN_JC_RESET_FAULT_CODES"); } void Xpl2Client::phResetFaultCodes (int printheadId) { sendCommand (m_commandSocket, "CN_PH_RESET_FAULT_CODES", { printheadId }); } void Xpl2Client::phNozzlesDisabled (int printheadId, const QString &mask) { sendCommand (m_commandSocket, "CN_PH_NOZZLES_DISABLED", { printheadId, mask }); } /* ------------------------------------------------------------------ */ /* CF_ Configuration commands */ /* ------------------------------------------------------------------ */ void Xpl2Client::phSetId (int printheadId) { sendCommand (m_commandSocket, "CF_PH_SET_ID", { printheadId }); } void Xpl2Client::phDeassignId () { sendCommand (m_commandSocket, "CF_PH_DEASSIGN_ID"); } void Xpl2Client::jcSetJettingParams (int dutyCycle, int pwmFreq, int drive, int nozzleDriveFreq, int nozzleDriveDutyCycle) { sendCommand ( m_commandSocket, "CF_JC_SET_JETTING_PARAMS", { dutyCycle, pwmFreq, drive, nozzleDriveFreq, nozzleDriveDutyCycle }); } void Xpl2Client::phSetJettingParams (int printheadId, int dutyCycle, int pwmFreq, int drive, int nozzleDriveFreq, int nozzleDriveDutyCycle) { sendCommand (m_commandSocket, "CF_PH_SET_JETTING_PARAMS", { printheadId, dutyCycle, pwmFreq, drive, nozzleDriveFreq, nozzleDriveDutyCycle }); } void Xpl2Client::phGetJettingParams (int printheadId) { sendCommand (m_commandSocket, "CF_PH_GET_JETTING_PARAMS", { printheadId }); } void Xpl2Client::jcSaveCalibration () { sendCommand (m_commandSocket, "CF_JC_SAVE_CALIBRATION"); } void Xpl2Client::phSaveCalibration (int printheadId) { sendCommand (m_commandSocket, "CF_PH_SAVE_CALIBRATION", { printheadId }); } void Xpl2Client::jcResetCalibration () { sendCommand (m_commandSocket, "CF_JC_RESET_CALIBRATION"); } void Xpl2Client::phResetCalibration (int printheadId) { sendCommand (m_commandSocket, "CF_PH_RESET_CALIBRATION", { printheadId }); } void Xpl2Client::jcSetPurgeSettings (int purgeIntervalMs, int purgeTimeMs) { sendCommand (m_commandSocket, "CF_JC_SET_PURGE_SETTINGS", { purgeIntervalMs, purgeTimeMs }); } void Xpl2Client::jcSwitchOffPurge () { sendCommand (m_commandSocket, "CF_JC_SWITCH_OFF_PURGE"); } void Xpl2Client::jcSetter (bool saveNewValue, int setterId, const QString &newValue) { sendCommand (m_commandSocket, "CF_JC_SETTER", { saveNewValue, setterId, newValue }); } void Xpl2Client::phSetter (int printheadId, bool saveNewValue, int setterId, const QString &newValue) { sendCommand (m_commandSocket, "CF_PH_SETTER", { printheadId, saveNewValue, setterId, newValue }); } void Xpl2Client::jcGetter (bool getSavedValue, int getterId) { sendCommand (m_commandSocket, "CF_JC_GETTER", { getSavedValue, getterId }); } void Xpl2Client::phGetter (int printheadId, bool getSavedValue, int getterId) { sendCommand (m_commandSocket, "CF_PH_GETTER", { printheadId, getSavedValue, getterId }); } void Xpl2Client::jcResetSettingsAllPrintheads () { sendCommand (m_commandSocket, "CF_JC_RESET_SETTINGS_ALL_PRINTHEADS"); } void Xpl2Client::phResetAllSettings (int printheadId) { sendCommand (m_commandSocket, "CF_PH_RESET_ALL_SETTINGS", { printheadId }); } void Xpl2Client::jcRebootAllPrintheads () { sendCommand (m_commandSocket, "CF_JC_REBOOT_ALL_PRINTHEADS"); } void Xpl2Client::phReboot (int printheadId) { sendCommand (m_commandSocket, "CF_PH_REBOOT", { printheadId }); } void Xpl2Client::jcResetControllerSoftware () { sendCommand (m_commandSocket, "CF_JC_RESET_CONTROLLER_SOFTWARE"); } void Xpl2Client::jcRestart () { sendCommand (m_commandSocket, "CF_JC_RESTART"); } void Xpl2Client::jcShutdown () { sendCommand (m_commandSocket, "CF_JC_SHUTDOWN"); } void Xpl2Client::jcSaveAllPrintheadSettings () { sendCommand (m_commandSocket, "CF_JC_SAVE_ALL_PRINTHEAD_SETTINGS"); } void Xpl2Client::phSaveSettings (int printheadId) { sendCommand (m_commandSocket, "CF_PH_SAVE_SETTINGS", { printheadId }); } /* ------------------------------------------------------------------ */ /* Send / dispatch */ /* ------------------------------------------------------------------ */ void Xpl2Client::sendCommand (QTcpSocket &socket, const QByteArray &command, const QVariantList ¶ms) { if (socket.state () != QAbstractSocket::ConnectedState) { emit errorOccurred (QStringLiteral ("Not connected for %1") .arg (QString::fromUtf8 (command))); return; } QByteArray data = Xpl2Protocol::buildMessage (command, params); socket.write (data); QByteArray wire; if (s_wireDebug) wire = " >> " + data.trimmed (); qDebug ("%s TX %s%s", qPrintable (logTag (&socket)), command.constData (), wire.constData ()); } void Xpl2Client::dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") handleKaPing (m_commandSocket); else if (msg.command == "GS_JC_VERSION") handleGsJcVersion (msg.params); else if (msg.command == "GS_PH_VERSION") handleGsPhVersion (msg.params); else if (!dispatchResponse (msg.command, msg.params, &m_commandSocket)) qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_commandSocket)), msg.command.constData ()); } void Xpl2Client::dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") handleKaPing (m_imagingSocket); else qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_imagingSocket)), msg.command.constData ()); } void Xpl2Client::dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") handleKaPing (m_statusSocket); else if (!dispatchResponse (msg.command, msg.params, &m_statusSocket)) qWarning ("%s Unknown command: %s", qPrintable (logTag (&m_statusSocket)), msg.command.constData ()); } void Xpl2Client::handleKaPing (QTcpSocket &socket) { sendCommand (socket, "KA_PING", { 1 }); } void Xpl2Client::handleGsJcVersion (const QVariantList ¶ms) { if (params.size () < 4) { qWarning () << "GS_JC_VERSION: expected 4 params, got" << params.size (); return; } m_controllerId = params[0].toInt (); m_firmwareVersion = params[1].toString (); m_hardwareVersion = params[2].toString (); m_printheadCount = params[3].toInt (); qDebug ("%s controller=%d fw=%s hw=%s phCount=%d", qPrintable (logTag (&m_commandSocket)), m_controllerId, qPrintable (m_firmwareVersion), qPrintable (m_hardwareVersion), m_printheadCount); emit statusMessage ( QStringLiteral ("RX JC Version: controller=%1 fw=%2 hw=%3 phCount=%4") .arg (m_controllerId) .arg (m_firmwareVersion) .arg (m_hardwareVersion) .arg (m_printheadCount)); emit jcVersionReceived (); } void Xpl2Client::handleGsPhVersion (const QVariantList ¶ms) { if (params.size () < 8) { qWarning () << "GS_PH_VERSION: expected 8 params, got" << params.size (); return; } int cid = params[0].toInt (); int phId = params[1].toInt (); QString mcuFw = params[2].toString (); QString mcuHw = params[3].toString (); QString mcuFwVar = params[4].toString (); QString fpgaFw = params[5].toString (); QString fpgaHw = params[6].toString (); QString bootVer = params[7].toString (); qDebug ("%s PH[%d] mcuFw=%s mcuHw=%s mcuFwVar=%s fpgaFw=%s fpgaHw=%s " "boot=%s", qPrintable (logTag (&m_commandSocket)), phId, qPrintable (mcuFw), qPrintable (mcuHw), qPrintable (mcuFwVar), qPrintable (fpgaFw), qPrintable (fpgaHw), qPrintable (bootVer)); emit statusMessage ( QStringLiteral ( "RX PH[%1] Version: mcuFw=%2 mcuHw=%3 fpgaFw=%4 fpgaHw=%5 boot=%6") .arg (phId) .arg (mcuFw) .arg (mcuHw) .arg (fpgaFw) .arg (fpgaHw) .arg (bootVer)); emit phVersionReceived (cid, phId, mcuFw, mcuHw, mcuFwVar, fpgaFw, fpgaHw, bootVer); } bool Xpl2Client::dispatchResponse (const QByteArray &command, const QVariantList ¶ms, const QTcpSocket *socket) { auto it = s_responseTable.find (command); if (it == s_responseTable.end ()) return false; const ResponseEntry &entry = *it; if (params.size () < entry.minParams) { qWarning ("%s %s: expected %d params, got %lld", qPrintable (logTag (socket)), command.constData (), entry.minParams, static_cast (params.size ())); return true; } int cid = params[0].toInt (); QString cmd = QString::fromUtf8 (command); QString logStr; switch (entry.shape) { case ResponseShape::JcSuccess: logStr = QStringLiteral ("RX %1: controller=%2 success=%3") .arg (cmd) .arg (cid) .arg (params[1].toInt () == 1); break; case ResponseShape::PhSuccess: logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 success=%4") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[2].toInt () == 1); break; case ResponseShape::CalData: logStr = QStringLiteral ("RX %1: controller=%2 ph=%3") .arg (cmd) .arg (cid) .arg (params[1].toInt ()); break; case ResponseShape::BaseFreq: logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 base=%4 active=%5") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[2].toDouble ()) .arg (params[3].toDouble ()); break; case ResponseShape::StatusStart: logStr = QStringLiteral ("RX %1: controller=%2 level=%3 interval=%4 " "success=%5") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[2].toInt ()) .arg (params[3].toInt () == 1); break; case ResponseShape::PurgeSettings: logStr = QStringLiteral ( "RX %1: controller=%2 interval=%3 time=%4 success=%5") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[2].toInt ()) .arg (params[3].toInt () == 1); break; case ResponseShape::JcJettingParams: logStr = QStringLiteral ("RX %1: controller=%2 result=%3") .arg (cmd) .arg (cid) .arg (params[6].toInt ()); break; case ResponseShape::PhJettingParams: logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 result=%4") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[7].toInt ()); break; case ResponseShape::JcSetter: logStr = QStringLiteral ("RX %1: controller=%2 setter=%3 result=%4") .arg (cmd) .arg (cid) .arg (params[2].toInt ()) .arg (params[4].toInt ()); break; case ResponseShape::PhSetter: logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 setter=%4 result=%5") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[3].toInt ()) .arg (params[5].toInt ()); break; case ResponseShape::JcGetter: logStr = QStringLiteral ("RX %1: controller=%2 getter=%3 value=%4 " "success=%5") .arg (cmd) .arg (cid) .arg (params[2].toInt ()) .arg (params[3].toString ()) .arg (params[4].toInt () == 1); break; case ResponseShape::PhGetter: logStr = QStringLiteral ("RX %1: controller=%2 ph=%3 getter=%4 " "value=%5 success=%6") .arg (cmd) .arg (cid) .arg (params[1].toInt ()) .arg (params[3].toInt ()) .arg (params[4].toString ()) .arg (params[5].toInt () == 1); break; } qDebug ("%s %s", qPrintable (logTag (socket)), qPrintable (logStr)); emit statusMessage (logStr); entry.emitter (this, params); return true; } /* ------------------------------------------------------------------ */ /* Internal */ /* ------------------------------------------------------------------ */ void Xpl2Client::updateConnectedState () { bool allConnected = m_commandSocket.state () == QAbstractSocket::ConnectedState && m_imagingSocket.state () == QAbstractSocket::ConnectedState && m_statusSocket.state () == QAbstractSocket::ConnectedState; if (m_connected == allConnected) return; m_connected = allConnected; emit connectedChanged (); } QString Xpl2Client::logTag (const QTcpSocket *socket) const { const char *name = "Unknown"; quint16 port = 0; if (socket == &m_commandSocket) { name = "Command"; port = m_commandPort; } else if (socket == &m_imagingSocket) { name = "Imaging"; port = m_imagingPort; } else if (socket == &m_statusSocket) { name = "Status"; port = m_statusPort; } return QStringLiteral ("[%1:%2]").arg (name).arg (port).leftJustified (15); } /* ------------------------------------------------------------------ */ /* Socket slots */ /* ------------------------------------------------------------------ */ void Xpl2Client::onSocketConnected () { auto *socket = qobject_cast (sender ()); qInfo ("%s Connected", qPrintable (logTag (socket))); updateConnectedState (); } void Xpl2Client::onSocketDisconnected () { auto *socket = qobject_cast (sender ()); if (!socket) { /* Identify by elimination — which socket just left ConnectedState? */ for (auto *s : { &m_commandSocket, &m_imagingSocket, &m_statusSocket }) if (s->state () != QAbstractSocket::ConnectedState) qInfo ("%s Disconnected", qPrintable (logTag (s))); } else qInfo ("%s Disconnected", qPrintable (logTag (socket))); updateConnectedState (); } void Xpl2Client::onSocketMessageReady () { auto *socket = qobject_cast (sender ()); while (socket->canReadLine ()) { QByteArray raw = socket->readLine (); auto msg = Xpl2Protocol::parseMessage (raw); if (msg.valid) { QByteArray wire; if (s_wireDebug) wire = " << " + raw.trimmed (); qDebug ("%s RX %s%s", qPrintable (logTag (socket)), msg.command.constData (), wire.constData ()); if (socket == &m_commandSocket) dispatchCommandMessage (msg); else if (socket == &m_imagingSocket) dispatchImagingMessage (msg); else dispatchStatusMessage (msg); } } } void Xpl2Client::onSocketError (QAbstractSocket::SocketError error) { Q_UNUSED (error) auto *socket = qobject_cast (sender ()); emit errorOccurred (QStringLiteral ("%1 %2") .arg (logTag (socket)) .arg (socket->errorString ())); }