• 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

73.45
/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,824✔
44
{
45
    m_id = Fixture::invalidId();
16,824✔
46

47
    m_address = 0;
16,824✔
48
    m_channels = 0;
16,824✔
49
    m_crossUniverse = false;
16,824✔
50

51
    m_fixtureDef = NULL;
16,824✔
52
    m_fixtureMode = NULL;
16,824✔
53
}
16,824✔
54

55
Fixture::~Fixture()
33,626✔
56
{
57
}
33,626✔
58

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

67
/*****************************************************************************
68
 * Fixture ID
69
 *****************************************************************************/
70

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

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

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

87
/*****************************************************************************
88
 * Name
89
 *****************************************************************************/
90

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

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

102
/*****************************************************************************
103
 * Fixture type
104
 *****************************************************************************/
105

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

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

122
/*****************************************************************************
123
 * Universe
124
 *****************************************************************************/
125

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

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

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

140
void Fixture::setCrossUniverse(bool enable)
×
141
{
142
    m_crossUniverse = enable;
×
143
}
×
144

145
bool Fixture::crossUniverse() const
18✔
146
{
147
    return m_crossUniverse;
18✔
148
}
149

150
/*****************************************************************************
151
 * Address
152
 *****************************************************************************/
153

154
void Fixture::setAddress(quint32 address)
374✔
155
{
156
    /* Don't allow more than 512 channels per universe */
157
    if (address > 511)
374✔
158
        return;
159

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

163
    emit changed(m_id);
371✔
164
}
165

166
quint32 Fixture::address() const
795,744✔
167
{
168
    /* The address part is stored in the lowest 9 bits */
169
    return (m_address & 0x01FF);
795,744✔
170
}
171

172
quint32 Fixture::universeAddress() const
7,724✔
173
{
174
    return m_address;
7,724✔
175
}
176

177
/*****************************************************************************
178
 * Channels
179
 *****************************************************************************/
180

181
void Fixture::setChannels(quint32 channels)
224✔
182
{
183
    if (m_fixtureDef == NULL && m_fixtureMode == NULL)
224✔
184
    {
185
        QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
224✔
186
        QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
224✔
187
        setFixtureDefinition(fixtureDef, fixtureMode);
224✔
188
    }
224✔
189
    else
190
    {
191
        if ((quint32)m_fixtureMode->channels().size() != channels)
×
192
        {
193
            QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
×
194
            QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
×
195
            setFixtureDefinition(fixtureDef, fixtureMode);
×
196
        }
197
    }
198

199
    m_channels = channels;
224✔
200

201
    emit changed(m_id);
224✔
202
}
224✔
203

204
quint32 Fixture::channels() const
10,311✔
205
{
206
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
10,311✔
207
        return m_fixtureMode->channels().size();
10,291✔
208
    else
209
        return m_channels;
20✔
210
}
211

212
const QLCChannel* Fixture::channel(quint32 channel) const
797,996✔
213
{
214
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
797,996✔
215
        return m_fixtureMode->channel(channel);
797,993✔
216
    else
217
        return NULL;
218
}
219

220
quint32 Fixture::channelAddress(quint32 channel) const
7✔
221
{
222
    if (channel < channels())
7✔
223
        return universeAddress() + channel;
5✔
224
    else
225
        return QLCChannel::invalid();
2✔
226
}
227

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

248
            if (group != QLCChannel::NoGroup && ch->group() != group)
21✔
249
            {
250
                /* Given group name doesn't match */
251
                continue;
15✔
252
            }
253
            else if (group != QLCChannel::Intensity || ch->colour() == color)
6✔
254
            {
255
                /* Found the channel */
256
                return i;
4✔
257
            }
258
        }
259

260
        /* Went thru all channels but a match was not found */
261
        return QLCChannel::invalid();
×
262
    }
263
}
264

265
QSet <quint32> Fixture::channels(QLCChannel::Group group, QLCChannel::PrimaryColour color) const
4✔
266
{
267
    QSet <quint32> set;
268
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
4✔
269
    {
270
        /* Search for the channel name (and group) from our list */
271
        for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
148✔
272
        {
273
            const QLCChannel* ch = m_fixtureMode->channel(i);
144✔
274
            Q_ASSERT(ch != NULL);
275

276
            if (group != QLCChannel::NoGroup && ch->group() != group)
144✔
277
            {
278
                /* Given group name doesn't match */
279
                continue;
48✔
280
            }
281
            else if (group != QLCChannel::Intensity || ch->colour() == color)
96✔
282
            {
283
                /* Found the channel */
284
                set << i;
285
            }
286
        }
287
    }
288

289
    return set;
4✔
290
}
291

292
quint32 Fixture::channelNumber(int type, int controlByte, int head) const
310✔
293
{
294
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
310✔
295
        return QLCChannel::invalid();
4✔
296

297
    return m_fixtureMode->heads().at(head).channelNumber(type, controlByte);
306✔
298
}
299

300
quint32 Fixture::masterIntensityChannel() const
40✔
301
{
302
    if (m_fixtureMode == NULL)
40✔
303
        return QLCChannel::invalid();
1✔
304

305
    return m_fixtureMode->masterIntensityChannel();
39✔
306
}
307

308
QVector <quint32> Fixture::rgbChannels(int head) const
15✔
309
{
310
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
15✔
311
        return QVector <quint32> ();
312

313
    return m_fixtureMode->heads().at(head).rgbChannels();
15✔
314
}
315

316
QVector <quint32> Fixture::cmyChannels(int head) const
1✔
317
{
318
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
1✔
319
        return QVector <quint32> ();
320

321
    return m_fixtureMode->heads().at(head).cmyChannels();
1✔
322
}
323

324
QList<SceneValue> Fixture::positionToValues(int type, float degrees, bool isRelative)
2✔
325
{
326
    QList<SceneValue> posList;
327
    // cache a list of channels processed, to avoid duplicates
328
    QList<quint32> chDone;
329

330
    if (m_fixtureMode == NULL)
2✔
331
        return posList;
332

333
    QLCPhysical phy = fixtureMode()->physical();
2✔
334
    qreal headDegrees = degrees, maxDegrees;
2✔
335
    float msbValue = 0, lsbValue = 0;
336

337
    if (type == QLCChannel::Pan)
2✔
338
    {
339
        maxDegrees = phy.focusPanMax();
1✔
340
        if (maxDegrees == 0) maxDegrees = 360;
1✔
341

342
        for (int i = 0; i < heads(); i++)
2✔
343
        {
344
            quint32 panMSB = channelNumber(QLCChannel::Pan, QLCChannel::MSB, i);
1✔
345
            if (panMSB == QLCChannel::invalid() || chDone.contains(panMSB))
1✔
346
                continue;
×
347
            quint32 panLSB = channelNumber(QLCChannel::Pan, QLCChannel::LSB, i);
1✔
348

349
            if (isRelative)
1✔
350
            {
351
                // degrees is a relative value upon the current value.
352
                // Recalculate absolute degrees here
353
                float chDegrees = (qreal(phy.focusPanMax()) / 256.0) * channelValueAt(panMSB);
×
354
                headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
355

356
                if (panLSB != QLCChannel::invalid())
×
357
                {
358
                    chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(panLSB);
×
359
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
360
                }
361
            }
362

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

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

369
            qDebug() << "[positionToValues] Pan MSB:" << msbValue << "LSB:" << lsbValue;
370

371
            chDone.append(panMSB);
1✔
372
        }
373
    }
374
    else if (type == QLCChannel::Tilt)
1✔
375
    {
376
        maxDegrees = phy.focusTiltMax();
1✔
377
        if (maxDegrees == 0) maxDegrees = 270;
1✔
378

379
        for (int i = 0; i < heads(); i++)
2✔
380
        {
381
            quint32 tiltMSB = channelNumber(QLCChannel::Tilt, QLCChannel::MSB, i);
1✔
382
            if (tiltMSB == QLCChannel::invalid() || chDone.contains(tiltMSB))
1✔
383
                continue;
×
384
            quint32 tiltLSB = channelNumber(QLCChannel::Tilt, QLCChannel::LSB, i);
1✔
385

386
            if (isRelative)
1✔
387
            {
388
                // degrees is a relative value upon the current value.
389
                // Recalculate absolute degrees here
390
                float chDegrees = (qreal(phy.focusTiltMax()) / 256.0) * channelValueAt(tiltMSB);
×
391
                headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
392

393
                if (tiltLSB != QLCChannel::invalid())
×
394
                {
395
                    chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(tiltLSB);
×
396
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
397
                }
398
            }
399

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

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

406
            qDebug() << "[positionToValues] Tilt MSB:" << msbValue << "LSB:" << lsbValue;
407

408
            chDone.append(tiltMSB);
1✔
409
        }
410

411
    }
412

413
    return posList;
414
}
2✔
415

