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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

18.29
/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)0x00);     // Sub Switch MSB
×
83
    data.append((char)0x00);     // Sub Switch LSB
×
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);
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
    info.shortName = QString(shortName.data()).simplified();
×
238
    info.longName = QString(longName.data()).simplified();
×
239

240
    qDebug() << "getArtPollReplyInfo shortName: " << info.shortName;
241
    qDebug() << "getArtPollReplyInfo longName: " << info.longName;
242

243
    return true;
244
}
×
245

246
bool ArtNetPacketizer::fillDMXdata(QByteArray const& data, QByteArray &dmx, quint32 &universe)
×
247
{
248
    if (data.isNull())
×
249
        return false;
250
    dmx.clear();
×
251
    //char sequence = data.at(12);
252
    //qDebug() << "Sequence: " << sequence;
253
    // char physical = data.at(13) // skipped
254
    universe = (data.at(15) << 8) + data.at(14);
×
255

256
    unsigned int msb = (data.at(16)&0xff);
×
257
    unsigned int lsb = (data.at(17)&0xff);
×
258
    int length = (msb << 8) | lsb;
×
259

260
    //qDebug() << "length: " << length;
261
    dmx.append(data.mid(18, length));
×
262
    return true;
×
263
}
264

265
bool ArtNetPacketizer::processTODdata(const QByteArray &data, quint32 &universe, QVariantMap &values)
×
266
{
267
    if (data.isNull() || data.length() < 28)
×
268
        return false;
269

270
    // 0 - 11 ArtNet header
271
    // 12 RDM version
272
    // 13 Port
273
    // 14 - 20 Spare
274
    // 21, 23 address
275
    universe = (data.at(21) << 8) + data.at(23);
×
276
    // 22 Command response
277
    // 24 - 25 UID total
278
    //quint16 uidTotal = (quint8(data.at(24)) << 8) + quint8(data.at(25));
279
    // 26 BlockCount (consider only when total > 200)
280
    // 27 UID count
281
    quint8 uidCount = quint8(data.at(27));
×
282

283
    qDebug() << "UID count:" << uidCount;
284

285
    for (int i = 0; i < uidCount; i++)
×
286
    {
287
        quint16 ESTAId;
288
        quint32 deviceId;
289
        QString UID = RDMProtocol::byteArrayToUID(data.mid(28 + (i * 6), 6), ESTAId, deviceId);
×
290
        qDebug() << "UID:" << UID;
291
        values.insert(QString("UID-%1").arg(i), UID);
×
292
    }
×
293
    values.insert("DISCOVERY_COUNT", uidCount);
×
294

295
    return true;
×
296
}
297

298
bool ArtNetPacketizer::processRDMdata(const QByteArray &data, quint32 &universe, QVariantMap &values)
×
299
{
300
    if (data.isNull() || data.length() < 24)
×
301
        return false;
302

303
    // 0 - 11 ArtNet header
304
    // 12 RDM version
305
    // 13 - 20 zero fillers
306
    // 21, 23 address
307
    universe = (data.at(21) << 8) + data.at(23);
×
308

309
    RDMProtocol rdm;
×
310
    return rdm.parsePacket(data.mid(24), values);
×
311
}
312

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