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

mcallegari / qlcplus / 15805077468

22 Jun 2025 08:36AM UTC coverage: 31.876% (-0.01%) from 31.89%
15805077468

push

github

mcallegari
plugins/dmxusb: fix RDM discovery and commands while DMX is running

0 of 1 new or added line in 1 file covered. (0.0%)

3722 existing lines in 175 files now uncovered.

16438 of 51569 relevant lines covered (31.88%)

19266.08 hits per line

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

0.0
/ui/src/rdmmanager.cpp
1
/*
2
  Q Light Controller Plus
3
  rdmmanager.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
#include <QMessageBox>
22

23
#include "rdmmanager.h"
24
#include "outputpatch.h"
25
#include "qlcioplugin.h"
26
#include "rdmprotocol.h"
27
#include "doc.h"
28

29
// RDM view column numbers
30
#define KColumnRDMModel    0
31
#define KColumnRDMUniverse 1
32
#define KColumnRDMAddress  2
33
#define KColumnRDMChannels 3
34
#define KColumnRDMUID      4
35

36
#define MAX_WAIT_COUNT  30
37

38
RDMManager::RDMManager(QWidget *parent, Doc *doc)
×
39
    : QWidget(parent)
40
    , m_doc(doc)
×
41
{
42
    setupUi(this);
×
43

44
    m_getInfoButton->setEnabled(false);
×
45
    m_readButton->setEnabled(false);
×
46

47
    connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh()));
×
48
    connect(m_getInfoButton, SIGNAL(clicked()), this, SLOT(slotGetInfo()));
×
49
    connect(m_rdmTree, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()));
×
50
    connect(m_readButton, SIGNAL(clicked()), this, SLOT(slotReadPID()));
×
51
    connect(m_writeButton, SIGNAL(clicked()), this, SLOT(slotWritePID()));
×
52
}
×
53

54
RDMManager::~RDMManager()
×
55
{
56
}
×
57

58
void RDMManager::slotRefresh()
×
59
{
60
    m_refreshButton->setEnabled(false);
×
61

62
    // reset any previously collected information
63
    m_rdmTree->clear();
×
64

65
    m_devFoundLabel->setText("Discovering fixtures...");
×
66

67
    // go through every universe and launch a RDM discovery for each
68
    // patched plugin which supports the RDM standard
69
    foreach (Universe *uni, m_doc->inputOutputMap()->universes())
×
70
    {
71
        for (int i = 0; i < uni->outputPatchesCount(); i++)
×
72
        {
73
            OutputPatch *op = uni->outputPatch(i);
×
74
            if (op->plugin()->capabilities() & QLCIOPlugin::RDM)
×
75
            {
76
                RDMWorker *wt = new RDMWorker(m_doc);
×
77
                connect(wt, SIGNAL(uidFound(QString, UIDInfo)),
×
78
                        this, SLOT(updateRDMTreeItem(QString, UIDInfo)));
79
                connect(wt, SIGNAL(requestPopup(QString, QString)),
×
80
                        this, SLOT(slotDisplayPopup(QString, QString)));
81
                connect(wt, SIGNAL(finished()),
×
82
                        this, SLOT(slotTaskFinished()));
83
                wt->runDiscovery(uni->id(), op->output());
×
84
            }
85
        }
UNCOV
86
    }
×
87
}
×
88

89
bool RDMManager::getPluginInfo(quint32 universe, quint32 line, quint32 &universeID, quint32 &outputLine)
×
90
{
91
    Universe *uni = m_doc->inputOutputMap()->universe(universe);
×
92
    if (uni == NULL)
×
93
    {
94
        qDebug() << "ERROR. Universe not found!";
×
95
        return false;
×
96
    }
97

UNCOV
98
    OutputPatch *op = NULL;
×
99
    for (int i = 0; i < uni->outputPatchesCount(); i++)
×
100
    {
101
        op = uni->outputPatch(i);
×
102
        if (op->output() == line)
×
UNCOV
103
            break;
×
104
    }
105
    if (op == NULL)
×
106
    {
107
        qDebug() << "ERROR. Output patch not found!";
×
108
        return false;
×
109
    }
110

111
    universeID = uni->id();
×
112
    outputLine = op->output();
×
113

114
    return true;
×
115
}
116

117
void RDMManager::slotGetInfo()
×
118
{
119
    QTreeWidgetItem *item = m_rdmTree->selectedItems().first();
×
120
    QString UID = item->text(KColumnRDMUID);
×
121
    UIDInfo info = m_uidMap.value(UID);
×
122
    quint32 uniID = 0, outLine = 0;
×
123

124
    if (getPluginInfo(info.universe, info.pluginLine, uniID, outLine) == false)
×
125
    {
126
        qDebug() << "ERROR. Cannot get plugin info";
×
UNCOV
127
        return;
×
128
    }
129

130
    RDMWorker *wt = new RDMWorker(m_doc);
×
131
    connect(wt, SIGNAL(fixtureInfoReady(QString&)), this, SIGNAL(fixtureInfoReady(QString&)));
×
132
    connect(wt, SIGNAL(requestPopup(QString, QString)), this, SLOT(slotDisplayPopup(QString, QString)));
×
133
    wt->getUidInfo(uniID, outLine, UID, info);
×
134
}
×
135

136
void RDMManager::slotReadPID()
×
137
{
138
    QTreeWidgetItem *item = m_rdmTree->selectedItems().first();
×
139
    QString UID = item->text(KColumnRDMUID);
×
140
    UIDInfo info = m_uidMap.value(UID);
×
141
    quint32 uniID = 0, outLine = 0;
×
142
    QVariantList params;
×
143

144
    if (getPluginInfo(info.universe, info.pluginLine, uniID, outLine) == false)
×
145
    {
146
        qDebug() << "ERROR. Cannot get plugin info";
×
UNCOV
147
        return;
×
148
    }
149

150
    m_pidResult->clear();
×
151
    QString args = m_pidArgsEdit->text().toLower();
×
152
    bool ok;
153

154
    if (args.length())
×
155
    {
156
        switch(m_dataTypeCombo->currentIndex())
×
157
        {
158
            case ByteArg:
×
159
                params.append(uchar(1));
×
160
                if (args.startsWith("0x"))
×
161
                    params.append(uchar(args.mid(2).toUShort(&ok, 16)));
×
162
                else
163
                    params.append(uchar(args.toUShort()));
×
UNCOV
164
            break;
×
165
            case ShortArg:
×
166
                params.append(uchar(2));
×
167
                if (args.startsWith("0x"))
×
168
                    params.append(args.mid(2).toUShort(&ok, 16));
×
169
                else
170
                    params.append(args.toShort());
×
UNCOV
171
            break;
×
172
            case LongArg:
×
173
                params.append(uchar(4));
×
174
                if (args.startsWith("0x"))
×
175
                    params.append(quint32(args.mid(2).toULong(&ok, 16)));
×
176
                else
177
                    params.append(quint32(args.toULong()));
×
UNCOV
178
            break;
×
179
            case ArrayArg:
×
180
                params.append(uchar(99));
×
181
                foreach (QString arg, args.split(","))
×
182
                    params.append(uchar(arg.toUShort(&ok, 16)));
×
183
            break;
×
184
        }
185
    }
186

187
    RDMWorker *wt = new RDMWorker(m_doc);
×
188
    connect(wt, SIGNAL(requestPopup(QString, QString)), this, SLOT(slotDisplayPopup(QString, QString)));
×
189
    connect(wt, SIGNAL(pidInfoReady(QString)), this, SLOT(slotUpdatePidInfo(QString)));
×
190
    wt->handlePID(uniID, outLine, UID, m_pidEdit->text(), params, false);
×
191
}
×
192

193
void RDMManager::slotWritePID()
×
194
{
195
    QTreeWidgetItem *item = m_rdmTree->selectedItems().first();
×
196
    QString UID = item->text(KColumnRDMUID);
×
197
    UIDInfo info = m_uidMap.value(UID);
×
198
    quint32 uniID = 0, outLine = 0;
×
199
    QVariantList params;
×
200

201
    if (getPluginInfo(info.universe, info.pluginLine, uniID, outLine) == false)
×
202
    {
203
        qDebug() << "ERROR. Cannot get plugin info";
×
UNCOV
204
        return;
×
205
    }
206

207
    m_pidResult->clear();
×
208

209
    if (m_pidArgsEdit->text().length())
×
210
    {
211
        QStringList argList = m_pidArgsEdit->text().split(",");
×
212
        bool ok;
213

214
        if (m_dataTypeCombo->currentIndex() == ArrayArg)
×
215
        {
216
            QByteArray baArg;
×
217

218
            params.append(uchar(99)); // special size for array
×
219

220
            for (int i = 0; i < argList.count(); i++)
×
221
                baArg.append(QByteArray::fromHex(argList.at(i).toUtf8()));
×
222

223
            params.append(baArg);
×
UNCOV
224
        }
×
225
        else
226
        {
227
            for (int i = 0; i < argList.count(); i++)
×
228
            {
UNCOV
229
                QString arg = argList.at(i);
×
230

231
                switch(m_dataTypeCombo->currentIndex())
×
232
                {
233
                    case ByteArg:
×
234
                        params.append(uchar(1));
×
235
                        if (arg.toLower().startsWith("0x"))
×
236
                            params.append(uchar(arg.mid(2).toUShort(&ok, 16)));
×
237
                        else
238
                            params.append(uchar(arg.toUShort()));
×
UNCOV
239
                    break;
×
240
                    case ShortArg:
×
241
                        params.append(uchar(2));
×
242
                        if (arg.toLower().startsWith("0x"))
×
243
                            params.append(arg.mid(2).toShort(&ok, 16));
×
244
                        else
245
                            params.append(arg.toShort());
×
UNCOV
246
                    break;
×
247
                    case LongArg:
×
248
                        params.append(uchar(4));
×
249
                        if (arg.toLower().startsWith("0x"))
×
250
                            params.append(quint32(arg.mid(2).toULong(&ok, 16)));
×
251
                        else
252
                            params.append(quint32(arg.toULong()));
×
UNCOV
253
                    break;
×
254
                }
UNCOV
255
            }
×
256
        }
UNCOV
257
    }
×
258

259
    RDMWorker *wt = new RDMWorker(m_doc);
×
260
    connect(wt, SIGNAL(requestPopup(QString, QString)), this, SLOT(slotDisplayPopup(QString, QString)));
×
261
    connect(wt, SIGNAL(pidInfoReady(QString)), this, SLOT(slotUpdatePidInfo(QString)));
×
262
    wt->handlePID(uniID, outLine, UID, m_pidEdit->text(), params, true);
×
263
}
×
264

265
void RDMManager::slotSelectionChanged()
×
266
{
267
    int selectedCount = m_rdmTree->selectedItems().size();
×
268

269
    m_getInfoButton->setEnabled(selectedCount ? true : false);
×
270
    m_readButton->setEnabled(selectedCount ? true : false);
×
271
}
×
272

273
void RDMManager::slotUpdatePidInfo(QString info)
×
274
{
275
    m_pidResult->setText(info);
×
276
}
×
277

278
void RDMManager::slotDisplayPopup(QString title, QString message)
×
279
{
280
    QMessageBox::information(this, title, message);
×
281
    m_refreshButton->setEnabled(true);
×
282
}
×
283

284
void RDMManager::slotTaskFinished()
×
285
{
286
    m_refreshButton->setEnabled(true);
×
287
}
×
288

289
void RDMManager::updateRDMTreeItem(QString UID, UIDInfo info)
×
290
{
UNCOV
291
    QTreeWidgetItem *item = NULL;
×
292

293
    qDebug() << "Got info for UID" << UID;
×
294

295
    m_uidMap[UID] = info;
×
296

297
    for (int i = 0; i < m_rdmTree->topLevelItemCount(); i++)
×
298
    {
299
        QTreeWidgetItem *tlItem = m_rdmTree->topLevelItem(i);
×
300
        QString itemUID = tlItem->text(KColumnRDMUID);
×
301
        if (itemUID == UID)
×
302
        {
UNCOV
303
            item = tlItem;
×
UNCOV
304
            break;
×
305
        }
UNCOV
306
    }
×
307

308
    if (item == NULL)
×
309
    {
310
        item = new QTreeWidgetItem(m_rdmTree);
×
311
        item->setText(KColumnRDMUID, UID);
×
312
    }
313

314
    item->setText(KColumnRDMModel, QString ("%1 - %2").arg(info.manufacturer).arg(info.name));
×
315
    item->setText(KColumnRDMUniverse, QString::number(info.universe + 1));
×
316
    item->setText(KColumnRDMAddress,  QString::number(info.dmxAddress));
×
317
    item->setText(KColumnRDMChannels, QString::number(info.channels));
×
318

319
    m_rdmTree->header()->resizeSections(QHeaderView::ResizeToContents);
×
320

321
    if (m_rdmTree->topLevelItemCount())
×
322
        m_devFoundLabel->setText(QString("Fixtures found: %1").arg(m_rdmTree->topLevelItemCount()));
×
323
    else
324
        m_devFoundLabel->setText("No fixtures found");
×
325
}
×
326

327
/************************************************************************
328
 * RDM worker implementation
329
 ************************************************************************/
