aboutsummaryrefslogtreecommitdiffstats
path: root/src/Xpl2Protocol.cpp
blob: 9ee7f4f9087527ff1ccb21f2902acfa5dbace488 (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
/**
 * @file   Xpl2Protocol.cpp
 * @brief  Wire framing for the XPL2 printhead protocol.
 */
#include "Xpl2Protocol.h"

#include <QMetaType>

namespace Xpl2Protocol
{

QByteArray
buildMessage (const QByteArray &command, const QVariantList &params)
{
  QByteArray msg = command;

  for (const QVariant &p : params)
    {
      msg += ',';

      switch (p.typeId ())
        {
        case QMetaType::Bool:
          msg += p.toBool () ? '1' : '0';
          break;
        case QMetaType::Int:
        case QMetaType::LongLong:
          msg += QByteArray::number (p.toLongLong ());
          break;
        case QMetaType::UInt:
        case QMetaType::ULongLong:
          msg += QByteArray::number (p.toULongLong ());
          break;
        case QMetaType::Float:
        case QMetaType::Double:
          msg += QByteArray::number (p.toDouble (), 'g', 10);
          break;
        default:
          msg += '"';
          msg += p.toString ().toUtf8 ();
          msg += '"';
          break;
        }
    }

  msg += Terminator;
  return msg;
}

static QList<QByteArray>
splitFields (const QByteArray &data)
{
  QList<QByteArray> fields;
  QByteArray current;
  bool inQuotes = false;

  for (int i = 0; i < data.size (); ++i)
    {
      char c = data.at (i);

      if (c == '"')
        {
          inQuotes = !inQuotes;
          current += c;
        }
      else if (c == ',' && !inQuotes)
        {
          fields.append (current);
          current.clear ();
        }
      else
        {
          current += c;
        }
    }

  if (!current.isEmpty ())
    fields.append (current);

  return fields;
}

static QVariant
parseField (const QByteArray &field)
{
  if (field.size () >= 2 && field.front () == '"' && field.back () == '"')
    return QString::fromUtf8 (field.mid (1, field.size () - 2));

  bool ok = false;

  int intVal = field.toInt (&ok);
  if (ok)
    return intVal;

  double dblVal = field.toDouble (&ok);
  if (ok)
    return dblVal;

  return QString::fromUtf8 (field);
}

ParsedMessage
parseMessage (const QByteArray &raw)
{
  QByteArray data = raw;

  /* Strip terminator. */
  while (!data.isEmpty () && data.back () == Terminator)
    data.chop (1);

  if (data.isEmpty ())
    return {};

  QList<QByteArray> fields = splitFields (data);
  if (fields.isEmpty ())
    return {};

  ParsedMessage msg;
  msg.command = fields.takeFirst ();
  msg.valid = true;

  for (const QByteArray &f : fields)
    msg.params.append (parseField (f));

  return msg;
}

} // namespace Xpl2Protocol