416
QList<SceneValue> Fixture::zoomToValues(float degrees, bool isRelative)
×
417
{
418
    QList<SceneValue> chList;
419

420
    if (m_fixtureMode == NULL)
×
421
        return chList;
422

423
    QLCPhysical phy = fixtureMode()->physical();
×
424
    if (!isRelative)
×
425
        degrees = qBound(float(phy.lensDegreesMin()), degrees, float(phy.lensDegreesMax()));
×
426

427
    float deltaDegrees = phy.lensDegreesMax() - phy.lensDegreesMin();
×
428
    // delta : 0xFFFF = deg : x
429
    quint16 degToDmx = ((qAbs(degrees) - (isRelative ? 0 : float(phy.lensDegreesMin()))) * 65535.0) / deltaDegrees;
×
430
    qDebug() << "Degrees" << degrees << "DMX" << QString::number(degToDmx, 16);
431

432
    for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
×
433
    {
434
        const QLCChannel *ch = m_fixtureMode->channel(i);
×
435

436
        if (ch->group() != QLCChannel::Beam)
×
437
            continue;
×
438

439
        if (ch->preset() != QLCChannel::BeamZoomBigSmall &&
×
440
            ch->preset() != QLCChannel::BeamZoomSmallBig &&
×
441
            ch->preset() != QLCChannel::BeamZoomFine)
×
442
            continue;
×
443

444
        if (isRelative)
×
445
        {
446
            // degrees is a relative value upon the current value.
447
            // Recalculate absolute degrees here
448
            qreal divider = ch->controlByte() == QLCChannel::MSB ? 256.0 : 65536.0;
×
449
            uchar currDmxValue = ch->preset() == QLCChannel::BeamZoomBigSmall ? UCHAR_MAX - channelValueAt(i) : channelValueAt(i);
×
450
            float chDegrees = float((phy.lensDegreesMax() - phy.lensDegreesMin()) / divider) * float(currDmxValue);
×
451

452
            qDebug() << "Relative channel degrees:" << chDegrees << "MSB?" << ch->controlByte();
453

454
            quint16 currDmxVal = (chDegrees * 65535.0) / deltaDegrees;
×
455
            if (degrees > 0)
×
456
                degToDmx = qBound(0, currDmxVal + degToDmx, 65535);
×
457
            else
458
                degToDmx = qBound(0, currDmxVal - degToDmx, 65535);
×
459
        }
460

461
        if (ch->controlByte() == QLCChannel::MSB)
×
462
        {
463
            if (ch->preset() == QLCChannel::BeamZoomBigSmall)
×
464
                chList.append(SceneValue(id(), i, static_cast<uchar>(UCHAR_MAX - (degToDmx >> 8))));
×
465
            else
466
                chList.append(SceneValue(id(), i, static_cast<uchar>(degToDmx >> 8)));
×
467
        }
468
        else if (ch->controlByte() == QLCChannel::LSB)
×
469
        {
470
            chList.append(SceneValue(id(), i, static_cast<uchar>(degToDmx & 0x00FF)));
×
471
        }
472
    }
473

474
    return chList;
475
}
×
476

477
void Fixture::setExcludeFadeChannels(QList<int> indices)
11✔
478
{
479
    if (indices.count() > (int)channels())
11✔
480
        return;
481

482
    m_excludeFadeIndices = indices;
11✔
483
}
484

485
QList<int> Fixture::excludeFadeChannels()
2✔
486
{
487
    return m_excludeFadeIndices;
2✔
488
}
489

490
void Fixture::setChannelCanFade(int idx, bool canFade)
1✔
491
{
492
    if (canFade == false && m_excludeFadeIndices.contains(idx) == false)
1✔
493
    {
494
        m_excludeFadeIndices.append(idx);
1✔
495
        std::sort(m_excludeFadeIndices.begin(), m_excludeFadeIndices.end());
1✔
496
    }
497
    else if (canFade == true && m_excludeFadeIndices.contains(idx) == true)
×
498
    {
499
        m_excludeFadeIndices.removeOne(idx);
×
500
    }
501
}
1✔
502

503
bool Fixture::channelCanFade(int index)
795,211✔
504
{
505
    if (m_excludeFadeIndices.contains(index))
795,211✔
506
        return false;
1✔
507

508
    return true;
509
}
510

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

522
QList<int> Fixture::forcedHTPChannels()
795,615✔
523
{
524
    return m_forcedHTPIndices;
795,615✔
525
}
526

527
void Fixture::setForcedLTPChannels(QList<int> indices)
13✔
528
{
529
    if (indices.count() > (int)channels())
13✔
530
        return;
531
    m_forcedLTPIndices = indices;
13✔
532
    // cross check: if a channel is forced LTP it must be removed from
533
    // the forced HTP list (if present)
534
    for (int i = 0; i < m_forcedLTPIndices.count(); i++)
15✔
535
        m_forcedHTPIndices.removeAll(m_forcedLTPIndices.at(i));
2✔
536
}
537

538
QList<int> Fixture::forcedLTPChannels()
795,614✔
539
{
540
    return m_forcedLTPIndices;
795,614✔
541
}
542

543
void Fixture::setChannelModifier(quint32 idx, ChannelModifier *mod)
×
544
{
545
    if (idx >= channels())
×
546
        return;
547

548
    if (mod == NULL)
×
549
    {
550
        m_channelModifiers.remove(idx);
×
551
        return;
×
552
    }
553

554
    qDebug() << Q_FUNC_INFO << idx << mod->name();
555
    m_channelModifiers[idx] = mod;
×
556
}
557

558
ChannelModifier *Fixture::channelModifier(quint32 idx)
2,572✔
559
{
560
    if (m_channelModifiers.contains(idx))
2,572✔
561
        return m_channelModifiers[idx];
×
562

563
    return NULL;
564
}
565

566
/*********************************************************************
567
 * Channel info
568
 *********************************************************************/
569

