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

mcallegari / qlcplus / 8961243534

05 May 2024 09:23PM UTC coverage: 32.068% (+4.0%) from 28.094%
8961243534

push

github

mcallegari
Merge branch 'master' into qmltoqt6

902 of 2557 new or added lines in 140 files covered. (35.28%)

166 existing lines in 76 files now uncovered.

15395 of 48008 relevant lines covered (32.07%)

22949.67 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
{
NEW
60
    m_refreshButton->setEnabled(false);
×
61

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

NEW
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)));
NEW
81
                connect(wt, SIGNAL(finished()),
×
82
                        this, SLOT(slotTaskFinished()));
UNCOV
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

152
    if (m_pidArgsEdit->text().length())
×
153
    {
154
        QStringList argList = m_pidArgsEdit->text().split(",");
×
155
        for (int i = 0; i < argList.count(); i++)
×
156
            params.append(argList.at(i));
×
157
    }
158

159
    RDMWorker *wt = new RDMWorker(m_doc);
×
160
    connect(wt, SIGNAL(requestPopup(QString, QString)), this, SLOT(slotDisplayPopup(QString, QString)));
×
161
    connect(wt, SIGNAL(pidInfoReady(QString)), this, SLOT(slotUpdatePidInfo(QString)));
×
162
    wt->handlePID(uniID, outLine, UID, m_pidEdit->text(), params, false);
×
163
}
164

165
void RDMManager::slotWritePID()
×
166
{
167
    QTreeWidgetItem *item = m_rdmTree->selectedItems().first();
×
168
    QString UID = item->text(KColumnRDMUID);
×
169
    UIDInfo info = m_uidMap.value(UID);
×
170
    quint32 uniID = 0, outLine = 0;
×
171
    QVariantList params;
×
172

173
    if (getPluginInfo(info.universe, info.pluginLine, uniID, outLine) == false)
×
174
    {
175
        qDebug() << "ERROR. Cannot get plugin info";
×
176
        return;
×
177
    }
178

179
    m_pidResult->clear();
×
180

181
    if (m_pidArgsEdit->text().length())
×
182
    {
183
        QStringList argList = m_pidArgsEdit->text().split(",");
×
184
        bool ok;
185

NEW
186
        if (m_dataTypeCombo->currentIndex() == ArrayArg)
×
187
        {
NEW
188
            QByteArray baArg;
×
189

NEW
190
            params.append(uchar(99)); // special size for array
×
191

NEW
192
            for (int i = 0; i < argList.count(); i++)
×
NEW
193
                baArg.append(QByteArray::fromHex(argList.at(i).toUtf8()));
×
194

NEW
195
            params.append(baArg);
×
196
        }
197
        else
198
        {
NEW
199
            for (int i = 0; i < argList.count(); i++)
×
200
            {
NEW
201
                QString arg = argList.at(i);
×
202

NEW
203
                switch(m_dataTypeCombo->currentIndex())
×
204
                {
NEW
205
                    case ByteArg:
×
NEW
206
                        params.append(uchar(1));
×
NEW
207
                        if (arg.toLower().startsWith("0x"))
×
NEW
208
                            params.append(uchar(arg.mid(2).toUShort(&ok, 16)));
×
209
                        else
NEW
210
                            params.append(uchar(arg.toUShort()));
×
NEW
211
                    break;
×
NEW
212
                    case ShortArg:
×
NEW
213
                        params.append(uchar(2));
×
NEW
214
                        if (arg.toLower().startsWith("0x"))
×
NEW
215
                            params.append(arg.mid(2).toShort(&ok, 16));
×
216
                        else
NEW
217
                            params.append(arg.toShort());
×
NEW
218
                    break;
×
NEW
219
                    case LongArg:
×
NEW
220
                        params.append(uchar(4));
×
NEW
221
                        if (arg.toLower().startsWith("0x"))
×
NEW
222
                            params.append(quint32(arg.mid(2).toULong(&ok, 16)));
×
223
                        else
NEW
224
                            params.append(quint32(arg.toULong()));
×
NEW
225
                    break;
×
226
                }
227
            }
228
        }
229
    }
230

231
    RDMWorker *wt = new RDMWorker(m_doc);
×
232
    connect(wt, SIGNAL(requestPopup(QString, QString)), this, SLOT(slotDisplayPopup(QString, QString)));
×
233
    connect(wt, SIGNAL(pidInfoReady(QString)), this, SLOT(slotUpdatePidInfo(QString)));
×
234
    wt->handlePID(uniID, outLine, UID, m_pidEdit->text(), params, true);
×
235
}
236

