/** * @file Xpl2Protocol.cpp * @brief Wire framing for the XPL2 printhead protocol. */ #include "Xpl2Protocol.h" #include namespace Xpl2Protocol { QByteArray buildMessage (const QByteArray &command, const QVariantList ¶ms) { 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 splitFields (const QByteArray &data) { QList 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 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