570
bool Fixture::setChannelValues(const QByteArray &values)
1✔
571
{
572
    const int addr = address();
1✔
573
    if (addr >= values.size())
1✔
574
        return false;
575

576
    const int chNum = qMin(values.size() - addr, (int)channels());
1✔
577
    bool changed = false;
578

579
    // Most of the times there are no changes,
580
    // so the lock is inside the cycle
581
    for (int i = 0; i < chNum; i++)
7✔
582
    {
583
        if (m_values.at(i) != values.at(i + addr))
6✔
584
        {
585
            changed = true;
586
            QMutexLocker locker(&m_channelsInfoMutex);
6✔
587
            m_values[i] = values.at(i + addr);
6✔
588
            checkAlias(i, m_values[i]);
6✔
589
        }
590
    }
591

592
    if (changed == true)
1✔
593
        emit valuesChanged();
1✔
594

595
    return changed;
596
}
597

598
QByteArray Fixture::channelValues()
11✔
599
{
600
    QMutexLocker locker(&m_channelsInfoMutex);
11✔
601
    return m_values;
11✔
602
}
603

604
uchar Fixture::channelValueAt(int idx)
×
605
{
606
    QMutexLocker locker(&m_channelsInfoMutex);
×
607
    if (idx >= 0 && idx < m_values.length())
×
608
        return (uchar)m_values.at(idx);
×
609
    return 0;
610
}
611

612
void Fixture::checkAlias(int chIndex, uchar value)
6✔
613
{
614
    if (chIndex < 0 || chIndex >= m_aliasInfo.count() ||
6✔
615
        m_aliasInfo[chIndex].m_hasAlias == false)
6✔
616
        return;
6✔
617

618
    // If the channel @chIndex has aliases, check
619
    // if replacements are to be done
620
    QLCCapability *cap = m_fixtureMode->channel(chIndex)->searchCapability(value);
×
621
    if (cap == NULL || cap == m_aliasInfo[chIndex].m_currCap)
×
622
        return;
×
623

624
    // first, revert any channel replaced to the original channel set
625
    foreach (AliasInfo alias, m_aliasInfo[chIndex].m_currCap->aliasList())
×
626
    {
627
        QLCFixtureMode *mode = m_fixtureDef->mode(alias.targetMode);
×
628
        if (mode != m_fixtureMode)
×
629
            continue;
630

631
        QLCChannel *currChannel = m_fixtureMode->channel(alias.targetChannel);
×
632
        QLCChannel *origChannel = m_fixtureDef->channel(alias.sourceChannel);
×
633

634
        m_fixtureMode->replaceChannel(currChannel, origChannel);
×
635
    }
×
636

637
    // now, apply the current alias changes
638
    foreach (AliasInfo alias, cap->aliasList())
×
639
    {
640
        QLCFixtureMode *mode = m_fixtureDef->mode(alias.targetMode);
×
641
        if (mode != m_fixtureMode)
×
642
            continue;
643

644
        QLCChannel *currChannel = m_fixtureMode->channel(alias.sourceChannel);
×
645
        QLCChannel *newChannel = m_fixtureDef->channel(alias.targetChannel);
×
646

647
        m_fixtureMode->replaceChannel(currChannel, newChannel);
×
648
    }
×
649

650
    emit aliasChanged();
×
651

652
    m_aliasInfo[chIndex].m_currCap = cap;
×
653

654
}
655

656
/*****************************************************************************
657
 * Fixture definition
658
 *****************************************************************************/
659

660
void Fixture::setFixtureDefinition(QLCFixtureDef* fixtureDef,
452✔
661
                                   QLCFixtureMode* fixtureMode)
662
{
663
    if (fixtureDef != NULL && fixtureMode != NULL)
452✔
664
    {
665
        int i, chNum;
666

667
        if (m_fixtureDef != NULL && m_fixtureDef != fixtureDef &&
40✔
668
            m_fixtureDef->manufacturer() == KXMLFixtureGeneric &&
929✔
669
            m_fixtureDef->model() == KXMLFixtureGeneric)
459✔
670
        {
671
            delete m_fixtureDef;
3✔
672
        }
673

674
        m_fixtureDef = fixtureDef;
450✔
675
        m_fixtureMode = fixtureMode;
450✔
676
        chNum = fixtureMode->channels().size();
450✔
677

678
        // If there are no head entries in the mode, create one that contains
679
        // all channels. This const_cast is a bit heretic, but it's easier this
680
        // way, than to change everything def & mode related non-const, which would
681
        // be worse than one constness violation here.
682
        if (fixtureMode->heads().size() == 0)
450✔
683
        {
684
            QLCFixtureHead head;
37✔
685
            for (i = 0; i < chNum; i++)
321✔
686
                head.addChannel(i);
284✔
687
            fixtureMode->insertHead(-1, head);
37✔
688
        }
37✔
689

690
        m_aliasInfo.resize(chNum);
450✔
691

692
        for (i = 0; i < chNum; i++)
3,734✔
693
        {
694
            QLCChannel *channel = fixtureMode->channel(i);
3,284✔
695
            const QList <QLCCapability*> capsList = channel->capabilities();
3,284✔
696

697
            // initialize values with the channel default
698
            m_values.append(channel->defaultValue());
3,284✔
699

700
            // look for aliases
701
            m_aliasInfo[i].m_hasAlias = false;
3,284✔
702
            m_aliasInfo[i].m_currCap = capsList.count() ? capsList.at(0) : NULL;
6,568✔
703

704
            foreach (QLCCapability *cap, capsList)
13,410✔
705
            {
706
                if (cap->preset() == QLCCapability::Alias)
10,126✔
707
                    m_aliasInfo[i].m_hasAlias = true;
×
708
            }
709
        }
3,284✔
710

711
        // Cache all head channels
712
        fixtureMode->cacheHeads();
450✔
713
    }
714
    else
715
    {
716
        m_fixtureDef = NULL;
2✔
717
        m_fixtureMode = NULL;
2✔
718
    }
719

720
    emit changed(m_id);
452✔
721
}
452✔
722

723
QLCFixtureDef* Fixture::fixtureDef() const
33✔
724
{
725
    return m_fixtureDef;
33✔
726
}
727

728
QLCFixtureMode* Fixture::fixtureMode() const
795,314✔
729
{
730
    return m_fixtureMode;
795,314✔
731
}
732

733
int Fixture::heads() const
820✔
734
{
735
    return m_fixtureMode->heads().size();
820✔
736
}
737

738
QLCFixtureHead Fixture::head(int index) const
30✔
739
{
740
    if (index < m_fixtureMode->heads().size())
30✔
741
        return m_fixtureMode->heads().at(index);
30✔
742
    else
743
        return QLCFixtureHead();
×
744
}
745

746
QString Fixture::iconResource(bool svg) const
12✔
747
{
748
    QString prefix = svg ? "qrc" : "";
21✔
749
    QString ext = svg ? "svg" : "png";
21✔
750

751
    switch(type())
12✔
752
    {
753
        case QLCFixtureDef::ColorChanger: return QString("%1:/fixture.%2").arg(prefix).arg(ext);
1✔
754
        case QLCFixtureDef::Dimmer: return QString("%1:/dimmer.%2").arg(prefix).arg(ext);
1✔
755
        case QLCFixtureDef::Effect: return QString("%1:/effect.%2").arg(prefix).arg(ext);
1✔
756
        case QLCFixtureDef::Fan: return QString("%1:/fan.%2").arg(prefix).arg(ext);
×
757
        case QLCFixtureDef::Flower: return QString("%1:/flower.%2").arg(prefix).arg(ext);
1✔
758
        case QLCFixtureDef::Hazer: return QString("%1:/hazer.%2").arg(prefix).arg(ext);
1✔
759
        case QLCFixtureDef::Laser: return QString("%1:/laser.%2").arg(prefix).arg(ext);
1✔
760
        case QLCFixtureDef::MovingHead: return QString("%1:/movinghead.%2").arg(prefix).arg(ext);
1✔
761
        case QLCFixtureDef::Scanner: return QString("%1:/scanner.%2").arg(prefix).arg(ext);
1✔
762
        case QLCFixtureDef::Smoke: return QString("%1:/smoke.%2").arg(prefix).arg(ext);
1✔
763
        case QLCFixtureDef::Strobe: return QString("%1:/strobe.%2").arg(prefix).arg(ext);
1✔
764
        case QLCFixtureDef::LEDBarBeams: return QString("%1:/ledbar_beams.%2").arg(prefix).arg(ext);
1✔
765
        case QLCFixtureDef::LEDBarPixels: return QString("%1:/ledbar_pixels.%2").arg(prefix).arg(ext);
1✔
766
        default: break;
767
    }
768

769
    return QString("%1:/other.%2").arg(prefix).arg(ext);
×
770
}
12✔
771

