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

mcallegari / qlcplus / 10668847825

02 Sep 2024 02:18PM UTC coverage: 31.426% (-0.07%) from 31.498%
10668847825

push

github

web-flow
Merge pull request #1610 from Ledjlale/fix/qt6_rebase2

qmltoqt6 rebasing

25 of 290 new or added lines in 19 files covered. (8.62%)

25 existing lines in 11 files now uncovered.

15012 of 47769 relevant lines covered (31.43%)

19745.74 hits per line

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

11.07
/engine/src/scriptrunner.cpp
1
/*
2
  Q Light Controller Plus
3
  scriptrunner.cpp
4

5
  Copyright (C) Massimo Callegari
6

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

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

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

20
#include <QQmlEngine>
21
#include <QJSEngine>
22
#include <QJSValue>
23
#include <QRandomGenerator>
24
#if !defined(Q_OS_IOS)
25
#include <QProcess>
26
#endif
27
#include <QDebug>
28

29
#include "scriptrunner.h"
30
#include "genericfader.h"
31
#include "fadechannel.h"
32
#include "mastertimer.h"
33
#include "universe.h"
34

35
ScriptRunner::ScriptRunner(Doc *doc, QString &content, QObject *parent)
1✔
36
    : QThread(parent)
37
    , m_doc(doc)
38
    , m_content(content)
39
    , m_running(false)
40
    , m_engine(NULL)
41
    , m_stopOnExit(true)
42
    , m_waitCount(0)
43
    , m_waitFunctionId(Function::invalidId())
1✔
44
{
45
}
1✔
46

47
ScriptRunner::~ScriptRunner()
×
48
{
49
    stop();
×
50
}
×
51

52
void ScriptRunner::execute()
1✔
53
{
54
    if (m_running)
1✔
55
        return;
×
56

57
    m_running = true;
1✔
58

59
    start();
1✔
60
}
61

62
void ScriptRunner::stop()
1✔
63
{
64
    if (m_running == false)
1✔
65
        return;
×
66

67
    if (m_engine)
1✔
68
    {
69
        m_engine->setInterrupted(true);
×
70
        m_engine->deleteLater();
×
71
        m_engine = NULL;
×
72
    }
73

74
    // Stop all functions started by this script
75
    foreach (quint32 fID, m_startedFunctions)
1✔
76
    {
77
        Function *function = m_doc->function(fID);
×
78
        if (function == NULL)
×
79
            continue;
×
80

81
        function->stop(FunctionParent::master());
×
82
    }
83
    m_startedFunctions.clear();
1✔
84

85
    // request to delete all the active faders
86
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
2✔
87
    {
88
        if (!fader.isNull())
×
89
            fader->requestDelete();
×
90
    }
91
    m_fadersMap.clear();
1✔
92

93
    m_running = false;
1✔
94
}
95

96
QStringList ScriptRunner::collectScriptData()
×
97
{
98
    QStringList syntaxErrorList;
×
99
    QJSEngine *engine = new QJSEngine();
×
100
    QJSValue objectValue = engine->newQObject(this);
×
101
    engine->globalObject().setProperty("Engine", objectValue);
×
102
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
×
103

104
    QJSValue script = engine->evaluate("(function run() { " + m_content + " })");
×
105
    if (script.isError())
×
106
    {
107
        QString msg = QString("Uncaught exception at line %2. %3")
×
108
                        .arg(script.property("lineNumber").toInt())
×
109
                        .arg(script.toString());
×
110
        qWarning() << msg;
×
111
        //qDebug() << "Stack: " << script.property("stack").toString();
112
        syntaxErrorList << msg;
×
113
    }
114
    else
115
    {
116
        qDebug() << "All good.";
×
117
    }
118

119
    if (script.isCallable() == false)
×
120
    {
121
        qDebug() << "ERROR. No function method found.";
×
122
    }
123
    else
124
    {
125
        QJSValue ret = script.call(QJSValueList());
×
126
        if (ret.isError())
×
127
        {
128
            QString msg = QString("Uncaught exception at line %2. %3")
×
129
                            .arg(ret.property("lineNumber").toInt())
×
130
                            .arg(ret.toString());
×
131
            qWarning() << msg;
×
132
            syntaxErrorList << msg;
×
133
        }
134
    }
135

136
    delete engine;
×
137

138
    return syntaxErrorList;
×
139
}
140

141
int ScriptRunner::currentWaitTime()
×
142
{
143
    return m_waitCount * MasterTimer::tick();
×
144
}
145

146
bool ScriptRunner::write(MasterTimer *timer, QList<Universe *> universes)
1✔
147
{
148
    if (m_waitCount > 0)
1✔
149
        m_waitCount--;
×
150

151
    if (m_fixtureValueQueue.count())
1✔
152
    {
153
        while (!m_fixtureValueQueue.isEmpty())
×
154
        {
155
            FixtureValue val = m_fixtureValueQueue.dequeue();
×
156

157
            QSharedPointer<GenericFader> fader = m_fadersMap.value(val.m_universe, QSharedPointer<GenericFader>());
×
158
            if (fader.isNull())
×
159
            {
160
                fader = universes[val.m_universe]->requestFader();
×
161
                //fader->adjustIntensity(getAttributeValue(Intensity));
162
                //fader->setBlendMode(blendMode());
163
                m_fadersMap[val.m_universe] = fader;
×
164
            }
165

166
            FadeChannel *fc = fader->getChannelFader(m_doc, universes[val.m_universe], val.m_fixtureID, val.m_channel);
×
167

168
            fc->setStart(fc->current());
×
169
            fc->setTarget(val.m_value);
×
170
            fc->setFadeTime(val.m_fadeTime);
×
171
            fc->setElapsed(0);
×
172
            fc->setReady(false);
×
173
        }
174
    }
175
    // if we don't have to wait and there are some funtions in the queue
176
    if (m_waitFunctionId == Function::invalidId() && m_functionQueue.count())
1✔
177
    {
178
        while (!m_functionQueue.isEmpty())
×
179
        {
NEW
180
            QPair<quint32, FunctionOperation> &pair = m_functionQueue.head();
×
181
            quint32 fID = pair.first;
×
NEW
182
            FunctionOperation operation = pair.second;
×
183

184
            Function *function = m_doc->function(fID);
×
185
            if (function == NULL)
×
186
            {
187
                qWarning() << QString("No such function (ID %1)").arg(fID);
×
188
                continue;
×
189
            }
190

NEW
191
            if (operation == FunctionOperation::START || operation == FunctionOperation::START_DONT_STOP)
×
192
            {
193
                function->start(timer, FunctionParent::master());
×
NEW
194
                if (operation == FunctionOperation::START)
×
NEW
195
                    m_startedFunctions << fID;
×
196
            }
NEW
197
            else if (operation == FunctionOperation::STOP)
×
198
            {
199
                function->stop(FunctionParent::master());
×
200
                m_startedFunctions.removeAll(fID);
×
201
            }
NEW
202
            else if (operation == FunctionOperation::WAIT_START)
×
203
            {
NEW
204
                if (!function->isRunning())
×
205
                {
206
                    // the function is not running, so we we wait and we stop dequeuing
NEW
207
                    m_waitFunctionId = fID;
×
NEW
208
                    connect(m_doc->masterTimer(), SIGNAL(functionStarted(quint32)), SLOT(slotWaitFunctionStarted(quint32)));
×
NEW
209
                    break;
×
210
                }
211
            }
NEW
212
            else if (operation == FunctionOperation::WAIT_STOP)
×
213
            {
NEW
214
                if (!function->stopped())
×
215
                {
216
                    // the function has to start or is still running, so we wait and we stop dequeuing
NEW
217
                    m_waitFunctionId = fID;
×
NEW
218
                    connect(m_doc->masterTimer(), SIGNAL(functionStopped(quint32)), SLOT(slotWaitFunctionStopped(quint32)));
×
NEW
219
                    break;
×
220
                }
221
            }
222
            // we can continue with the next function in the queue
NEW
223
            m_functionQueue.removeFirst();
×
224
        }
225
    }
226

227
    // If the JS call method has ended on its own, the thread
228
    // has finished, therefore there's nothing else to run here
229
    if (m_running == false)
1✔
230
        return false;
×
231

232
    return true;
1✔
233
}
234

NEW
235
void ScriptRunner::slotWaitFunctionStarted(quint32 fid)
×
236
{
NEW
237
    if (m_waitFunctionId == fid)
×
238
    {
NEW
239
        disconnect(m_doc->masterTimer(), SIGNAL(functionStarted(quint32)), this, SLOT(slotWaitFunctionStarted(quint32)));
×
NEW
240
        m_waitFunctionId = Function::invalidId();
×
241
    }
NEW
242
}
×
243

NEW
244
void ScriptRunner::slotWaitFunctionStopped(quint32 fid)
×
245
{
NEW
246
    if (m_waitFunctionId == fid)
×
247
    {
NEW
248
        disconnect(m_doc->masterTimer(), SIGNAL(functionStopped(quint32)), this, SLOT(slotWaitFunctionStopped(quint32)));
×
NEW
249
        m_startedFunctions.removeAll(fid);
×
NEW
250
        m_waitFunctionId = Function::invalidId();
×
251
    }
NEW
252
}
×
253

254
void ScriptRunner::run()
1✔
255
{
256
    m_waitCount = 0;
1✔
257

258
    m_engine = new QJSEngine();
1✔
259
    QJSValue objectValue = m_engine->newQObject(this);
1✔
260
    m_engine->globalObject().setProperty("Engine", objectValue);
1✔
261
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
1✔
262

263
    QJSValue script = m_engine->evaluate("(function run() { " + m_content + " })");
2✔
264

265
    if (script.isCallable() == false)
1✔
266
    {
267
        qDebug() << "ERROR. No function method found.";
1✔
268
        return;
1✔
269
    }
270
    else
271
    {
272
        QJSValue ret = script.call(QJSValueList());
×
273
        if (ret.isError())
×
274
        {
275
            QString msg("Uncaught exception at line %2. Error: %3");
×
276
            qWarning() << msg.arg(ret.property("lineNumber").toInt())
×
277
                             .arg(ret.toString());
×
278
        }
279
    }
280

281
    qDebug() << "[ScriptRunner] Code executed";
×
282

283
    // this thread is done. Wait for the calling Script to stop
284
    while (m_running)
×
285
        msleep(50);
×
286
}
287

288
/************************************************************************
289
 * JS exported methods
290
 ************************************************************************/