237
void RDMManager::slotSelectionChanged()
×
238
{
239
    int selectedCount = m_rdmTree->selectedItems().size();
×
240

241
    m_getInfoButton->setEnabled(selectedCount ? true : false);
×
242
    m_readButton->setEnabled(selectedCount ? true : false);
×
243
}
×
244

245
void RDMManager::slotUpdatePidInfo(QString info)
×
246
{
247
    m_pidResult->setText(info);
×
248
}
×
249

250
void RDMManager::slotDisplayPopup(QString title, QString message)
×
251
{
252
    QMessageBox::information(this, title, message);
×
NEW
253
    m_refreshButton->setEnabled(true);
×
NEW
254
}
×
255

NEW
256
void RDMManager::slotTaskFinished()
×
257
{
NEW
258
    m_refreshButton->setEnabled(true);
×
UNCOV
259
}
×
260

261
void RDMManager::updateRDMTreeItem(QString UID, UIDInfo info)
×
262
{
263
    QTreeWidgetItem *item = NULL;
×
264

265
    qDebug() << "Got info for UID" << UID;
×
266

267
    m_uidMap[UID] = info;
×
268

269
    for (int i = 0; i < m_rdmTree->topLevelItemCount(); i++)
×
270
    {
271
        QTreeWidgetItem *tlItem = m_rdmTree->topLevelItem(i);
×
272
        QString itemUID = tlItem->text(KColumnRDMUID);
×
273
        if (itemUID == UID)
×
274
        {
275
            item = tlItem;
×
276
            break;
×
277
        }
278
    }
279

280
    if (item == NULL)
×
281
    {
282
        item = new QTreeWidgetItem(m_rdmTree);
×
283
        item->setText(KColumnRDMUID, UID);
×
284
    }
285

NEW
286
    item->setText(KColumnRDMModel, QString ("%1 - %2").arg(info.manufacturer).arg(info.name));
×
287
    item->setText(KColumnRDMUniverse, QString::number(info.universe + 1));
×
288
    item->setText(KColumnRDMAddress,  QString::number(info.dmxAddress));
×
289
    item->setText(KColumnRDMChannels, QString::number(info.channels));
×
290

291
    m_rdmTree->header()->resizeSections(QHeaderView::ResizeToContents);
×
292

NEW
293
    if (m_rdmTree->topLevelItemCount())
×
NEW
294
        m_devFoundLabel->setText(QString("Fixtures found: %1").arg(m_rdmTree->topLevelItemCount()));
×
295
    else
NEW
296
        m_devFoundLabel->setText("No fixtures found");
×
UNCOV
297
}
×
298

299
/************************************************************************
300
 * RDM worker implementation
301
 ************************************************************************/
302

303
RDMWorker::RDMWorker(Doc *doc)
×
304
    : m_doc(doc)
305
    , m_running(false)
306
    , m_requestState(StateNone)
×
307
{
308
}
×
309

310
RDMWorker::~RDMWorker()
×
311
{
312
    stop();
×
313
}
×
314

315
void RDMWorker::runDiscovery(quint32 uni, quint32 line)
×
316
{
317
    m_universe = uni;
×
318
    m_line = line;
×
319

320
    DiscoveryInfo info;
321
    info.startUID = 0;
×
322
    info.endUID = (qulonglong(BROADCAST_ESTA_ID) << 32) + qulonglong(BROADCAST_DEVICE_ID);
×
323
    m_discoveryList.append(info);
×
324

325
    m_requestState = StateDiscoveryStart;
×
326
    start();
×
327
}
×
328