772
QIcon Fixture::getIconFromType() const
×
773
{
774
    return QIcon(iconResource());
×
775
}
776

777
QRectF Fixture::degreesRange(int head) const
7✔
778
{
779
    // TODO: handle fixtures with only pan or tilt
780

781
    if (m_fixtureMode != NULL && head < m_fixtureMode->heads().size())
7✔
782
    {
783
        QLCPhysical physical(m_fixtureMode->physical());
7✔
784
        qreal pan = physical.focusPanMax();
7✔
785
        qreal tilt = physical.focusTiltMax();
7✔
786

787
        if (pan != 0 && tilt != 0)
7✔
788
        {
789
            return QRectF(-pan/2, -tilt/2, pan, tilt);
7✔
790
        }
791
    }
7✔
792

793
    return QRectF();
794
}
795

796
/*********************************************************************
797
 * Generic Dimmer
798
 *********************************************************************/
799

800
QLCFixtureDef *Fixture::genericDimmerDef(int channels)
224✔
801
{
802
    QLCFixtureDef *def = new QLCFixtureDef();
224✔
803
    def->setManufacturer(KXMLFixtureGeneric);
224✔
804
    def->setModel(KXMLFixtureGeneric);
224✔
805
    def->setType(QLCFixtureDef::Dimmer);
224✔
806
    def->setAuthor("QLC+");
224✔
807

808
    for (int i = 0; i < channels; i++)
927✔
809
    {
810
        QLCChannel *intensity = new QLCChannel();
703✔
811
        intensity->setGroup(QLCChannel::Intensity);
703✔
812
        intensity->setName(tr("Dimmer #%1").arg(i + 1));
703✔
813
        intensity->addCapability(new QLCCapability(0, UCHAR_MAX, tr("Intensity")));
703✔
814
        def->addChannel(intensity);
703✔
815
    }
816

817
    return def;
224✔
818
}
819

820
QLCFixtureMode *Fixture::genericDimmerMode(QLCFixtureDef *def, int channels)
224✔
821
{
822
    Q_ASSERT(def != NULL);
823
    QLCFixtureMode *mode = new QLCFixtureMode(def);
224✔
824

825
    mode->setName(QString("%1 Channel").arg(channels));
448✔
826
    QList<QLCChannel *>chList = def->channels();
224✔
827
    for (int i = 0; i < chList.count(); i++)
927✔
828
    {
829
        QLCChannel *ch = chList.at(i);
703✔
830
        mode->insertChannel(ch, i);
703✔
831
        QLCFixtureHead head;
703✔
832
        head.addChannel(i);
703✔
833
        mode->insertHead(-1, head);
703✔
834
    }
703✔
835

836
    QLCPhysical physical;
224✔
837
    physical.setWidth(300 * channels);
224✔
838
    physical.setHeight(300);
224✔
839
    physical.setDepth(300);
224✔
840

841
    mode->setPhysical(physical);
224✔
842
    def->addMode(mode);
224✔
843

844
    return mode;
224✔
845
}
224✔
846

847
/*********************************************************************
848
 * Generic RGB panel
849
 *********************************************************************/
850

851
QString Fixture::componentsToString(Components comp, bool is16bit)
8✔
852
{
853
    QString compStr;
854

855
    switch (comp)
8✔
856
    {
857
        case BGR:
1✔
858
            compStr = "BGR";
1✔
859
        break;
860
        case BRG:
1✔
861
            compStr = "BRG";
1✔
862
        break;
863
        case GBR:
1✔
864
            compStr = "GBR";
1✔
865
        break;
866
        case GRB:
1✔
867
            compStr = "GRB";
1✔
868
        break;
869
        case RBG:
1✔
870
            compStr = "RBG";
1✔
871
        break;
872
        case RGBW:
1✔
873
            compStr = "RGBW";
1✔
874
        break;
875
        default:
2✔
876
            compStr = "RGB";
2✔
877
        break;
878
    }
879

880
    if (is16bit)
8✔
881
        compStr += " 16bit";
1✔
882

883
    return compStr;
8✔
884
}
×
885

886
Fixture::Components Fixture::stringToComponents(QString str, bool &is16bit)
×
887
{
888
    QStringList strToken = str.split(' ');
×
889
    is16bit = false;
×
890

891
    if (strToken.count() == 2)
×
892
    {
893
        if (strToken.at(1) == "16bit")
×
894
            is16bit = true;
×
895
    }
896

897
    if (strToken.at(0) == "BGR") return BGR;
×
898
    else if (strToken.at(0) == "BRG") return BRG;
×
899
    else if (strToken.at(0) == "GBR") return GBR;
×
900
    else if (strToken.at(0) == "GRB") return GRB;
×
901
    else if (strToken.at(0) == "RBG") return RBG;
×
902
    else if (strToken.at(0) == "RGBW") return RGBW;
×
903
    else return RGB;
×
904
}
905