330

331
RDMWorker::RDMWorker(Doc *doc)
×
332
    : m_doc(doc)
×
333
    , m_running(false)
×
334
    , m_requestState(StateNone)
×
335
{
336
}
×
337

338
RDMWorker::~RDMWorker()
×
339
{
340
    stop();
×
341
}
×
342

343
void RDMWorker::runDiscovery(quint32 uni, quint32 line)
×
344
{
345
    m_universe = uni;
×
346
    m_line = line;
×
347

348
    DiscoveryInfo info;
349
    info.startUID = 0;
×
350
    info.endUID = (qulonglong(BROADCAST_ESTA_ID) << 32) + qulonglong(BROADCAST_DEVICE_ID);
×
351
    m_discoveryList.append(info);
×
352

353
    m_requestState = StateDiscoveryStart;
×
354
    start();
×
355
}
×
356

357
void RDMWorker::getUidInfo(quint32 uni, quint32 line, QString UID, UIDInfo &info)
×
358
{
359
    m_universe = uni;
×
360
    m_line = line;
×
361
    m_uidMap[UID] = info;
×
362

363
    QPalette pal;
×
364
    QColor hlBack(pal.color(QPalette::Highlight));
×
365
    QColor hlText(pal.color(QPalette::HighlightedText));
×
366

367
    // initialize the fixture information
368
    m_fixtureInfo = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">";
×
369
    m_fixtureInfo += "<HTML><HEAD></HEAD><STYLE>";
×
370
    m_fixtureInfo += QString(".hilite {" \
×
371
                             "        background-color: %1;" \
372
                             "        color: %2;" \
373
                             "        font-size: x-large;" \
374
                             "}").arg(hlBack.name()).arg(hlText.name());
×
375
    m_fixtureInfo += QString(".subhi {" \
×
376
                             "        background-color: %1;" \
377
                             "        color: %2;" \
378
                             "        font-weight: bold;" \
379
                             "}").arg(hlBack.name()).arg(hlText.name());
×
380
    m_fixtureInfo += QString(".emphasis {" \
×
381
                             "        font-weight: bold;" \
UNCOV
382
                             "}");
×
383
    m_fixtureInfo += QString(".tiny {"\
×
384
                             "   font-size: small;" \
UNCOV
385
                             "}");
×
386
    m_fixtureInfo += "</STYLE>";
×
387

388
    QString title("<TR CLASS='hilite'><TD COLSPAN='2'>%1</TD><TD COLSPAN='2' ALIGN='right'>UID: %3</TD></TR>");
×
389
    QString genInfo("<TR><TD CLASS='emphasis'>%1</TD><TD COLSPAN='3'>%2</TD></TR>");
×
390

391
    /********************************************************************
392
     * General info
393
     ********************************************************************/
394

395
    m_fixtureInfo += "<TABLE COLS='4' WIDTH='100%'>";
×
396

397
    // Fixture title
398
    m_fixtureInfo += title.arg(info.name).arg(UID);
×
399

400
    // Manufacturer
401
    m_fixtureInfo += genInfo.arg(tr("Manufacturer")).arg(info.manufacturer);
×
402
    m_fixtureInfo += genInfo.arg(tr("Model")).arg(info.name);
×
403
    //info += genInfo.arg(tr("Mode")).arg(m_fixtureMode->name());
404
    m_fixtureInfo += genInfo.arg(tr("Type")).arg(info.params.value("TYPE").toString());
×
405

406
    // Universe
407
    m_fixtureInfo += genInfo.arg(tr("Universe")).arg(info.universe + 1);
×
408

409
    // Address
410
    QString range = QString("%1 - %2").arg(info.dmxAddress).arg(info.dmxAddress + info.channels);
×
411
    m_fixtureInfo += genInfo.arg(tr("Address Range")).arg(range);
×
412

413
    // Channels
414
    m_fixtureInfo += genInfo.arg(tr("Channels")).arg(info.channels);
×
415

416
    QString header("<TR CLASS='hilite'><TD COLSPAN='4'>%1</TD></TR>");
×
417
    m_fixtureInfo += header.arg(tr("Personalities"));
×
418

419
    m_requestState = StatePersonalities;
×
420
    start();
×
421
}
×
422

