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

mcallegari / qlcplus / 13633248611

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

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

6.51
/engine/src/avolitesd4parser.cpp
1
/*
2
  Q Light Controller Plus
3
  avolitesd4parser.cpp
4

5
  Copyright (C) Rui Barreiros
6
                Heikki Junnila
7
                Massimo Callegari
8

9
  Licensed under the Apache License, Version 2.0 (the "License");
10
  you may not use this file except in compliance with the License.
11
  You may obtain a copy of the License at
12

13
      http://www.apache.org/licenses/LICENSE-2.0.txt
14

15
  Unless required by applicable law or agreed to in writing, software
16
  distributed under the License is distributed on an "AS IS" BASIS,
17
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
  See the License for the specific language governing permissions and
19
  limitations under the License.
20
*/
21

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

26
#include "avolitesd4parser.h"
27
#include "qlcfixturemode.h"
28
#include "qlcfixturedef.h"
29
#include "qlccapability.h"
30
#include "qlcphysical.h"
31
#include "qlcchannel.h"
32
#include "qlcfile.h"
33

34
// Channel groups
35
#define KD4GroupSpecial   "S"
36
#define KD4GroupIntensity "I"
37
#define KD4GroupPanTilt   "P"
38
#define KD4GroupColour    "C"
39
#define KD4GroupGobo      "G"
40
#define KD4GroupBeam      "B"
41
#define KD4GroupEffect    "E"
42

43
// Channels
44
#define KD4TagFixture   QString("Fixture")
45
#define KD4TagName      QString("Name")
46
#define KD4TagShortName QString("ShortName")
47
#define KD4TagCompany   QString("Company")
48
#define KD4TagControl   QString("Control")
49
#define KD4TagID        QString("ID")
50
#define KD4TagGroup     QString("Group")
51
#define KD4TagSpeed     QString("Speed")
52
#define KD4TagMacro     QString("Macro")
53
#define KD4TagReserved  QString("Reserved")
54
#define KD4TagShutter   QString("Shutter")
55
#define KD4TagPan       QString("Pan")
56
#define KD4TagTilt      QString("Tilt")
57
#define KD4TagCyan      QString("Cyan")
58
#define KD4TagMagenta   QString("Magenta")
59
#define KD4TagYellow    QString("Yellow")
60
#define KD4TagRed       QString("Red")
61
#define KD4TagBlue      QString("Blue")
62
#define KD4TagGreen     QString("Green")
63
#define KD4TagPrism     QString("Prism")
64
#define KD4TagEffect    QString("Effect")
65
#define KD4TagAttribute QString("Attribute")
66
#define KD4TagUpdate    QString("Update")
67

68
// Capabilities
69
#define KD4TagFunction                  QString("Function")
70
#define KD4TagFunctionName              QString("Name")
71
#define KD4TagFunctionDmx               QString("Dmx")
72
#define KD4TagFunctionDmxValueSeparator '~'
73

74
// Mode section
75
#define KD4TagMode                 QString("Mode")
76
#define KD4TagModeName             QString("Name")
77
#define KD4TagModeInclude          QString("Include")
78
#define KD4TagModeAttribute        QString("Attribute")
79
#define KD4TagModeChannelOffset    QString("ChannelOffset")
80
#define KD4TagModeID               QString("ID")
81
#define KD4TagModeChannelSeparator ','
82

83
// Palettes section
84
#define KD4TagPalettes          QString("Palettes")
85
#define KD4TagPalette           QString("Palette")
86

87
// Physical section
88
#define KD4TagPhysical                     QString("Physical")
89
#define KD4TagPhysicalBulb                 QString("Bulb")
90
#define KD4TagPhysicalBulbType             QString("Type")
91
#define KD4TagPhysicalBulbLumens           QString("Lumens")
92
#define KD4TagPhysicalBulbColourTemp       QString("ColourTemp")
93
#define KD4TagPhysicalLens                 QString("Lens")
94
#define KD4TagPhysicalLensName             QString("Name")
95
#define KD4TagPhysicalLensDegrees          QString("Degrees")
96
#define KD4TagPhysicalLensDegreesSeparator QString('~')
97
#define KD4TagPhysicalWeight               QString("Weight")
98
#define KD4TagPhysicalWeightKg             QString("Kg")
99
#define KD4TagPhysicalSize                 QString("Size")
100
#define KD4TagPhysicalSizeHeight           QString("Height")
101
#define KD4TagPhysicalSizeWidth            QString("Width")
102
#define KD4TagPhysicalSizeDepth            QString("Depth")
103
#define KD4TagPhysicalFocus                QString("Focus")
104
#define KD4TagPhysicalFocusType            QString("Type")
105
#define KD4TagPhysicalFocusPanMax          QString("PanMax")
106
#define KD4TagPhysicalFocusTiltMax         QString("TiltMax")
107

