aboutsummaryrefslogtreecommitdiffstats
path: root/mock-server/MockServer.cpp
blob: 981bd1ceaea63c37c3f13d3b845ce0a6c819df66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
 * @file   MockServer.cpp
 * @brief  Mock XPL2 server — listens on all three protocol ports.
 */
#include "MockServer.h"

#include <QTcpSocket>

bool MockServer::s_wireDebug = false;

void
MockServer::enableWireDebug ()
{
  s_wireDebug = true;
}

MockServer::MockServer (QObject *parent) : QObject (parent)
{
  setupPort (m_command, "Command", 9110);
  setupPort (m_imaging, "Imaging", 9111);
  setupPort (m_status, "Status", 9112);

  connect (&m_pingTimer, &QTimer::timeout, this, &MockServer::sendKaPing);
  m_pingTimer.start (1000);
}

void
MockServer::setupPort (Port &port, const char *name, quint16 number)
{
  port.name = name;
  port.number = number;

  connect (&port.server, &QTcpServer::newConnection, this,
           &MockServer::onNewConnection);

  if (!port.server.listen (QHostAddress::Any, number))
    qCritical ("%s Failed to listen: %s", qPrintable (logTag (number)),
               qPrintable (port.server.errorString ()));
  else
    qInfo ("%s Listening", qPrintable (logTag (number)));
}

QString
MockServer::logTag (quint16 localPort) const
{
  const char *name = "Unknown";
  if (localPort == m_command.number)
    name = m_command.name;
  else if (localPort == m_imaging.number)
    name = m_imaging.name;
  else if (localPort == m_status.number)
    name = m_status.name;

  /* Fixed-width tag: "[Command:9110]" = 14 chars, left-padded to 15. */
  return QStringLiteral ("[%1:%2]").arg (name).arg (localPort).leftJustified (
      15);
}

void
MockServer::onNewConnection ()
{
  auto *server = qobject_cast<QTcpServer *> (sender ());
  while (auto *sock = server->nextPendingConnection ())
    {
      quint16 lp = sock->localPort ();
      qInfo ("%s Client connected", qPrintable (logTag (lp)));
      m_clients.append (sock);
      connect (sock, &QTcpSocket::readyRead, this,
               &MockServer::onClientMessageReady);
      connect (sock, &QTcpSocket::disconnected, this,
               &MockServer::onClientDisconnected);
    }
}

void
MockServer::onClientMessageReady ()
{
  auto *sock = qobject_cast<QTcpSocket *> (sender ());
  while (sock->canReadLine ())
    {
      QByteArray line = sock->readLine ();
      handleCommand (sock, line);
    }
}

void
MockServer::onClientDisconnected ()
{
  auto *sock = qobject_cast<QTcpSocket *> (sender ());
  quint16 lp = sock->localPort ();
  qInfo ("%s Client disconnected", qPrintable (logTag (lp)));
  m_clients.removeOne (sock);
  sock->deleteLater ();
}

void
MockServer::sendKaPing ()
{
  for (auto *client : m_clients)
    {
      if (client->state () == QAbstractSocket::ConnectedState)
        sendReply (client, "KA_PING\n");
    }
}

void
MockServer::handleCommand (QTcpSocket *client, const QByteArray &line)
{
  QByteArray trimmed = line.trimmed ();
  if (trimmed.isEmpty ())
    return;

  /* Split on first comma to get command token and remaining params. */
  int comma = trimmed.indexOf (',');
  QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed;
  QByteArray params = (comma >= 0) ? trimmed.mid (comma + 1) : QByteArray ();

  if (cmd == "KA_PING")
    handleKaPing (client, params);
  else if (cmd == "GS_JC_VERSION")
    handleGsJcVersion (client);
  else if (cmd == "GS_PH_VERSION")
    handleGsPhVersion (client, params);
  else
    qWarning ("%s Unknown command: %s",
              qPrintable (logTag (client->localPort ())), cmd.constData ());
}

void
MockServer::sendReply (QTcpSocket *client, const QByteArray &data)
{
  client->write (data);
  QByteArray trimmed = data.trimmed ();
  int comma = trimmed.indexOf (',');
  QByteArray cmd = (comma >= 0) ? trimmed.left (comma) : trimmed;
  QByteArray wire;
  if (s_wireDebug)
    wire = " >> " + trimmed;
  qDebug ("%s TX %s%s", qPrintable (logTag (client->localPort ())),
          cmd.constData (), wire.constData ());
}

void
MockServer::handleKaPing (QTcpSocket *client, const QByteArray &params)
{
  QByteArray wire;
  if (s_wireDebug)
    wire = " << KA_PING," + params;
  qDebug ("%s RX KA_PING ACK%s", qPrintable (logTag (client->localPort ())),
          wire.constData ());
}

void
MockServer::handleGsJcVersion (QTcpSocket *client)
{
  qDebug ("%s RX GS_JC_VERSION", qPrintable (logTag (client->localPort ())));
  sendReply (client, "GS_JC_VERSION,1,\"1.05\",\"2.00\",15\n");
}

void
MockServer::handleGsPhVersion (QTcpSocket *client, const QByteArray &params)
{
  qDebug ("%s RX GS_PH_VERSION,%s", qPrintable (logTag (client->localPort ())),
          params.constData ());
  /* Echo back canned version info for whatever printhead ID was requested. */
  sendReply (client, QByteArray ("GS_PH_VERSION,1,") + params
                         + ",\"3.10\",\"1.00\",\"Standard\","
                           "\"2.05\",\"1.02\",\"0.9.1\"\n");
}