423
void RDMWorker::handlePID(quint32 uni, quint32 line, QString UID, QString pid, QVariantList args, bool write)
×
424
{
425
    m_universe = uni;
×
426
    m_line = line;
×
UNCOV
427
    UIDInfo info;
×
428
    bool ok;
429

430
    if (pid.toLower().startsWith("0x"))
×
431
        info.dmxAddress = pid.mid(2).toUInt(&ok, 16);
×
432
    else
433
        info.dmxAddress = pid.toUInt(&ok, 10);
×
434

435
    if (ok == false)
×
436
    {
437
        emit requestPopup("Error", "Invalid PID entered!");
×
UNCOV
438
        return;
×
439
    }
440

441
    if (args.length())
×
442
    {
443
        for (int i = 0; i < args.count(); i++)
×
444
            info.params.insert(QString::number(i), args.at(i));
×
445
    }
446

447
    m_uidMap[UID] = info;
×
448

449
    if (write)
×
450
        m_requestState = StateWriteSinglePid;
×
451
    else
452
        m_requestState = StateReadSinglePid;
×
453

454
    start();
×
455
}
×
456

457
void RDMWorker::run()
×
458
{
UNCOV
459
    int waitCount = 0;
×
460

461
    Universe *uni = m_doc->inputOutputMap()->universe(m_universe);
×
462
    if (uni == NULL)
×
463
    {
464
        qDebug() << "ERROR. Universe not found!";
×
465
        return;
×
466
    }
467

UNCOV
468
    OutputPatch *op = NULL;
×
469
    for (int i = 0; i < uni->outputPatchesCount(); i++)
×
470
    {
471
        op = uni->outputPatch(i);
×
472
        if (op->output() == m_line)
×
UNCOV
473
            break;
×
474
    }
475
    if (op == NULL)
×
476
    {
477
        qDebug() << "ERROR. Output patch not found!";
×
478
        return;
×
479
    }
480
    m_plugin = op->plugin();
×
481

482
    connect(m_plugin, SIGNAL(rdmValueChanged(quint32, quint32, QVariantMap)),
×
483
            this, SLOT(slotRDMDataReady(quint32, quint32, QVariantMap)));
484

485
    m_running = true;
×
486
    while (m_running == true)
×
487
    {
488
        switch (m_requestState)
×
489
        {
490
            case StateNone:
×
491
            {
492
                // Nothing to do. Terminate the thread
493
                m_running = false;
×
494
            }
495
            break;
×
496
            case StateDiscoveryStart:
×
497
            {
498
                m_requestState = StateDiscoveryContinue;
×
499
                //m_plugin->sendRDMCommand(m_universe, m_line, DISCOVERY_COMMAND,
500
                //                         QVariantList() << RDMProtocol::broadcastAddress() << PID_DISC_UN_MUTE);
501
            }
502
            break;
×
503
            case StateDiscoveryContinue:
×
504
            {
UNCOV
505
                waitCount = 0;
×
506
#if 0
507
                for (int i = 0; i < m_discoveryList.count(); i++)
508
                    qDebug() << "DISCOVERY QUEUE" <<
509
                        QString::number(m_discoveryList.at(i).startUID, 16) <<
510
                        QString::number(m_discoveryList.at(i).endUID, 16);
511
#endif
UNCOV
512
                if (m_discoveryList.isEmpty())
×
513
                {
514
                    m_requestState = StateDiscoveryEnd;
×
UNCOV
515
                    break;
×
516
                }
517

518
                DiscoveryInfo info = m_discoveryList.first();
×
519
                qDebug() << "Discovery - CONTINUE between" << QString::number(info.startUID, 16)
×
520
                         << "and" << QString::number(info.endUID, 16);
×
521
                m_plugin->sendRDMCommand(m_universe, m_line, DISCOVERY_COMMAND,
×
522
                                         QVariantList() << RDMProtocol::broadcastAddress()
×
523
                                                        << PID_DISC_UNIQUE_BRANCH << info.startUID << info.endUID);
×
524

525
                m_requestState = StateWait;
×
526
            }
527
            break;
×
528
            case StateDiscoveryEnd:
×
529
            {
530
                if (m_uidMap.isEmpty())
×
531
                    emit requestPopup("Warning", "No RDM devices found");
×
532
                m_requestState = StateNone;
×
533
            }
534
            break;
×
535
            case StatePersonalities:
×
536
            {
UNCOV
537
                waitCount = 0;
×
UNCOV
538
                QString UID = m_uidMap.firstKey();
×
539
                m_requestState = StatePersonalityInfo;
×
540
                qDebug() << "Requesting personalities of UID" << UID;
×
541
                bool result = m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
542
                                                       QVariantList() << UID << PID_DMX_PERSONALITY);
×
543
                if (result == false)
×
544
                {
545
                    requestPopup("Error", "RDM command failed");
×
546
                    m_requestState = StateNone;
×
547
                }
UNCOV
548
            }
×
549
            break;
×
550
            case StateReadSinglePid:
×
551
            {
UNCOV
552
                waitCount = 0;
×
553
                UIDInfo info = m_uidMap.first();
×
UNCOV
554
                QString UID = m_uidMap.firstKey();
×
555
                m_requestState = StateWaitPidInfo;
×
556
                qDebug().nospace().noquote() << "Read PID 0x" << QString::number(info.dmxAddress, 16);
×
557
                QVariantList args;
×
558
                args << UID;
×
559
                args << info.dmxAddress; // actually the PID to read
×
560
                for (QVariantMap::const_iterator it = info.params.begin(); it != info.params.end(); ++it)
×
UNCOV
561
                    args << it.value(); // add parameters
×
562

563
                uchar command = info.dmxAddress < 0x04 ? DISCOVERY_COMMAND : GET_COMMAND;
×
564
                bool result = m_plugin->sendRDMCommand(m_universe, m_line, command, args);
×
565
                if (result == false)
×
566
                {
567
                    requestPopup("Error", "RDM command failed");
×
568
                    m_requestState = StateNone;
×
569
                }
570
            }
×
571
            break;
×
572
            case StateWriteSinglePid:
×
573
            {
UNCOV
574
                waitCount = 0;
×
575
                UIDInfo info = m_uidMap.first();
×
UNCOV
576
                QString UID = m_uidMap.firstKey();
×
577
                m_requestState = StateWaitPidInfo;
×
578
                qDebug().nospace().noquote() << "Write PID 0x" << QString::number(info.dmxAddress, 16);
×
579
                QVariantList args;
×
580
                args << UID;
×
581
                args << info.dmxAddress; // actually the PID to write
×
582
                for (QVariantMap::const_iterator it = info.params.begin(); it != info.params.end(); ++it)
×
UNCOV
583
                    args << it.value(); // forward parameters
×
584

585
                bool result = m_plugin->sendRDMCommand(m_universe, m_line, SET_COMMAND, args);
×
586
                if (result == false)
×
587
                {
588
                    requestPopup("Error", "RDM command failed");
×
589
                    m_requestState = StateNone;
×
590
                }
591
            }
×
592
            break;
×
593
            default:
×
594
            {
595
                //qDebug() << "[RDM] ....WAIT....";
596
                msleep(50);
×
597
                waitCount++;
×
598
                if (m_requestState == StateWait && waitCount == MAX_WAIT_COUNT)
×
599
                {
600
                    qDebug() << "Exit for timeout...";
×
601
                    emit requestPopup("Warning", "Process timed out");
×
602
                    m_running = false;
×
603
                }
604
            }
UNCOV
605
            break;
×
606
        }
607
    }