108
// Static attibute map shared between instances of the parser, initialized only
109
// once per application.
110
AvolitesD4Parser::StringToEnumMap AvolitesD4Parser::s_attributesMap;
111

112
AvolitesD4Parser::AvolitesD4Parser()
1✔
113
{
114
    if (s_attributesMap.isEmpty() == true)
1✔
115
    {
116
        // Setup our attribute mapping map helper
117
        s_attributesMap.insert(KD4GroupSpecial, AvolitesD4Parser::SPECIAL);
1✔
118
        s_attributesMap.insert(KD4GroupIntensity, AvolitesD4Parser::INTENSITY);
1✔
119
        s_attributesMap.insert(KD4GroupPanTilt, AvolitesD4Parser::PANTILT);
1✔
120
        s_attributesMap.insert(KD4GroupColour, AvolitesD4Parser::COLOUR);
1✔
121
        s_attributesMap.insert(KD4GroupGobo, AvolitesD4Parser::GOBO);
1✔
122
        s_attributesMap.insert(KD4GroupBeam, AvolitesD4Parser::BEAM);
1✔
123
        s_attributesMap.insert(KD4GroupEffect, AvolitesD4Parser::EFFECT);
1✔
124
    }
125
}
1✔
126

127
AvolitesD4Parser::~AvolitesD4Parser()
1✔
128
{
129
}
1✔
130

131
bool AvolitesD4Parser::loadXML(const QString& path, QLCFixtureDef *fixtureDef)
1✔
132
{
133
    m_lastError = QString();
1✔
134
    m_channels.clear();
1✔
135

136
    if (path.isEmpty())
1✔
137
    {
138
        m_lastError = "filename not specified";
×
139
        return false;
×
140
    }
141

142
    QXmlStreamReader *doc = QLCFile::getXMLReader(path);
1✔
143
    if (doc == NULL || doc->device() == NULL || doc->hasError())
1✔
144
    {
145
        m_lastError = QString("Unable to read from %1").arg(path);
1✔
146
        return false;
1✔
147
    }
148

149
    // check if the document has <Fixture></Fixture> if not then it's not a valid file
150
    if (doc->readNextStartElement() == false || doc->name() != KD4TagFixture)
×
151
    {
152
        m_lastError = "wrong document format";
×
153
        return false;
×
154
    }
155

156
    QXmlStreamAttributes attrs = doc->attributes();
×
157
    if ((!attrs.hasAttribute(KD4TagName)) || (!attrs.hasAttribute(KD4TagCompany)))
×
158
    {
159
        m_lastError = "the document doesn't have the required attributes";
×
160
        return false;
161
    }
162

163
    fixtureDef->setManufacturer(doc->attributes().value(KD4TagCompany).toString());
×
164
    fixtureDef->setModel(doc->attributes().value(KD4TagName).toString());
×
165
    fixtureDef->setAuthor("Avolites");
×
166

167
    while (doc->readNextStartElement())
×
168
    {
169
        if (doc->name() == KD4TagControl)
×
170
        {
171
            // Parse a channel
172
            if (parseChannel(doc, fixtureDef) == false)
×
173
                return false;
174
        }
175
        else if (doc->name() == KD4TagMode)
×
176
        {
177
            // Parse mode tag
178
            parseMode(doc, fixtureDef);
×
179
        }
180
        else if (doc->name() == KD4TagPalettes)
×
181
        {
182
            // TODO TODO TODO
183
            // Maybe also import preset palettes and macros ?!?!?!?!
184
            /**
185
                Can't be done for now, as qxf files don't have any information on preset palettes or macros
186
                for fixtures, they are automatically generated on the main application maybe in future... **/
187
            doc->skipCurrentElement();
×
188
        }
189
        else
190
        {
191
            qWarning() << Q_FUNC_INFO << "Unknown D4 tag:" << doc->name().toString();
×
192
            doc->skipCurrentElement();
×
193
        }
194
    }
195

196
    fixtureDef->setType(guessType(fixtureDef));
×
197

198
    QLCFile::releaseXMLReader(doc);
×
199

200
    return true;
201
}
202

203
QString AvolitesD4Parser::lastError() const
1✔
204
{
205
    return m_lastError;
1✔
206
}
207

