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

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

16.84
/plugins/artnet/src/artnetpacketizer.cpp
1
/*
2
  Q Light Controller Plus
3
  artnetpacketizer.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 "artnetpacketizer.h"
21
#include "rdmprotocol.h"
22

23
#include <QStringList>
24
#include <QDebug>
25

26
ArtNetPacketizer::ArtNetPacketizer()
1✔
27
{
28
    // Initialize a commond header.
29
    // Changing only the tenth byte will create a valid ArtNet header
30
    m_commonHeader.clear();
1✔
31
    m_commonHeader.append(ARTNET_CODE_STR);
1✔
32
    m_commonHeader.append('\0');
1✔
33

34
    // empty opcode
35
    m_commonHeader.append('\0');
1✔
36
    m_commonHeader.append('\0');
1✔
37

38
    // version 14 by default
39
    m_commonHeader.append('\0');
1✔
40
    m_commonHeader.append((char)0x0e);
1✔
41

42
    m_sequence[0] = 1;
1✔
43
    m_sequence[1] = 1;
1✔
44
    m_sequence[2] = 1;
1✔
45
    m_sequence[3] = 1;
1✔
46
}
1✔
47

48
ArtNetPacketizer::~ArtNetPacketizer()
1✔
49
{
50
}
1✔
51

52
/*********************************************************************
53
 * Sender functions
54
 *********************************************************************/
55

56
void ArtNetPacketizer::setupArtNetPoll(QByteArray& data)
×
57
{
58
    data.clear();
×
59
    data.append(m_commonHeader);
×
60
    const char opCodeMSB = (ARTNET_POLL >> 8);
×
61
    data[9] = opCodeMSB;
×
62
    data.append((char)0x02); // TalkToMe
×
63
    data.append('\0'); // Priority
×
64
}
×
65

66
void ArtNetPacketizer::setupArtNetPollReply(QByteArray &data, QHostAddress ipAddr, QString MACaddr, quint32 universe, bool isInput)
×
67
{
68
    int i = 0;
×
69
    data.clear();
×
70
    data.append(m_commonHeader);
×
71
    data.remove(9, 2);
×
72
    const char opCodeMSB = (ARTNET_POLLREPLY >> 8);
×
73
    data[9] = opCodeMSB;
×
74
    QString ipStr = ipAddr.toString();
×
75
    QStringList ipAddrList = ipStr.split(".");
×
76
    foreach (QString val, ipAddrList)
×
77
        data.append((char)val.toInt()); // IP address[4]
×
78
    data.append((char)0x36);     // Port LSB
×
79
    data.append((char)0x19);     // Port MSB
×
80
    data.append((char)0x04);     // Version MSB
×
81
    data.append((char)0x20);     // Version LSB
×
82
    data.append((char)((universe >> 8) & 0xFF)); // NetSwitch (universe bits 14-8)Add commentMore actions
×
83
    data.append((char)((universe >> 4) & 0x0F)); // SubSwitch (universe bits 7-4)
×
84
    data.append((char)0xFF);     // OEM Value MSB
×
85
    data.append((char)0xFF);     // OEM Value LSB
×
86
    data.append((char)0x00);     // UBEA version
×
87
    data.append((char)0xF0);     // Status1 - Ready and booted
×
88
    data.append((char)0xFF);     // ESTA Manufacturer MSB
×
89
    data.append((char)0xFF);     // ESTA Manufacturer LSB
×
90

91
    data.append("QLC+");   // Short Name
×
92
    for (i = 0; i < 14; i++)
×
93
        data.append((char)0x00); // 14 bytes of stuffing
×
94
    data.append("Q Light Controller Plus - ArtNet interface"); // Long Name
×
95
    for (i = 0; i < 22; i++) // 64-42 bytes of stuffing. 42 is the length of the long name
×
96
        data.append((char)0x00);
×
97

98
    for (i = 0; i < 64; i++)
×
99
        data.append((char)0x00); // Node report
×
100
    data.append((char)0x00);     // NumPortsHi
×
101
    data.append((char)0x01);     // NumPortsLo
×
102
    data.append(isInput ? (char)0x40 : (char)0x80); // PortTypes[0]: can input or output DMX512 data
×
103
    data.append((char)0x00);     // PortTypes[1]: nothing
×
104
    data.append((char)0x00);     // PortTypes[2]: nothing
×
105
    data.append((char)0x00);     // PortTypes[3]: nothing
×
106

107
    data.append(isInput ? (char)0x80 : (char)0x00); // GoodInput[0] - input status port 1
×
108
    data.append((char)0x00);     // GoodInput[1] - input status port 2
×
109
    data.append((char)0x00);     // GoodInput[2] - input status port 3
×
110
    data.append((char)0x00);     // GoodInput[3] - input status port 4
×
111

112
    data.append(isInput ? (char)0x00 : (char)0x80); // GoodOutputA[0] - output status port 1
×
113
    data.append((char)0x00);     // GoodOutputA[0] - output status port 2
×
114
    data.append((char)0x00);     // GoodOutputA[0] - output status port 3
×
115
    data.append((char)0x00);     // GoodOutputA[0] - output status port 4
×
116

117
    data.append(isInput ? (char)universe : (char)0x00); // SwIn[0] - port 1
×
118
    data.append((char)0x00);     // SwIn[1] - port 2
×
119
    data.append((char)0x00);     // SwIn[2] - port 3
×
120
    data.append((char)0x00);     // SwIn[3] - port 4
×
121

122
    data.append(isInput ? (char)0x00 : (char)universe); // SwOut[0] - port 1
×
123
    data.append((char)0x00);     // SwOut[1] - port 2
×
124
    data.append((char)0x00);     // SwOut[2] - port 3
×
125
    data.append((char)0x00);     // SwOut[3] - port 4
×
126
    for (i = 0; i < 7; i++)
×
127
        data.append((char)0x00);  // SwVideo, SwMacro, SwRemote and 4 spare bytes
×
128
    QStringList MAC = MACaddr.split(":");
×
129
    foreach (QString couple, MAC)
×
130
    {
131
        bool ok;
132
        data.append((char)couple.toInt(&ok, 16));
×
133
    }
×
134
    for (i = 0; i < 32; i++)
×
135
        data.append((char)0x00); // bindIp[4], BindIndex, Status2 and filler
×
136
}
×
137