608

609
    disconnect(m_plugin, SIGNAL(rdmValueChanged(quint32, quint32, QVariantMap)),
×
610
              this, SLOT(slotRDMDataReady(quint32, quint32, QVariantMap)));
611

612
    qDebug() << "Terminating RDM worker thread";
×
613
}
614

615
void RDMWorker::stop()
×
616
{
617
    if (m_running == true)
×
618
    {
619
        m_running = false;
×
620
        wait();
×
621
    }
622
}
×
623

624
void RDMWorker::slotRDMDataReady(quint32 universe, quint32 line, QVariantMap data)
×
625
{
626
    //qDebug() << "Got signal from universe" << universe << ", line" << line;
627
    //qDebug() << "RDM map" << data;
628

629
    if (m_requestState == StateWaitPidInfo)
×
630
    {
631
        QString pidInfo;
×
632
        for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
×
633
            pidInfo += QString("<b>%1</b>: %2<br>").arg(it.key()).arg(it.value().toString());
×
634

635
        emit pidInfoReady(pidInfo);
×
636
        m_requestState = StateNone;
×
UNCOV
637
        return;
×
UNCOV
638
    }
×
639

640
    // check the signal reason
641
    if (data.contains("DISCOVERY_COUNT"))
×
642
    {
NEW
643
        m_discoveryList.removeFirst();
×
644
        int count = data.value("DISCOVERY_COUNT").toInt();
×
645
        for (int i = 0; i < count; i++)
×
646
        {
647
            QString UID = data.value(QString("UID-%1").arg(i)).toString();
×
648
            if (m_uidMap.contains(UID) == false)
×
649
            {
UNCOV
650
                UIDInfo info;
×
651
                info.universe = universe;
×
652
                info.pluginLine = line;
×
653
                m_uidMap[UID] = info;
×
654

655
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
656
                                         QVariantList() << UID << PID_DEVICE_MODEL_DESCRIPTION);
×
657

658
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
659
                                         QVariantList() << UID << PID_MANUFACTURER_LABEL);
×
660

661
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
662
                                         QVariantList() << UID << PID_DEVICE_INFO);
×
663
            }