329
void RDMWorker::getUidInfo(quint32 uni, quint32 line, QString UID, UIDInfo &info)
×
330
{
331
    m_universe = uni;
×
332
    m_line = line;
×
333
    m_uidMap[UID] = info;
×
334

335
    QPalette pal;
×
336
    QColor hlBack(pal.color(QPalette::Highlight));
×
337
    QColor hlText(pal.color(QPalette::HighlightedText));
×
338

339
    // initialize the fixture information
340
    m_fixtureInfo = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">";
×
341
    m_fixtureInfo += "<HTML><HEAD></HEAD><STYLE>";
×
342
    m_fixtureInfo += QString(".hilite {" \
×
343
                             "        background-color: %1;" \
344
                             "        color: %2;" \
345
                             "        font-size: x-large;" \
346
                             "}").arg(hlBack.name()).arg(hlText.name());
×
347
    m_fixtureInfo += QString(".subhi {" \
×
348
                             "        background-color: %1;" \
349
                             "        color: %2;" \
350
                             "        font-weight: bold;" \
351
                             "}").arg(hlBack.name()).arg(hlText.name());
×
352
    m_fixtureInfo += QString(".emphasis {" \
×
353
                             "        font-weight: bold;" \
354
                             "}");
×
355
    m_fixtureInfo += QString(".tiny {"\
×
356
                             "   font-size: small;" \
357
                             "}");
×
358
    m_fixtureInfo += "</STYLE>";
×
359

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

363
    /********************************************************************
364
     * General info
365
     ********************************************************************/
366

367
    m_fixtureInfo += "<TABLE COLS='4' WIDTH='100%'>";
×
368

369
    // Fixture title
370
    m_fixtureInfo += title.arg(info.name).arg(UID);
×
371

372
    // Manufacturer
NEW
373
    m_fixtureInfo += genInfo.arg(tr("Manufacturer")).arg(info.manufacturer);
×
374
    m_fixtureInfo += genInfo.arg(tr("Model")).arg(info.name);
×
375
    //info += genInfo.arg(tr("Mode")).arg(m_fixtureMode->name());
376
    m_fixtureInfo += genInfo.arg(tr("Type")).arg(info.params.value("TYPE").toString());
×
377

378
    // Universe
379
    m_fixtureInfo += genInfo.arg(tr("Universe")).arg(info.universe + 1);
×
380

381
    // Address
382
    QString range = QString("%1 - %2").arg(info.dmxAddress).arg(info.dmxAddress + info.channels);
×
383
    m_fixtureInfo += genInfo.arg(tr("Address Range")).arg(range);
×
384

385
    // Channels
386
    m_fixtureInfo += genInfo.arg(tr("Channels")).arg(info.channels);
×
387

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

391
    m_requestState = StatePersonalities;
×
392
    start();
×
393
}
×
394

395
void RDMWorker::handlePID(quint32 uni, quint32 line, QString UID, QString pid, QVariantList args, bool write)
×
396
{
397
    m_universe = uni;
×
398
    m_line = line;
×
399
    UIDInfo info;
×
400
    bool ok;
401

402
    if (pid.toLower().startsWith("0x"))
×
403
        info.dmxAddress = pid.mid(2).toUInt(&ok, 16);
×
404
    else
405
        info.dmxAddress = pid.toUInt(&ok, 10);
×
406

407
    if (ok == false)
×
408
    {
409
        emit requestPopup("Error", "Invalid PID entered!");
×
410
        return;
×
411
    }
412

413
    if (args.length())
×
414
    {
415
        for (int i = 0; i < args.count(); i++)
×
416
            info.params.insert(QString::number(i), args.at(i));
×
417
    }
418

419
    m_uidMap[UID] = info;
×
420

421
    if (write)
×
422
        m_requestState = StateWriteSinglePid;
×
423
    else
424
        m_requestState = StateReadSinglePid;
×
425

426
    start();
×
427
}
428