906
QLCFixtureDef *Fixture::genericRGBPanelDef(int columns, Components components, bool is16bit)
8✔
907
{
908
    QLCFixtureDef *def = new QLCFixtureDef();
8✔
909
    def->setManufacturer(KXMLFixtureGeneric);
8✔
910
    def->setModel(KXMLFixtureRGBPanel);
8✔
911
    def->setType(QLCFixtureDef::LEDBarPixels);
8✔
912
    def->setAuthor("QLC+");
8✔
913

914
    for (int i = 0; i < columns; i++)
88✔
915
    {
916
        QLCChannel *red = new QLCChannel();
80✔
917
        red->setName(QString("Red %1").arg(i + 1));
80✔
918
        red->setGroup(QLCChannel::Intensity);
80✔
919
        red->setColour(QLCChannel::Red);
80✔
920

921
        QLCChannel *green = new QLCChannel();
80✔
922
        green->setName(QString("Green %1").arg(i + 1));
160✔
923
        green->setGroup(QLCChannel::Intensity);
80✔
924
        green->setColour(QLCChannel::Green);
80✔
925

926
        QLCChannel *blue = new QLCChannel();
80✔
927
        blue->setName(QString("Blue %1").arg(i + 1));
160✔
928
        blue->setGroup(QLCChannel::Intensity);
80✔
929
        blue->setColour(QLCChannel::Blue);
80✔
930

931
        QLCChannel *redFine = NULL;
932
        QLCChannel *greenFine = NULL;
933
        QLCChannel *blueFine = NULL;
934

935
        if (is16bit)
80✔
936
        {
937
            redFine = new QLCChannel();
10✔
938
            redFine->setName(QString("Red Fine %1").arg(i + 1));
20✔
939
            redFine->setGroup(QLCChannel::Intensity);
10✔
940
            redFine->setColour(QLCChannel::Red);
10✔
941
            redFine->setControlByte(QLCChannel::LSB);
10✔
942

943
            greenFine = new QLCChannel();
10✔
944
            greenFine->setName(QString("Green Fine %1").arg(i + 1));
20✔
945
            greenFine->setGroup(QLCChannel::Intensity);
10✔
946
            greenFine->setColour(QLCChannel::Green);
10✔
947
            greenFine->setControlByte(QLCChannel::LSB);
10✔
948

949
            blueFine = new QLCChannel();
10✔
950
            blueFine->setName(QString("Blue Fine %1").arg(i + 1));
20✔
951
            blueFine->setGroup(QLCChannel::Intensity);
10✔
952
            blueFine->setColour(QLCChannel::Blue);
10✔
953
            blueFine->setControlByte(QLCChannel::LSB);
10✔
954
        }
955

956
        if (components == BGR)
80✔
957
        {
958
            def->addChannel(blue);
10✔
959
            if (is16bit) def->addChannel(blueFine);
10✔
960
            def->addChannel(green);
10✔
961
            if (is16bit) def->addChannel(greenFine);
10✔
962
            def->addChannel(red);
10✔
963
            if (is16bit) def->addChannel(redFine);
10✔
964
        }
965
        else if (components == BRG)
966
        {
967
            def->addChannel(blue);
10✔
968
            if (is16bit) def->addChannel(blueFine);
10✔
969
            def->addChannel(red);
10✔
970
            if (is16bit) def->addChannel(redFine);
10✔
971
            def->addChannel(green);
10✔
972
            if (is16bit) def->addChannel(greenFine);
10✔
973
        }
974
        else if (components == GBR)
975
        {
976
            def->addChannel(green);
10✔
977
            if (is16bit) def->addChannel(greenFine);
10✔
978
            def->addChannel(blue);
10✔
979
            if (is16bit) def->addChannel(blueFine);
10✔
980
            def->addChannel(red);
10✔
981
            if (is16bit) def->addChannel(redFine);
10✔
982
        }
983
        else if (components == GRB)
984
        {
985
            def->addChannel(green);
10✔
986
            if (is16bit) def->addChannel(greenFine);
10✔
987
            def->addChannel(red);
10✔
988
            if (is16bit) def->addChannel(redFine);
10✔
989
            def->addChannel(blue);
10✔
990
            if (is16bit) def->addChannel(blueFine);
10✔
991
        }
992
        else if (components == RBG)
993
        {
994
            def->addChannel(red);
10✔
995
            if (is16bit) def->addChannel(redFine);
10✔
996
            def->addChannel(blue);
10✔
997
            if (is16bit) def->addChannel(blueFine);
10✔
998
            def->addChannel(green);
10✔
999
            if (is16bit) def->addChannel(greenFine);
10✔
1000
        }
1001
        else if (components == RGBW)
1002
        {
1003
            QLCChannel *white = new QLCChannel();
10✔
1004
            white->setName(QString("White %1").arg(i + 1));
20✔
1005
            white->setGroup(QLCChannel::Intensity);
10✔
1006
            white->setColour(QLCChannel::White);
10✔
1007

1008
            def->addChannel(red);
10✔
1009
            if (is16bit) def->addChannel(redFine);
10✔
1010
            def->addChannel(green);
10✔
1011
            if (is16bit) def->addChannel(greenFine);
10✔
1012
            def->addChannel(blue);
10✔
1013
            if (is16bit) def->addChannel(blueFine);
10✔
1014
            def->addChannel(white);
10✔
1015

1016
            if (is16bit)
10✔
1017
            {
1018
                QLCChannel *whiteFine = new QLCChannel();
×
1019
                whiteFine->setName(QString("White Fine %1").arg(i + 1));
×
1020
                whiteFine->setGroup(QLCChannel::Intensity);
×
1021
                whiteFine->setColour(QLCChannel::White);
×
1022
                whiteFine->setControlByte(QLCChannel::LSB);
×
1023
                def->addChannel(whiteFine);
×
1024
            }
1025
        }
1026
        else
1027
        {
1028
            def->addChannel(red);
20✔
1029
            if (is16bit) def->addChannel(redFine);
20✔
1030
            def->addChannel(green);
20✔
1031
            if (is16bit) def->addChannel(greenFine);
20✔
1032
            def->addChannel(blue);
20✔
1033
            if (is16bit) def->addChannel(blueFine);
20✔
1034
        }
1035
    }
1036

1037
    return def;
8✔
1038
}
1039

1040
QLCFixtureMode *Fixture::genericRGBPanelMode(QLCFixtureDef *def, Components components, bool is16bit,
8✔
1041
                                             quint32 width, quint32 height)
1042
{
1043
    Q_ASSERT(def != NULL);
1044

1045
    QLCFixtureMode *mode = new QLCFixtureMode(def);
8✔
1046
    QString modeName = componentsToString(components, is16bit);
8✔
1047
    mode->setName(modeName);
8✔
1048

1049
    int compNum = components == RGBW ? 4 : 3;
8✔
1050
    if (is16bit)
8✔
1051
        compNum *= 2;
1✔
1052

1053
    QList<QLCChannel *>channels = def->channels();
8✔
1054
    int i = 0;
1055

1056
    // add channels and heads
1057
    for (int h = 0; h < channels.count() / compNum; h++)
88✔
1058
    {
1059
        QLCFixtureHead head;
80✔
1060

1061
        for (int c = 0; c < compNum; c++, i++)
360✔
1062
        {
1063
            QLCChannel *ch = channels.at(i);
280✔
1064
            mode->insertChannel(ch, i);
280✔
1065
            head.addChannel(i);
280✔
1066
        }
1067

1068
        mode->insertHead(-1, head);
80✔
1069
    }
80✔
1070

1071
    QLCPhysical physical;
8✔
1072
    physical.setWidth(width);
8✔
1073
    physical.setHeight(height);
8✔
1074
    physical.setDepth(height);
8✔
1075
    physical.setLayoutSize(QSize(mode->heads().count(), 1));
8✔
1076

1077
    mode->setPhysical(physical);
8✔
1078
    def->addMode(mode);
8✔
1079

1080
    return mode;
8✔
1081
}
8✔
1082

1083
/*****************************************************************************
1084
 * Load & Save
1085
 *****************************************************************************/
1086

1087
bool Fixture::loader(QXmlStreamReader &root, Doc* doc)
4✔
1088
{
1089
    bool result = false;
1090

1091
    Fixture* fxi = new Fixture(doc);
4✔
1092
    Q_ASSERT(fxi != NULL);
1093

1094
    if (fxi->loadXML(root, doc, doc->fixtureDefCache()) == true)
4✔
1095
    {
1096
        if (doc->addFixture(fxi, fxi->id(), fxi->crossUniverse()) == true)
4✔
1097
        {
1098
            /* Success */
1099
            result = true;
1100
        }
1101
        else
1102
        {
1103
            /* Doc is full */
1104
            qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name()
×
1105
                       << "cannot be created.";
×
1106
            delete fxi;
×
1107
        }
1108
    }
1109
    else
1110
    {
1111
        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
1112
        delete fxi;
×
1113
    }
1114

1115
    return result;
4✔
1116
}
1117

1118
bool Fixture::loadXML(QXmlStreamReader &xmlDoc, Doc *doc,
11✔
1119
                      QLCFixtureDefCache *fixtureDefCache)