×
UNCOV
664
        }
×
665
    }
666
    else if (data.contains("DISCOVERY_ERRORS"))
×
667
    {
668
        if (m_discoveryList.isEmpty())
×
669
            return;
×
670

671
        // Discovery errors mean collisions and/or bad checksum.
672
        // Split the current range into two branches
673
        DiscoveryInfo currentRange = m_discoveryList.first();
×
674
        DiscoveryInfo lowerRange, upperRange;
675

676
        qulonglong midPosition = ((currentRange.startUID & (0x0000800000000000-1)) +
×
677
                                  (currentRange.endUID & (0x0000800000000000-1))) / 2
×
678
                                + ((currentRange.endUID & 0x0000800000000000) ? 0x0000400000000000 : 0)
×
679
                                + ((currentRange.startUID & 0x0000800000000000) ? 0x0000400000000000 :0);
×
680
        lowerRange.startUID = currentRange.startUID;
×
681
        lowerRange.endUID = midPosition;
×
682
        upperRange.startUID = midPosition + 1;
×
683
        upperRange.endUID = currentRange.endUID;
×
684

685
        //qDebug() << "Discovery errors detected" << data.value("DISCOVERY_ERRORS").toInt();
686
        //qDebug() << "Add lower range" << QString::number(lowerRange.startUID, 16) << "-" << QString::number(lowerRange.endUID, 16);
687
        //qDebug() << "Add upper range" << QString::number(upperRange.startUID, 16) << "-" << QString::number(upperRange.endUID, 16);
UNCOV
688
        m_discoveryList.removeFirst();
×
UNCOV
689
        m_discoveryList.prepend(lowerRange);
×
UNCOV
690
        m_discoveryList.prepend(upperRange);
×
691
        m_requestState = StateDiscoveryContinue;
×
692
    }