429
void RDMWorker::run()
×
430
{
431
    int waitCount = 0;
×
432

433
    Universe *uni = m_doc->inputOutputMap()->universe(m_universe);
×
434
    if (uni == NULL)
×
435
    {
436
        qDebug() << "ERROR. Universe not found!";
×
437
        return;
×
438
    }
439

440
    OutputPatch *op = NULL;
×
441
    for (int i = 0; i < uni->outputPatchesCount(); i++)
×
442
    {
443
        op = uni->outputPatch(i);
×
444
        if (op->output() == m_line)
×
445
            break;
×
446
    }
447
    if (op == NULL)
×
448
    {
449
        qDebug() << "ERROR. Output patch not found!";
×
450
        return;
×
451
    }
452
    m_plugin = op->plugin();
×
453

454
    connect(m_plugin, SIGNAL(rdmValueChanged(quint32, quint32, QVariantMap)),
×
455
            this, SLOT(slotRDMDataReady(quint32, quint32, QVariantMap)));
456

457
    m_running = true;
×
458
    while (m_running == true)
×
459
    {
460
        switch (m_requestState)
×
461
        {
462
            case StateNone:
×
463
            {
464
                // Nothing to do. Terminate the thread
465
                m_running = false;
×
466
            }
467
            break;
×
468
            case StateDiscoveryStart:
×
469
            {
470
                m_requestState = StateDiscoveryContinue;
×
471
                //m_plugin->sendRDMCommand(m_universe, m_line, DISCOVERY_COMMAND,
472
                //                         QVariantList() << RDMProtocol::broadcastAddress() << PID_DISC_UN_MUTE);
473
            }
474
            break;
×
475
            case StateDiscoveryContinue:
×
476
            {
477
                waitCount = 0;
×
478

479
                if (m_discoveryList.isEmpty())
×
480
                {
481
                    m_requestState = StateDiscoveryEnd;
×
482
                    break;
×
483
                }
484

485
                DiscoveryInfo info = m_discoveryList.first();
×
486
                qDebug() << "Discovery - CONTINUE between" << QString::number(info.startUID, 16)
×
487
                         << "and" << QString::number(info.endUID, 16);
×
488
                m_plugin->sendRDMCommand(m_universe, m_line, DISCOVERY_COMMAND,
×
489
                                         QVariantList() << RDMProtocol::broadcastAddress()
×
490
                                                        << PID_DISC_UNIQUE_BRANCH << info.startUID << info.endUID);
×
491

492
                m_requestState = StateWait;
×
493
            }
494
            break;
×
495
            case StateDiscoveryEnd:
×
496
            {
497
                if (m_uidMap.isEmpty())
×
498
                    emit requestPopup("Warning", "No RDM devices found");
×
499
                m_requestState = StateNone;
×
500
            }
501
            break;
×
502
            case StatePersonalities:
×
503
            {
504
                waitCount = 0;
×
505
                QString UID = m_uidMap.firstKey();
×
506
                m_requestState = StatePersonalityInfo;
×
507
                qDebug() << "Requesting personalities of UID" << UID;
×
508
                bool result = m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
509
                                                       QVariantList() << UID << PID_DMX_PERSONALITY);
×
510
                if (result == false)
×
511
                {
512
                    requestPopup("Error", "RDM command failed");
×
513
                    m_requestState = StateNone;
×
514
                }
515
            }
516
            break;
×
517
            case StateReadSinglePid:
×
518
            {
519
                waitCount = 0;
×
520
                UIDInfo info = m_uidMap.first();
×
521
                QString UID = m_uidMap.firstKey();
×
522
                m_requestState = StateWaitPidInfo;
×
523
                qDebug().nospace().noquote() << "Read PID 0x" << QString::number(info.dmxAddress, 16);
×
524
                QVariantList args;
×
525
                args << UID;
×
526
                args << info.dmxAddress; // actually the PID to read
×
527
                for (QVariantMap::const_iterator it = info.params.begin(); it != info.params.end(); ++it)
×
528
                    args << it.value().toUInt(); // add parameters
×
529

NEW
530
                uchar command = info.dmxAddress < 0x04 ? DISCOVERY_COMMAND : GET_COMMAND;
×
NEW
531
                bool result = m_plugin->sendRDMCommand(m_universe, m_line, command, args);
×
UNCOV
532
                if (result == false)
×
533
                {
534
                    requestPopup("Error", "RDM command failed");
×
535
                    m_requestState = StateNone;
×
536
                }
537
            }
538
            break;
×
539
            case StateWriteSinglePid:
×
540
            {
541
                waitCount = 0;
×
542
                UIDInfo info = m_uidMap.first();
×
543
                QString UID = m_uidMap.firstKey();
×
544
                m_requestState = StateWaitPidInfo;
×
545
                qDebug().nospace().noquote() << "Write PID 0x" << QString::number(info.dmxAddress, 16);
×
546
                QVariantList args;
×
547
                args << UID;
×
548
                args << info.dmxAddress; // actually the PID to write
×
549
                for (QVariantMap::const_iterator it = info.params.begin(); it != info.params.end(); ++it)
×
550
                    args << it.value(); // forward parameters
×
551

552
                bool result = m_plugin->sendRDMCommand(m_universe, m_line, SET_COMMAND, args);
×
553
                if (result == false)
×
554
                {
555
                    requestPopup("Error", "RDM command failed");
×
556
                    m_requestState = StateNone;
×
557
                }
558
            }
559
            break;
×
560
            default:
×
561
            {
562
                //qDebug() << "[RDM] ....WAIT....";
563
                msleep(50);
×
564
                waitCount++;
×
565
                if (m_requestState == StateWait && waitCount == MAX_WAIT_COUNT)
×
566
                {
567
                    qDebug() << "Exit for timeout...";
×
568
                    emit requestPopup("Warning", "Process timed out");
×
569
                    m_running = false;
×
570
                }
571
            }
572
            break;
×
573
        }
574
    }