291

292
int ScriptRunner::getChannelValue(int universe, int channel)
×
293
{
294
    if (m_running == false)
×
295
        return false;
×
296

297
    QList<Universe*> uniList = m_doc->inputOutputMap()->claimUniverses();
×
298
    uchar dmxValue = 0;
×
299

300
    if (universe >= 0 && universe < uniList.count())
×
301
    {
302
        Universe *uni = uniList.at(universe);
×
303
        dmxValue = uni->preGMValue(channel);
×
304
    }
305
    m_doc->inputOutputMap()->releaseUniverses(false);
×
306

307
    return dmxValue;
×
308
}
309

310
bool ScriptRunner::setFixture(quint32 fxID, quint32 channel, uchar value, uint time)
×
311
{
312
    if (m_running == false)
×
313
        return false;
×
314

315
    qDebug() << Q_FUNC_INFO;
×
316

317
    Fixture *fxi = m_doc->fixture(fxID);
×
318
    if (fxi == NULL)
×
319
    {
320
        qWarning() << QString("No such fixture (ID: %1)").arg(fxID);
×
321
        return false;
×
322
    }
323

324
    if (channel >= fxi->channels())
×
325
    {
326
        qWarning() << QString("Fixture (%1) has no channel number %2").arg(fxi->name()).arg(channel);
×
327
        return false;
×
328
    }
329

330
    int address = fxi->address() + channel;
×
331
    if (address >= 512)
×
332
    {
333
        qWarning() << QString("Invalid address: %1").arg(address);
×
334
        return false;
×
335
    }
336

337
    // enqueue this fixture value to be processed at the next write call
338
    FixtureValue val;
339
    val.m_universe = fxi->universe();
×
340
    val.m_fixtureID = fxID;
×
341
    val.m_channel = channel;
×
342
    val.m_value = value;
×
343
    val.m_fadeTime = time;
×
344
    m_fixtureValueQueue.enqueue(val);
×
345

346
    return true;
×
347
}
348