693
    else if (data.contains("DISCOVERY_NO_REPLY"))
×
694
    {
695
        // No reply means a dead branch. Remove it and continue on other branches.
UNCOV
696
        qDebug() << "Discovery: no reply";
×
UNCOV
697
        m_discoveryList.removeFirst();
×
698
        if (m_discoveryList.isEmpty())
×
699
            m_requestState = StateDiscoveryEnd;
×
700
        else
701
            m_requestState = StateDiscoveryContinue;
×
702
    }
703
    else if (data.contains("UID_INFO"))
×
704
    {
705
        QString UID = data.value("UID_INFO").toString();
×
706
        UIDInfo info = m_uidMap.value(UID);
×
707
        for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
×
708
        {
UNCOV
709
            QString key = it.key();
×
710

711
            if (key == "MODEL_NAME")
×
712
            {
713
                info.name = it.value().toString();
×
714
            }
715
            else if (key == "MANUFACTURER")
×
716
            {
717
                info.manufacturer = it.value().toString();
×
718
            }
719
            else if (key == "DMX_CHANNELS")
×
720
            {
721
                info.channels = it.value().toUInt();
×
722
            }
723
            else if (key == "DMX_START_ADDRESS")
×
724
            {
725
                info.dmxAddress = it.value().toUInt();
×
726
                m_uidMap[UID] = info;
×
727
                emit uidFound(UID, info);
×
728
                if (m_discoveryList.isEmpty())
×
729
                {
730
                    m_requestState = StateDiscoveryEnd;
×
731
                }
732
                else
733
                {
734
                    //m_discoveryList.removeFirst();
735
                    m_requestState = StateDiscoveryContinue;
×
736
                }
737
            }
738
            else if (key == "PERS_COUNT")
×
739
            {
740
                for (quint16 p = 0; p < it.value().toUInt(); p++)
×
741
                    m_requestList.append(p + 1);
×
742
            }
743
            else if (key == "PERS_DESC")
×
744
            {
745
                quint16 cIndex = info.params.value("PERS_CURRENT").toUInt();
×
746
                quint16 pIndex = data.value("PERS_INDEX").toUInt();
×
747
                quint16 channels = data.value("PERS_CHANNELS").toUInt();
×
748
                QString label = it.value().toString();
×
749

750
                m_fixtureInfo += QString("<TR><TD CLASS='emphasis'>%1 %2 %3</TD><TD>%4</TD>")
×
751
                                .arg(tr("Personality")).arg(pIndex)
×
752
                                .arg(cIndex == pIndex ? tr("(Selected)") : "").arg(label);
×
753
                m_fixtureInfo += QString("<TD CLASS='emphasis'>%1</TD><TD>%2</TD></TR>")
×
754
                                        .arg(tr("Channels")).arg(channels);
×
UNCOV
755
            }
×
756
            else if (key == "SLOT_LIST")
×
757
            {
758
                QVariant var = it.value();
×
759
                if (m_requestList.isEmpty())
×
760
                {
761
                    QString header("<TR CLASS='hilite'><TD COLSPAN='4'>%1</TD></TR>");
×
762
                    m_fixtureInfo += header.arg(tr("Channel list"));
×
UNCOV
763
                }
×
764

765
                m_requestList.append(var.value<QVector<quint16>>());
×
766

767
                // check if channels don't fit in a single reply
768
                if (info.params.value("Response") == RDMProtocol::responseToString(RESPONSE_TYPE_ACK_OVERFLOW))
×
769
                {
770
                    m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
771
                                             QVariantList() << UID << PID_SLOT_INFO);
×
772
                }
773
                else
774
                {
775
                    m_requestState = StateSlots;
×
776
                }
777
            }
×
778
            else if (key == "SLOT_DESC")
×
779
            {
780
                quint16 slotId = data.value("SLOT_ID").toUInt();
×
781
                QString label = it.value().toString();
×
782

783
                qDebug() << "SLOT label" << label;
×
784

785
                m_fixtureInfo += QString("<TR><TD CLASS='emphasis'>%1 %2</TD><TD COLSPAN='3'>%3</TD></TR>")
×
786
                                         .arg(tr("Channel")).arg(slotId + 1).arg(label);
×
UNCOV
787
            }
×
788
            else if (key == "PID_LIST")
×
789
            {
790
                QVariant var = it.value();
×
791
                m_requestList = var.value<QVector<quint16>>();
×
792

793
                // remove PIDs handled elsewhere
794
                m_requestList.removeAll(PID_DMX_PERSONALITY);
×
UNCOV
795
                m_requestList.removeAll(PID_DMX_PERSONALITY_DESCRIPTION);
×
UNCOV
796
                m_requestList.removeAll(PID_SLOT_INFO);
×
UNCOV
797
                m_requestList.removeAll(PID_SLOT_DESCRIPTION);
×
798
                std::sort(m_requestList.begin(), m_requestList.end());
×
799

800
                QString header("<TR CLASS='hilite'><TD COLSPAN='4'>%1</TD></TR>");
×
801
                m_fixtureInfo += header.arg(tr("Supported PIDs"));
×
802
                m_requestState = StateSupportedPids;
×
803
            }
×
804
            else if (key == "PID")
×
805
            {
806
                if (m_requestState == StateSupportedPids)
×
807
                {
808
                    quint16 pid = it.value().toUInt();
×
809
                    if (pid < 0x8000 && pid != PID_PARAMETER_DESCRIPTION)
×
810
                    {
811
                        QString sPid = QString("%1").arg(pid, 4, 16, QChar('0'));
×
812
                        m_fixtureInfo += QString("<TR><TD CLASS='emphasis' COLSPAN='4'>PID: 0x%1 (%2)</TD></TR>")
×
813
                                .arg(sPid.toUpper()).arg(RDMProtocol::pidToString(pid));
×
UNCOV
814
                    }                }
×
UNCOV
815
                else if (m_requestState == StateReadSinglePid)
×
816
                {
817
                    // TODO
818
                }
819
            }
820
            else if (key == "PID_DESC")
×
821
            {
822
                if (m_requestState == StateSupportedPids)
×
823
                {
824
                    quint16 pid = data.value("PID_INFO").toUInt();
×
825
                    QString sPid = QString("%1").arg(pid, 4, 16, QChar('0'));
×
826
                    m_fixtureInfo += QString("<TR><TD CLASS='emphasis' COLSPAN='4'>PID: 0x%1 (%2)</TD></TR>")
×
827
                            .arg(sPid.toUpper()).arg(it.value().toString());
×
UNCOV
828
                }
×
UNCOV
829
                else if (m_requestState == StateReadSinglePid)
×
830
                {
831
                    // TODO
832
                }
833
            }
834
            else
835
            {
836
                info.params.insert(it.key(), it.value());
×
837
            }
838

839
            //qDebug() << it.key() << it.value();
840

841
            m_uidMap[UID] = info;
×
UNCOV
842
        }