138
void ArtNetPacketizer::setupArtNetDmx(QByteArray& data, const int &universe, const QByteArray &values)
4✔
139
{
140
    data.clear();
4✔
141
    data.append(m_commonHeader);
4✔
142
    const char opCodeMSB = (ARTNET_DMX >> 8);
4✔
143
    data[9] = opCodeMSB;
4✔
144
    data.append(m_sequence[universe]); // Sequence
4✔
145
    data.append('\0'); // Physical
4✔
146
    data.append((char)(universe & 0x00FF));
4✔
147
    data.append((char)(universe >> 8));
4✔
148
    int padLength = values.isEmpty() ? 2 : (values.length() % 2); // length must be even in the range 2-512
4✔
149
    int len = values.length() + padLength;
4✔
150
    data.append((char)(len >> 8));
4✔
151
    data.append((char)(len & 0x00FF));
4✔
152
    data.append(values);
4✔
153
    data.append(QByteArray(padLength, 0));
4✔
154

155
    if (m_sequence[universe] == 0xff)
4✔
156
        m_sequence[universe] = 1;
×
157
    else
158
        m_sequence[universe]++;
4✔
159
}
4✔
160

161
void ArtNetPacketizer::setupArtNetTodRequest(QByteArray &data, const int &universe)
×
162
{
163
    data.clear();
×
164
    data.append(m_commonHeader);
×
165
    data[9] = char(ARTNET_TODREQUEST >> 8);
×
166
    data.append(char(0x00)); // Filler1
×
167
    data.append(char(0x00)); // Filler2
×
168
    data.append(char(0x00)); // Spare1
×
169
    data.append(char(0x00)); // Spare2
×
170
    data.append(char(0x00)); // Spare3
×
171
    data.append(char(0x00)); // Spare4
×
172
    data.append(char(0x00)); // Spare5
×
173
    data.append(char(0x00)); // Spare6
×
174
    data.append(char(0x00)); // Spare7
×
175

176
    data.append((char)(universe >> 8));     // Net
×
177
    data.append(char(0x00));                // Command: TodFull
×
178
    data.append(char(0x01));                // AddCount
×
179
    data.append((char)(universe & 0x00FF)); // Address
×
180
}
×
181

182
void ArtNetPacketizer::setupArtNetRdm(QByteArray &data, const int &universe, uchar command, QVariantList params)
×
183
{
184
    RDMProtocol rdm;
×
185
    QByteArray ba;
×
186

187
    data.clear();
×
188
    data.append(m_commonHeader);
×
189
    data[9] = char(ARTNET_RDM >> 8);
×
190
    data.append(char(0x01)); // RDM version 1.0
×
191
    data.append(char(0x00)); // Filler1
×
192
    data.append(char(0x00)); // Spare1
×
193
    data.append(char(0x00)); // Spare2
×
194
    data.append(char(0x00)); // Spare3
×
195
    data.append(char(0x00)); // Spare4
×
196
    data.append(char(0x00)); // Spare5
×
197
    data.append(char(0x00)); // Spare6
×
198
    data.append(char(0x00)); // Spare7
×
199

200
    data.append((char)(universe >> 8));     // Net
×
201
    data.append(char(0x00));                // ArProcess
×
202
    data.append((char)(universe & 0x00FF)); // Address
×
203

204
    rdm.packetizeCommand(command, params, false, ba);
×
205
    data.append(ba);
×
206
}
×
207

208
/*********************************************************************
209
 * Receiver functions
210
 *********************************************************************/