208
QLCChannel::Group AvolitesD4Parser::getGroup(QString ID, QString name, QString group)
×
209
{
210
    if (name.isEmpty() && group.isEmpty())
×
211
        return QLCChannel::NoGroup;
212

213
    switch (stringToAttributeEnum(group))
×
214
    {
215
        case AvolitesD4Parser::SPECIAL:
×
216
            if (ID.contains(KD4TagSpeed, Qt::CaseInsensitive) ||
×
217
                name.contains(KD4TagSpeed, Qt::CaseInsensitive))
×
218
                    return QLCChannel::Speed;
219
            else if (ID.contains(KD4TagMacro, Qt::CaseInsensitive) ||
×
220
                     name.contains(KD4TagMacro, Qt::CaseInsensitive))
×
221
                        return QLCChannel::Effect;
222
            else if (ID.contains(KD4TagReserved, Qt::CaseInsensitive) ||
×
223
                     name.contains(KD4TagReserved, Qt::CaseInsensitive))
×
224
                        return QLCChannel::NoGroup;
225
            else
226
                return QLCChannel::Maintenance;
×
227
        break;
228

229
        default:
×
230
        case AvolitesD4Parser::INTENSITY:
231
            if (ID.contains(KD4TagShutter, Qt::CaseInsensitive) ||
×
232
                name.contains(KD4TagShutter, Qt::CaseInsensitive))
×
233
                    return QLCChannel::Shutter;
234
            else
235
                return QLCChannel::Intensity;
×
236
        break;
237

238
        case AvolitesD4Parser::PANTILT:
×
239
            if (ID.contains(KD4TagPan, Qt::CaseInsensitive) ||
×
240
                name.contains(KD4TagPan, Qt::CaseInsensitive))
×
241
                    return QLCChannel::Pan;
242
            else if (ID.contains(KD4TagTilt, Qt::CaseInsensitive) ||
×
243
                    name.contains(KD4TagTilt, Qt::CaseInsensitive))
×
244
                        return QLCChannel::Tilt;
245
            else
246
                return QLCChannel::NoGroup;
×
247
        break;
248

249
        case AvolitesD4Parser::COLOUR:
×
250
            if (ID.contains(KD4TagCyan, Qt::CaseInsensitive) ||
×
251
                name.contains(KD4TagCyan, Qt::CaseInsensitive))
×
252
                    return QLCChannel::Intensity;
253
            else if (ID.contains(KD4TagMagenta, Qt::CaseInsensitive) ||
×
254
                    name.contains(KD4TagMagenta, Qt::CaseInsensitive))
×
255
                        return QLCChannel::Intensity;
256
            else if (ID.contains(KD4TagYellow, Qt::CaseInsensitive) ||
×
257
                    name.contains(KD4TagYellow, Qt::CaseInsensitive))
×
258
                        return QLCChannel::Intensity;
259
            else if (ID.contains(KD4TagRed, Qt::CaseInsensitive) ||
×
260
                    name.contains(KD4TagRed, Qt::CaseInsensitive))
×
261
                        return QLCChannel::Intensity;
262
            else if (ID.contains(KD4TagGreen, Qt::CaseInsensitive) ||
×
263
                    name.contains(KD4TagGreen, Qt::CaseInsensitive))
×
264
                        return QLCChannel::Intensity;
265
            else if (ID.contains(KD4TagBlue, Qt::CaseInsensitive) ||
×
266
                    name.contains(KD4TagBlue, Qt::CaseInsensitive))
×
267
                        return QLCChannel::Intensity;
268
            else
269
                return QLCChannel::Colour;
×
270
        break;
271

272
        case AvolitesD4Parser::GOBO:
273
            return QLCChannel::Gobo;
274
        break;
275

276
        case AvolitesD4Parser::BEAM:
×
277
            return QLCChannel::Beam;
×
278
        break;
279

280
        case AvolitesD4Parser::EFFECT:
×
281
            if (ID.contains(KD4TagPrism, Qt::CaseInsensitive) ||
×
282
                name.contains(KD4TagPrism, Qt::CaseInsensitive))
×
283
                    return QLCChannel::Prism;
284
            else if (ID.contains(KD4TagEffect, Qt::CaseInsensitive) ||
×
285
                     name.contains(KD4TagEffect, Qt::CaseInsensitive))
×
286
                        return QLCChannel::Effect;
287
            else if (ID.contains(KD4TagMacro, Qt::CaseInsensitive) ||
×
288
                     name.contains(KD4TagMacro, Qt::CaseInsensitive))
×
289
                        return QLCChannel::Effect;
290
            else
291
                return QLCChannel::NoGroup;
×
292
        break;
293
    }
294

295
    return QLCChannel::NoGroup;
296
}
297

298
QLCChannel::PrimaryColour AvolitesD4Parser::getColour(QString ID, QString name, QString group)
×
299
{
300
    if (group.compare(KD4GroupColour, Qt::CaseInsensitive) != 0)
×
301
        return QLCChannel::NoColour;
302

303
    if (ID.contains(KD4TagCyan, Qt::CaseInsensitive) ||
×
304
        name.contains(KD4TagCyan, Qt::CaseInsensitive))
×
305
            return QLCChannel::Cyan;
306
    else if (ID.contains(KD4TagMagenta, Qt::CaseInsensitive) ||
×
307
            name.contains(KD4TagMagenta, Qt::CaseInsensitive))
×
308
                return QLCChannel::Magenta;
309
    else if (ID.contains(KD4TagYellow, Qt::CaseInsensitive) ||
×
310
             name.contains(KD4TagYellow, Qt::CaseInsensitive))
×
311
                return QLCChannel::Yellow;
312
    else if (ID.contains(KD4TagRed, Qt::CaseInsensitive) ||
×
313
            name.contains(KD4TagRed, Qt::CaseInsensitive))
×
314
                return QLCChannel::Red;
315
    else if (ID.contains(KD4TagGreen, Qt::CaseInsensitive) ||
×
316
            name.contains(KD4TagGreen, Qt::CaseInsensitive))
×
317
                return QLCChannel::Green;
318
    else if (ID.contains(KD4TagBlue, Qt::CaseInsensitive) ||
×
319
            name.contains(KD4TagBlue, Qt::CaseInsensitive))
×
320
                return QLCChannel::Blue;
321
    else
322
        return QLCChannel::NoColour;
×
323
}
324
/*
325
bool AvolitesD4Parser::isFunction(const QDomElement& elem) const
326
{
327
    QDomElement el = elem.firstChildElement(KD4TagFunction);
328
    for (; !el.isNull(); el = el.nextSiblingElement(KD4TagFunction))
329
    {
330
        if (!el.attribute(KD4TagUpdate).isEmpty())
331
            return true;
332
    }
333

334
    return false;
335
}
336
*/
337
bool AvolitesD4Parser::is16Bit(QString dmx) const
×
338
{
339
    QStringList dmxValues = dmx.split(KD4TagFunctionDmxValueSeparator);
×
340

341
    if (dmxValues.isEmpty())
×
342
        return false;
343

344
    // I remember avolites sometimes switches the sides on dmx values, sometimes, the left of the ~
345
    // is not always the lowest, better check both sides
346

347
    if (dmxValues.value(0).toInt() > 256)
×
348
        return true;
349

350
    // Is there a right side ? (there should always be something in the right side of the ~,
351
    // or avolites desks won't parse the file, anyway, there should be a check, if some smart
352
    // dude will complain this crashes with his D4 file)
353

354
    if (dmxValues.size() > 1)
×
355
    {
356
        if (dmxValues.value(1).toInt() > 256)
×
357
            return true;
×
358
    }
359

360
    return false;
361
}
362

363
QLCCapability *AvolitesD4Parser::getCapability(QString dmx, QString name, bool isFine)
×
364
{
365
    if (dmx.isEmpty())
×
366
        return NULL;
367

368
    QStringList dmxValues = dmx.split(KD4TagFunctionDmxValueSeparator);
×
369

370
    // Here, instead of checking all the time for both dmxValues, it's more efficient to
371
    // set a default value if it's missing
372
    if (dmxValues.size() == 0)
×
373
        dmxValues << QString("0") << QString("0");
×
374
    else if (dmxValues.size() == 1)
×
375
        dmxValues << QString("0");
×
376

377
    // if were trying to get capabilities from a 16bit channel, we need to change them to 8 bit
378
    int minValue = 0, maxValue = 0;
379

380
    if (dmxValues.value(0).toInt() > 256)
×
381
        minValue = 0xFF & (dmxValues.value(0).toInt() >> 8);
×
382
    else
383
        minValue = dmxValues.value(0).toInt();
×
384

385
    if (dmxValues.value(1).toInt() > 256)
×
386
        maxValue = 0xFF & (dmxValues.value(1).toInt() >> 8);
×
387
    else
388
        maxValue = dmxValues.value(1).toInt();
×
389

390
    // Guess what, I seen this happen, it seems min value is not always on the left of the ~
391
    // sometimes they're switched!
392
    if (minValue > maxValue)
×
393
    {
394
        int tmp = maxValue;
395
        maxValue = minValue;
396
        minValue = tmp;
397
    }
398

399
    if (isFine)
×
400
        name += " Fine";
×
401

402
    QLCCapability *cap = new QLCCapability(minValue, maxValue, name);
×
403

404
    return cap;
405
}
406

407
bool AvolitesD4Parser::parseChannel(QXmlStreamReader *doc, QLCFixtureDef *fixtureDef)
×
408
{
409
    if (doc->name() != KD4TagControl)
×
410
        return false;
411

412
    while (doc->readNextStartElement())
×
413
    {
414
        if (doc->name() == KD4TagAttribute)
×
415
        {
416
            QString ID = doc->attributes().value(KD4TagID).toString();
×
417
            if (ID.isEmpty())
×
418
            {
419
                doc->skipCurrentElement();
×
420
                continue;
421
            }
422

423
            parseAttribute(doc, fixtureDef);
×
424
        }
×
425
        else
426
        {
427
            qWarning() << Q_FUNC_INFO << "Unknown control tag:" << doc->name().toString();
×
428
            doc->skipCurrentElement();
×
429
        }
430
    }
431

432
    return true;
433
}
434

435
bool AvolitesD4Parser::parseFunction(QXmlStreamReader *doc, QLCFixtureDef *fixtureDef,
×
436
                                     QLCChannel *channel, QString ID, QString group)
437
{
438
    QXmlStreamAttributes attrs = doc->attributes();
×
439
    QString name = attrs.value(KD4TagFunctionName).toString();
×
440
    if (name.isEmpty())
×
441
    {
442
        doc->skipCurrentElement();
×
443
        return true;
444
    }
445

446
    QString dmx = attrs.value(KD4TagFunctionDmx).toString();
×
447
    QLCCapability *cap = getCapability(dmx, name);
×
448

449
    if (cap != NULL)
×
450
    {
451
        // We just ignore capability adding errors, because avolites often repeats attributes due to conditionals
452
        // so we just add the first one we get, the repeating ones are ignored naturally and
453
        // obviously further human verification is needed on the fixture definition to fix this issues
454
        channel->addCapability(cap);
×
455
    }
456

457
    if (is16Bit(dmx))
×
458
    {
459
        QLCChannel *fineChan = new QLCChannel();
×
460
        fineChan->setName(name + " Fine");
×
461
        fineChan->setGroup(getGroup(ID, name, group));
×
462
        fineChan->setColour(getColour(ID, name, group));
×
463
        fineChan->setControlByte(QLCChannel::LSB);
×
464
        QLCCapability *fineCap = getCapability(dmx, name, true);
×
465
        if (fineCap != NULL)
×
466
            fineChan->addCapability(fineCap);
×
467
        fixtureDef->addChannel(fineChan);
×
468
        m_channels.insert(ID + " Fine", fineChan);
×
469
    }
470
    //qDebug() << "Capability found" << cap->name() << cap->min() << cap->max();
471
    doc->skipCurrentElement();
×
472

473
    return true;
474
}
×
475

476
bool AvolitesD4Parser::parseAttribute(QXmlStreamReader *doc, QLCFixtureDef *fixtureDef)
×
477
{
478
    if (doc->name() != KD4TagAttribute)
×
479
        return false;
480

481
    QXmlStreamAttributes attrs = doc->attributes();
×
482
    QString ID = doc->attributes().value(KD4TagID).toString();
×
483
    QString name = attrs.value(KD4TagName).toString();
×
484
    QString group = attrs.value(KD4TagGroup).toString();
×
485

486
    QLCChannel *chan = new QLCChannel();
×
487
    chan->setName(name);
×
488
    chan->setGroup(getGroup(ID, name, group));
×
489
    chan->setColour(getColour(ID, name, group));
×
490
    chan->setControlByte(QLCChannel::MSB);
×
491

492
    // add channel to fixture definition
493
    fixtureDef->addChannel(chan);
×
494
    m_channels.insert(ID, chan);
×
495

496
    // if this channel is a NoGroup then we don't need to continue
497
    // no capabilities nor 16 bit channel
498
    if (chan->group() == QLCChannel::NoGroup)
×
499
    {
500
        doc->skipCurrentElement();
×
501
        return true;
502
    }
503

504
    while (doc->readNextStartElement())
×
505
    {
506
        if (doc->name() == KD4TagFunction)
×
507
        {
508
            parseFunction(doc, fixtureDef, chan, ID, group);
×
509
        }
510
        else
511
        {
512
            qWarning() << Q_FUNC_INFO << "Unknown attribute tag:" << doc->name().toString();
×
513
            doc->skipCurrentElement();
×
514
        }
515
    }
516
    chan->sortCapabilities();
×
517

518
    return true;
519
}
×
520

521
bool AvolitesD4Parser::parseMode(QXmlStreamReader *doc, QLCFixtureDef *fixtureDef)
×
522
{
523
    if (doc->name() != KD4TagMode)
×
524
        return false;
525

526
    QString name = doc->attributes().value(KD4TagModeName).toString();
×
527

528
    if (name.isEmpty())
×
529
        return false;
530

531
    QLCFixtureMode *mode = new QLCFixtureMode(fixtureDef);
×
532
    mode->setName(name);
×
533

534
    while (doc->readNextStartElement())
×
535
    {
536
        if (doc->name() == KD4TagModeInclude)
×
537
        {
538
            parseInclude(doc, mode);
×
539
        }
540
        else if (doc->name() == KD4TagPhysical)
×
541
        {
542
            // Parse physical
543
            parsePhysical(doc, fixtureDef, mode);
×
544
        }
545
        else
546
        {
547
            qWarning() << Q_FUNC_INFO << "Unknown mode tag:" << doc->name().toString();
×
548
            doc->skipCurrentElement();
×
549
        }
550
    }
551

552
    // Add the mode
553
    fixtureDef->addMode(mode);
×
554

555
    return true;
556
}
×
557

558
bool AvolitesD4Parser::comparePhysical(const QLCPhysical &globalPhy, const QLCPhysical &modePhy) const
×
559
{
560
    if (globalPhy.isEmpty())
×
561
        return true;
562

563
    if (globalPhy.bulbLumens() != modePhy.bulbLumens() ||
×
564
        globalPhy.bulbColourTemperature() != modePhy.bulbColourTemperature() ||
×
565
        globalPhy.weight() != modePhy.weight() ||
×
566
        globalPhy.width() != modePhy.width() ||
×
567
        globalPhy.height() != modePhy.height() ||
×
568
        globalPhy.depth() != modePhy.depth() ||
×
569
        globalPhy.lensDegreesMin() != modePhy.lensDegreesMin() ||
×
570
        globalPhy.lensDegreesMax() != modePhy.lensDegreesMax() ||
×
571
        globalPhy.focusPanMax() != modePhy.focusPanMax() ||
×
572
        globalPhy.focusTiltMax() != modePhy.focusTiltMax() ||
×
573
        globalPhy.powerConsumption() != modePhy.powerConsumption())
×
574
            return false;
×
575

576
    return true;
577
}
578

579
void AvolitesD4Parser::parsePhysical(QXmlStreamReader *doc, QLCFixtureDef *fixtureDef, QLCFixtureMode *mode)
×
580
{
581
    if (doc->name() != KD4TagPhysical)
×
582
        return;
×
583

584
    QLCPhysical phys;
×
585

586
    while (doc->readNextStartElement())
×
587
    {
588
        QXmlStreamAttributes attrs = doc->attributes();
×
589

590
        if (doc->name() == KD4TagPhysicalBulb)
×
591
        {
592
            phys.setBulbType(attrs.value(KD4TagPhysicalBulbType).toString());
×
593
            phys.setBulbLumens(attrs.value(KD4TagPhysicalBulbLumens).toString().toInt());
×
594
            // this is kind of wrong cause a ColourTemp tag can be "0, 0, 0"
595
            phys.setBulbColourTemperature(attrs.value(KD4TagPhysicalBulbColourTemp).toString().toInt());
×
596
        }
597
        else if (doc->name() == KD4TagPhysicalLens)
×
598
        {
599
            phys.setLensName(attrs.value(KD4TagName).toString());
×
600

601
            QString degrees = attrs.value(KD4TagPhysicalLensDegrees).toString();
×
602
            if (degrees.contains(KD4TagPhysicalLensDegreesSeparator))
×
603
            {
604
                QStringList deg = degrees.split(KD4TagPhysicalLensDegreesSeparator);
×
605
                if (deg.size() == 2)
×
606
                {
607
                    if (deg.value(0).toInt() > deg.value(1).toInt())
×
608
                    {
609
                        phys.setLensDegreesMin(deg.value(1).toInt());
×
610
                        phys.setLensDegreesMax(deg.value(0).toInt());
×
611
                    }
612
                    else
613
                    {
614
                        phys.setLensDegreesMin(deg.value(0).toInt());
×
615
                        phys.setLensDegreesMax(deg.value(1).toInt());
×
616
                    }
617
                } else if (deg.size() == 1)
×
618
                {
619
                    phys.setLensDegreesMax(deg.value(0).toInt());
×
620
                    phys.setLensDegreesMin(deg.value(0).toInt());
×
621
                }
622
            }
623
            else if (!degrees.isEmpty())
×
624
            {
625
                phys.setLensDegreesMax(degrees.toInt());
×
626
                phys.setLensDegreesMin(degrees.toInt());
×
627
            }
628
        }
×
629
        else if (doc->name() == KD4TagPhysicalWeight)
×
630
        {
631
            phys.setWeight(attrs.value(KD4TagPhysicalWeightKg).toString().toDouble());
×
632
        }
633
        else if (doc->name() == KD4TagPhysicalSize)
×
634
        {
635
            phys.setHeight(attrs.value(KD4TagPhysicalSizeHeight).toString().toDouble() * 1000);
×
636
            phys.setWidth(attrs.value(KD4TagPhysicalSizeWidth).toString().toDouble() * 1000);
×
637
            phys.setDepth(attrs.value(KD4TagPhysicalSizeDepth).toString().toDouble() * 1000);
×
638
        }
639
        else if (doc->name() == KD4TagPhysicalFocus)
×
640
        {
641
            phys.setFocusType(attrs.value(KD4TagPhysicalFocusType).toString());
×
642
            phys.setFocusPanMax(attrs.value(KD4TagPhysicalFocusPanMax).toString().toInt());
×
643
            phys.setFocusTiltMax(attrs.value(KD4TagPhysicalFocusTiltMax).toString().toInt());
×
644
        }
645
        else
646
        {
647
            qWarning() << Q_FUNC_INFO << "Unknown physical tag:" << doc->name().toString();
×
648
        }
649
        doc->skipCurrentElement();
×
650
    }
651

652
    if (comparePhysical(fixtureDef->physical(), phys) == true)
×
653
        fixtureDef->setPhysical(phys);
×
654
    else
655
        mode->setPhysical(phys);
×
656
}
×
657

658
void AvolitesD4Parser::parseInclude(QXmlStreamReader *doc, QLCFixtureMode *mode)
×
659
{
660
    if (doc->name() != KD4TagModeInclude)
×
661
        return;
×
662

663
    QMap <int, QLCChannel*> channelList;
664

665
    // loop through Attribute tags
666
    while (doc->readNextStartElement())
×
667
    {
668
        if (doc->name() == KD4TagModeAttribute)
×
669
        {
670
            QXmlStreamAttributes attrs = doc->attributes();
×
671
            // Some channels are conditionals not real channels
672
            if (attrs.value(KD4TagModeChannelOffset).toString().isEmpty())
×
673
            {
674
                doc->skipCurrentElement();
×
675
                continue;
×
676
            }
677

678
            QString modeID = attrs.value(KD4TagModeID).toString();
×
679
            if (m_channels.contains(modeID))
×
680
            {
681
                // might be a 16 bit channel, so we have 2 offsets
682
                QString offset = attrs.value(KD4TagModeChannelOffset).toString();
×
683
                if (offset.contains(KD4TagModeChannelSeparator, Qt::CaseInsensitive))
×
684
                {
685
                    // 16 bit address, we need to add 2 channels, this one, and we need the fine one
686
                    QStringList offsetValues = offset.split(KD4TagModeChannelSeparator);
×
687
                    // if there's more than 2 addresses, or less than 2, bail out, don't know how to handle this, shouldn't happen ever.
688
                    if (offsetValues.size() > 2 || offsetValues.size() < 2)
×
689
                        continue;
690

691
                    // Add this one
692
                    channelList.insert(offsetValues.value(0).toInt(), m_channels.value(modeID));
×
693
                    QString name = m_channels.value(modeID)->name();
×
694

695
                    // Search for the fine one
696
                    QMapIterator <QString,QLCChannel*> it(m_channels);
×
697
                    while (it.hasNext() == true)
×
698
                    {
699
                        it.next();
×
700
                        QLCChannel *ch(it.value());
×
701
                        Q_ASSERT(ch != NULL);
702

703
                        if (ch->name() == QString(name + " Fine"))
×
704
                            channelList.insert(offsetValues.value(1).toInt(), ch);
×
705
                    }
706
                }
×
707
                else
708
                {
709
                    channelList.insert(offset.toInt(), m_channels.value(modeID));
×
710
                }
711
            }
×
712
        }
×
713
        else
714
        {
715
            qWarning() << Q_FUNC_INFO << "Unknown include tag:" << doc->name().toString();
×
716
        }
717
        doc->skipCurrentElement();
×
718
    }
719

720
    QMapIterator <int,QLCChannel*> it(channelList);
×
721
    while (it.hasNext() == true)
×
722
    {
723
        it.next();
×
724
        Q_ASSERT(mode != NULL);
725
        mode->insertChannel(it.value(), it.key());
×
726
    }
727
}
×
728

