• 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

38.87
/engine/src/scriptv4.cpp
1
/*
2
  Q Light Controller Plus
3
  scriptv4.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 <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QRandomGenerator>
23
#include <QRegularExpression>
24
#include <QDebug>
25
#include <QUrl>
26

27
#include "scriptrunner.h"
28
#include "mastertimer.h"
29
#include "qlcmacros.h"
30
#include "universe.h"
31
#include "scriptv4.h"
32
#include "doc.h"
33

34
#define KXMLQLCScriptCommand QString("Command")
35
#define KXMLQLCScriptVersion QString("Version")
36

37
const QString Script::stopOnExitLegacy = QString("stoponexit");
38
const QString Script::stopOnExitCmd = QString("Engine.stopOnExit");
39
const QString Script::startFunctionLegacy = QString("startfunction");
40
const QString Script::startFunctionCmd = QString("Engine.startFunction");
41
const QString Script::stopFunctionLegacy = QString("stopfunction");
42
const QString Script::stopFunctionCmd = QString("Engine.stopFunction");
43
const QString Script::blackoutLegacy = QString("blackout");
44
const QString Script::blackoutCmd = QString("Engine.setBlackout");
45
const QString Script::waitLegacy = QString("wait");
46
const QString Script::waitCmd = QString("Engine.waitTime");
47
const QString Script::waitFunctionStartLegacy = QString("waitfunctionstart");
48
const QString Script::waitFunctionStartCmd = QString("Engine.waitFunctionStart");
49
const QString Script::waitFunctionStopLegacy = QString("waitfunctionstop");
50
const QString Script::waitFunctionStopCmd = QString("Engine.waitFunctionStop");
51
const QString Script::setFixtureLegacy = QString("setfixture");
52
const QString Script::setFixtureCmd = QString("Engine.setFixture");
53
const QString Script::systemLegacy = QString("systemcommand");
54
const QString Script::systemCmd = QString("Engine.systemCommand");
55
const QStringList knownKeywords(QStringList() << "ch" << "val" << "arg");
56

57
const QString Script::blackoutOn = QString("on"); // LEGACY - NOT USED
58
const QString Script::blackoutOff = QString("off"); // LEGACY - NOT USED
59
const QString Script::waitKeyCmd = QString("waitkey"); // LEGACY - NOT USED
60

61
/****************************************************************************
62
 * Initialization
63
 ****************************************************************************/
64

65
Script::Script(Doc* doc) : Function(doc, Function::ScriptType)
2✔
66
    , m_runner(NULL)
2✔
67
{
68
    setName(tr("New Script"));
2✔
69
}
2✔
70

71
Script::~Script()
3✔
72
{
73
}
3✔
74

75
QIcon Script::getIcon() const
×
76
{
77
    return QIcon(":/script.png");
×
78
}
79

80
quint32 Script::totalDuration()
×
81
{
82
    quint32 totalDuration = 0;
×
83

84
    ScriptRunner *runner = new ScriptRunner(doc(), m_data);
×
85
    runner->collectScriptData();
×
86
    totalDuration = runner->currentWaitTime();
×
87
    //runner->deleteLater();
88

89
    qDebug() << "Script total duration:" << totalDuration;
×
90

91
    return totalDuration;
×
92
}
93

94
Function* Script::createCopy(Doc* doc, bool addToDoc)
×
95
{
96
    Q_ASSERT(doc != NULL);
×
97

98
    Function* copy = new Script(doc);
×
99
    if (copy->copyFrom(this) == false)
×
100
    {
101
        delete copy;
×
102
        copy = NULL;
×
103
    }
104
    if (addToDoc == true && doc->addFunction(copy) == false)
×
105
    {
106
        delete copy;
×
107
        copy = NULL;
×
108
    }
109

110
    return copy;
×
111
}
112

113
bool Script::copyFrom(const Function* function)
×
114
{
115
    const Script* script = qobject_cast<const Script*> (function);
×
116
    if (script == NULL)
×
117
        return false;
×
118

119
    setData(script->data());
×
120

121
    return Function::copyFrom(function);
×
122
}
123

124
/****************************************************************************
125
 * Script data
126
 ****************************************************************************/
127

128
bool Script::setData(const QString& str)
1✔
129
{
130
    if (str == m_data)
1✔
131
        return false;
×
132

133
    m_data = str;
1✔
134

135
    Doc* doc = qobject_cast<Doc*> (parent());
1✔
136
    Q_ASSERT(doc != NULL);
1✔
137
    doc->setModified();
1✔
138

139
    return true;
1✔
140
}
141

142
bool Script::appendData(const QString &str)
1✔
143
{
144
    //m_data.append(str + QString("\n"));
145
    m_data.append(convertLine(str + QString("\n")));
1✔
146

147
    return true;
1✔
148
}
149