NEW
349
bool ScriptRunner::stopOnExit(bool value)
×
350
{
NEW
351
    m_stopOnExit = value;
×
352

NEW
353
    return true;
×
354
}
355

NEW
356
Function* ScriptRunner::getFunctionIfRunning(quint32 fID)
×
357
{
358
    if (m_running == false)
×
NEW
359
        return NULL;
×
360

361
    Function *function = m_doc->function(fID);
×
362
    if (function == NULL)
×
363
    {
364
        qWarning() << QString("No such function (ID %1)").arg(fID);
×
NEW
365
        return NULL;
×
366
    }
367

NEW
368
    return function;
×
369
}
370

NEW
371
bool ScriptRunner::enqueueFunction(quint32 fID, FunctionOperation operation)
×
372
{
NEW
373
    Function *function = getFunctionIfRunning(fID);
×
374
    if (function == NULL)
×
UNCOV
375
        return false;
×
376

NEW
377
    QPair<quint32, FunctionOperation> pair;
×
378
    pair.first = fID;
×
NEW
379
    pair.second = operation;
×
380

381
    m_functionQueue.enqueue(pair);
×
382

383
    return true;
×
384
}
385

NEW
386
bool ScriptRunner::startFunction(quint32 fID)
×
387
{
NEW
388
    return enqueueFunction(fID, m_stopOnExit ? FunctionOperation::START : FunctionOperation::START_DONT_STOP);
×
389
}
390

