• 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

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
        }
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

98
    OutputPatch *op = NULL;
×
99
    for (int i = 0; i < uni->outputPatchesCount(); i++)
×
100
    {
101
        op = uni->outputPatch(i);
×
102
        if (op->output() == line)
×
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";
×
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";
×
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()));
×
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());
×
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()));
×
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";
×
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);
×
224
        }
×
225
        else
226
        {
227
            for (int i = 0; i < argList.count(); i++)
×
228
            {
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()));
×
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());
×
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()));
×
253
                    break;
×
254
                }
255
            }
×
256
        }
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
{
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
        {
303
            item = tlItem;
×
304
            break;
×
305
        }
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;" \
382
                             "}");
×
383
    m_fixtureInfo += QString(".tiny {"\
×
384
                             "   font-size: small;" \
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;
×
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!");
×
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
{
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

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)
×
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
            {
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
512
                if (m_discoveryList.isEmpty())
×
513
                {
514
                    m_requestState = StateDiscoveryEnd;
×
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
            {
537
                waitCount = 0;
×
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
                }
548
            }
×
549
            break;
×
550
            case StateReadSinglePid:
×
551
            {
552
                waitCount = 0;
×
553
                UIDInfo info = m_uidMap.first();
×
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)
×
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
            {
574
                waitCount = 0;
×
575
                UIDInfo info = m_uidMap.first();
×
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)
×
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
            }
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;
×
637
        return;
×
638
    }
×
639

640
    // check the signal reason
641
    if (data.contains("DISCOVERY_COUNT"))
×
642
    {
643
        if (m_discoveryList.count())
×
644
            m_discoveryList.removeFirst();
×
645

646
        int count = data.value("DISCOVERY_COUNT").toInt();
×
647
        for (int i = 0; i < count; i++)
×
648
        {
649
            QString UID = data.value(QString("UID-%1").arg(i)).toString();
×
650
            if (m_uidMap.contains(UID) == false)
×
651
            {
652
                UIDInfo info;
×
653
                info.universe = universe;
×
654
                info.pluginLine = line;
×
655
                m_uidMap[UID] = info;
×
656

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

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

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

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

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

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

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

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

767
                m_requestList.append(var.value<QVector<quint16>>());
×
768

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

785
                qDebug() << "SLOT label" << label;
×
786

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

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

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

841
            //qDebug() << it.key() << it.value();
842

843
            m_uidMap[UID] = info;
×
844
        }
×
845

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

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