/** * @file Xpl2Client.cpp * @brief TCP client for the Alchemie XPL2 printhead protocol. */ #include "Xpl2Client.h" #include Xpl2Client::Xpl2Client (QObject *parent) : QObject (parent) { connect (&m_commandSocket, &QTcpSocket::connected, this, &Xpl2Client::onCommandSocketConnected); connect (&m_commandSocket, &QTcpSocket::disconnected, this, &Xpl2Client::onCommandSocketDisconnected); connect (&m_commandSocket, &QTcpSocket::readyRead, this, &Xpl2Client::onCommandSocketMessageReady); connect (&m_commandSocket, &QAbstractSocket::errorOccurred, this, &Xpl2Client::onCommandSocketError); connect (&m_imagingSocket, &QTcpSocket::connected, this, &Xpl2Client::onImagingSocketConnected); connect (&m_imagingSocket, &QTcpSocket::disconnected, this, &Xpl2Client::onImagingSocketDisconnected); connect (&m_imagingSocket, &QTcpSocket::readyRead, this, &Xpl2Client::onImagingSocketMessageReady); connect (&m_imagingSocket, &QAbstractSocket::errorOccurred, this, &Xpl2Client::onImagingSocketError); connect (&m_statusSocket, &QTcpSocket::connected, this, &Xpl2Client::onStatusSocketConnected); connect (&m_statusSocket, &QTcpSocket::disconnected, this, &Xpl2Client::onStatusSocketDisconnected); connect (&m_statusSocket, &QTcpSocket::readyRead, this, &Xpl2Client::onStatusSocketMessageReady); connect (&m_statusSocket, &QAbstractSocket::errorOccurred, this, &Xpl2Client::onStatusSocketError); } /* ------------------------------------------------------------------ */ /* 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; } bool Xpl2Client::wireDebug () const { return m_wireDebug; } void Xpl2Client::setWireDebug (bool enabled) { if (m_wireDebug == enabled) return; m_wireDebug = enabled; emit wireDebugChanged (); } 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; } /* ------------------------------------------------------------------ */ /* 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"); } /* ------------------------------------------------------------------ */ /* Send / dispatch */ /* ------------------------------------------------------------------ */ void Xpl2Client::sendCommand (QTcpSocket &socket, const QByteArray &command, const QVariantList ¶ms) { if (socket.state () != QAbstractSocket::ConnectedState) { emit errorOccurred (QStringLiteral ("Socket not connected for %1") .arg (QString::fromUtf8 (command))); return; } QByteArray data = Xpl2Protocol::buildMessage (command, params); socket.write (data); if (m_wireDebug) emit wireDebugMessage (QStringLiteral ("TX [%1:%2] %3") .arg (socket.peerAddress ().toString ()) .arg (socket.peerPort ()) .arg (QString::fromUtf8 (data).trimmed ())); } void Xpl2Client::dispatchCommandMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") { handleKaPing (m_commandSocket); return; } if (msg.command == "GS_JC_VERSION") { handleGsJcVersion (msg.params); return; } } void Xpl2Client::dispatchImagingMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") { handleKaPing (m_imagingSocket); return; } } void Xpl2Client::dispatchStatusMessage (const Xpl2Protocol::ParsedMessage &msg) { if (msg.command == "KA_PING") { handleKaPing (m_statusSocket); return; } } void Xpl2Client::handleKaPing (QTcpSocket &socket) { qDebug () << "KA_PING received, replying"; 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 (); emit jcVersionReceived (); } /* ------------------------------------------------------------------ */ /* 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 (); } /* ------------------------------------------------------------------ */ /* Socket slots — command */ /* ------------------------------------------------------------------ */ void Xpl2Client::onCommandSocketConnected () { emit statusMessage (QStringLiteral ("Command socket connected")); updateConnectedState (); } void Xpl2Client::onCommandSocketDisconnected () { emit statusMessage (QStringLiteral ("Command socket disconnected")); updateConnectedState (); } void Xpl2Client::onCommandSocketMessageReady () { while (m_commandSocket.canReadLine ()) { QByteArray raw = m_commandSocket.readLine (); if (m_wireDebug) emit wireDebugMessage ( QStringLiteral ("RX [%1:%2] %3") .arg (m_commandSocket.peerAddress ().toString ()) .arg (m_commandSocket.peerPort ()) .arg (QString::fromUtf8 (raw).trimmed ())); auto msg = Xpl2Protocol::parseMessage (raw); if (msg.valid) dispatchCommandMessage (msg); } } void Xpl2Client::onCommandSocketError (QAbstractSocket::SocketError error) { Q_UNUSED (error) emit errorOccurred ( QStringLiteral ("Command: %1").arg (m_commandSocket.errorString ())); } /* ------------------------------------------------------------------ */ /* Socket slots — imaging */ /* ------------------------------------------------------------------ */ void Xpl2Client::onImagingSocketConnected () { emit statusMessage (QStringLiteral ("Imaging socket connected")); updateConnectedState (); } void Xpl2Client::onImagingSocketDisconnected () { emit statusMessage (QStringLiteral ("Imaging socket disconnected")); updateConnectedState (); } void Xpl2Client::onImagingSocketMessageReady () { while (m_imagingSocket.canReadLine ()) { QByteArray raw = m_imagingSocket.readLine (); if (m_wireDebug) emit wireDebugMessage ( QStringLiteral ("RX [%1:%2] %3") .arg (m_imagingSocket.peerAddress ().toString ()) .arg (m_imagingSocket.peerPort ()) .arg (QString::fromUtf8 (raw).trimmed ())); auto msg = Xpl2Protocol::parseMessage (raw); if (msg.valid) dispatchImagingMessage (msg); } } void Xpl2Client::onImagingSocketError (QAbstractSocket::SocketError error) { Q_UNUSED (error) emit errorOccurred ( QStringLiteral ("Imaging: %1").arg (m_imagingSocket.errorString ())); } /* ------------------------------------------------------------------ */ /* Socket slots — status */ /* ------------------------------------------------------------------ */ void Xpl2Client::onStatusSocketConnected () { emit statusMessage (QStringLiteral ("Status socket connected")); updateConnectedState (); } void Xpl2Client::onStatusSocketDisconnected () { emit statusMessage (QStringLiteral ("Status socket disconnected")); updateConnectedState (); } void Xpl2Client::onStatusSocketMessageReady () { while (m_statusSocket.canReadLine ()) { QByteArray raw = m_statusSocket.readLine (); if (m_wireDebug) emit wireDebugMessage ( QStringLiteral ("RX [%1:%2] %3") .arg (m_statusSocket.peerAddress ().toString ()) .arg (m_statusSocket.peerPort ()) .arg (QString::fromUtf8 (raw).trimmed ())); auto msg = Xpl2Protocol::parseMessage (raw); if (msg.valid) dispatchStatusMessage (msg); } } void Xpl2Client::onStatusSocketError (QAbstractSocket::SocketError error) { Q_UNUSED (error) emit errorOccurred ( QStringLiteral ("Status: %1").arg (m_statusSocket.errorString ())); }