NEW
391
bool ScriptRunner::stopFunction(quint32 fID)
×
392
{
NEW
393
    return enqueueFunction(fID, FunctionOperation::STOP);
×
394
}
395

NEW
396
bool ScriptRunner::isFunctionRunning(quint32 fID)
×
397
{
NEW
398
    Function *function = getFunctionIfRunning(fID);
×
NEW
399
    return function == NULL ? false : function->isRunning();
×
400
}
401

402
float ScriptRunner::getFunctionAttribute(quint32 fID, int attributeIndex)
×
403
{
NEW
404
    Function *function = getFunctionIfRunning(fID);
×
NEW
405
    return function == NULL ? 0 : function->getAttributeValue(attributeIndex);
×
406
}
407

408
bool ScriptRunner::setFunctionAttribute(quint32 fID, int attributeIndex, float value)
×
409
{
NEW
410
    Function *function = getFunctionIfRunning(fID);
×
411
    if (function == NULL)
×
UNCOV
412
        return false;
×
413

UNCOV
414
    function->adjustAttribute(value, attributeIndex);
×
415

416
    return true;
×
417
}
418

419
bool ScriptRunner::setFunctionAttribute(quint32 fID, QString attributeName, float value)
×
420
{
NEW
421
    Function *function = getFunctionIfRunning(fID);
×
422
    if (function == NULL)
×
UNCOV
423
        return false;
×
424

UNCOV
425
    int attrIndex = function->getAttributeIndex(attributeName);
×
426
    function->adjustAttribute(value, attrIndex);
×
427

428
    return true;
×
429
}
430