575

576
    disconnect(m_plugin, SIGNAL(rdmValueChanged(quint32, quint32, QVariantMap)),
×
577
              this, SLOT(slotRDMDataReady(quint32, quint32, QVariantMap)));
578

579
    qDebug() << "Terminating RDM worker thread";
×
580
}
581

582
void RDMWorker::stop()
×
583
{
584
    if (m_running == true)
×
585
    {
586
        m_running = false;
×
587
        wait();
×
588
    }
589
}
×
590

591
void RDMWorker::slotRDMDataReady(quint32 universe, quint32 line, QVariantMap data)
×
592
{
593
    //qDebug() << "Got signal from universe" << universe << ", line" << line;
594
    //qDebug() << "RDM map" << data;
595

596
    if (m_requestState == StateWaitPidInfo)
×
597
    {
598
        QString pidInfo;
×
599
        for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
×
600
            pidInfo += QString("<b>%1</b>: %2<br>").arg(it.key()).arg(it.value().toString());
×
601

602
        emit pidInfoReady(pidInfo);
×
603
        m_requestState = StateNone;
×
604
        return;
×
605
    }
606

607
    // check the signal reason
608
    if (data.contains("DISCOVERY_COUNT"))
×
609
    {
610
        int count = data.value("DISCOVERY_COUNT").toInt();
×
611
        for (int i = 0; i < count; i++)
×
612
        {
613
            QString UID = data.value(QString("UID-%1").arg(i)).toString();
×
614
            if (m_uidMap.contains(UID) == false)
×
615
            {
616
                UIDInfo info;
×
617
                info.universe = universe;
×
618
                info.pluginLine = line;
×
619
                m_uidMap[UID] = info;
×
620

621
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
622
                                         QVariantList() << UID << PID_DEVICE_MODEL_DESCRIPTION);
×
623

624
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
625
                                         QVariantList() << UID << PID_MANUFACTURER_LABEL);
×
626

627
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
628
                                         QVariantList() << UID << PID_DEVICE_INFO);
×
629
            }
630
        }
631
    }
632
    else if (data.contains("DISCOVERY_ERRORS"))
×
633
    {
NEW
634
        if (m_discoveryList.isEmpty())
×
NEW
635
            return;
×
636

637
        // Discovery errors mean collisions and/or bad checksum.
638
        // Split the current range into two branches
639
        DiscoveryInfo currentRange = m_discoveryList.first();
×
640
        DiscoveryInfo lowerRange, upperRange;
641

642
        qulonglong midPosition = ((currentRange.startUID & (0x0000800000000000-1)) +
×
643
                                  (currentRange.endUID & (0x0000800000000000-1))) / 2
×
644
                                + ((currentRange.endUID & 0x0000800000000000) ? 0x0000400000000000 : 0)
×
645
                                + ((currentRange.startUID & 0x0000800000000000) ? 0x0000400000000000 :0);
×
646
        lowerRange.startUID = currentRange.startUID;
×
647
        lowerRange.endUID = midPosition;
×
648
        upperRange.startUID = midPosition + 1;
×
649
        upperRange.endUID = currentRange.endUID;
×
650

651
        qDebug() << "Discovery errors detected" << data.value("DISCOVERY_ERRORS").toInt();
×
652
        //qDebug() << "Add lower range" << QString::number(lowerRange.startUID, 16) << "-" << QString::number(lowerRange.endUID, 16);
653
        //qDebug() << "Add upper range" << QString::number(upperRange.startUID, 16) << "-" << QString::number(upperRange.endUID, 16);
654
        m_discoveryList.removeFirst();
×
655
        m_discoveryList.prepend(lowerRange);
×
656
        m_discoveryList.prepend(upperRange);
×
657
        m_requestState = StateDiscoveryContinue;
×
658
    }