150
QString Script::data() const
×
151
{
152
    return m_data;
×
153
}
154

155
QStringList Script::dataLines() const
7✔
156
{
157
    QStringList result = m_data.split(QRegularExpression("(\\r\\n|\\n\\r|\\r|\\n)"));
14✔
158

159
    while (result.count() && result.last().isEmpty())
14✔
160
        result.takeLast();
7✔
161

162
    return result;
7✔
163
}
164

165
QList<quint32> Script::functionList() const
7✔
166
{
167
    QList<quint32> list;
7✔
168
    int count = 0;
7✔
169

170
    foreach (QString line, dataLines())
21✔
171
    {
172
        count ++;
7✔
173
        if (line.startsWith(startFunctionCmd + "(") ||
14✔
174
                line.startsWith(stopFunctionCmd + "("))
7✔
175
        {
176
            QStringList tokens = line.split("(");
7✔
177
            if (tokens.isEmpty() || tokens.count() < 2)
7✔
178
                continue;
×
179

180
            tokens = tokens[1].split(")");
7✔
181
            if (tokens.isEmpty() || tokens.count() < 2)
7✔
182
                continue;
×
183

184
            QStringList params = tokens[0].split(",");
7✔
185
            if (tokens.isEmpty())
7✔
186
                continue;
×
187

188
            quint32 funcID = params[0].toUInt();
7✔
189
            if (list.contains(funcID) == false)
7✔
190
            {
191
                list.append(funcID);
7✔
192
                list.append(count - 1);
7✔
193
            }
194
        }
195
    }
196

197
    return list;
7✔
198
}
199

200
QList<quint32> Script::fixtureList() const
×
201
{
202
    QList<quint32> list;
×
203

204
    foreach (QString line, dataLines())
×
205
    {
206
        if (line.contains("setFixture"))
×
207
        {
208
            QStringList tokens = line.split("(");
×
209
            if (tokens.isEmpty() || tokens.count() < 2)
×
210
                continue;
×
211

212
            QStringList params = tokens[1].split(",");
×
213
            if (tokens.isEmpty())
×
214
                continue;
×
215

216
            quint32 fxID = params[0].toUInt();
×
217
            if (list.contains(fxID) == false)
×
218
                list.append(fxID);
×
219
        }
220
    }
221

222
    return list;
×
223
}
224

225
QStringList Script::syntaxErrorsLines()
×
226
{
227
    ScriptRunner *runner = new ScriptRunner(doc(), m_data);
×
228
    QStringList errorList = runner->collectScriptData();
×
229
    //runner->deleteLater();
230

231
    return errorList;
×
232
}
233

234
/****************************************************************************
235
 * Load & Save
236
 ****************************************************************************/
237

238
bool Script::loadXML(QXmlStreamReader &root)
×
239
{
240
    if (root.name() != KXMLQLCFunction)
×
241
    {
242
        qWarning() << Q_FUNC_INFO << "Function node not found";
×
243
        return false;
×
244
    }
245

246
    QXmlStreamAttributes attrs = root.attributes();
×
247

248
    if (attrs.value(KXMLQLCFunctionType).toString() != typeToString(Function::ScriptType))
×
249
    {
250
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
×
251
                   << "is not a script";
×
252
        return false;
×
253
    }
254

255
    int version = 1;
×
256

257
    if (attrs.hasAttribute(KXMLQLCScriptVersion))
×
258
        version = attrs.value(KXMLQLCScriptVersion).toInt();
×
259

260
    /* Load script contents */
261
    while (root.readNextStartElement())
×
262
    {
263
        if (root.name() == KXMLQLCFunctionSpeed)
×
264
        {
265
            loadXMLSpeed(root);
×
266
        }
267
        else if (root.name() == KXMLQLCFunctionDirection)
×
268
        {
269
            loadXMLDirection(root);
×
270
        }
271
        else if (root.name() == KXMLQLCFunctionRunOrder)
×
272
        {
273
            loadXMLRunOrder(root);
×
274
        }
275
        else if (root.name() == KXMLQLCScriptCommand)
×
276
        {
277
            if (version == 1)
×
278
                m_data.append(convertLine(QUrl::fromPercentEncoding(root.readElementText().toUtf8()) + QString("\n")));
×
279
            else
280
                m_data.append(QUrl::fromPercentEncoding(root.readElementText().toUtf8()) + QString("\n"));
×
281
        }
282
        else
283
        {
284
            qWarning() << Q_FUNC_INFO << "Unknown script tag:" << root.name();
×
285
            root.skipCurrentElement();
×
286
        }
287
    }
288

289
    return true;
×
290
}
291