1120
{
1121
    QLCFixtureDef* fixtureDef = NULL;
1122
    QLCFixtureMode* fixtureMode = NULL;
1123
    QString manufacturer;
1124
    QString model;
1125
    QString modeName;
1126
    QString name;
1127
    quint32 id = Fixture::invalidId();
11✔
1128
    quint32 universe = 0;
1129
    quint32 address = 0;
1130
    quint32 channels = 0;
1131
    quint32 width = 0, height = 0;
1132
    QList<int> excludeList;
1133
    QList<int> forcedHTP;
1134
    QList<int> forcedLTP;
1135
    QList<quint32>modifierIndices;
1136
    QList<ChannelModifier *>modifierPointers;
1137

1138
    if (xmlDoc.name() != KXMLFixture)
22✔
1139
    {
1140
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
1✔
1141
        return false;
1✔
1142
    }
1143

1144
    while (xmlDoc.readNextStartElement())
90✔
1145
    {
1146
        if (xmlDoc.name() == KXMLQLCFixtureDefManufacturer)
160✔
1147
        {
1148
            manufacturer = xmlDoc.readElementText();
10✔
1149
        }
1150
        else if (xmlDoc.name() == KXMLQLCFixtureDefModel)
140✔
1151
        {
1152
            model = xmlDoc.readElementText();
10✔
1153
        }
1154
        else if (xmlDoc.name() == KXMLQLCFixtureMode)
120✔
1155
        {
1156
            modeName = xmlDoc.readElementText();
10✔
1157
        }
1158
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsWidth)
100✔
1159
        {
1160
            width = xmlDoc.readElementText().toUInt();
×
1161
        }
1162
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsHeight)
100✔
1163
        {
1164
            height = xmlDoc.readElementText().toUInt();
×
1165
        }
1166
        else if (xmlDoc.name() == KXMLFixtureID)
100✔
1167
        {
1168
            id = xmlDoc.readElementText().toUInt();
10✔
1169
        }
1170
        else if (xmlDoc.name() == KXMLFixtureName)
80✔
1171
        {
1172
            name = xmlDoc.readElementText();
10✔
1173
        }
1174
        else if (xmlDoc.name() == KXMLFixtureUniverse)
60✔
1175
        {
1176
            universe = xmlDoc.readElementText().toInt();
10✔
1177
        }
1178
        else if (xmlDoc.name() == KXMLFixtureCrossUniverse)
40✔
1179
        {
1180
            xmlDoc.readElementText();
×
1181
            setCrossUniverse(true);
×
1182
        }
1183
        else if (xmlDoc.name() == KXMLFixtureAddress)
40✔
1184
        {
1185
            address = xmlDoc.readElementText().toInt();
10✔
1186
        }
1187
        else if (xmlDoc.name() == KXMLFixtureChannels)
20✔
1188
        {
1189
            channels = xmlDoc.readElementText().toInt();
10✔
1190
        }
1191
        else if (xmlDoc.name() == KXMLFixtureExcludeFade)
×
1192
        {
1193
            QString list = xmlDoc.readElementText();
×
1194
            QStringList values = list.split(",");
×
1195

1196
            for (int i = 0; i < values.count(); i++)
×
1197
                excludeList.append(values.at(i).toInt());
×
1198
        }
×
1199
        else if (xmlDoc.name() == KXMLFixtureForcedHTP)
×
1200
        {
1201
            QString list = xmlDoc.readElementText();
×
1202
            QStringList values = list.split(",");
×
1203

1204
            for (int i = 0; i < values.count(); i++)
×
1205
                forcedHTP.append(values.at(i).toInt());
×
1206
        }
×
1207
        else if (xmlDoc.name() == KXMLFixtureForcedLTP)
×
1208
        {
1209
            QString list = xmlDoc.readElementText();
×
1210
            QStringList values = list.split(",");
×
1211

1212
            for (int i = 0; i < values.count(); i++)
×
1213
                forcedLTP.append(values.at(i).toInt());
×
1214
        }
×
1215
        else if (xmlDoc.name() == KXMLFixtureChannelModifier)
×
1216
        {
1217
            QXmlStreamAttributes attrs = xmlDoc.attributes();
×
1218
            if (attrs.hasAttribute(KXMLFixtureChannelIndex) &&
×
1219
                attrs.hasAttribute(KXMLFixtureModifierName))
×
1220
            {
1221
                quint32 chIdx = attrs.value(KXMLFixtureChannelIndex).toString().toUInt();
×
1222
                QString modName = attrs.value(KXMLFixtureModifierName).toString();
×
1223
                ChannelModifier *chMod = doc->modifiersCache()->modifier(modName);
×
1224
                if (chMod != NULL)
×
1225
                {
1226
                    modifierIndices.append(chIdx);
×
1227
                    modifierPointers.append(chMod);
×
1228
                }
1229
                xmlDoc.skipCurrentElement();
×
1230
            }
×
1231
        }
1232
        else
1233
        {
1234
            qWarning() << Q_FUNC_INFO << "Unknown fixture tag:" << xmlDoc.name();
×
1235
            xmlDoc.skipCurrentElement();
×
1236
        }
1237
    }
1238

1239
    /* Find the given fixture definition, unless its a generic dimmer */
1240
    if (model != KXMLFixtureGeneric && model != KXMLFixtureRGBPanel)
30✔
1241
    {
1242
        fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
10✔
1243
        if (fixtureDef == NULL)
10✔
1244
        {
1245
            // fallback to project local path
1246
            QString man(manufacturer);
1247
            QString mod(model);
1248
            QString path = QString("%1%2%3-%4%5")
7✔
1249
                    .arg(doc->getWorkspacePath()).arg(QDir::separator())
14✔
1250
                    .arg(man.replace(" ", "-")).arg(mod.replace(" ", "-")).arg(KExtFixture);
14✔
1251

1252
            qDebug() << "Fixture not found. Fallback to:" << path;
1253

1254
            if (fixtureDefCache->loadQXF(path, true) == false)
7✔
1255
                qDebug() << "Failed to load definition" << path;
1256

1257
            fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
7✔
1258
            if (fixtureDef == NULL)
7✔
1259
            {
1260
                doc->appendToErrorLog(QString("No fixture definition found for <b>%1</b> <b>%2</b>")
21✔
1261
                                      .arg(manufacturer).arg(model));
14✔
1262
            }
1263
        }
7✔
1264

1265
        if (fixtureDef != NULL)
10✔
1266
        {
1267
            /* Find the given fixture mode */
1268
            fixtureMode = fixtureDef->mode(modeName);
3✔
1269
            if (fixtureMode == NULL)
3✔
1270
            {
1271
                doc->appendToErrorLog(QString("Fixture mode <b>%1</b> not found for <b>%2</b> <b>%3</b>")
×
1272
                                      .arg(modeName).arg(manufacturer).arg(model));
×
1273

1274
                /* Set this also NULL so that a generic dimmer will be
1275
                   created instead as a backup. */
1276
                fixtureDef = NULL;
1277
            }
1278
        }
1279
    }
1280

1281
    /* Number of channels */
1282
    if (channels <= 0)
10✔
1283
    {
1284
        doc->appendToErrorLog(QString("%1 channels of fixture <b>%2</b> are out of bounds")
×
1285
                              .arg(QString::number(channels))
×
1286
                              .arg(name));
×
1287
        channels = 1;
1288
    }
1289

1290
    /* Make sure that address is something sensible */
1291
    if (!crossUniverse() && (address > 511 || address + (channels - 1) > 511))
10✔
1292
    {
1293
        doc->appendToErrorLog(QString("Fixture address range %1-%2 is out of DMX bounds")
3✔
1294
                              .arg(QString::number(address))
2✔
1295
                              .arg(QString::number(address + channels - 1)));
2✔
1296
        address = 0;
1297
    }
1298

1299
    /* Check that the invalid ID is not used */
1300
    if (id == Fixture::invalidId())