211

212
bool ArtNetPacketizer::checkPacketAndCode(QByteArray const& data, quint16 &code)
×
213
{
214
    /* An ArtNet header must be at least 12 bytes long */
215
    if (data.length() < 12)
×
216
        return false;
×
217

218
    /* Check "Art-Net" keyword presence */
219
    if (data.indexOf(ARTNET_CODE_STR) != 0)
×
220
        return false;
×
221

222
    if (data.at(7) != 0x00)
×
223
        return false;
×
224

225
    code = (quint16(data.at(9)) << 8) + quint16(data.at(8));
×
226

227
    return true;
×
228
}
229

230
bool ArtNetPacketizer::fillArtPollReplyInfo(QByteArray const& data, ArtNetNodeInfo& info)
×
231
{
232
    if (data.isNull())
×
233
        return false;
×
234

235
    QByteArray shortName = data.mid(26, 18);
×
236
    QByteArray longName = data.mid(44, 64);
×
237
    QByteArray nodeReport = data.mid(108, 64);
×
238
        uchar inputStatus = uchar(data.at(178));
×
239

240
    info.shortName = QString(shortName.replace(0, 0x20)).simplified();
×
241
    info.longName = QString(longName.replace(0, 0x20)).simplified();
×
242
    info.portsNumber = (uchar(data.at(172)) << 8) + uchar(data.at(173));
×
243
    info.isInput = (inputStatus & 0x04) == 0 ? true : false;
×
244
    info.isOutput = (inputStatus & 0x04) ? true : false;
×
245
    info.universe = (ushort(data.at(18)) << 8) + (ushort(data.at(19)) << 4) + ushort(data.at(186));
×
246

247
#if 0
248
    qDebug() << "getArtPollReplyInfo shortName:" << info.shortName;
249
    qDebug() << "getArtPollReplyInfo longName:" << info.longName;Add commentMore actions
250
    qDebug() << "getArtPollReplyInfo nodeReport:" << QString(nodeReport).simplified();
251
    qDebug() << "getArtPollReplyInfo universe:" << QString::number(info.universe);
252
#endif
253

254
    return true;
×
255
}
×
256

257
bool ArtNetPacketizer::fillDMXdata(QByteArray const& data, QByteArray &dmx, quint32 &universe)
×
258
{
259
    if (data.isNull())
×
260
        return false;
×
261
    dmx.clear();
×
262
    //char sequence = data.at(12);
263
    //qDebug() << "Sequence: " << sequence;
264
    // char physical = data.at(13) // skipped
265
    universe = (data.at(15) << 8) + data.at(14);
×
266

267
    unsigned int msb = (data.at(16)&0xff);
×
268
    unsigned int lsb = (data.at(17)&0xff);
×
269
    int length = (msb << 8) | lsb;
×
270

271
    //qDebug() << "length: " << length;
272
    dmx.append(data.mid(18, length));
×
273
    return true;
×
274
}
275

276
bool ArtNetPacketizer::processTODdata(const QByteArray &data, quint32 &universe, QVariantMap &values)
×
277
{
278
    if (data.isNull() || data.length() < 28)
×
279
        return false;
×
280

281
    // 0 - 11 ArtNet header
282
    // 12 RDM version
283
    // 13 Port
284
    // 14 - 20 Spare
285
    // 21, 23 address
286
    universe = (data.at(21) << 8) + data.at(23);
×
287
    // 22 Command response
288
    // 24 - 25 UID total
289
    //quint16 uidTotal = (quint8(data.at(24)) << 8) + quint8(data.at(25));
290
    // 26 BlockCount (consider only when total > 200)
291
    // 27 UID count
292
    quint8 uidCount = quint8(data.at(27));
×
293

294
    qDebug() << "UID count:" << uidCount;
×
295

296
    for (int i = 0; i < uidCount; i++)
×
297
    {
298
        quint16 ESTAId;
299
        quint32 deviceId;
300
        QString UID = RDMProtocol::byteArrayToUID(data.mid(28 + (i * 6), 6), ESTAId, deviceId);
×
301
        qDebug() << "UID:" << UID;
×
302
        values.insert(QString("UID-%1").arg(i), UID);
×
303
    }
×
304
    values.insert("DISCOVERY_COUNT", uidCount);
×
305

306
    return true;
×
307
}
308

309
bool ArtNetPacketizer::processRDMdata(const QByteArray &data, quint32 &universe, QVariantMap &values)
×
310
{
311
    if (data.isNull() || data.length() < 24)
×
312
        return false;
×
313

314
    // 0 - 11 ArtNet header
315
    // 12 RDM version
316
    // 13 - 20 zero fillers
317
    // 21, 23 address
318
    universe = (data.at(21) << 8) + data.at(23);
×
319

320
    RDMProtocol rdm;
×
321
    return rdm.parsePacket(data.mid(24), values);
×
322
}
323

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