292
bool Script::saveXML(QXmlStreamWriter *doc)
×
293
{
294
    Q_ASSERT(doc != NULL);
×
295

296
    /* Function tag */
297
    doc->writeStartElement(KXMLQLCFunction);
×
298

299
    /* Common attributes */
300
    saveXMLCommon(doc);
×
301

302
    /* Version */
303
    doc->writeAttribute(KXMLQLCScriptVersion, QString::number(2));
×
304

305
    /* Speed */
306
    saveXMLSpeed(doc);
×
307

308
    /* Direction */
309
    saveXMLDirection(doc);
×
310

311
    /* Run order */
312
    saveXMLRunOrder(doc);
×
313

314
    /* Contents */
315
    foreach (QString cmd, dataLines())
×
316
    {
317
        doc->writeTextElement(KXMLQLCScriptCommand, QUrl::toPercentEncoding(cmd));
×
318
    }
319

320
    /* End the <Function> tag */
321
    doc->writeEndElement();
×
322

323
    return true;
×
324
}
325

326
/****************************************************************************
327
 * Running
328
 ****************************************************************************/
329

330
void Script::preRun(MasterTimer* timer)
1✔
331
{
332
    m_runner = new ScriptRunner(doc(), m_data);
1✔
333
    connect(m_runner, SIGNAL(finished()), this, SLOT(slotRunnerFinished()));
1✔
334
    m_runner->execute();
1✔
335

336
    Function::preRun(timer);
1✔
337
}
1✔
338

339
void Script::write(MasterTimer *timer, QList<Universe *> universes)
1✔
340
{
341
    if (isPaused())
1✔
342
        return;
×
343

344
    incrementElapsed();
1✔
345

346
    if (m_runner)
1✔
347
    {
348
        if (m_runner->write(timer, universes) == false)
1✔
349
            stop(FunctionParent::master());
×
350
    }
351
}
352

353
void Script::postRun(MasterTimer* timer, QList<Universe *> universes)
1✔
354
{
355
    if (m_runner)
1✔
356
    {
357
        m_runner->stop();
1✔
358
        m_runner->exit();
1✔
359
        m_runner->wait();
1✔
360
    }
361

362
    Function::postRun(timer, universes);
1✔
363
}
1✔
364

365
void Script::slotRunnerFinished()
×
366
{
367
    delete m_runner;
×
368
    m_runner = NULL;
×
369
}
×
370

371
quint32 Script::getValueFromString(QString str, bool *ok)
×
372
{
373
    if (str.startsWith("random") == false)
×
374
    {
375
        *ok = true;
×
376
        return Function::stringToSpeed(str);
×
377
    }
378

379
    QString strippedStr = str.remove("random(");
×
380
    strippedStr.remove(")");
×
381
    if (strippedStr.contains(",") == false)
×
382
        return -1;
×
383

384
    QStringList valList = strippedStr.split(",");
×
385
    int min = Function::stringToSpeed(valList.at(0));
×
386
    int max = Function::stringToSpeed(valList.at(1));
×
387

388
    *ok = true;
×
389
    return QRandomGenerator::global()->generate() % ((max + 1) - min) + min;
×
390
}
391