10✔
1301
    {
1302
        qWarning() << Q_FUNC_INFO << "Fixture ID" << id << "is not allowed.";
1✔
1303
        return false;
1✔
1304
    }
1305

1306
    if (model == KXMLFixtureGeneric)
9✔
1307
    {
1308
        fixtureDef = genericDimmerDef(channels);
×
1309
        fixtureMode = genericDimmerMode(fixtureDef, channels);
×
1310
    }
1311
    else if (model == KXMLFixtureRGBPanel)
9✔
1312
    {
1313
        bool is16bit = false;
×
1314
        Components components = stringToComponents(modeName, is16bit);
×
1315
        int compNum = components == RGBW ? 4 : 3;
×
1316
        if (is16bit)
×
1317
            compNum *= 2;
×
1318

1319
        fixtureDef = genericRGBPanelDef(channels / compNum, components, is16bit);
×
1320
        fixtureMode = genericRGBPanelMode(fixtureDef, components, is16bit, width, height);
×
1321
    }
1322

1323
    if (fixtureDef != NULL && fixtureMode != NULL)
9✔
1324
    {
1325
        /* Assign fixtureDef & mode only if BOTH are not NULL */
1326
        setFixtureDefinition(fixtureDef, fixtureMode);
2✔
1327
    }
1328
    else
1329
    {
1330
        /* Otherwise set just the channel count */
1331
        setChannels(channels);
7✔
1332
    }
1333

1334
    setAddress(address);
9✔
1335
    setUniverse(universe);
9✔
1336
    setName(name);
9✔
1337
    setExcludeFadeChannels(excludeList);
9✔
1338
    setForcedHTPChannels(forcedHTP);
9✔
1339
    setForcedLTPChannels(forcedLTP);
9✔
1340
    for (int i = 0; i < modifierIndices.count(); i++)
9✔
1341
        setChannelModifier(modifierIndices.at(i), modifierPointers.at(i));
×
1342
    setID(id);
9✔
1343

1344
    return true;
1345
}
11✔
1346

1347
bool Fixture::saveXML(QXmlStreamWriter *doc) const
4✔
1348
{
1349
    Q_ASSERT(doc != NULL);
1350

1351
    /* Fixture Instance entry */
1352
    doc->writeStartElement(KXMLFixture);
4✔
1353

1354
    /* Manufacturer */
1355
    if (m_fixtureDef != NULL)
4✔
1356
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, m_fixtureDef->manufacturer());
4✔
1357
    else
1358
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, KXMLFixtureGeneric);
×
1359

1360
    /* Model */
1361
    if (m_fixtureDef != NULL)
4✔
1362
        doc->writeTextElement(KXMLQLCFixtureDefModel, m_fixtureDef->model());
4✔
1363
    else
1364
        doc->writeTextElement(KXMLQLCFixtureDefModel, KXMLFixtureGeneric);
×
1365

1366
    /* Fixture mode */
1367
    if (m_fixtureMode != NULL)
4✔
1368
        doc->writeTextElement(KXMLQLCFixtureMode, m_fixtureMode->name());
4✔
1369
    else
1370
        doc->writeTextElement(KXMLQLCFixtureMode, KXMLFixtureGeneric);
×
1371

1372
    /* RGB Panel physical dimensions */
1373
    if (m_fixtureDef != NULL && m_fixtureDef->model() == KXMLFixtureRGBPanel && m_fixtureMode != NULL)
4✔
1374
    {
1375
        doc->writeTextElement(KXMLQLCPhysicalDimensionsWidth,
×
1376
                              QString::number(m_fixtureMode->physical().width()));
×
1377

1378
        doc->writeTextElement(KXMLQLCPhysicalDimensionsHeight,
×
1379
                              QString::number(m_fixtureMode->physical().height()));
×
1380
    }
1381

1382
    /* ID */
1383
    doc->writeTextElement(KXMLFixtureID, QString::number(id()));
4✔
1384
    /* Name */
1385
    doc->writeTextElement(KXMLFixtureName, m_name);
4✔
1386
    /* Universe */
1387
    doc->writeTextElement(KXMLFixtureUniverse, QString::number(universe()));
4✔
1388
    if (crossUniverse())
4✔
1389
        doc->writeTextElement(KXMLFixtureCrossUniverse, KXMLQLCTrue);
×
1390
    /* Address */
1391
    doc->writeTextElement(KXMLFixtureAddress, QString::number(address()));
4✔
1392
    /* Channel count */
1393
    doc->writeTextElement(KXMLFixtureChannels, QString::number(channels()));
4✔
1394

1395
    if (m_excludeFadeIndices.count() > 0)
4✔
1396
    {
1397
        QString list;
1398
        for (int i = 0; i < m_excludeFadeIndices.count(); i++)
×
1399
        {
1400
            if (list.isEmpty() == false)
×
1401
                list.append(QString(","));
×
1402
            list.append(QString("%1").arg(m_excludeFadeIndices.at(i)));
×
1403
        }
1404
        doc->writeTextElement(KXMLFixtureExcludeFade, list);
×
1405
    }
×
1406

1407
    if (m_forcedHTPIndices.count() > 0)
4✔
1408
    {
1409
        QString list;
1410
        for (int i = 0; i < m_forcedHTPIndices.count(); i++)
×
1411
        {
1412
            if (list.isEmpty() == false)
×
1413
                list.append(QString(","));
×
1414
            list.append(QString("%1").arg(m_forcedHTPIndices.at(i)));
×
1415
        }
1416
        doc->writeTextElement(KXMLFixtureForcedHTP, list);
×
1417
    }
×
1418

1419
    if (m_forcedLTPIndices.count() > 0)
4✔
1420
    {
1421
        QString list;
1422
        for (int i = 0; i < m_forcedLTPIndices.count(); i++)
×
1423
        {
1424
            if (list.isEmpty() == false)
×
1425
                list.append(QString(","));
×
1426
            list.append(QString("%1").arg(m_forcedLTPIndices.at(i)));
×
1427
        }
1428
        doc->writeTextElement(KXMLFixtureForcedLTP, list);
×
1429
    }
×
1430

1431
    if (m_channelModifiers.isEmpty() == false)
4✔
1432
    {
1433
        QHashIterator<quint32, ChannelModifier *> it(m_channelModifiers);
×
1434
        while (it.hasNext())
×
1435
        {
1436
            it.next();
×
1437
            quint32 ch = it.key();
×
1438
            ChannelModifier *mod = it.value();
×
1439
            if (mod != NULL)
×
1440
            {
1441
                doc->writeStartElement(KXMLFixtureChannelModifier);
×
1442
                doc->writeAttribute(KXMLFixtureChannelIndex, QString::number(ch));
×
1443
                doc->writeAttribute(KXMLFixtureModifierName, mod->name());
×
1444
                doc->writeEndElement();
×
1445
            }
1446
        }
1447
    }
1448

1449
    /* End the <Fixture> tag */
1450
    doc->writeEndElement();
4✔
1451

1452
    return true;
4✔
1453
}
1454

1455
/*****************************************************************************
1456
 * Status
1457
 *****************************************************************************/
1458