659
    else if (data.contains("DISCOVERY_NO_REPLY"))
×
660
    {
NEW
661
        if (m_discoveryList.isEmpty())
×
NEW
662
            return;
×
663

664
        // No reply means a dead branch. Remove it and continue on other branches.
665
        qDebug() << "Discovery: no reply";
×
666
        m_discoveryList.removeFirst();
×
667
        if (m_discoveryList.isEmpty())
×
668
            m_requestState = StateDiscoveryEnd;
×
669
        else
670
            m_requestState = StateDiscoveryContinue;
×
671
    }
672
    else if (data.contains("UID_INFO"))
×
673
    {
674
        QString UID = data.value("UID_INFO").toString();
×
675
        UIDInfo info = m_uidMap.value(UID);
×
676
        for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
×
677
        {
678
            QString key = it.key();
×
679

680
            if (key == "MODEL_NAME")
×
681
            {
682
                info.name = it.value().toString();
×
683
            }
684
            else if (key == "MANUFACTURER")
×
685
            {
NEW
686
                info.manufacturer = it.value().toString();
×
687
            }
688
            else if (key == "DMX_CHANNELS")
×
689
            {
690
                info.channels = it.value().toUInt();
×
691
            }
692
            else if (key == "DMX_START_ADDRESS")
×
693
            {
694
                info.dmxAddress = it.value().toUInt();
×
695
                m_uidMap[UID] = info;
×
696
                emit uidFound(UID, info);
×
697
                if (m_discoveryList.isEmpty())
×
698
                {
699
                    m_requestState = StateDiscoveryEnd;
×
700
                }
701
                else
702
                {
703
                    //m_discoveryList.removeFirst();
704
                    m_requestState = StateDiscoveryContinue;
×
705
                }
706
            }
707
            else if (key == "PERS_COUNT")
×
708
            {
709
                for (quint16 p = 0; p < it.value().toUInt(); p++)
×
710
                    m_requestList.append(p + 1);
×
711
            }
712
            else if (key == "PERS_DESC")
×
713
            {
714
                quint16 cIndex = info.params.value("PERS_CURRENT").toUInt();
×
715
                quint16 pIndex = data.value("PERS_INDEX").toUInt();
×
716
                quint16 channels = data.value("PERS_CHANNELS").toUInt();
×
717
                QString label = it.value().toString();
×
718

719
                m_fixtureInfo += QString("<TR><TD CLASS='emphasis'>%1 %2 %3</TD><TD>%4</TD>")
×
720
                                .arg(tr("Personality")).arg(pIndex)
×
721
                                .arg(cIndex == pIndex ? tr("(Selected)") : "").arg(label);
×
722
                m_fixtureInfo += QString("<TD CLASS='emphasis'>%1</TD><TD>%2</TD></TR>")
×
723
                                        .arg(tr("Channels")).arg(channels);
×
724
            }
725
            else if (key == "SLOT_LIST")
×
726
            {
727
                QVariant var = it.value();
×
NEW
728
                if (m_requestList.isEmpty())
×
729
                {
NEW
730
                    QString header("<TR CLASS='hilite'><TD COLSPAN='4'>%1</TD></TR>");
×
NEW
731
                    m_fixtureInfo += header.arg(tr("Channel list"));
×
732
                }
733

NEW
734
                m_requestList.append(var.value<QVector<quint16>>());
×
735

736
                // check if channels don't fit in a single reply
NEW
737
                if (info.params.value("Response") == RDMProtocol::responseToString(RESPONSE_TYPE_ACK_OVERFLOW))
×
738
                {
NEW
739
                    m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
NEW
740
                                             QVariantList() << UID << PID_SLOT_INFO);
×
741
                }
742
                else
743
                {
NEW
744
                    m_requestState = StateSlots;
×
745
                }
746
            }
747
            else if (key == "SLOT_DESC")
×
748
            {
749
                quint16 slotId = data.value("SLOT_ID").toUInt();
×
750
                QString label = it.value().toString();
×
751

752
                qDebug() << "SLOT label" << label;
×
753

754
                m_fixtureInfo += QString("<TR><TD CLASS='emphasis'>%1 %2</TD><TD COLSPAN='3'>%3</TD></TR>")
×
755
                                         .arg(tr("Channel")).arg(slotId + 1).arg(label);
×
756
            }
757
            else if (key == "PID_LIST")
×
758
            {
759
                QVariant var = it.value();
×
760
                m_requestList = var.value<QVector<quint16>>();
×
761

762
                // remove PIDs handled elsewhere
763
                m_requestList.removeAll(PID_DMX_PERSONALITY);
×
764
                m_requestList.removeAll(PID_DMX_PERSONALITY_DESCRIPTION);
×
765
                m_requestList.removeAll(PID_SLOT_INFO);
×
766
                m_requestList.removeAll(PID_SLOT_DESCRIPTION);
×
767
                std::sort(m_requestList.begin(), m_requestList.end());
×
768

769
                QString header("<TR CLASS='hilite'><TD COLSPAN='4'>%1</TD></TR>");
×
770
                m_fixtureInfo += header.arg(tr("Supported PIDs"));
×
771
                m_requestState = StateSupportedPids;
×
772
            }
773
            else if (key == "PID")
×
774
            {
775
                if (m_requestState == StateSupportedPids)
×
776
                {
777
                    quint16 pid = it.value().toUInt();
×
NEW
778
                    if (pid < 0x8000 && pid != PID_PARAMETER_DESCRIPTION)
×
779
                    {
780
                        QString sPid = QString("%1").arg(pid, 4, 16, QChar('0'));
×
781
                        m_fixtureInfo += QString("<TR><TD CLASS='emphasis' COLSPAN='4'>PID: 0x%1 (%2)</TD></TR>")
×
782
                                .arg(sPid.toUpper()).arg(RDMProtocol::pidToString(pid));
×
783
                    }                }
UNCOV
784
                else if (m_requestState == StateReadSinglePid)
×
785
                {
786
                    // TODO
787
                }
788
            }
789
            else if (key == "PID_DESC")
×
790
            {
791
                if (m_requestState == StateSupportedPids)
×
792
                {
793
                    quint16 pid = data.value("PID_INFO").toUInt();
×
794
                    QString sPid = QString("%1").arg(pid, 4, 16, QChar('0'));
×
795
                    m_fixtureInfo += QString("<TR><TD CLASS='emphasis' COLSPAN='4'>PID: 0x%1 (%2)</TD></TR>")
×
796
                            .arg(sPid.toUpper()).arg(it.value().toString());
×
797
                }
798
                else if (m_requestState == StateReadSinglePid)
×
799
                {
800
                    // TODO
801
                }
802
            }
803
            else
804
            {
805
                info.params.insert(it.key(), it.value());
×
806
            }
807

808
            //qDebug() << it.key() << it.value();
809

810
            m_uidMap[UID] = info;
×
811
        }