392
QString Script::convertLine(const QString& str, bool *ok)
1✔
393
{
394
    QStringList values;
1✔
395
    QString comment;
1✔
396
    QString keyword;
1✔
397
    QString command;
1✔
398
    QString value;
1✔
399

400
    if (ok != NULL)
1✔
401
        *ok = true; // in case, this is set to false afterwards
×
402

403
    if (str.simplified().startsWith("//") == true || str.simplified().isEmpty() == true)
1✔
404
        return str;
×
405

406
    qDebug() << "---> " << str;
1✔
407

408
    // Save everything after the first comment sign
409
    QString line = str;
2✔
410
    int left = 0;
1✔
411

412
    while (left != -1)
2✔
413
    {
414
        left = line.indexOf("//", left);
1✔
415
        if (left != -1)
1✔
416
        {
417
            // if we stumbled into a URL like http:// or ftp://
418
            // then it's not a comment !
419
            if (line.at(left - 1) != ':')
×
420
            {
421
                comment = line.mid(left);
×
422
                line.truncate(left);
×
423
            }
424
            left += 2;
×
425
        }
426
    }
427

428
    left = 0;
1✔
429
    while (left < line.length())
2✔
430
    {
431
        // Find the next colon to get the keyword
432
        int right = line.indexOf(":", left);
1✔
433
        if (right == -1)
1✔
434
        {
435
            qDebug() << "Syntax error (colon missing after keyword):" << line.mid(left);
×
436
            if (ok != NULL)
×
437
                *ok = false;
×
438
            break;
×
439
        }
440
        else
441
        {
442
            // Keyword found
443
            keyword = line.mid(left, right - left);
1✔
444
            left = right + 1;
1✔
445
            if (command.isEmpty())
1✔
446
                command = keyword;
1✔
447
        }
448

449
        // Try to see if there is a value between quotes
450
        int quoteleft = -1;
1✔
451
        if (line.mid(left, 1) == "\"")
1✔
452
            quoteleft = left + 1;
×
453
        if (quoteleft != -1)
1✔
454
        {
455
            int quoteright = line.indexOf("\"", quoteleft + 1);
×
456
            if (quoteright != -1)
×
457
            {
458
                // Include the "" in the string
459
                value = line.mid(quoteleft - 1, quoteright - quoteleft + 2);
×
460
                value.replace("\"", "'");
×
461
                left = quoteright + 2;
×
462
            }
463
            else
464
            {
465
                qDebug() << "Syntax error (unbalanced quotes):" << line.mid(quoteleft);
×
466
                if (ok != NULL)
×
467
                    *ok = false;
×
468
                break;
×
469
            }
470
        }
471
        else
472
        {
473
            // No quotes. Find the next whitespace.
474
            right = line.indexOf(QRegularExpression("\\s"), left);
1✔
475
            if (right == -1)
1✔
476
            {
477
                qDebug() << "Syntax error (whitespace before value missing):" << line.mid(left);
×
478
                if (ok != NULL)
×
479
                    *ok = false;
×
480
                break;
×
481
            }
482
            else
483
            {
484
                // Value found
485
                value = line.mid(left, right - left);
1✔
486
                left = right + 1;
1✔
487
            }
488
        }
489

490
        if (values.count() > 0 && knownKeywords.contains(keyword.trimmed()) == false)
1✔
491
        {
492
            qDebug() << "Syntax error. Unknown keyword detected:" << keyword.trimmed();
×
493
            if (ok != NULL)
×
494
                *ok = false;
×
495
            break;
×
496
        }
497
        else
498
        {
499
            if (value.startsWith("random"))
1✔
500
            {
501
                QStringList rToks = value.split(",");
×
502
                QString min = rToks[0].mid(rToks[0].indexOf("(") + 1);
×
503
                QString max = rToks[1].mid(0, rToks[1].indexOf(")"));
×
504
                if (min.contains("s") || min.contains("m") || min.contains("h"))
×
505
                {
506
                    min.prepend("\"");
×
507
                    min.append("\"");
×
508
                }
509
                if (max.contains("s") || max.contains("m") || max.contains("h"))
×
510
                {
511
                    max.prepend("\"");
×
512
                    max.append("\"");
×
513
                }
514

515
                value = QString("Engine.random(%1,%2)").arg(min).arg(max);
×
516
            }
517
            else if (command == waitLegacy)
1✔
518
            {
519
                if (value.contains("s") || value.contains("m") || value.contains("h"))
×
520
                {
521
                    value.prepend("\"");
×
522
                    value.append("\"");
×
523
                }
524
            }
525

526
            values << value.trimmed();
1✔
527
        }
528
    }
529

530
    if (command == systemLegacy)
1✔
531
    {
532
        QString cmd = values.join(" ");
×
533
        cmd.prepend("\"");
×
534
        cmd.append("\"");
×
535
        values.clear();
×
536
        values << cmd;
×
537
    }
538

539
    qDebug() << "COMMAND:" << command << "Values:" << values;
1✔
540

541
    line = convertLegacyMethod(command);
1✔
542
    line.append("(");
1✔
543
    for (int i = 0; i < values.count(); i++)
2✔
544
    {
545
        line.append(values.at(i));
1✔
546
        if (i < values.count() - 1)
1✔
547
            line.append(",");
×
548
    }
549

550
    if (comment.isEmpty())
1✔
551
        line.append(");\n");
1✔
552
    else
553
        line.append("); " + comment);
×
554

555
    return line;
1✔
556
}
557

558
QString Script::convertLegacyMethod(QString method)
1✔
559
{
560
    if (method == stopOnExitLegacy) return stopOnExitCmd;
1✔
561
    else if (method == startFunctionLegacy) return startFunctionCmd;
1✔
562
    else if (method == stopFunctionLegacy) return stopFunctionCmd;
×
563
    else if (method == blackoutLegacy) return blackoutCmd;
×
564
    else if (method == waitLegacy) return waitCmd;
×
NEW
565
    else if (method == waitFunctionStartLegacy) return waitFunctionStartCmd;
×
NEW
566
    else if (method == waitFunctionStopLegacy) return waitFunctionStopCmd;
×
567
    else if (method == setFixtureLegacy) return setFixtureCmd;
×
568
    else if (method == systemLegacy) return systemCmd;
×
569
    else return "";
×
570
}
571

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