1459
QString Fixture::status() const
7✔
1460
{
1461
    QString info;
1462
    QString t;
1463

1464
    QString title("<TR><TD CLASS='hilite' COLSPAN='3'>%1</TD></TR>");
7✔
1465
    QString subTitle("<TR><TD CLASS='subhi' COLSPAN='3'>%1</TD></TR>");
7✔
1466
    QString genInfo("<TR><TD CLASS='emphasis'>%1</TD><TD COLSPAN='2'>%2</TD></TR>");
7✔
1467

1468
    /********************************************************************
1469
     * General info
1470
     ********************************************************************/
1471

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

1474
    // Fixture title
1475
    info += title.arg(name());
14✔
1476

1477
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
7✔
1478
    {
1479
        // Manufacturer
1480
        info += genInfo.arg(tr("Manufacturer")).arg(m_fixtureDef->manufacturer());
4✔
1481
        info += genInfo.arg(tr("Model")).arg(m_fixtureDef->model());
4✔
1482
        info += genInfo.arg(tr("Mode")).arg(m_fixtureMode->name());
4✔
1483
        info += genInfo.arg(tr("Type")).arg(m_fixtureDef->typeToString(m_fixtureDef->type()));
4✔
1484
    }
1485

1486
    // Universe
1487
    info += genInfo.arg(tr("Universe")).arg(universe() + 1);
7✔
1488

1489
    // Address
1490
    QString range = QString("%1 - %2").arg(address() + 1).arg(address() + channels());
7✔
1491
    info += genInfo.arg(tr("Address Range")).arg(range);
14✔
1492

1493
    // Channels
1494
    info += genInfo.arg(tr("Channels")).arg(channels());
14✔
1495

1496
    // Binary address
1497
    QString binaryStr = QString("%1").arg(address() + 1, 10, 2, QChar('0'));
7✔
1498
    QString dipTable("<TABLE COLS='33' cellspacing='0'><TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_top.png\"></TD></TR>");
7✔
1499
    dipTable += "<TR><TD><IMG SRC=\"" ":/ds_border.png\"></TD><TD><IMG SRC=\"" ":/ds_border.png\"></TD>";
7✔
1500
    for (int i = 9; i >= 0; i--)
77✔
1501
    {
1502
        if (binaryStr.at(i) == '0')
70✔
1503
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_off.png\"></TD>";
45✔
1504
        else
1505
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_on.png\"></TD>";
25✔
1506
    }
1507
    dipTable += "<TD><IMG SRC=\"" ":/ds_border.png\"></TD></TR>";
7✔
1508
    dipTable += "<TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_bottom.png\"></TD></TR>";
7✔
1509
    dipTable += "</TABLE>";
7✔
1510

1511
    info += genInfo.arg(tr("Binary Address (DIP)"))
14✔
1512
            .arg(QString("%1").arg(dipTable));
14✔
1513

1514
    /********************************************************************
1515
     * Channels
1516
     ********************************************************************/
1517

1518
    // Title row
1519
    info += QString("<TR><TD CLASS='subhi'>%1</TD>").arg(tr("Channel"));
21✔
1520
    info += QString("<TD CLASS='subhi'>%1</TD>").arg(tr("DMX"));
21✔
1521
    info += QString("<TD CLASS='subhi'>%1</TD></TR>").arg(tr("Name"));
21✔
1522

1523
    // Fill table with the fixture's channels
1524
    for (quint32 ch = 0; ch < channels();        ch++)
28✔
1525
    {
1526
        QString chInfo("<TR><TD>%1</TD><TD>%2</TD><TD>%3</TD></TR>");
21✔
1527
        info += chInfo.arg(ch + 1).arg(address() + ch + 1)
42✔
1528
                .arg(channel(ch)->name());
42✔
1529
    }
21✔
1530

1531
    /********************************************************************
1532
     * Extended device information
1533
     ********************************************************************/
1534

1535
    if (m_fixtureMode != NULL)
7✔
1536
    {
1537
        QLCPhysical physical = m_fixtureMode->physical();
2✔
1538
        info += title.arg(tr("Physical"));
4✔
1539

1540
        float mmInch = 0.0393700787;
1541
        float kgLbs = 2.20462262;
1542
        QString mm("%1mm (%2\")");
2✔
1543
        QString kg("%1kg (%2 lbs)");
2✔
1544
        QString W("%1W");
2✔
1545
        info += genInfo.arg(tr("Width")).arg(mm.arg(physical.width()))
6✔
1546
                                        .arg(physical.width() * mmInch, 0, 'g', 4);
4✔
1547
        info += genInfo.arg(tr("Height")).arg(mm.arg(physical.height()))
6✔
1548
                                         .arg(physical.height() * mmInch, 0, 'g', 4);
4✔
1549
        info += genInfo.arg(tr("Depth")).arg(mm.arg(physical.depth()))
6✔
1550
                                        .arg(physical.depth() * mmInch, 0, 'g', 4);
4✔
1551
        info += genInfo.arg(tr("Weight")).arg(kg.arg(physical.weight()))
4✔
1552
                                         .arg(physical.weight() * kgLbs, 0, 'g', 4);
4✔
1553
        info += genInfo.arg(tr("Power consumption")).arg(W.arg(physical.powerConsumption()));
6✔
1554
        info += genInfo.arg(tr("DMX Connector")).arg(physical.dmxConnector());
4✔
1555

1556
        // Bulb
1557
        QString K("%1K");
2✔
1558
        QString lm("%1lm");
2✔
1559
        info += subTitle.arg(tr("Bulb"));
4✔
1560
        info += genInfo.arg(tr("Type")).arg(physical.bulbType());
4✔
1561
        info += genInfo.arg(tr("Luminous Flux")).arg(lm.arg(physical.bulbLumens()));
6✔
1562
        info += genInfo.arg(tr("Colour Temperature")).arg(K.arg(physical.bulbColourTemperature()));
6✔
1563

1564
        // Lens
1565
        QString angle1("%1&deg;");
2✔
1566
        QString angle2("%1&deg; &ndash; %2&deg;");
2✔
1567

1568
        info += subTitle.arg(tr("Lens"));
4✔
1569
        info += genInfo.arg(tr("Name")).arg(physical.lensName());
4✔
1570

1571
        if (physical.lensDegreesMin() == physical.lensDegreesMax())
2✔
1572
        {
1573
            info += genInfo.arg(tr("Beam Angle"))
4✔
1574
                .arg(angle1.arg(physical.lensDegreesMin()));
4✔
1575
        }
1576
        else
1577
        {
1578
            info += genInfo.arg(tr("Beam Angle"))
×
1579
                .arg(angle2.arg(physical.lensDegreesMin())
×
1580
                .arg(physical.lensDegreesMax()));
×
1581
        }
1582

1583
        // Focus
1584
        QString frange("%1&deg;");
2✔
1585
        info += subTitle.arg(tr("Head(s)"));
4✔
1586
        info += genInfo.arg(tr("Type")).arg(physical.focusType());
4✔
1587
        info += genInfo.arg(tr("Pan Range")).arg(frange.arg(physical.focusPanMax()));
6✔
1588
        info += genInfo.arg(tr("Tilt Range")).arg(frange.arg(physical.focusTiltMax()));
6✔
1589
        if (physical.layoutSize() != QSize(1, 1))
4✔
1590
        {
1591
            info += genInfo.arg(tr("Layout"))
×
1592
                           .arg(QString("%1 x %2").arg(physical.layoutSize().width()).arg(physical.layoutSize().height()));
×
1593
        }
1594
    }
2✔
1595

1596
    // HTML document & table closure
1597
    info += "</TABLE>";
7✔
1598

1599
    if (m_fixtureDef != NULL)
7✔
1600
    {
1601
        info += "<HR>";
2✔
1602
        info += "<DIV CLASS='author' ALIGN='right'>";
2✔
1603
        info += tr("Fixture definition author: ") + m_fixtureDef->author();
6✔
1604
        info += "</DIV>";
2✔
1605
    }
1606

1607
    return info;
7✔
1608
}
7✔
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