• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 26065606211

18 May 2026 11:05PM UTC coverage: 35.646% (+0.6%) from 35.037%
26065606211

Pull #2024

github

web-flow
Merge 11dd00f00 into 2aa9da5af
Pull Request #2024: Validate malformed input packets before decoding

22 of 34 new or added lines in 3 files covered. (64.71%)

2 existing lines in 1 file now uncovered.

18621 of 52239 relevant lines covered (35.65%)

41136.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.36
/plugins/interfaces/rdmprotocol.cpp
1
/*
2
  Q Light Controller Plus
3
  rdmprotocol.cpp
4

5
  Copyright (c) Massimo Callegari
6

7
  Licensed under the Apache License Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing software
14
  distributed under the License is distributed on an "AS IS" BASIS
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QDebug>
21

22
#include "rdmprotocol.h"
23

24
RDMProtocol::RDMProtocol()
11✔
25
    : m_estaID(QLCPLUS_ESTA_ID)
11✔
26
    , m_deviceID(QLCPLUS_DEVICE_ID)
11✔
27
    , m_transactionNum(0x01)
11✔
28
{
29
}
11✔
30

31
void RDMProtocol::setEstaID(quint16 id)
5✔
32
{
33
    m_estaID = id;
5✔
34
}
5✔
35

36
void RDMProtocol::setDeviceId(quint32 id)
5✔
37
{
38
    m_deviceID = id;
5✔
39
}
5✔
40

41
bool RDMProtocol::packetizeCommand(ushort command, QVariantList params, bool startCode, QByteArray &buffer)
5✔
42
{
43
    buffer.clear();
5✔
44

45
    if (startCode)
5✔
46
        buffer.append(char(RDM_START_CODE));
×
47

48
    buffer.append(char(RDM_SC_SUB_MESSAGE));
5✔
49

50
    // temporary length. Fixed at the end
51
    buffer.append(char(0x00));
5✔
52

53
    if (params.length() == 0)
5✔
54
        return false;
×
55

56
    // destination UID
57
    buffer.append(QByteArray::fromHex(params.at(0).toString().toUtf8()));
5✔
58

59
    // source UID
60
    buffer.append(UIDToByteArray(m_estaID, m_deviceID));
5✔
61

62
    // transaction number
63
    buffer.append(char(m_transactionNum));
5✔
64

65
    // Port ID
66
    buffer.append(char(0x01));
5✔
67

68
    // message count
69
    buffer.append(char(0x00));
5✔
70

71
    // SUB device
72
    buffer.append(char(0x00));
5✔
73
    buffer.append(char(0x00));
5✔
74

75
    // Command
76
    buffer.append(char(command));
5✔
77

78
    switch (command)
5✔
79
    {
80
        case DISCOVERY_COMMAND:
×
81
        {
82
            if (params.length() < 2)
×
83
                return false;
×
84

85
            quint16 pid = params.at(1).toUInt();
×
86
            buffer.append(shortToByteArray(pid));
×
87

88
            // check if this is a mute/unmute command
89
            if (pid == PID_DISC_MUTE || pid == PID_DISC_UN_MUTE)
×
90
            {
91
                buffer.append(char(0x00)); // no payload
×
92
            }
93
            else
94
            {
95
                buffer.append(char(0x0C)); // PDL
×
96
                qulonglong start = params.at(2).toULongLong();
×
97
                qulonglong end = params.at(3).toULongLong();
×
98

99
                buffer.append(UIDToByteArray(start >> 32, start & 0x00000000FFFFFFFF)); // Lower bound UID
×
100
                buffer.append(UIDToByteArray(end >> 32, end & 0x00000000FFFFFFFF)); // Upper bound UID
×
101
            }
102
        }
103
        break;
×
104
        case GET_COMMAND:
5✔
105
        {
106
            if (params.length() < 2)
5✔
107
                return false;
×
108

109
            quint16 pid = params.at(1).toUInt();
5✔
110
            buffer.append(shortToByteArray(pid));
5✔
111

112
            if (params.length() > 3)
5✔
113
            {
114
                uchar size = params.at(2).toUInt();
1✔
115
                buffer.append(size); // add PDL
1✔
116
                switch (size)
1✔
117
                {
118
                case 1:
×
119
                    buffer.append(uchar(params.at(3).toUInt()));
×
120
                    break;
×
121
                case 2:
×
122
                    buffer.append(shortToByteArray(params.at(3).toUInt()));
×
123
                    break;
×
124
                case 4:
1✔
125
                    buffer.append(longToByteArray(params.at(3).toUInt()));
1✔
126
                    break;
1✔
127
                default:
×
128
                    break;
×
129
                }
130
            }
131
            else
132
            {
133
                buffer.append(char(0));
4✔
134
            }
135
        }
136
        break;
5✔
137
        case SET_COMMAND:
×
138
        {
139
            if (params.length() < 2)
×
140
                return false;
×
141

142
            quint16 pid = params.at(1).toUInt();
×
143
            buffer.append(shortToByteArray(pid));
×
144
            int pdlPosition = buffer.length();
×
145

146
            // Temporarily add a zero PDL.
147
            // Will be fixed after parameters append
148
            buffer.append(char(0));
×
149

150
            for (int i = 0; i < params.length(); i += 2)
×
151
            {
152
                int size = params.at(i).toInt();
×
153

154
                // special case for byte arrays
155
                if (size == 99)
×
156
                {
157
                    QByteArray ba = params.at(i + 1).toByteArray();
×
158
                    buffer.append(ba);
×
159
                    break;
×
160
                }
×
161

162
                switch (size)
163
                {
164
                    case 1:
×
165
                        buffer.append(uchar(params.at(i + 1).toUInt()));
×
166
                    break;
×
167
                    case 2:
×
168
                        buffer.append(shortToByteArray(params.at(i + 1).toUInt()));
×
169
                    break;
×
170
                    case 4:
×
171
                        buffer.append(longToByteArray(params.at(i + 1).toUInt()));
×
172
                    break;
×
173
                    default:
×
174
                    break;
×
175
                }
176
            }
177

178
            int pdl = buffer.length() - pdlPosition - 1;
×
179
            buffer[pdlPosition] = pdl;
×
180
        }
181
        break;
×
182
        default:
×
183
        break;
×
184
    }
185

186
    // set the correct length
187
    buffer[startCode ? 2 : 1] = buffer.length() + (startCode ? 0 : 1);
5✔
188

189
    // append checksum
190
    buffer.append(shortToByteArray(calculateChecksum(startCode, buffer, buffer.length())));
5✔
191

192
    m_transactionNum++;
5✔
193

194
    return true;
5✔
195
}
196

197
bool RDMProtocol::parseDiscoveryReply(const QByteArray &buffer, QVariantMap &values)
×
198
{
199
    if (buffer.length() < 24)
×
200
        return false;
×
201

202
    int i = 0;
×
203

204
    // check preamble
205
    if (buffer.at(i) != char(0xFE) ||
×
206
        buffer.at(i + 1) != char(0xFE) ||
×
207
        buffer.at(i + 2) != char(0xFE) ||
×
208
        buffer.at(i + 3) != char(0xFE) ||
×
209
        buffer.at(i + 4) != char(0xFE) ||
×
210
        buffer.at(i + 5) != char(0xFE) ||
×
211
        buffer.at(i + 6) != char(0xFE))
×
212
            return false;
×
213

214
    i += 7;
×
215
    // check separator
216
    if (buffer.at(i++) != char(0xAA))
×
217
        return false;
×
218

219
    quint8 mMSB = quint8(buffer.at(i)) & quint8(buffer.at(i + 1));
×
220
    quint8 mLSB = quint8(buffer.at(i + 2)) & quint8(buffer.at(i + 3));
×
221
    i += 4;
×
222

223
    quint8 dMSB3 = quint8(buffer.at(i)) & quint8(buffer.at(i + 1));
×
224
    quint8 dMSB2 = quint8(buffer.at(i + 2)) & quint8(buffer.at(i + 3));
×
225
    quint8 dMSB1 = quint8(buffer.at(i + 4)) & quint8(buffer.at(i + 5));
×
226
    quint8 dLSB  = quint8(buffer.at(i + 6)) & quint8(buffer.at(i + 7));
×
227
    i += 8;
×
228

229
    quint16 ESTAId;
230
    quint32 deviceId;
231
    QByteArray ba;
×
232
    ba.append(mMSB);
×
233
    ba.append(mLSB);
×
234
    ba.append(dMSB3);
×
235
    ba.append(dMSB2);
×
236
    ba.append(dMSB1);
×
237
    ba.append(dLSB);
×
238

239
    QString UID = byteArrayToUID(ba, ESTAId, deviceId);
×
240

241
    // calculate checksum
242
    quint8 cMSB = quint8(buffer.at(i)) & quint8(buffer.at(i + 1));
×
243
    quint8 cLSB = quint8(buffer.at(i + 2)) & quint8(buffer.at(i + 3));
×
244
    quint16 readCS = (cMSB << 8) | cLSB;
×
245
    quint16 calcCS = calculateChecksum(true, buffer.mid(8), 12);
×
246

247
    if (readCS != calcCS)
×
248
    {
249
        qDebug().nospace().noquote() << "ERROR: Read checksum 0x" << QString::number(readCS, 16)
×
250
                                     << ", calculated 0x"<< QString::number(calcCS, 16);
×
251
        return false;
×
252
    }
253

254
    qDebug() << "[RDM] Detected UID:" << UID;
×
255
    values.insert("DISCOVERY_COUNT", 1);
×
256
    values.insert("UID-0", UID);
×
257

258
    return true;
×
259
}
×
260

261
bool RDMProtocol::parsePacket(const QByteArray &buffer, QVariantMap &values)
7✔
262
{
263
    int i = 0;
7✔
264
    bool startCode = false;
7✔
265

266
#ifdef DEBUG_RDM
267
    qDebug() << "[RDM] Packet payload:" << buffer.toHex(',');
268
#endif
269

270
    if (buffer.length() == 0)
7✔
271
        return false;
×
272

273
    // check first bytes
274
    if (buffer.at(i) == char(RDM_START_CODE))
7✔
275
    {
276
        startCode = true;
1✔
277
        i++;
1✔
278
    }
279

280
    if (buffer.length() < i + 23)
7✔
281
        return false;
2✔
282

283
    if (buffer.at(i++) != char(RDM_SC_SUB_MESSAGE))
5✔
284
        return false;
×
285

286
    // Data length
287
    quint8 length = quint8(buffer.at(i++));
5✔
288
    if (buffer.length() < int(length) + (startCode ? 2 : 1))
5✔
289
        return false;
1✔
290

291
    // Destination UID and source UID
292
    quint16 ESTAId;
293
    quint32 deviceId;
294
    QString destUID = byteArrayToUID(buffer.mid(i, 6), ESTAId, deviceId);
4✔
295
    i += 6;
4✔
296
    QString sourceUID = byteArrayToUID(buffer.mid(i, 6), ESTAId, deviceId);
4✔
297
    i += 6;
4✔
298

299
    // check if we are reading our own request
300
    if (ESTAId == m_estaID && deviceId == m_deviceID)
4✔
301
        return false;
×
302

303
    values.insert("UID_INFO", sourceUID);
4✔
304

305
    // transaction number
306
    quint8 transactionNum = quint8(buffer.at(i++));
4✔
307

308
    // Response type
309
    quint8 responseType = quint8(buffer.at(i++));
4✔
310
    values.insert("Response", responseToString(responseType));
4✔
311

312
    if (responseType != RESPONSE_TYPE_ACK)
4✔
313
        qWarning() << "[RDM] bad response type" << responseType;
×
314

315
    // message count
316
    quint8 messageCount = quint8(buffer.at(i++));
4✔
317

318
    // sub device
319
    quint16 subDevice = byteArrayToShort(buffer, i);
4✔
320
    i+=2;
4✔
321

322
    // command class
323
    quint8 commandClass = quint8(buffer.at(i++));
4✔
324

325
    // Parameter ID
326
    quint16 PID = byteArrayToShort(buffer, i);
4✔
327
    values.insert("PID", PID);
4✔
328
    i += 2;
4✔
329

330
    // Parameter data length
331
    quint8 PDL = quint8(buffer.at(i++));
4✔
332

333
    if (buffer.length() < i + PDL + 2)
4✔
334
        return false;
1✔
335

336
#ifdef DEBUG_RDM
337
    qDebug().nospace().noquote() <<
338
        "[RDM] Data length: " << QString::number(length) <<
339
        ", source UID: " << sourceUID << ", destination UID: " << destUID <<
340
        ", transaction number: " << QString::number(transactionNum) <<
341
        ", Response type: " << QString::number(responseType) <<
342
        ", Message count: " << QString::number(messageCount) <<
343
        ", Sub-device: " << QString::number(subDevice) <<
344
        ", Command class: 0x" << QString::number(commandClass, 16) <<
345
        ", Parameter ID: 0x" << QString::number(PID, 16) <<
346
        ", Parameter data length: " << QString::number(PDL);
347
#else
348
    Q_UNUSED(length)
349
    Q_UNUSED(transactionNum)
350
    Q_UNUSED(messageCount)
351
    Q_UNUSED(subDevice)
352
    Q_UNUSED(commandClass)
353
#endif
354

355
    switch (PID)
3✔
356
    {
357
        case PID_SUPPORTED_PARAMETERS:
1✔
358
        {
359
            if (PDL % 2 != 0)
1✔
NEW
360
                return false;
×
361

362
            QVector<quint16> pidList;
1✔
363
#ifdef DEBUG_RDM
364
            QDebug out = qDebug();
365
            out.nospace().noquote() << "Supported PIDs list: ";
366
#endif
367
            for (int n = 0; n < PDL; n += 2)
3✔
368
            {
369
                quint16 pid = byteArrayToShort(buffer, i + n);
2✔
370
                pidList.append(pid);
2✔
371
#ifdef DEBUG_RDM
372
                out << "0x" << QString::number(pid, 16) << ", ";
373
#endif
374
            }
375
            values.insert("PID_LIST", QVariant::fromValue(pidList));
1✔
376
        }
1✔
377
        break;
1✔
378
        case PID_DEVICE_INFO:
2✔
379
        {
380
            if (PDL < 19)
2✔
381
                return false;
1✔
382

383
            values.insert("RDM version", byteArrayToShort(buffer, i));
1✔
384
            values.insert("Device model ID", byteArrayToShort(buffer, i + 2));
1✔
385
            values.insert("TYPE", categoryToString(byteArrayToShort(buffer, i + 4)));
1✔
386
            values.insert("Software version", byteArrayToLong(buffer, i + 6));
1✔
387
            values.insert("DMX_CHANNELS", byteArrayToShort(buffer, i + 10));
1✔
388
            values.insert("PERSONALITY_INDEX", quint8(buffer.at(i + 12)));
1✔
389
            values.insert("PERSONALITY_COUNT", quint8(buffer.at(i + 13)));
1✔
390
            values.insert("DMX_START_ADDRESS", byteArrayToShort(buffer, i + 14));
1✔
391
            values.insert("Sub-device count", byteArrayToShort(buffer, i + 16));
1✔
392
            values.insert("Number of sensors", quint8(buffer.at(i + 18)));
1✔
393
        }
394
        break;
1✔
395
        case PID_DEVICE_MODEL_DESCRIPTION:
×
396
        {
397
            values.insert("MODEL_NAME", QString(buffer.mid(i, PDL)));
×
398
        }
399
        break;
×
400
        case PID_MANUFACTURER_LABEL:
×
401
        {
402
            values.insert("MANUFACTURER", QString(buffer.mid(i, PDL)));
×
403
        }
404
        break;
×
405
        case PID_PARAMETER_DESCRIPTION:
×
406
        {
407
            if (PDL < 20)
×
NEW
408
                return false;
×
409

410
            values.insert("PID_INFO", byteArrayToShort(buffer, i));
×
411
            values.insert("PDL Size", quint8(buffer.at(i + 2)));
×
412
            values.insert("Data type", quint8(buffer.at(i + 3)));
×
413
            values.insert("Command class", quint8(buffer.at(i + 4)));
×
414
            values.insert("Type", quint8(buffer.at(i + 5)));
×
415
            values.insert("Unit", quint8(buffer.at(i + 6)));
×
416
            values.insert("Prefix", quint8(buffer.at(i + 7)));
×
417
            values.insert("Min Valid Value", byteArrayToLong(buffer, i + 8));
×
418
            values.insert("Max Valid Value", byteArrayToLong(buffer, i + 12));
×
419
            values.insert("Default Value", byteArrayToLong(buffer, i + 16));
×
420
            values.insert("PID_DESC", QString(buffer.mid(i + 20, PDL - 20)));
×
421
        }
422
        break;
×
423
        case PID_DMX_PERSONALITY:
×
424
        {
NEW
425
            if (PDL < 2)
×
NEW
426
                return false;
×
427

428
            values.insert("PERS_CURRENT", quint8(buffer.at(i)));
×
429
            values.insert("PERS_COUNT", quint8(buffer.at(i + 1)));
×
430
        }
431
        break;
×
432
        case PID_DMX_PERSONALITY_DESCRIPTION:
×
433
        {
NEW
434
            if (PDL < 3)
×
NEW
435
                return false;
×
436

437
            values.insert("PERS_INDEX", quint8(buffer.at(i)));
×
438
            values.insert("PERS_CHANNELS", byteArrayToShort(buffer, i + 1));
×
439
            values.insert("PERS_DESC", QString(buffer.mid(i + 3, PDL - 3)));
×
440
        }
441
        break;
×
442
        case PID_DMX_START_ADDRESS:
×
443
        {
NEW
444
            if (PDL < 2)
×
NEW
445
                return false;
×
446

UNCOV
447
            values.insert("DMX_START_ADDRESS", byteArrayToShort(buffer, i));
×
448
        }
449
        break;
×
450
        case PID_SLOT_INFO:
×
451
        {
NEW
452
            if (PDL % 5 != 0)
×
NEW
453
                return false;
×
454

UNCOV
455
            QVector<quint16> slotList;
×
456

457
            for (int n = 0; n < PDL; n += 5)
×
458
            {
459
                quint16 slotId = byteArrayToShort(buffer, i + n);
×
460
                slotList.append(slotId);
×
461
                //qDebug().nospace().noquote() << "SLOT ID: " << QString::number(slotId);
462
            }
463
            values.insert("SLOT_LIST", QVariant::fromValue(slotList));
×
464
        }
×
465
        break;
×
466
        case PID_SLOT_DESCRIPTION:
×
467
        {
NEW
468
            if (PDL < 2)
×
NEW
469
                return false;
×
470

471
            values.insert("SLOT_ID", byteArrayToShort(buffer, i));
×
472
            values.insert("SLOT_DESC", QString(buffer.mid(i + 2, PDL - 2)));
×
473
        }
474
        break;
×
475
        default:
×
476
        break;
×
477
    }
478

479
    if (PDL)
2✔
480
    {
481
        QByteArray data = buffer.mid(i, PDL);
2✔
482
        QByteArray hexData = data.toHex(',');
2✔
483
        values.insert("RAW_DATA", hexData);
2✔
484
        QString dString;
2✔
485
        for (int c = 0; c < data.length(); c++)
25✔
486
        {
487
            if (data.at(c) < 0x20)
23✔
488
                dString.append("#");
21✔
489
            else
490
                dString.append(data.at(c));
2✔
491
        }
492
        values.insert("RAW_DATA_STRING", dString);
2✔
493
    }
2✔
494

495
    i += PDL;
2✔
496

497
    quint16 csFromData = byteArrayToShort(buffer, i);
2✔
498
    quint16 csElapsed = calculateChecksum(startCode, buffer, i);
2✔
499

500
    if (csFromData != csElapsed)
2✔
501
    {
502
        qDebug() << "Checksum ERROR. Got:" << QString::number(csFromData, 16) << ", calculated:" << QString::number(csElapsed, 16);
×
503
        return false;
×
504
    }
505

506
    return true;
2✔
507
}
4✔
508

509
QByteArray RDMProtocol::UIDToByteArray(quint16 manufacturer, quint32 deviceId)
5✔
510
{
511
    QByteArray ba;
5✔
512

513
    ba.append(char(manufacturer >> 8));
5✔
514
    ba.append(char(manufacturer & 0x00FF));
5✔
515

516
    ba.append(char((deviceId >> 24) & 0x00FF));
5✔
517
    ba.append(char((deviceId >> 16) & 0x00FF));
5✔
518
    ba.append(char((deviceId >> 8) & 0x00FF));
5✔
519
    ba.append(char(deviceId & 0x00FF));
5✔
520

521
    return ba;
5✔
522
}
×
523

524
QString RDMProtocol::byteArrayToUID(QByteArray buffer, quint16 &ESTAId, quint32 &deviceId)
9✔
525
{
526
    int i = 0;
9✔
527
    ESTAId = quint8(buffer.at(i)) << 8 | quint8(buffer.at(i + 1));
9✔
528
    i += 2;
9✔
529

530
    deviceId  = quint8(buffer.at(i++)) << 24;
9✔
531
    deviceId |= quint8(buffer.at(i++)) << 16;
9✔
532
    deviceId |= quint8(buffer.at(i++)) << 8;
9✔
533
    deviceId |= quint8(buffer.at(i++));
9✔
534

535
    return QString("%1%2")
18✔
536
            .arg(ESTAId, 4, 16, QLatin1Char('0'))
18✔
537
            .arg(deviceId, 8, 16, QLatin1Char('0')).toUpper();
27✔
538
}
539

540
QString RDMProtocol::broadcastAddress()
5✔
541
{
542
    return QString("%1%2")
10✔
543
            .arg(BROADCAST_ESTA_ID, 4, 16)
10✔
544
            .arg(BROADCAST_DEVICE_ID, 6, 16);
10✔
545
}
546

547
QByteArray RDMProtocol::shortToByteArray(quint16 data)
10✔
548
{
549
    QByteArray ba;
10✔
550

551
    ba.append(char(data >> 8));
10✔
552
    ba.append(char(data & 0x00FF));
10✔
553

554
    return ba;
10✔
555
}
×
556

557
QByteArray RDMProtocol::longToByteArray(quint32 data)
1✔
558
{
559
    QByteArray ba;
1✔
560

561
    ba.append(char((data >> 24) & 0x00FF));
1✔
562
    ba.append(char((data >> 16) & 0x00FF));
1✔
563
    ba.append(char((data >> 8) & 0x00FF));
1✔
564
    ba.append(char(data & 0x00FF));
1✔
565

566

567
    return ba;
1✔
568
}
×
569

570
quint16 RDMProtocol::byteArrayToShort(const QByteArray &buffer, int index)
18✔
571
{
572
    if (buffer.length() < index + 2)
18✔
573
        return 0;
×
574

575
    quint16 value = quint8(buffer.at(index)) << 8 |
18✔
576
                    quint8(buffer.at(index + 1));
18✔
577
    return value;
18✔
578
}
579

580
quint32 RDMProtocol::byteArrayToLong(const QByteArray &buffer, int index)
1✔
581
{
582
    if (buffer.length() < index + 4)
1✔
583
        return 0;
×
584

585
    quint32 value = quint8(buffer.at(index)) << 24 |
1✔
586
                    quint8(buffer.at(index + 1)) << 16 |
1✔
587
                    quint8(buffer.at(index + 2)) << 8  |
1✔
588
                    quint8(buffer.at(index + 3));
1✔
589
    return value;
1✔
590
}
591

592
quint16 RDMProtocol::calculateChecksum(bool startCode, const QByteArray &ba, int len)
12✔
593
{
594
    ushort cs = startCode ? 0 : RDM_START_CODE;
12✔
595

596
    for (int i = 0; i < len; i++)
338✔
597
        cs += uchar(ba.at(i));
326✔
598

599
    return cs;
12✔
600
}
601

602
QString RDMProtocol::responseToString(quint8 response)
4✔
603
{
604
    switch (response)
4✔
605
    {
606
        case RESPONSE_TYPE_ACK: return "ACK";
4✔
607
        case RESPONSE_TYPE_ACK_TIMER: return "TIMEOUT";
×
608
        case RESPONSE_TYPE_NACK_REASON: return "NACK";
×
609
        case RESPONSE_TYPE_ACK_OVERFLOW: return "OVERFLOW";
×
610
        default: return "UNKNOWN";
×
611
    }
612
}
613

614
QString RDMProtocol::categoryToString(quint16 category)
1✔
615
{
616
    switch (category)
1✔
617
    {
618
        case 0x0000: return "Not Declared";
1✔
619
        case 0x0100: return "Fixture";
×
620
        case 0x0101: return "Fixture Fixed";
×
621
        case 0x0102: return "Fixture Moving Yoke";
×
622
        case 0x0103: return "Fixture Moving Mirror";
×
623
        case 0x01FF: return "Fixture Other";
×
624
        case 0x0200: return "Fixture Accessory";
×
625
        case 0x0201: return "Fixture Accessory Color";
×
626
        case 0x0202: return "Fixture Accessory Yoke";
×
627
        case 0x0203: return "Fixture Accessory Mirror";
×
628
        case 0x0204: return "Fixture Accessory Effect";
×
629
        case 0x0205: return "Fixture Accessory Beam";
×
630
        case 0x02FF: return "Fixture Accessory Other";
×
631
        case 0x0300: return "Projector";
×
632
        case 0x0301: return "Projector Fixed";
×
633
        case 0x0302: return "Projector Moving Yoke";
×
634
        case 0x0303: return "Projector Moving Mirror";
×
635
        case 0x03FF: return "Projector Other";
×
636
        case 0x0400: return "Atmospheric";
×
637
        case 0x0401: return "Atmospheric Effect";
×
638
        case 0x0402: return "Atmospheric Pyro";
×
639
        case 0x04FF: return "Atmospheric Other";
×
640
        case 0x0500: return "Dimmer";
×
641
        case 0x0501: return "Dimmer AC Incandescent";
×
642
        case 0x0502: return "Dimmer AC Fluorescent";
×
643
        case 0x0503: return "Dimmer AC Cold Cathode";
×
644
        case 0x0504: return "Dimmer AC non-dim";
×
645
        case 0x0505: return "Dimmer AC ELV";
×
646
        case 0x0506: return "Dimmer AC Other";
×
647
        case 0x0507: return "Dimmer DC Level";
×
648
        case 0x0508: return "Dimmer DC PWM";
×
649
        case 0x0509: return "Dimmer CS LED";
×
650
        case 0x05FF: return "Dimmer Other";
×
651
        case 0x0600: return "Power";
×
652
        case 0x0601: return "Power Control";
×
653
        case 0x0602: return "Power Source";
×
654
        case 0x06FF: return "Power Other";
×
655
        case 0x0700: return "Scenic";
×
656
        case 0x0701: return "Scenic Drive";
×
657
        case 0x07FF: return "Scenic Other";
×
658
        case 0x0800: return "Data";
×
659
        case 0x0801: return "Data Distribution";
×
660
        case 0x0802: return "Data Conversion";
×
661
        case 0x08FF: return "Data Other";
×
662
        case 0x0900: return "AV";
×
663
        case 0x0901: return "AV Audio";
×
664
        case 0x0902: return "AV Video";
×
665
        case 0x09FF: return "AV Other";
×
666
        case 0x0A00: return "Monitor";
×
667
        case 0x0A01: return "Monitor AC Line Power";
×
668
        case 0x0A02: return "Monitor DC Power";
×
669
        case 0x0A03: return "Monitor Environmental";
×
670
        case 0x0AFF: return "Monitor Other";
×
671
        case 0x7000: return "Control";
×
672
        case 0x7001: return "Control Controller";
×
673
        case 0x7002: return "Control Backup Device";
×
674
        case 0x70FF: return "Control Other";
×
675
        case 0x7100: return "Test";
×
676
        case 0x7101: return "Test Equipment";
×
677
        case 0x71FF: return "Test Equipment Other";
×
678
        case 0x7FFF: return "Other";
×
679
        default: return "Unknown";
×
680
    }
681
}
682

683
QString RDMProtocol::pidToString(quint16 pid)
×
684
{
685
    switch (pid)
×
686
    {
687
        case PID_DISC_UNIQUE_BRANCH: return "PID_DISC_UNIQUE_BRANCH";
×
688
        case PID_DISC_MUTE: return "PID_DISC_MUTE";
×
689
        case PID_DISC_UN_MUTE: return "PID_DISC_UN_MUTE";
×
690
        case PID_PROXIED_DEVICES: return "PID_PROXIED_DEVICES";
×
691
        case PID_PROXIED_DEVICE_COUNT: return "PID_PROXIED_DEVICE_COUNT";
×
692
        case PID_COMMS_STATUS: return "PID_COMMS_STATUS";
×
693
        case PID_QUEUED_MESSAGE: return "PID_QUEUED_MESSAGE";
×
694
        case PID_STATUS_MESSAGES: return "PID_STATUS_MESSAGES";
×
695
        case PID_STATUS_ID_DESCRIPTION: return "PID_STATUS_ID_DESCRIPTION";
×
696
        case PID_CLEAR_STATUS_ID: return "PID_CLEAR_STATUS_ID";
×
697
        case PID_SUB_DEVICE_STATUS_REPORT_THRESHOLD: return "PID_SUB_DEVICE_STATUS_REPORT_THRESHOLD";
×
698
        case PID_SUPPORTED_PARAMETERS: return "PID_SUPPORTED_PARAMETERS";
×
699
        case PID_PARAMETER_DESCRIPTION: return "PID_PARAMETER_DESCRIPTION";
×
700
        case PID_DEVICE_INFO: return "PID_DEVICE_INFO";
×
701
        case PID_PRODUCT_DETAIL_ID_LIST: return "PID_PRODUCT_DETAIL_ID_LIST";
×
702
        case PID_DEVICE_MODEL_DESCRIPTION: return "PID_DEVICE_MODEL_DESCRIPTION";
×
703
        case PID_MANUFACTURER_LABEL: return "PID_MANUFACTURER_LABEL";
×
704
        case PID_DEVICE_LABEL: return "PID_DEVICE_LABEL";
×
705
        case PID_FACTORY_DEFAULTS: return "PID_FACTORY_DEFAULTS";
×
706
        case PID_LANGUAGE_CAPABILITIES: return "PID_LANGUAGE_CAPABILITIES";
×
707
        case PID_LANGUAGE: return "PID_LANGUAGE";
×
708
        case PID_SOFTWARE_VERSION_LABEL: return "PID_SOFTWARE_VERSION_LABEL";
×
709
        case PID_BOOT_SOFTWARE_VERSION_ID: return "PID_BOOT_SOFTWARE_VERSION_ID";
×
710
        case PID_BOOT_SOFTWARE_VERSION_LABEL: return "PID_BOOT_SOFTWARE_VERSION_LABEL";
×
711
        case PID_DMX_PERSONALITY: return "PID_DMX_PERSONALITY";
×
712
        case PID_DMX_PERSONALITY_DESCRIPTION: return "PID_DMX_PERSONALITY_DESCRIPTION";
×
713
        case PID_DMX_START_ADDRESS: return "PID_DMX_START_ADDRESS";
×
714
        case PID_SLOT_INFO: return "PID_SLOT_INFO";
×
715
        case PID_SLOT_DESCRIPTION: return "PID_SLOT_DESCRIPTION";
×
716
        case PID_DEFAULT_SLOT_VALUE: return "PID_DEFAULT_SLOT_VALUE";
×
717
        case PID_SENSOR_DEFINITION: return "PID_SENSOR_DEFINITION";
×
718
        case PID_SENSOR_VALUE: return "PID_SENSOR_VALUE";
×
719
        case PID_RECORD_SENSORS: return "PID_RECORD_SENSORS";
×
720
        case PID_DEVICE_HOURS: return "PID_DEVICE_HOURS";
×
721
        case PID_LAMP_HOURS: return "PID_LAMP_HOURS";
×
722
        case PID_LAMP_STRIKES: return "PID_LAMP_STRIKES";
×
723
        case PID_LAMP_STATE: return "PID_LAMP_STATE";
×
724
        case PID_LAMP_ON_MODE: return "PID_LAMP_ON_MODE";
×
725
        case PID_DEVICE_POWER_CYCLES: return "PID_DEVICE_POWER_CYCLES";
×
726
        case PID_DISPLAY_INVERT: return "PID_DISPLAY_INVERT";
×
727
        case PID_DISPLAY_LEVEL: return "PID_DISPLAY_LEVEL";
×
728
        case PID_PAN_INVERT: return "PID_PAN_INVERT";
×
729
        case PID_TILT_INVERT: return "PID_TILT_INVERT";
×
730
        case PID_PAN_TILT_SWAP: return "PID_PAN_TILT_SWAP";
×
731
        case PID_REAL_TIME_CLOCK: return "PID_REAL_TIME_CLOCK";
×
732
        case PID_IDENTIFY_DEVICE: return "PID_IDENTIFY_DEVICE";
×
733
        case PID_RESET_DEVICE: return "PID_RESET_DEVICE";
×
734
        case PID_POWER_STATE: return "PID_POWER_STATE";
×
735
        case PID_PERFORM_SELFTEST: return "PID_PERFORM_SELFTEST";
×
736
        case PID_SELF_TEST_DESCRIPTION: return "PID_SELF_TEST_DESCRIPTION";
×
737
        case PID_CAPTURE_PRESET: return "PID_CAPTURE_PRESET";
×
738
        case PID_PRESET_PLAYBACK: return "PID_PRESET_PLAYBACK";
×
739
        case PID_DMX_BLOCK_ADDRESS: return "PID_DMX_BLOCK_ADDRESS";
×
740
        case PID_DMX_FAIL_MODE: return "PID_DMX_FAIL_MODE";
×
741
        case PID_DMX_STARTUP_MODE: return "PID_DMX_STARTUP_MODE";
×
742
        case PID_DIMMER_INFO: return "PID_DIMMER_INFO";
×
743
        case PID_MINIMUM_LEVEL: return "PID_MINIMUM_LEVEL";
×
744
        case PID_MAXIMUM_LEVEL: return "PID_MAXIMUM_LEVEL";
×
745
        case PID_CURVE: return "PID_CURVE";
×
746
        case PID_CURVE_DESCRIPTION: return "PID_CURVE_DESCRIPTION";
×
747
        case PID_OUTPUT_RESPONSE_TIME: return "PID_OUTPUT_RESPONSE_TIME";
×
748
        case PID_OUTPUT_RESPONSE_TIME_DESCRIPTION: return "PID_OUTPUT_RESPONSE_TIME_DESCRIPTION";
×
749
        case PID_MODULATION_FREQUENCY: return "PID_MODULATION_FREQUENCY";
×
750
        case PID_MODULATION_FREQUENCY_DESCRIPTION: return "PID_MODULATION_FREQUENCY_DESCRIPTION";
×
751
        case PID_BURN_IN: return "PID_BURN_IN";
×
752
        case PID_LOCK_PIN: return "PID_LOCK_PIN";
×
753
        case PID_LOCK_STATE: return "PID_LOCK_STATE";
×
754
        case PID_LOCK_STATE_DESCRIPTION: return "PID_LOCK_STATE_DESCRIPTION";
×
755
        case PID_IDENTIFY_MODE: return "PID_IDENTIFY_MODE";
×
756
        case PID_PRESET_INFO: return "PID_PRESET_INFO";
×
757
        case PID_PRESET_STATUS: return "PID_PRESET_STATUS";
×
758
        case PID_PRESET_MERGEMODE: return "PID_PRESET_MERGEMODE";
×
759
        case PID_POWER_ON_SELF_TEST: return "PID_POWER_ON_SELF_TEST";
×
760
        default: return "";
×
761
    }
762
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc