• 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

74.3
/engine/src/fixture.cpp
1
/*
2
  Q Light Controller Plus
3
  fixture.cpp
4

5
  Copyright (C) Heikki Junnila
6
                Massimo Callegari
7

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

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

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

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QString>
24
#include <QtMath>
25
#include <QDebug>
26

27
#include "qlcfixturedefcache.h"
28
#include "channelmodifier.h"
29
#include "qlcfixturemode.h"
30
#include "qlcfixturehead.h"
31
#include "qlcfixturedef.h"
32
#include "qlccapability.h"
33
#include "qlcchannel.h"
34
#include "qlcfile.h"
35

36
#include "fixture.h"
37
#include "doc.h"
38

39
/*****************************************************************************
40
 * Initialization
41
 *****************************************************************************/
42

43
Fixture::Fixture(QObject* parent) : QObject(parent)
16,822✔
44
{
45
    m_id = Fixture::invalidId();
16,822✔
46

47
    m_address = 0;
16,822✔
48
    m_channels = 0;
16,822✔
49

50
    m_fixtureDef = NULL;
16,822✔
51
    m_fixtureMode = NULL;
16,822✔
52
}
16,822✔
53

54
Fixture::~Fixture()
33,623✔
55
{
56
}
33,623✔
57

58
bool Fixture::operator<(const Fixture& fxi)
12✔
59
{
60
    if (m_address < fxi.m_address)
12✔
61
        return true;
5✔
62
    else
63
        return false;
7✔
64
}
65

66
/*****************************************************************************
67
 * Fixture ID
68
 *****************************************************************************/
69

70
void Fixture::setID(quint32 id)
16,806✔
71
{
72
    m_id = id;
16,806✔
73
    emit changed(m_id);
16,806✔
74
}
16,806✔
75

76
quint32 Fixture::id() const
18,121✔
77
{
78
    return m_id;
18,121✔
79
}
80

81
quint32 Fixture::invalidId()
832,241✔
82
{
83
    return UINT_MAX;
832,241✔
84
}
85

86
/*****************************************************************************
87
 * Name
88
 *****************************************************************************/
89

90
void Fixture::setName(const QString& name)
82✔
91
{
92
    m_name = name;
82✔
93
    emit changed(m_id);
82✔
94
}
82✔
95

96
QString Fixture::name() const
35✔
97
{
98
    return m_name;
35✔
99
}
100

101
/*****************************************************************************
102
 * Fixture type
103
 *****************************************************************************/
104

105
QString Fixture::typeString()
12✔
106
{
107
    if (m_fixtureDef != NULL)
12✔
108
        return m_fixtureDef->typeToString(m_fixtureDef->type());
11✔
109
    else
110
        return QString(KXMLFixtureDimmer);
1✔
111
}
112

113
QLCFixtureDef::FixtureType Fixture::type() const
24✔
114
{
115
    if (m_fixtureDef != NULL)
24✔
116
        return m_fixtureDef->type();
22✔
117
    else
118
        return QLCFixtureDef::Dimmer;
2✔
119
}
120

121
/*****************************************************************************
122
 * Universe
123
 *****************************************************************************/
124

125
void Fixture::setUniverse(quint32 universe)
95✔
126
{
127
    /* The universe part is stored in the highest 7 bits */
128
    m_address = (m_address & 0x01FF) | (universe << 9);
95✔
129

130
    emit changed(m_id);
95✔
131
}
95✔
132

133
quint32 Fixture::universe() const
796,279✔
134
{
135
    /* The universe part is stored in the highest 7 bits */
136
    return (m_address >> 9);
796,279✔
137
}
138

139
/*****************************************************************************
140
 * Address
141
 *****************************************************************************/
142

143
void Fixture::setAddress(quint32 address)
374✔
144
{
145
    /* Don't allow more than 512 channels per universe */
146
    if (address > 511)
374✔
147
        return;
3✔
148

149
    /* The address part is stored in the lowest 9 bits */
150
    m_address = (m_address & 0xFE00) | (address & 0x01FF);
371✔
151

152
    emit changed(m_id);
371✔
153
}
154

155
quint32 Fixture::address() const
795,740✔
156
{
157
    /* The address part is stored in the lowest 9 bits */
158
    return (m_address & 0x01FF);
795,740✔
159
}
160

161
quint32 Fixture::universeAddress() const
7,173✔
162
{
163
    return m_address;
7,173✔
164
}
165

166
/*****************************************************************************
167
 * Channels
168
 *****************************************************************************/
169

170
void Fixture::setChannels(quint32 channels)
224✔
171
{
172
    if (m_fixtureDef == NULL && m_fixtureMode == NULL)
224✔
173
    {
174
        QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
224✔
175
        QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
224✔
176
        setFixtureDefinition(fixtureDef, fixtureMode);
224✔
177
    }
178
    else
179
    {
180
        if ((quint32)m_fixtureMode->channels().size() != channels)
×
181
        {
182
            QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
×
183
            QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
×
184
            setFixtureDefinition(fixtureDef, fixtureMode);
×
185
        }
186
    }
187

188
    m_channels = channels;
224✔
189

190
    emit changed(m_id);
224✔
191
}
224✔
192

193
quint32 Fixture::channels() const
10,258✔
194
{
195
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
10,258✔
196
        return m_fixtureMode->channels().size();
10,238✔
197
    else
198
        return m_channels;
20✔
199
}
200

201
const QLCChannel* Fixture::channel(quint32 channel) const
797,969✔
202
{
203
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
797,969✔
204
        return m_fixtureMode->channel(channel);
797,966✔
205
    else
206
        return NULL;
3✔
207
}
208

209
quint32 Fixture::channelAddress(quint32 channel) const
7✔
210
{
211
    if (channel < channels())
7✔
212
        return universeAddress() + channel;
5✔
213
    else
214
        return QLCChannel::invalid();
2✔
215
}
216

217
quint32 Fixture::channel(QLCChannel::Group group,
4✔
218
    QLCChannel::PrimaryColour color) const
219
{
220
    if (m_fixtureDef == NULL && m_fixtureMode == NULL)
4✔
221
    {
222
        /* There's just one generic channel object with "Intensity" as
223
           its name that is the same for all channel numbers. So
224
           there's really no point in returning 0 or any otherwise
225
           valid channel number here. Which one of them the user would
226
           want to get? */
227
        return QLCChannel::invalid();
×
228
    }
229
    else
230
    {
231
        /* Search for the channel name (and group) from our list */
232
        for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
21✔
233
        {
234
            const QLCChannel* ch = m_fixtureMode->channel(i);
21✔
235
            Q_ASSERT(ch != NULL);
21✔
236

237
            if (group != QLCChannel::NoGroup && ch->group() != group)
21✔
238
            {
239
                /* Given group name doesn't match */
240
                continue;
15✔
241
            }
242
            else if (group != QLCChannel::Intensity || ch->colour() == color)
6✔
243
            {
244
                /* Found the channel */
245
                return i;
4✔
246
            }
247
        }
248

249
        /* Went thru all channels but a match was not found */
250
        return QLCChannel::invalid();
×
251
    }
252
}
253

254
QSet <quint32> Fixture::channels(QLCChannel::Group group, QLCChannel::PrimaryColour color) const
4✔
255
{
256
    QSet <quint32> set;
4✔
257
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
4✔
258
    {
259
        /* Search for the channel name (and group) from our list */
260
        for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
148✔
261
        {
262
            const QLCChannel* ch = m_fixtureMode->channel(i);
144✔
263
            Q_ASSERT(ch != NULL);
144✔
264

265
            if (group != QLCChannel::NoGroup && ch->group() != group)
144✔
266
            {
267
                /* Given group name doesn't match */
268
                continue;
48✔
269
            }
270
            else if (group != QLCChannel::Intensity || ch->colour() == color)
96✔
271
            {
272
                /* Found the channel */
273
                set << i;
24✔
274
            }
275
        }
276
    }
277

278
    return set;
4✔
279
}
280

281
quint32 Fixture::channelNumber(int type, int controlByte, int head) const
310✔
282
{
283
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
310✔
284
        return QLCChannel::invalid();
4✔
285

286
    return m_fixtureMode->heads().at(head).channelNumber(type, controlByte);
306✔
287
}
288

289
quint32 Fixture::masterIntensityChannel() const
40✔
290
{
291
    if (m_fixtureMode == NULL)
40✔
292
        return QLCChannel::invalid();
1✔
293

294
    return m_fixtureMode->masterIntensityChannel();
39✔
295
}
296

297
QVector <quint32> Fixture::rgbChannels(int head) const
15✔
298
{
299
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
15✔
300
        return QVector <quint32> ();
×
301

302
    return m_fixtureMode->heads().at(head).rgbChannels();
15✔
303
}
304

305
QVector <quint32> Fixture::cmyChannels(int head) const
1✔
306
{
307
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
1✔
308
        return QVector <quint32> ();
×
309

310
    return m_fixtureMode->heads().at(head).cmyChannels();
1✔
311
}
312

313
QList<SceneValue> Fixture::positionToValues(int type, int degrees, bool isRelative)
2✔
314
{
315
    QList<SceneValue> posList;
2✔
316
    // cache a list of channels processed, to avoid duplicates
317
    QList<quint32> chDone;
4✔
318

319
    if (m_fixtureMode == NULL)
2✔
320
        return posList;
×
321

322
    QLCPhysical phy = fixtureMode()->physical();
4✔
323
    qreal headDegrees = degrees, maxDegrees;
2✔
324
    float msbValue = 0, lsbValue = 0;
2✔
325

326
    if (type == QLCChannel::Pan)
2✔
327
    {
328
        maxDegrees = phy.focusPanMax();
1✔
329
        if (maxDegrees == 0) maxDegrees = 360;
1✔
330

331
        for (int i = 0; i < heads(); i++)
2✔
332
        {
333
            quint32 panMSB = channelNumber(QLCChannel::Pan, QLCChannel::MSB, i);
1✔
334
            if (panMSB == QLCChannel::invalid() || chDone.contains(panMSB))
1✔
335
                continue;
×
336
            quint32 panLSB = channelNumber(QLCChannel::Pan, QLCChannel::LSB, i);
1✔
337

338
            if (isRelative)
1✔
339
            {
340
                // degrees is a relative value upon the current value.
341
                // Recalculate absolute degrees here
NEW
342
                float chDegrees = (qreal(phy.focusPanMax()) / 256.0) * channelValueAt(panMSB);
×
NEW
343
                headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
344

NEW
345
                if (panLSB != QLCChannel::invalid())
×
346
                {
NEW
347
                    chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(panLSB);
×
NEW
348
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
349
                }
350
            }
351

352
            quint16 degToDmx = (headDegrees * 65535.0) / qreal(phy.focusPanMax());
1✔
353
            posList.append(SceneValue(id(), panMSB, static_cast<uchar>(degToDmx >> 8)));
1✔
354

355
            if (panLSB != QLCChannel::invalid())
1✔
356
                posList.append(SceneValue(id(), panLSB, static_cast<uchar>(degToDmx & 0x00FF)));
1✔
357

358
            qDebug() << "[positionToValues] Pan MSB:" << msbValue << "LSB:" << lsbValue;
1✔
359

360
            chDone.append(panMSB);
1✔
361
        }
362
    }
363
    else if (type == QLCChannel::Tilt)
1✔
364
    {
365
        maxDegrees = phy.focusTiltMax();
1✔
366
        if (maxDegrees == 0) maxDegrees = 270;
1✔
367

368
        for (int i = 0; i < heads(); i++)
2✔
369
        {
370
            quint32 tiltMSB = channelNumber(QLCChannel::Tilt, QLCChannel::MSB, i);
1✔
371
            if (tiltMSB == QLCChannel::invalid() || chDone.contains(tiltMSB))
1✔
372
                continue;
×
373
            quint32 tiltLSB = channelNumber(QLCChannel::Tilt, QLCChannel::LSB, i);
1✔
374

375
            if (isRelative)
1✔
376
            {
377
                // degrees is a relative value upon the current value.
378
                // Recalculate absolute degrees here
NEW
379
                float chDegrees = (qreal(phy.focusTiltMax()) / 256.0) * channelValueAt(tiltMSB);
×
NEW
380
                headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
381

NEW
382
                if (tiltLSB != QLCChannel::invalid())
×
383
                {
NEW
384
                    chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(tiltLSB);
×
NEW
385
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
386
                }
387
            }
388

389
            quint16 degToDmx = (headDegrees * 65535.0) / qreal(phy.focusTiltMax());
1✔
390
            posList.append(SceneValue(id(), tiltMSB, static_cast<uchar>(degToDmx >> 8)));
1✔
391

392
            if (tiltLSB != QLCChannel::invalid())
1✔
393
                posList.append(SceneValue(id(), tiltLSB, static_cast<uchar>(degToDmx & 0x00FF)));
1✔
394

395
            qDebug() << "[positionToValues] Tilt MSB:" << msbValue << "LSB:" << lsbValue;
1✔
396

397
            chDone.append(tiltMSB);
1✔
398
        }
399

400
    }
401

402
    return posList;
2✔
403
}
404

NEW
405
QList<SceneValue> Fixture::zoomToValues(float degrees, bool isRelative)
×
406
{
407
    QList<SceneValue> chList;
×
408

409
    if (m_fixtureMode == NULL)
×
410
        return chList;
×
411

412
    QLCPhysical phy = fixtureMode()->physical();
×
NEW
413
    if (!isRelative)
×
NEW
414
        degrees = qBound(float(phy.lensDegreesMin()), degrees, float(phy.lensDegreesMax()));
×
415

NEW
416
    float deltaDegrees = phy.lensDegreesMax() - phy.lensDegreesMin();
×
417
    // delta : 0xFFFF = deg : x
NEW
418
    quint16 degToDmx = ((degrees - (isRelative ? 0 : float(phy.lensDegreesMin()))) * 65535.0) / deltaDegrees;
×
419
    //qDebug() << "Degrees" << degrees << "DMX" << QString::number(degToDmx, 16);
420

421
    for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
×
422
    {
423
        const QLCChannel *ch = m_fixtureMode->channel(i);
×
424

425
        if (ch->group() != QLCChannel::Beam)
×
426
            continue;
×
427

428
        if (ch->preset() != QLCChannel::BeamZoomBigSmall &&
×
429
            ch->preset() != QLCChannel::BeamZoomSmallBig &&
×
430
            ch->preset() != QLCChannel::BeamZoomFine)
×
431
            continue;
×
432

NEW
433
        if (isRelative)
×
434
        {
435
            // degrees is a relative value upon the current value.
436
            // Recalculate absolute degrees here
NEW
437
            qreal divider = ch->controlByte() == QLCChannel::MSB ? 256.0 : 65536.0;
×
NEW
438
            float chDegrees = float((phy.lensDegreesMax() - phy.lensDegreesMin()) / divider) * float(channelValueAt(i));
×
439

440
            //qDebug() << "Relative channel degrees:" << chDegrees << "MSB?" << ch->controlByte();
441

NEW
442
            quint16 currDmxVal = (chDegrees * 65535.0) / deltaDegrees;
×
NEW
443
            degToDmx += currDmxVal;
×
444
        }
445

UNCOV
446
        if (ch->controlByte() == QLCChannel::MSB)
×
447
        {
448
            if (ch->preset() == QLCChannel::BeamZoomBigSmall)
×
NEW
449
                chList.append(SceneValue(id(), i, static_cast<uchar>(UCHAR_MAX - (degToDmx >> 8))));
×
450
            else
NEW
451
                chList.append(SceneValue(id(), i, static_cast<uchar>(degToDmx >> 8)));
×
452
        }
NEW
453
        else if (ch->controlByte() == QLCChannel::LSB)
×
454
        {
NEW
455
            chList.append(SceneValue(id(), i, static_cast<uchar>(degToDmx & 0x00FF)));
×
456
        }
457
    }
458

459
    return chList;
×
460
}
461

462
void Fixture::setExcludeFadeChannels(QList<int> indices)
11✔
463
{
464
    if (indices.count() > (int)channels())
11✔
465
        return;
×
466

467
    m_excludeFadeIndices = indices;
11✔
468
}
469

470
QList<int> Fixture::excludeFadeChannels()
2✔
471
{
472
    return m_excludeFadeIndices;
2✔
473
}
474

475
void Fixture::setChannelCanFade(int idx, bool canFade)
1✔
476
{
477
    if (canFade == false && m_excludeFadeIndices.contains(idx) == false)
1✔
478
    {
479
        m_excludeFadeIndices.append(idx);
1✔
480
        std::sort(m_excludeFadeIndices.begin(), m_excludeFadeIndices.end());
1✔
481
    }
482
    else if (canFade == true && m_excludeFadeIndices.contains(idx) == true)
×
483
    {
484
        m_excludeFadeIndices.removeOne(idx);
×
485
    }
486
}
1✔
487

488
bool Fixture::channelCanFade(int index)
795,208✔
489
{
490
    if (m_excludeFadeIndices.contains(index))
795,208✔
491
        return false;
1✔
492

493
    return true;
795,207✔
494
}
495

496
void Fixture::setForcedHTPChannels(QList<int> indices)
13✔
497
{
498
    if (indices.count() > (int)channels())
13✔
499
        return;
×
500
    m_forcedHTPIndices = indices;
13✔
501
    // cross check: if a channel is forced HTP it must be removed from
502
    // the forced LTP list (if present)
503
    for (int i = 0; i < m_forcedHTPIndices.count(); i++)
16✔
504
        m_forcedLTPIndices.removeAll(m_forcedHTPIndices.at(i));
3✔
505
}
506

507
QList<int> Fixture::forcedHTPChannels()
795,611✔
508
{
509
    return m_forcedHTPIndices;
795,611✔
510
}
511

512
void Fixture::setForcedLTPChannels(QList<int> indices)
13✔
513
{
514
    if (indices.count() > (int)channels())
13✔
515
        return;
×
516
    m_forcedLTPIndices = indices;
13✔
517
    // cross check: if a channel is forced LTP it must be removed from
518
    // the forced HTP list (if present)
519
    for (int i = 0; i < m_forcedLTPIndices.count(); i++)
15✔
520
        m_forcedHTPIndices.removeAll(m_forcedLTPIndices.at(i));
2✔
521
}
522

523
QList<int> Fixture::forcedLTPChannels()
795,610✔
524
{
525
    return m_forcedLTPIndices;
795,610✔
526
}
527

528
void Fixture::setChannelModifier(quint32 idx, ChannelModifier *mod)
×
529
{
530
    if (idx >= channels())
×
531
        return;
×
532

533
    if (mod == NULL)
×
534
    {
535
        m_channelModifiers.remove(idx);
×
536
        return;
×
537
    }
538

539
    qDebug() << Q_FUNC_INFO << idx << mod->name();
×
540
    m_channelModifiers[idx] = mod;
×
541
}
542

543
ChannelModifier *Fixture::channelModifier(quint32 idx)
2,556✔
544
{
545
    if (m_channelModifiers.contains(idx))
2,556✔
546
        return m_channelModifiers[idx];
×
547

548
    return NULL;
2,556✔
549
}
550

551
/*********************************************************************
552
 * Channel info
553
 *********************************************************************/
554

555
bool Fixture::setChannelValues(const QByteArray &values)
1✔
556
{
557
    const int addr = address();
1✔
558
    if (addr >= values.size())
1✔
559
        return false;
×
560

561
    const int chNum = qMin(values.size() - addr, (int)channels());
1✔
562
    bool changed = false;
1✔
563

564
    // Most of the times there are no changes,
565
    // so the lock is inside the cycle
566
    for (int i = 0; i < chNum; i++)
7✔
567
    {
568
        if (m_values.at(i) != values.at(i + addr))
6✔
569
        {
570
            changed = true;
6✔
571
            QMutexLocker locker(&m_channelsInfoMutex);
6✔
572
            m_values[i] = values.at(i + addr);
6✔
573
            checkAlias(i, m_values[i]);
6✔
574
        }
575
    }
576

577
    if (changed == true)
1✔
578
        emit valuesChanged();
1✔
579

580
    return changed;
1✔
581
}
582

583
QByteArray Fixture::channelValues()
11✔
584
{
585
    QMutexLocker locker(&m_channelsInfoMutex);
22✔
586
    return m_values;
11✔
587
}
588

589
uchar Fixture::channelValueAt(int idx)
×
590
{
591
    QMutexLocker locker(&m_channelsInfoMutex);
×
592
    if (idx >= 0 && idx < m_values.length())
×
593
        return (uchar)m_values.at(idx);
×
594
    return 0;
×
595
}
596

597
void Fixture::checkAlias(int chIndex, uchar value)
6✔
598
{
599
    if (chIndex < 0 || chIndex >= m_aliasInfo.count() ||
12✔
600
        m_aliasInfo[chIndex].m_hasAlias == false)
6✔
601
        return;
6✔
602

603
    // If the channel @chIndex has aliases, check
604
    // if replacements are to be done
605
    QLCCapability *cap = m_fixtureMode->channel(chIndex)->searchCapability(value);
×
606
    if (cap == NULL || cap == m_aliasInfo[chIndex].m_currCap)
×
607
        return;
×
608

609
    // first, revert any channel replaced to the original channel set
610
    foreach (AliasInfo alias, m_aliasInfo[chIndex].m_currCap->aliasList())
×
611
    {
612
        QLCFixtureMode *mode = m_fixtureDef->mode(alias.targetMode);
×
613
        if (mode != m_fixtureMode)
×
614
            continue;
×
615

616
        QLCChannel *currChannel = m_fixtureMode->channel(alias.targetChannel);
×
617
        QLCChannel *origChannel = m_fixtureDef->channel(alias.sourceChannel);
×
618

619
        m_fixtureMode->replaceChannel(currChannel, origChannel);
×
620
    }
621

622
    // now, apply the current alias changes
623
    foreach (AliasInfo alias, cap->aliasList())
×
624
    {
625
        QLCFixtureMode *mode = m_fixtureDef->mode(alias.targetMode);
×
626
        if (mode != m_fixtureMode)
×
627
            continue;
×
628

629
        QLCChannel *currChannel = m_fixtureMode->channel(alias.sourceChannel);
×
630
        QLCChannel *newChannel = m_fixtureDef->channel(alias.targetChannel);
×
631

632
        m_fixtureMode->replaceChannel(currChannel, newChannel);
×
633
    }
634

635
    emit aliasChanged();
×
636

637
    m_aliasInfo[chIndex].m_currCap = cap;
×
638

639
}
640

641
/*****************************************************************************
642
 * Fixture definition
643
 *****************************************************************************/
644

645
void Fixture::setFixtureDefinition(QLCFixtureDef* fixtureDef,
450✔
646
                                   QLCFixtureMode* fixtureMode)
647
{
648
    if (fixtureDef != NULL && fixtureMode != NULL)
450✔
649
    {
650
        int i, chNum;
651

652
        if (m_fixtureDef != NULL && m_fixtureDef != fixtureDef &&
40✔
653
            m_fixtureDef->manufacturer() == KXMLFixtureGeneric &&
936✔
654
            m_fixtureDef->model() == KXMLFixtureGeneric)
457✔
655
        {
656
            delete m_fixtureDef;
3✔
657
        }
658

659
        m_fixtureDef = fixtureDef;
448✔
660
        m_fixtureMode = fixtureMode;
448✔
661
        chNum = fixtureMode->channels().size();
448✔
662

663
        // If there are no head entries in the mode, create one that contains
664
        // all channels. This const_cast is a bit heretic, but it's easier this
665
        // way, than to change everything def & mode related non-const, which would
666
        // be worse than one constness violation here.
667
        if (fixtureMode->heads().size() == 0)
448✔
668
        {
669
            QLCFixtureHead head;
72✔
670
            for (i = 0; i < chNum; i++)
304✔
671
                head.addChannel(i);
268✔
672
            fixtureMode->insertHead(-1, head);
36✔
673
        }
674

675
        m_aliasInfo.resize(chNum);
448✔
676

677
        for (i = 0; i < chNum; i++)
3,656✔
678
        {
679
            QLCChannel *channel = fixtureMode->channel(i);
3,208✔
680
            const QList <QLCCapability*> capsList = channel->capabilities();
6,416✔
681

682
            // initialize values with the channel default
683
            m_values.append(channel->defaultValue());
3,208✔
684

685
            // look for aliases
686
            m_aliasInfo[i].m_hasAlias = false;
3,208✔
687
            m_aliasInfo[i].m_currCap = capsList.count() ? capsList.at(0) : NULL;
3,208✔
688

689
            foreach (QLCCapability *cap, capsList)
23,242✔
690
            {
691
                if (cap->preset() == QLCCapability::Alias)
10,017✔
692
                    m_aliasInfo[i].m_hasAlias = true;
×
693
            }
694
        }
695

696
        // Cache all head channels
697
        fixtureMode->cacheHeads();
448✔
698
    }
699
    else
700
    {
701
        m_fixtureDef = NULL;
2✔
702
        m_fixtureMode = NULL;
2✔
703
    }
704

705
    emit changed(m_id);
450✔
706
}
450✔
707

708
QLCFixtureDef* Fixture::fixtureDef() const
33✔
709
{
710
    return m_fixtureDef;
33✔
711
}
712

713
QLCFixtureMode* Fixture::fixtureMode() const
795,309✔
714
{
715
    return m_fixtureMode;
795,309✔
716
}
717

718
int Fixture::heads() const
820✔
719
{
720
    return m_fixtureMode->heads().size();
820✔
721
}
722

723
QLCFixtureHead Fixture::head(int index) const
30✔
724
{
725
    if (index < m_fixtureMode->heads().size())
30✔
726
        return m_fixtureMode->heads().at(index);
30✔
727
    else
728
        return QLCFixtureHead();
×
729
}
730

731
QString Fixture::iconResource(bool svg) const
12✔
732
{
733
    QString prefix = svg ? "qrc" : "";
24✔
734
    QString ext = svg ? "svg" : "png";
24✔
735

736
    switch(type())
12✔
737
    {
738
        case QLCFixtureDef::ColorChanger: return QString("%1:/fixture.%2").arg(prefix).arg(ext);
1✔
739
        case QLCFixtureDef::Dimmer: return QString("%1:/dimmer.%2").arg(prefix).arg(ext);
1✔
740
        case QLCFixtureDef::Effect: return QString("%1:/effect.%2").arg(prefix).arg(ext);
1✔
741
        case QLCFixtureDef::Fan: return QString("%1:/fan.%2").arg(prefix).arg(ext);
×
742
        case QLCFixtureDef::Flower: return QString("%1:/flower.%2").arg(prefix).arg(ext);
1✔
743
        case QLCFixtureDef::Hazer: return QString("%1:/hazer.%2").arg(prefix).arg(ext);
1✔
744
        case QLCFixtureDef::Laser: return QString("%1:/laser.%2").arg(prefix).arg(ext);
1✔
745
        case QLCFixtureDef::MovingHead: return QString("%1:/movinghead.%2").arg(prefix).arg(ext);
1✔
746
        case QLCFixtureDef::Scanner: return QString("%1:/scanner.%2").arg(prefix).arg(ext);
1✔
747
        case QLCFixtureDef::Smoke: return QString("%1:/smoke.%2").arg(prefix).arg(ext);
1✔
748
        case QLCFixtureDef::Strobe: return QString("%1:/strobe.%2").arg(prefix).arg(ext);
1✔
749
        case QLCFixtureDef::LEDBarBeams: return QString("%1:/ledbar_beams.%2").arg(prefix).arg(ext);
1✔
750
        case QLCFixtureDef::LEDBarPixels: return QString("%1:/ledbar_pixels.%2").arg(prefix).arg(ext);
1✔
751
        default: break;
×
752
    }
753

754
    return QString("%1:/other.%2").arg(prefix).arg(ext);
×
755
}
756

757
QIcon Fixture::getIconFromType() const
×
758
{
759
    return QIcon(iconResource());
×
760
}
761

762
QRectF Fixture::degreesRange(int head) const
7✔
763
{
764
    // TODO: handle fixtures with only pan or tilt
765

766
    if (m_fixtureMode != NULL && head < m_fixtureMode->heads().size())
7✔
767
    {
768
        QLCPhysical physical(m_fixtureMode->physical());
7✔
769
        qreal pan = physical.focusPanMax();
7✔
770
        qreal tilt = physical.focusTiltMax();
7✔
771

772
        if (pan != 0 && tilt != 0)
7✔
773
        {
774
            return QRectF(-pan/2, -tilt/2, pan, tilt);
7✔
775
        }
776
    }
777

778
    return QRectF();
×
779
}
780

781
/*********************************************************************
782
 * Generic Dimmer
783
 *********************************************************************/
784

785
QLCFixtureDef *Fixture::genericDimmerDef(int channels)
224✔
786
{
787
    QLCFixtureDef *def = new QLCFixtureDef();
224✔
788
    def->setManufacturer(KXMLFixtureGeneric);
224✔
789
    def->setModel(KXMLFixtureGeneric);
224✔
790
    def->setType(QLCFixtureDef::Dimmer);
224✔
791
    def->setAuthor("QLC+");
224✔
792

793
    for (int i = 0; i < channels; i++)
927✔
794
    {
795
        QLCChannel *intensity = new QLCChannel();
703✔
796
        intensity->setGroup(QLCChannel::Intensity);
703✔
797
        intensity->setName(tr("Dimmer #%1").arg(i + 1));
703✔
798
        intensity->addCapability(new QLCCapability(0, UCHAR_MAX, tr("Intensity")));
703✔
799
        def->addChannel(intensity);
703✔
800
    }
801

802
    return def;
224✔
803
}
804

805
QLCFixtureMode *Fixture::genericDimmerMode(QLCFixtureDef *def, int channels)
224✔
806
{
807
    Q_ASSERT(def != NULL);
224✔
808
    QLCFixtureMode *mode = new QLCFixtureMode(def);
224✔
809

810
    mode->setName(QString("%1 Channel").arg(channels));
224✔
811
    QList<QLCChannel *>chList = def->channels();
448✔
812
    for (int i = 0; i < chList.count(); i++)
927✔
813
    {
814
        QLCChannel *ch = chList.at(i);
703✔
815
        mode->insertChannel(ch, i);
703✔
816
        QLCFixtureHead head;
1,406✔
817
        head.addChannel(i);
703✔
818
        mode->insertHead(-1, head);
703✔
819
    }
820

821
    QLCPhysical physical;
224✔
822
    physical.setWidth(300 * channels);
224✔
823
    physical.setHeight(300);
224✔
824
    physical.setDepth(300);
224✔
825

826
    mode->setPhysical(physical);
224✔
827
    def->addMode(mode);
224✔
828

829
    return mode;
448✔
830
}
831

832
/*********************************************************************
833
 * Generic RGB panel
834
 *********************************************************************/
835

836
QLCFixtureDef *Fixture::genericRGBPanelDef(int columns, Components components)
7✔
837
{
838
    QLCFixtureDef *def = new QLCFixtureDef();
7✔
839
    def->setManufacturer(KXMLFixtureGeneric);
7✔
840
    def->setModel(KXMLFixtureRGBPanel);
7✔
841
    def->setType(QLCFixtureDef::LEDBarPixels);
7✔
842
    def->setAuthor("QLC+");
7✔
843
    for (int i = 0; i < columns; i++)
77✔
844
    {
845
        QLCChannel* red = new QLCChannel();
70✔
846
        red->setName(QString("Red %1").arg(i + 1));
70✔
847
        red->setGroup(QLCChannel::Intensity);
70✔
848
        red->setColour(QLCChannel::Red);
70✔
849

850
        QLCChannel* green = new QLCChannel();
70✔
851
        green->setName(QString("Green %1").arg(i + 1));
70✔
852
        green->setGroup(QLCChannel::Intensity);
70✔
853
        green->setColour(QLCChannel::Green);
70✔
854

855
        QLCChannel* blue = new QLCChannel();
70✔
856
        blue->setName(QString("Blue %1").arg(i + 1));
70✔
857
        blue->setGroup(QLCChannel::Intensity);
70✔
858
        blue->setColour(QLCChannel::Blue);
70✔
859

860
        if (components == BGR)
70✔
861
        {
862
            def->addChannel(blue);
10✔
863
            def->addChannel(green);
10✔
864
            def->addChannel(red);
10✔
865
        }
866
        else if (components == BRG)
60✔
867
        {
868
            def->addChannel(blue);
10✔
869
            def->addChannel(red);
10✔
870
            def->addChannel(green);
10✔
871
        }
872
        else if (components == GBR)
50✔
873
        {
874
            def->addChannel(green);
10✔
875
            def->addChannel(blue);
10✔
876
            def->addChannel(red);
10✔
877
        }
878
        else if (components == GRB)
40✔
879
        {
880
            def->addChannel(green);
10✔
881
            def->addChannel(red);
10✔
882
            def->addChannel(blue);
10✔
883
        }
884
        else if (components == RBG)
30✔
885
        {
886
            def->addChannel(red);
10✔
887
            def->addChannel(blue);
10✔
888
            def->addChannel(green);
10✔
889
        }
890
        else if (components == RGBW)
20✔
891
        {
892
            QLCChannel* white = new QLCChannel();
10✔
893
            white->setName(QString("White %1").arg(i + 1));
10✔
894
            white->setGroup(QLCChannel::Intensity);
10✔
895
            white->setColour(QLCChannel::White);
10✔
896

897
            def->addChannel(red);
10✔
898
            def->addChannel(green);
10✔
899
            def->addChannel(blue);
10✔
900
            def->addChannel(white);
10✔
901
        }
902
        else
903
        {
904
            def->addChannel(red);
10✔
905
            def->addChannel(green);
10✔
906
            def->addChannel(blue);
10✔
907
        }
908
    }
909

910
    return def;
7✔
911
}
912

913
QLCFixtureMode *Fixture::genericRGBPanelMode(QLCFixtureDef *def, Components components, quint32 width, quint32 height)
7✔
914
{
915
    Q_ASSERT(def != NULL);
7✔
916
    QLCFixtureMode *mode = new QLCFixtureMode(def);
7✔
917
    int compNum = 3;
7✔
918
    if (components == BGR)
7✔
919
        mode->setName("BGR");
1✔
920
    else if (components == BRG)
6✔
921
        mode->setName("BRG");
1✔
922
    else if (components == GBR)
5✔
923
        mode->setName("GBR");
1✔
924
    else if (components == GRB)
4✔
925
        mode->setName("GRB");
1✔
926
    else if (components == RBG)
3✔
927
        mode->setName("RBG");
1✔
928
    else if (components == RGBW)
2✔
929
    {
930
        mode->setName("RGBW");
1✔
931
        compNum = 4;
1✔
932
    }
933
    else
934
        mode->setName("RGB");
1✔
935

936
    QList<QLCChannel *>channels = def->channels();
14✔
937
    for (int i = 0; i < channels.count(); i++)
227✔
938
    {
939
        QLCChannel *ch = channels.at(i);
220✔
940
        mode->insertChannel(ch, i);
220✔
941
        if (i%compNum == 0)
220✔
942
        {
943
            QLCFixtureHead head;
140✔
944
            head.addChannel(i);
70✔
945
            head.addChannel(i+1);
70✔
946
            head.addChannel(i+2);
70✔
947
            if (components == RGBW)
70✔
948
                head.addChannel(i+3);
10✔
949
            mode->insertHead(-1, head);
70✔
950
        }
951
    }
952
    QLCPhysical physical;
7✔
953
    physical.setWidth(width);
7✔
954
    physical.setHeight(height);
7✔
955
    physical.setDepth(height);
7✔
956

957
    mode->setPhysical(physical);
7✔
958
    def->addMode(mode);
7✔
959

960
    return mode;
14✔
961
}
962

963
/*****************************************************************************
964
 * Load & Save
965
 *****************************************************************************/
966

967
bool Fixture::loader(QXmlStreamReader &root, Doc* doc)
4✔
968
{
969
    bool result = false;
4✔
970

971
    Fixture* fxi = new Fixture(doc);
4✔
972
    Q_ASSERT(fxi != NULL);
4✔
973

974
    if (fxi->loadXML(root, doc, doc->fixtureDefCache()) == true)
4✔
975
    {
976
        if (doc->addFixture(fxi, fxi->id()) == true)
4✔
977
        {
978
            /* Success */
979
            result = true;
4✔
980
        }
981
        else
982
        {
983
            /* Doc is full */
984
            qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name()
×
985
                       << "cannot be created.";
×
986
            delete fxi;
×
987
        }
988
    }
989
    else
990
    {
991
        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
992
        delete fxi;
×
993
    }
994

995
    return result;
4✔
996
}
997

998
bool Fixture::loadXML(QXmlStreamReader &xmlDoc, Doc *doc,
11✔
999
                      QLCFixtureDefCache *fixtureDefCache)
1000
{
1001
    QLCFixtureDef* fixtureDef = NULL;
11✔
1002
    QLCFixtureMode* fixtureMode = NULL;
11✔
1003
    QString manufacturer;
22✔
1004
    QString model;
22✔
1005
    QString modeName;
22✔
1006
    QString name;
22✔
1007
    quint32 id = Fixture::invalidId();
11✔
1008
    quint32 universe = 0;
11✔
1009
    quint32 address = 0;
11✔
1010
    quint32 channels = 0;
11✔
1011
    quint32 width = 0, height = 0;
11✔
1012
    QList<int> excludeList;
22✔
1013
    QList<int> forcedHTP;
22✔
1014
    QList<int> forcedLTP;
22✔
1015
    QList<quint32>modifierIndices;
22✔
1016
    QList<ChannelModifier *>modifierPointers;
22✔
1017

1018
    if (xmlDoc.name() != KXMLFixture)
11✔
1019
    {
1020
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
1✔
1021
        return false;
1✔
1022
    }
1023

1024
    while (xmlDoc.readNextStartElement())
90✔
1025
    {
1026
        if (xmlDoc.name() == KXMLQLCFixtureDefManufacturer)
80✔
1027
        {
1028
            manufacturer = xmlDoc.readElementText();
10✔
1029
        }
1030
        else if (xmlDoc.name() == KXMLQLCFixtureDefModel)
70✔
1031
        {
1032
            model = xmlDoc.readElementText();
10✔
1033
        }
1034
        else if (xmlDoc.name() == KXMLQLCFixtureMode)
60✔
1035
        {
1036
            modeName = xmlDoc.readElementText();
10✔
1037
        }
1038
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsWeight)
50✔
1039
        {
1040
            width = xmlDoc.readElementText().toUInt();
×
1041
        }
1042
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsHeight)
50✔
1043
        {
1044
            height = xmlDoc.readElementText().toUInt();
×
1045
        }
1046
        else if (xmlDoc.name() == KXMLFixtureID)
50✔
1047
        {
1048
            id = xmlDoc.readElementText().toUInt();
10✔
1049
        }
1050
        else if (xmlDoc.name() == KXMLFixtureName)
40✔
1051
        {
1052
            name = xmlDoc.readElementText();
10✔
1053
        }
1054
        else if (xmlDoc.name() == KXMLFixtureUniverse)
30✔
1055
        {
1056
            universe = xmlDoc.readElementText().toInt();
10✔
1057
        }
1058
        else if (xmlDoc.name() == KXMLFixtureAddress)
20✔
1059
        {
1060
            address = xmlDoc.readElementText().toInt();
10✔
1061
        }
1062
        else if (xmlDoc.name() == KXMLFixtureChannels)
10✔
1063
        {
1064
            channels = xmlDoc.readElementText().toInt();
10✔
1065
        }
1066
        else if (xmlDoc.name() == KXMLFixtureExcludeFade)
×
1067
        {
1068
            QString list = xmlDoc.readElementText();
×
1069
            QStringList values = list.split(",");
×
1070

1071
            for (int i = 0; i < values.count(); i++)
×
1072
                excludeList.append(values.at(i).toInt());
×
1073
        }
1074
        else if (xmlDoc.name() == KXMLFixtureForcedHTP)
×
1075
        {
1076
            QString list = xmlDoc.readElementText();
×
1077
            QStringList values = list.split(",");
×
1078

1079
            for (int i = 0; i < values.count(); i++)
×
1080
                forcedHTP.append(values.at(i).toInt());
×
1081
        }
1082
        else if (xmlDoc.name() == KXMLFixtureForcedLTP)
×
1083
        {
1084
            QString list = xmlDoc.readElementText();
×
1085
            QStringList values = list.split(",");
×
1086

1087
            for (int i = 0; i < values.count(); i++)
×
1088
                forcedLTP.append(values.at(i).toInt());
×
1089
        }
1090
        else if (xmlDoc.name() == KXMLFixtureChannelModifier)
×
1091
        {
1092
            QXmlStreamAttributes attrs = xmlDoc.attributes();
×
1093
            if (attrs.hasAttribute(KXMLFixtureChannelIndex) &&
×
1094
                attrs.hasAttribute(KXMLFixtureModifierName))
×
1095
            {
1096
                quint32 chIdx = attrs.value(KXMLFixtureChannelIndex).toString().toUInt();
×
1097
                QString modName = attrs.value(KXMLFixtureModifierName).toString();
×
1098
                ChannelModifier *chMod = doc->modifiersCache()->modifier(modName);
×
1099
                if (chMod != NULL)
×
1100
                {
1101
                    modifierIndices.append(chIdx);
×
1102
                    modifierPointers.append(chMod);
×
1103
                }
1104
                xmlDoc.skipCurrentElement();
×
1105
            }
1106
        }
1107
        else
1108
        {
1109
            qWarning() << Q_FUNC_INFO << "Unknown fixture tag:" << xmlDoc.name();
×
1110
            xmlDoc.skipCurrentElement();
×
1111
        }
1112
    }
1113

1114
    /* Find the given fixture definition, unless its a generic dimmer */
1115
    if (model != KXMLFixtureGeneric && model != KXMLFixtureRGBPanel)
10✔
1116
    {
1117
        fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
10✔
1118
        if (fixtureDef == NULL)
10✔
1119
        {
1120
            // fallback to project local path
1121
            QString man(manufacturer);
14✔
1122
            QString mod(model);
14✔
1123
            QString path = QString("%1%2%3-%4%5")
7✔
1124
                    .arg(doc->getWorkspacePath()).arg(QDir::separator())
14✔
1125
                    .arg(man.replace(" ", "-")).arg(mod.replace(" ", "-")).arg(KExtFixture);
21✔
1126

1127
            qDebug() << "Fixture not found. Fallback to:" << path;
7✔
1128

1129
            if (fixtureDefCache->loadQXF(path, true) == false)
7✔
1130
                qDebug() << "Failed to load definition" << path;
7✔
1131

1132
            fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
7✔
1133
            if (fixtureDef == NULL)
7✔
1134
            {
1135
                doc->appendToErrorLog(QString("No fixture definition found for <b>%1</b> <b>%2</b>")
21✔
1136
                                      .arg(manufacturer).arg(model));
14✔
1137
            }
1138
        }
1139

1140
        if (fixtureDef != NULL)
10✔
1141
        {
1142
            /* Find the given fixture mode */
1143
            fixtureMode = fixtureDef->mode(modeName);
3✔
1144
            if (fixtureMode == NULL)
3✔
1145
            {
1146
                doc->appendToErrorLog(QString("Fixture mode <b>%1</b> not found for <b>%2</b> <b>%3</b>")
×
1147
                                      .arg(modeName).arg(manufacturer).arg(model));
×
1148

1149
                /* Set this also NULL so that a generic dimmer will be
1150
                   created instead as a backup. */
1151
                fixtureDef = NULL;
×
1152
            }
1153
        }
1154
    }
1155

1156
    /* Number of channels */
1157
    if (channels <= 0)
10✔
1158
    {
1159
        doc->appendToErrorLog(QString("%1 channels of fixture <b>%2</b> are our of bounds")
×
1160
                              .arg(QString::number(channels))
×
1161
                              .arg(name));
×
1162
        channels = 1;
×
1163
    }
1164

1165
    /* Make sure that address is something sensible */
1166
    if (address > 511 || address + (channels - 1) > 511)
10✔
1167
    {
1168
        doc->appendToErrorLog(QString("Fixture address range %1-%2 is out of DMX bounds")
2✔
1169
                              .arg(QString::number(address))
2✔
1170
                              .arg(QString::number(address + channels - 1)));
2✔
1171
        address = 0;
1✔
1172
    }
1173

1174
    /* Check that the invalid ID is not used */
1175
    if (id == Fixture::invalidId())
10✔
1176
    {
1177
        qWarning() << Q_FUNC_INFO << "Fixture ID" << id << "is not allowed.";
1✔
1178
        return false;
1✔
1179
    }
1180

1181
    if (model == KXMLFixtureGeneric)
9✔
1182
    {
1183
        fixtureDef = genericDimmerDef(channels);
×
1184
        fixtureMode = genericDimmerMode(fixtureDef, channels);
×
1185
    }
1186
    else if (model == KXMLFixtureRGBPanel)
9✔
1187
    {
1188
        Components components = RGB;
×
1189
        int compNum = 3;
×
1190
        if (modeName == "BGR") components = BGR;
×
1191
        else if (modeName == "BRG") components = BRG;
×
1192
        else if (modeName == "GBR") components = GBR;
×
1193
        else if (modeName == "GRB") components = GRB;
×
1194
        else if (modeName == "RBG") components = RBG;
×
1195
        else if (modeName == "RGBW")
×
1196
        {
1197
            components = RGBW;
×
1198
            compNum = 4;
×
1199
        }
1200

1201
        fixtureDef = genericRGBPanelDef(channels / compNum, components);
×
1202
        fixtureMode = genericRGBPanelMode(fixtureDef, components, width, height);
×
1203
    }
1204

1205
    if (fixtureDef != NULL && fixtureMode != NULL)
9✔
1206
    {
1207
        /* Assign fixtureDef & mode only if BOTH are not NULL */
1208
        setFixtureDefinition(fixtureDef, fixtureMode);
2✔
1209
    }
1210
    else
1211
    {
1212
        /* Otherwise set just the channel count */
1213
        setChannels(channels);
7✔
1214
    }
1215

1216
    setAddress(address);
9✔
1217
    setUniverse(universe);
9✔
1218
    setName(name);
9✔
1219
    setExcludeFadeChannels(excludeList);
9✔
1220
    setForcedHTPChannels(forcedHTP);
9✔
1221
    setForcedLTPChannels(forcedLTP);
9✔
1222
    for (int i = 0; i < modifierIndices.count(); i++)
9✔
1223
        setChannelModifier(modifierIndices.at(i), modifierPointers.at(i));
×
1224
    setID(id);
9✔
1225

1226
    return true;
9✔
1227
}
1228

1229
bool Fixture::saveXML(QXmlStreamWriter *doc) const
4✔
1230
{
1231
    Q_ASSERT(doc != NULL);
4✔
1232

1233
    /* Fixture Instance entry */
1234
    doc->writeStartElement(KXMLFixture);
4✔
1235

1236
    /* Manufacturer */
1237
    if (m_fixtureDef != NULL)
4✔
1238
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, m_fixtureDef->manufacturer());
4✔
1239
    else
1240
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, KXMLFixtureGeneric);
×
1241

1242
    /* Model */
1243
    if (m_fixtureDef != NULL)
4✔
1244
        doc->writeTextElement(KXMLQLCFixtureDefModel, m_fixtureDef->model());
4✔
1245
    else
1246
        doc->writeTextElement(KXMLQLCFixtureDefModel, KXMLFixtureGeneric);
×
1247

1248
    /* Fixture mode */
1249
    if (m_fixtureMode != NULL)
4✔
1250
        doc->writeTextElement(KXMLQLCFixtureMode, m_fixtureMode->name());
4✔
1251
    else
1252
        doc->writeTextElement(KXMLQLCFixtureMode, KXMLFixtureGeneric);
×
1253

1254
    /* RGB Panel physical dimensions */
1255
    if (m_fixtureDef != NULL && m_fixtureDef->model() == KXMLFixtureRGBPanel && m_fixtureMode != NULL)
4✔
1256
    {
1257
        doc->writeTextElement(KXMLQLCPhysicalDimensionsWeight,
×
1258
                              QString::number(m_fixtureMode->physical().width()));
×
1259

1260
        doc->writeTextElement(KXMLQLCPhysicalDimensionsHeight,
×
1261
                              QString::number(m_fixtureMode->physical().height()));
×
1262
    }
1263

1264
    /* ID */
1265
    doc->writeTextElement(KXMLFixtureID, QString::number(id()));
4✔
1266
    /* Name */
1267
    doc->writeTextElement(KXMLFixtureName, m_name);
4✔
1268
    /* Universe */
1269
    doc->writeTextElement(KXMLFixtureUniverse, QString::number(universe()));
4✔
1270
    /* Address */
1271
    doc->writeTextElement(KXMLFixtureAddress, QString::number(address()));
4✔
1272
    /* Channel count */
1273
    doc->writeTextElement(KXMLFixtureChannels, QString::number(channels()));
4✔
1274

1275
    if (m_excludeFadeIndices.count() > 0)
4✔
1276
    {
1277
        QString list;
×
1278
        for (int i = 0; i < m_excludeFadeIndices.count(); i++)
×
1279
        {
1280
            if (list.isEmpty() == false)
×
1281
                list.append(QString(","));
×
1282
            list.append(QString("%1").arg(m_excludeFadeIndices.at(i)));
×
1283
        }
1284
        doc->writeTextElement(KXMLFixtureExcludeFade, list);
×
1285
    }
1286

1287
    if (m_forcedHTPIndices.count() > 0)
4✔
1288
    {
1289
        QString list;
×
1290
        for (int i = 0; i < m_forcedHTPIndices.count(); i++)
×
1291
        {
1292
            if (list.isEmpty() == false)
×
1293
                list.append(QString(","));
×
1294
            list.append(QString("%1").arg(m_forcedHTPIndices.at(i)));
×
1295
        }
1296
        doc->writeTextElement(KXMLFixtureForcedHTP, list);
×
1297
    }
1298

1299
    if (m_forcedLTPIndices.count() > 0)
4✔
1300
    {
1301
        QString list;
×
1302
        for (int i = 0; i < m_forcedLTPIndices.count(); i++)
×
1303
        {
1304
            if (list.isEmpty() == false)
×
1305
                list.append(QString(","));
×
1306
            list.append(QString("%1").arg(m_forcedLTPIndices.at(i)));
×
1307
        }
1308
        doc->writeTextElement(KXMLFixtureForcedLTP, list);
×
1309
    }
1310

1311
    if (m_channelModifiers.isEmpty() == false)
4✔
1312
    {
1313
        QHashIterator<quint32, ChannelModifier *> it(m_channelModifiers);
×
1314
        while (it.hasNext())
×
1315
        {
1316
            it.next();
×
1317
            quint32 ch = it.key();
×
1318
            ChannelModifier *mod = it.value();
×
1319
            if (mod != NULL)
×
1320
            {
1321
                doc->writeStartElement(KXMLFixtureChannelModifier);
×
1322
                doc->writeAttribute(KXMLFixtureChannelIndex, QString::number(ch));
×
1323
                doc->writeAttribute(KXMLFixtureModifierName, mod->name());
×
1324
                doc->writeEndElement();
×
1325
            }
1326
        }
1327
    }
1328

1329
    /* End the <Fixture> tag */
1330
    doc->writeEndElement();
4✔
1331

1332
    return true;
4✔
1333
}
1334

1335
/*****************************************************************************
1336
 * Status
1337
 *****************************************************************************/
1338

1339
QString Fixture::status() const
7✔
1340
{
1341
    QString info;
7✔
1342
    QString t;
14✔
1343

1344
    QString title("<TR><TD CLASS='hilite' COLSPAN='3'>%1</TD></TR>");
14✔
1345
    QString subTitle("<TR><TD CLASS='subhi' COLSPAN='3'>%1</TD></TR>");
14✔
1346
    QString genInfo("<TR><TD CLASS='emphasis'>%1</TD><TD COLSPAN='2'>%2</TD></TR>");
14✔
1347

1348
    /********************************************************************
1349
     * General info
1350
     ********************************************************************/
1351

1352
    info += "<TABLE COLS='3' WIDTH='100%'>";
7✔
1353

1354
    // Fixture title
1355
    info += title.arg(name());
7✔
1356

1357
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
7✔
1358
    {
1359
        // Manufacturer
1360
        info += genInfo.arg(tr("Manufacturer")).arg(m_fixtureDef->manufacturer());
2✔
1361
        info += genInfo.arg(tr("Model")).arg(m_fixtureDef->model());
2✔
1362
        info += genInfo.arg(tr("Mode")).arg(m_fixtureMode->name());
2✔
1363
        info += genInfo.arg(tr("Type")).arg(m_fixtureDef->typeToString(m_fixtureDef->type()));
2✔
1364
    }
1365

1366
    // Universe
1367
    info += genInfo.arg(tr("Universe")).arg(universe() + 1);
7✔
1368

1369
    // Address
1370
    QString range = QString("%1 - %2").arg(address() + 1).arg(address() + channels());
14✔
1371
    info += genInfo.arg(tr("Address Range")).arg(range);
7✔
1372

1373
    // Channels
1374
    info += genInfo.arg(tr("Channels")).arg(channels());
7✔
1375

1376
    // Binary address
1377
    QString binaryStr = QString("%1").arg(address() + 1, 10, 2, QChar('0'));
14✔
1378
    QString dipTable("<TABLE COLS='33' cellspacing='0'><TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_top.png\"></TD></TR>");
14✔
1379
    dipTable += "<TR><TD><IMG SRC=\"" ":/ds_border.png\"></TD><TD><IMG SRC=\"" ":/ds_border.png\"></TD>";
7✔
1380
    for (int i = 9; i >= 0; i--)
77✔
1381
    {
1382
        if (binaryStr.at(i) == '0')
70✔
1383
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_off.png\"></TD>";
45✔
1384
        else
1385
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_on.png\"></TD>";
25✔
1386
    }
1387
    dipTable += "<TD><IMG SRC=\"" ":/ds_border.png\"></TD></TR>";
7✔
1388
    dipTable += "<TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_bottom.png\"></TD></TR>";
7✔
1389
    dipTable += "</TABLE>";
7✔
1390

1391
    info += genInfo.arg(tr("Binary Address (DIP)"))
14✔
1392
            .arg(QString("%1").arg(dipTable));
7✔
1393

1394
    /********************************************************************
1395
     * Channels
1396
     ********************************************************************/
1397

1398
    // Title row
1399
    info += QString("<TR><TD CLASS='subhi'>%1</TD>").arg(tr("Channel"));
7✔
1400
    info += QString("<TD CLASS='subhi'>%1</TD>").arg(tr("DMX"));
7✔
1401
    info += QString("<TD CLASS='subhi'>%1</TD></TR>").arg(tr("Name"));
7✔
1402

1403
    // Fill table with the fixture's channels
1404
    for (quint32 ch = 0; ch < channels();        ch++)
28✔
1405
    {
1406
        QString chInfo("<TR><TD>%1</TD><TD>%2</TD><TD>%3</TD></TR>");
21✔
1407
        info += chInfo.arg(ch + 1).arg(address() + ch + 1)
42✔
1408
                .arg(channel(ch)->name());
21✔
1409
    }
1410

1411
    /********************************************************************
1412
     * Extended device information
1413
     ********************************************************************/
1414

1415
    if (m_fixtureMode != NULL)
7✔
1416
    {
1417
        QLCPhysical physical = m_fixtureMode->physical();
4✔
1418
        info += title.arg(tr("Physical"));
2✔
1419

1420
        float mmInch = 0.0393700787;
2✔
1421
        float kgLbs = 2.20462262;
2✔
1422
        QString mm("%1mm (%2\")");
4✔
1423
        QString kg("%1kg (%2 lbs)");
4✔
1424
        QString W("%1W");
4✔
1425
        info += genInfo.arg(tr("Width")).arg(mm.arg(physical.width()))
4✔
1426
                                        .arg(physical.width() * mmInch, 0, 'g', 4);
2✔
1427
        info += genInfo.arg(tr("Height")).arg(mm.arg(physical.height()))
4✔
1428
                                         .arg(physical.height() * mmInch, 0, 'g', 4);
2✔
1429
        info += genInfo.arg(tr("Depth")).arg(mm.arg(physical.depth()))
4✔
1430
                                        .arg(physical.depth() * mmInch, 0, 'g', 4);
2✔
1431
        info += genInfo.arg(tr("Weight")).arg(kg.arg(physical.weight()))
4✔
1432
                                         .arg(physical.weight() * kgLbs, 0, 'g', 4);
2✔
1433
        info += genInfo.arg(tr("Power consumption")).arg(W.arg(physical.powerConsumption()));
2✔
1434
        info += genInfo.arg(tr("DMX Connector")).arg(physical.dmxConnector());
2✔
1435

1436
        // Bulb
1437
        QString K("%1K");
4✔
1438
        QString lm("%1lm");
4✔
1439
        info += subTitle.arg(tr("Bulb"));
2✔
1440
        info += genInfo.arg(tr("Type")).arg(physical.bulbType());
2✔
1441
        info += genInfo.arg(tr("Luminous Flux")).arg(lm.arg(physical.bulbLumens()));
2✔
1442
        info += genInfo.arg(tr("Colour Temperature")).arg(K.arg(physical.bulbColourTemperature()));
2✔
1443

1444
        // Lens
1445
        QString angle1("%1&deg;");
4✔
1446
        QString angle2("%1&deg; &ndash; %2&deg;");
4✔
1447

1448
        info += subTitle.arg(tr("Lens"));
2✔
1449
        info += genInfo.arg(tr("Name")).arg(physical.lensName());
2✔
1450

1451
        if (physical.lensDegreesMin() == physical.lensDegreesMax())
2✔
1452
        {
1453
            info += genInfo.arg(tr("Beam Angle"))
4✔
1454
                .arg(angle1.arg(physical.lensDegreesMin()));
2✔
1455
        }
1456
        else
1457
        {
1458
            info += genInfo.arg(tr("Beam Angle"))
×
1459
                .arg(angle2.arg(physical.lensDegreesMin())
×
1460
                .arg(physical.lensDegreesMax()));
×
1461
        }
1462

1463
        // Focus
1464
        QString frange("%1&deg;");
4✔
1465
        info += subTitle.arg(tr("Head(s)"));
2✔
1466
        info += genInfo.arg(tr("Type")).arg(physical.focusType());
2✔
1467
        info += genInfo.arg(tr("Pan Range")).arg(frange.arg(physical.focusPanMax()));
2✔
1468
        info += genInfo.arg(tr("Tilt Range")).arg(frange.arg(physical.focusTiltMax()));
2✔
1469
        if (physical.layoutSize() != QSize(1, 1))
2✔
1470
        {
1471
            info += genInfo.arg(tr("Layout"))
×
1472
                           .arg(QString("%1 x %2").arg(physical.layoutSize().width()).arg(physical.layoutSize().height()));
×
1473
        }
1474
    }
1475

1476
    // HTML document & table closure
1477
    info += "</TABLE>";
7✔
1478

1479
    if (m_fixtureDef != NULL)
7✔
1480
    {
1481
        info += "<HR>";
2✔
1482
        info += "<DIV CLASS='author' ALIGN='right'>";
2✔
1483
        info += tr("Fixture definition author: ") + m_fixtureDef->author();
2✔
1484
        info += "</DIV>";
2✔
1485
    }
1486

1487
    return info;
14✔
1488
}
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