812

813
        if (m_requestState == StatePersonalityInfo)
×
814
        {
815
            if (m_requestList.isEmpty() == false)
×
816
            {
817
                quint16 idx = m_requestList.takeFirst();
×
818
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
819
                                       QVariantList() << UID << PID_DMX_PERSONALITY_DESCRIPTION << idx);
×
820
            }
821
            else
822
            {
823
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
824
                                       QVariantList() << UID << PID_SLOT_INFO);
×
825
            }
826
        }
827
        else if (m_requestState == StateSlots)
×
828
        {
829
            if (m_requestList.isEmpty() == false)
×
830
            {
831
                quint16 slotId = m_requestList.takeFirst();
×
832
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
833
                                         QVariantList() << UID << PID_SLOT_DESCRIPTION << slotId);
×
834
            }
835
            else
836
            {
837
                m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
838
                                         QVariantList() << UID << PID_SUPPORTED_PARAMETERS);
×
839
            }
840
        }
841
        else if (m_requestState == StateSupportedPids)
×
842
        {
843
            if (m_requestList.isEmpty() == false)
×
844
            {
845
                quint16 pid = m_requestList.takeFirst();
×
846

847
                if (pid >= 0x8000)
×
848
                    m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
849
                                             QVariantList() << UID << PID_PARAMETER_DESCRIPTION << pid);
×
850
                else
851
                    m_plugin->sendRDMCommand(m_universe, m_line, GET_COMMAND,
×
852
                                             QVariantList() << UID << pid);
×
853
            }
854
            else
855
            {
856
                emit fixtureInfoReady(m_fixtureInfo);
×
857
                m_requestState = StateNone;
×
858
            }
859
        }
860
    }
861
}
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