729
AvolitesD4Parser::Attributes AvolitesD4Parser::stringToAttributeEnum(const QString& attr)
×
730
{
731
    // If there is none, empty or whatever always return something, default is SPECIAL
732
    if (attr.isEmpty())
×
733
        return AvolitesD4Parser::SPECIAL;
734

735
    if (s_attributesMap.value(attr.toUpper()))
×
736
        return s_attributesMap.value(attr.toUpper());
×
737
    else
738
        return AvolitesD4Parser::SPECIAL;
739
}
740

741
QLCFixtureDef::FixtureType AvolitesD4Parser::guessType(QLCFixtureDef *def) const
×
742
{
743
    Q_ASSERT(def != NULL);
744

745
    int pan = 0, tilt = 0;
746
    int r = 0, g = 0, b = 0, c = 0, m = 0, y = 0, nocol = 0;
747
    int gobo = 0, colour = 0;
748
    int haze = 0, smoke = 0;
749
    int strobe = 0;
750

751
    QListIterator <QLCChannel*> it(def->channels());
×
752
    while (it.hasNext() == true)
×
753
    {
754
        const QLCChannel *ch(it.next());
×
755
        if (ch->group() == QLCChannel::Pan)
×
756
        {
757
            pan++;
×
758
        }
759
        else if (ch->group() == QLCChannel::Tilt)
×
760
        {
761
            tilt++;
×
762
        }
763
        else if (ch->group() == QLCChannel::Intensity)
×
764
        {
765
            if (ch->colour() == QLCChannel::Red)
×
766
                r++;
×
767
            else if (ch->colour() == QLCChannel::Green)
×
768
                g++;
×
769
            else if (ch->colour() == QLCChannel::Blue)
×
770
                b++;
×
771
            else if (ch->colour() == QLCChannel::Cyan)
×
772
                c++;
×
773
            else if (ch->colour() == QLCChannel::Magenta)
×
774
                m++;
×
775
            else if (ch->colour() == QLCChannel::Yellow)
×
776
                y++;
×
777
            else
778
                nocol++;
×
779
        }
780
        else if (ch->group() == QLCChannel::Shutter)
×
781
        {
782
            if (ch->searchCapability(/*S/s*/"trobe", false) != NULL)
×
783
                strobe++;
×
784
        }
785
        else if (ch->group() == QLCChannel::Gobo)
×
786
        {
787
            gobo++;
×
788
        }
789
        else if (ch->group() == QLCChannel::Colour)
×
790
        {
791
            colour++;
×
792
        }
793
        else if (ch->name().contains("strobe", Qt::CaseInsensitive) == true)
×
794
        {
795
            strobe++;
×
796
        }
797
        else if (ch->name().contains("haze", Qt::CaseInsensitive) == true)
×
798
        {
799
            haze++;
×
800
        }
801
        else if (ch->name().contains("smoke", Qt::CaseInsensitive) == true)
×
802
        {
803
            smoke++;
×
804
        }
805
    }
806

807
    if (pan >= 2 && tilt >= 2)
×
808
        return QLCFixtureDef::MovingHead; // Quite probable, few scanners with 16bit addressing
809
    else if (pan == 1 && tilt == 1)
×
810
        return QLCFixtureDef::Scanner; // Quite probable, though some moving heads are only 8bit
811
    else if (gobo > 0)
×
812
        return QLCFixtureDef::Flower; // No pan/tilt, but gobo, fairly certain
813
    else if (colour > 0 || (r > 0 && g > 0 && b > 0) || (c > 0 && m > 0 && y > 0))
×
814
        return QLCFixtureDef::ColorChanger; // No pan/tilt/gobos, but RGB/CMY mixing or dichro
815
    else if (strobe > 0)
×
816
        return QLCFixtureDef::Strobe; // Duh.
817
    else if (smoke > 0)
×
818
        return QLCFixtureDef::Smoke; // Duh.
819
    else if (haze > 0)
×
820
        return QLCFixtureDef::Hazer; // Duh.
821
    else if (nocol > 0)
×
822
        return QLCFixtureDef::Dimmer; // Kinda..mmmmh..
823
    else
824
        return QLCFixtureDef::Other; // Give up
×
825
}
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

© 2025 Coveralls, Inc