×
843

844
        if (m_requestState == StatePersonalityInfo)
×
845
        {
846
            if (m_requestList.isEmpty() == false)
×
847
            {
UNCOV
848
                quint16 idx = m_requestList.takeFirst();
×
849
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
850
                                         QVariantList() << UID << PID_DMX_PERSONALITY_DESCRIPTION << 1 << idx);
×
851
            }
852
            else
853
            {
854
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
855
                                       QVariantList() << UID << PID_SLOT_INFO);
×
856
            }
857
        }
858
        else if (m_requestState == StateSlots)
×
859
        {
860
            if (m_requestList.isEmpty() == false)
×
861
            {
UNCOV
862
                quint16 slotId = m_requestList.takeFirst();
×
863
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
864
                                         QVariantList() << UID << PID_SLOT_DESCRIPTION << 2 << slotId);
×
865
            }
866
            else
867
            {
868
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
869
                                         QVariantList() << UID << PID_SUPPORTED_PARAMETERS);
×
870
            }
871
        }
872
        else if (m_requestState == StateSupportedPids)
×
873
        {
874
            if (m_requestList.isEmpty() == false)
×
875
            {
UNCOV
876
                quint16 pid = m_requestList.takeFirst();
×
877

878
                if (pid >= 0x8000)
×
879
                    m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
880
                                             QVariantList() << UID << PID_PARAMETER_DESCRIPTION << 2 << pid);
×
881
                else
882
                    m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
883
                                             QVariantList() << UID << pid);
×
884
            }
885
            else
886
            {
887
                emit fixtureInfoReady(m_fixtureInfo);
×
888
                m_requestState = StateNone;
×
889
            }
890
        }
891
    }
×
892
}
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