431
bool ScriptRunner::systemCommand(QString command)
×
432
{
433
    if (m_running == false)
×
434
        return false;
×
435

436
    qDebug() << Q_FUNC_INFO;
×
437

438
    // tokenize the command by splitting the base
439
    // program name and the arguments
440
    QStringList tokens = command.split(" ");
×
441
    if (tokens.count() == 0)
×
442
        return false;
×
443

444
    QString programName = tokens.first();
×
445
    QString multiPartArg;
×
446
    QStringList programArgs;
×
447
    for (int i = 1; i < tokens.size(); i++)
×
448
    {
449
        QString token = tokens.at(i);
×
450
        if (token.startsWith("'"))
×
451
        {
452
            multiPartArg.clear();
×
453
            multiPartArg.append(token.mid(1));
×
454
        }
455
        else
456
        {
457
            if (multiPartArg.isEmpty())
×
458
                programArgs << token;
×
459
            else
460
            {
461
                multiPartArg.append(" ");
×
462
                if (token.endsWith("'"))
×
463
                {
464
                    multiPartArg.append(token.mid(0, token.length() - 1));
×
465
                    programArgs << multiPartArg;
×
466
                    multiPartArg.clear();
×
467
                }
468
                else
469
                {
470
                    multiPartArg.append(token);
×
471
                }
472
            }
473
        }
474
    }
475

476
#if !defined(Q_OS_IOS)
477
    qint64 pid;
478
    QProcess *newProcess = new QProcess();
×
479
    newProcess->setProgram(programName);
×
480
    newProcess->setArguments(programArgs);
×
481
    newProcess->startDetached(&pid);
×
482
#endif
483

484
    return true;
×
485
}
486

487
bool ScriptRunner::waitTime(uint ms)
×
488
{
489
    m_waitCount += ms / MasterTimer::tick();
×
490

491
    if (m_running == false)
×
492
        return false;
×
493

494
    qDebug() << Q_FUNC_INFO;
×
495

496
    while (m_waitCount > 0)
×
497
    {
498
        if (m_running == false)
×
499
            break;
×
500

501
        usleep(10000);
×
502
    }
503

504
    return true;
×
505
}
506

507
bool ScriptRunner::waitTime(QString time)
×
508
{
509
    m_waitCount += Function::stringToSpeed(time) / MasterTimer::tick();
×
510

511
    if (m_running == false)
×
512
        return false;
×
513

514
    qDebug() << Q_FUNC_INFO;
×
515

516
    while (m_waitCount > 0)
×
517
    {
518
        if (m_running == false)
×
519
            break;
×
520

521
        usleep(10000);
×
522
    }
523

524
    return true;
×
525
}
526

NEW
527
bool ScriptRunner::waitFunctionStart(quint32 fID)
×
528
{
NEW
529
    return enqueueFunction(fID, FunctionOperation::WAIT_START);
×
530
}
531

NEW
532
bool ScriptRunner::waitFunctionStop(quint32 fID)
×
533
{
NEW
534
    return enqueueFunction(fID, FunctionOperation::WAIT_STOP);
×
535
}
536

UNCOV
537
bool ScriptRunner::setBlackout(bool enable)
×
538
{
539
    if (m_running == false)
×
540
        return false;
×
541

542
    qDebug() << Q_FUNC_INFO;
×
543

544
    m_doc->inputOutputMap()->requestBlackout(enable ? InputOutputMap::BlackoutRequestOn :
×
545
                                                      InputOutputMap::BlackoutRequestOff);
546

547
    return true;
×
548
}
549

550
bool ScriptRunner::setBPM(int bpm)
×
551
{
552
    if (m_running == false)
×
553
        return false;
×
554

555
    qDebug() << Q_FUNC_INFO;
×
556

557
    m_doc->inputOutputMap()->setBpmNumber(bpm);
×
558

559
    return true;
×
560
}
561

562
int ScriptRunner::random(QString minTime, QString maxTime)
×
563
{
564
    if (m_running == false)
×
565
        return 0;
×
566

567
    int min = Function::stringToSpeed(minTime);
×
568
    int max = Function::stringToSpeed(maxTime);
×
569

570
    return QRandomGenerator::global()->generate() % ((max + 1) - min) + min;
×
571
}
572

573
int ScriptRunner::random(int minTime, int maxTime)
×
574
{
575
    if (m_running == false)
×
576
        return 0;
×
577

578
    return QRandomGenerator::global()->generate() % ((maxTime + 1) - minTime) + minTime;
×
579
}
580

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