Subversion Repositories tpanel

Rev

Rev 449 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
486 andreas 2
 * Copyright (C) 2020 to 2025 by Andreas Theofilu <andreas@theosys.at>
446 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include "tamxcommands.h"
20
#include "tpagemanager.h"
21
#include "terror.h"
22
#include "tresources.h"
23
#include "tconfig.h"
24
#include "texpat++.h"
25
#if TESTMODE == 1
26
#include "testmode.h"
27
#endif
28
 
29
#include <string>
30
#include <vector>
31
 
32
#if __cplusplus < 201402L
33
#   error "This module requires at least C++14 standard!"
34
#else
35
#   if __cplusplus < 201703L
36
#       include <experimental/filesystem>
37
namespace fs = std::experimental::filesystem;
38
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
39
#   else
40
#       include <filesystem>
41
#       ifdef __ANDROID__
42
namespace fs = std::__fs::filesystem;
43
#       else
44
namespace fs = std::filesystem;
45
#       endif
46
#   endif
47
#endif
48
 
49
using std::string;
50
using std::vector;
51
using namespace Expat;
52
 
53
typedef struct CMD_DEFINATIONS
54
{
55
    string cmd;
56
    bool hasChannels{false};
57
    bool hasPars{false};
58
    char separator{0};
59
}CMD_DEFINATIONS;
60
 
61
CMD_DEFINATIONS cmdDefinations[] = {
62
    { "@WLD", false, true, ',' },
63
    { "@AFP", false, true, ',' },
64
    { "^AFP", false, true, ',' },
65
    { "@GCE", false, true, ',' },
66
    { "^GCE", false, true, ',' },
67
    { "@APG", false, true, ';' },
68
    { "@CPG", false, true, ',' },
69
    { "@DPG", false, true, ';' },
70
    { "@PDR", false, true, ';' },
71
    { "@PHE", false, true, ';' },
72
    { "@PHP", false, true, ';' },
73
    { "@PHT", false, true, ';' },
74
    { "@PPA", false, true, ',' },
75
    { "^PPA", false, true, ',' },
76
    { "@PPF", false, true, ';' },
77
    { "^PPF", false, true, ';' },
78
    { "@PPG", false, true, ';' },
79
    { "^PPG", false, true, ';' },
80
    { "@PPK", false, true, ',' },
81
    { "^PPK", false, true, ',' },
82
    { "@PPM", false, true, ';' },
83
    { "^PPM", false, true, ';' },
84
    { "@PPN", false, true, ';' },
85
    { "^PPN", false, true, ';' },
86
    { "@PPT", false, true, ';' },
87
    { "^PPT", false, true, ';' },
88
    { "@PPX", false, false, '\0' },
89
    { "^PPX", false, false, '\0' },
90
    { "@PSE", false, true, ';' },
91
    { "@PSP", false, true, ';' },
92
    { "@PST", false, true, ';' },
93
    { "PAGE", false, true, ',' },
94
    { "^PGE", false, true, ',' },
95
    { "PPOF", false, true, ';' },
96
    { "PPOG", false, true, ';' },
97
    { "PPON", false, true, ';' },
98
    { "^ANI", true, true, ',' },
99
    { "^APF", true, true, ',' },
100
    { "^BAT", true, true, ',' },
101
    { "^BAU", true, true, ',' },
102
    { "^BCB", true, true, ',' },
103
    { "?BCB", true, true, ',' },
104
    { "^BCF", true, true, ',' },
105
    { "?BCF", true, true, ',' },
106
    { "^BCT", true, true, ',' },
107
    { "?BCT", true, true, ',' },
108
    { "^BDO", true, true, ',' },
109
    { "^BFB", true, true, ',' },
110
    { "^BIM", true, true, ',' },
111
    { "^BLN", true, true, ',' },
112
    { "^BMC", true, true, ',' },
113
    { "^BMF", true, true, ',' },
114
    { "^BMI", true, true, ',' },
115
    { "^BML", true, true, ',' },
116
    { "^BMP", true, true, ',' },
117
    { "?BMP", true, true, ',' },
118
    { "^BNC", true, true, ',' },
119
    { "^BNN", true, true, ',' },
120
    { "^BNT", true, true, ',' },
121
    { "^BOP", true, true, ',' },
122
    { "?BOP", true, true, ',' },
123
    { "^BOR", true, true, ',' },
124
    { "^BOS", true, true, ',' },
125
    { "^BPP", true, true, ',' },
126
    { "^BRD", true, true, ',' },
127
    { "?BRD", true, true, ',' },
128
    { "^BSF", true, true, ',' },
129
    { "^BSP", true, true, ',' },
130
    { "^BSM", true, false, ',' },
131
    { "^BSO", true, true, ',' },
132
    { "^BVL", true, true, ',' },
133
    { "^BVN", false, true, ',' },
134
    { "^BVP", true, true, ',' },
135
    { "^BVT", true, true, ',' },
136
    { "^BWW", true, true, ',' },
137
    { "?BWW", true, true, ',' },
138
    { "^CPF", true, false, ',' },
139
    { "^DLD", false, false, ',' },
140
    { "^DPF", true, true, ',' },
141
    { "^ENA", true, true, ',' },
142
    { "^FON", true, true, ',' },
143
    { "?FON", true, true, ',' },
144
    { "^GDI", true, true, ',' },
145
    { "^GIV", true, true, ',' },
146
    { "^GLH", true, true, ',' },
147
    { "^GLL", true, true, ',' },
148
    { "^GRD", true, true, ',' },
149
    { "^GRU", true, true, ',' },
150
    { "^GSC", true, true, ',' },
151
    { "^GSN", true, true, ',' },
152
    { "^ICO", true, true, ',' },
153
    { "?ICO", true, true, ',' },
154
    { "^IRM", false, true, ',' },
155
    { "^JSB", true, true, ',' },
156
    { "?JSB", true, true, ',' },
157
    { "^JSI", true, true, ',' },
158
    { "?JSI", true, true, ',' },
159
    { "^JST", true, true, ',' },
160
    { "?JST", true, true, ',' },
161
    { "^MSP", true, true, ',' },
162
    { "^MBT", false, false, ',' },
163
    { "^MDC", false, false, ',' },
164
    { "^SHO", true, true, ',' },
165
    { "^TEC", true, true, ',' },
166
    { "?TEC", true, true, ',' },
167
    { "^TEF", true, true, ',' },
168
    { "?TEF", true, true, ',' },
169
    { "^TOP", false, true, ',' },
170
    { "^TXT", true, true, ',' },
171
    { "?TXT", true, true, ',' },
172
    { "^UNI", true, true, ',' },
173
    { "^UTF", true, true, ',' },
174
    { "^LPB", true, true, ',' },
175
    { "^LPC", false, false, ',' },
176
    { "^LPR", false, true, ',' },
177
    { "^LPS", false, true, ',' },
178
    { "ABEEP", false, false, ',' },
179
    { "ADBEEP", false, false, ',' },
180
    { "@AKB", false, true, ';' },
181
    { "AKEYB", false, true, ',' },
182
    { "AKEYP", false, true, ',' },
183
    { "AKEYR", false, true, ',' },
184
    { "@AKP", false, true, ';' },
185
    { "@AKR", false, false, ',' },
186
    { "BEEP", false, false, ',' },
187
    { "^ABP", false, false, ',' },
188
    { "BRIT", false, true, ',' },
189
    { "@BRT", false, true, ',' },
190
    { "DBEEP", false, false, ',' },
191
    { "^ADP", false, false, ',' },
192
    { "@EKP", false, true, ';' },
193
    { "PKEYP", false, true, ',' },
194
    { "@PKB", false, true, ';' },
195
    { "^PKB", false, true, ';' },
196
    { "@PKP", false, true, ';' },
197
    { "^PKP", false, true, ';' },
198
    { "^RPP", false, false, ',' },
199
    { "SETUP", false, false, ',' },
200
    { "^STP", false, false, ',' },
201
    { "SHUTDOWN", false, false, ',' },
202
    { "SLEEP", false, false, ',' },
203
    { "@SOU", false, true, ',' },
204
    { "^SOU", false, true, ',' },
205
    { "@TKP", false, true, ';' },
206
    { "^TKP", false, true, ';' },
207
    { "TPAGEON", false, false, ',' },
208
    { "TPAGEOFF", false, false, ',' },
209
    { "@VKB", false, false, ',' },
210
    { "^VKB", false, false, ',' },
211
    { "WAKE", false, false, ',' },
212
    { "^CAL", false, false, ',' },
213
    { "^KPS", false, true, ',' },
214
    { "^VKS", false, true, ',' },
215
    { "^WCN?", false, true, ',' },
216
    { "@PWD", false, true, ',' },
217
    { "^PWD", false, true, ',' },
218
    { "^BBR", true, true, ',' },
219
    { "^RAF", false, true, ',' },
220
    { "^RFR", false, true, ',' },
221
    { "^RMF", false, true, ',' },
222
    { "^RSR", false, true, ',' },
223
    { "^MODEL?", false, false, ',' },
224
    { "^MOD", false, false, ',' },
225
    { "^VER?", false, false, ',' },
226
    { "^ICS", false, true, ',' },
227
    { "^ICE", false, false, ',' },
228
    { "^ICM", false, true, ',' },
229
    { "^PHN", false, true, ',' },
230
    { "?PHN", false, true, ',' },
231
    { "^LVC", false, true, ',' },
232
    { "^LVD", true, true, ',' },
233
    { "^LVE", true, true, ',' },
234
    { "^LVF", true, true, ',' },
235
    { "^LVL", true, true, ',' },
236
    { "^LVM", true, true, '|' },
237
    { "^LVN", true, true, ',' },
238
    { "^LVR", true, true, ',' },
239
    { "^LVS", true, true, ',' },
240
    { "GET ", false, false, ',' },
241
    { "SET ", false, false, ',' },
242
    { "LEVON", false, false, ',' },
243
    { "LEVOF", false, false, ',' },
244
    { "RXON", false, false, ',' },
245
    { "ON", false, true, ',' },
246
    { "OFF", false, true, ',' },
247
    { "LEVEL", false, true, ',' },
248
    { "BLINK", false, true, ',' },
249
    { "#FTR", false, true, ':' },
250
    { "TPCCMD", false, true, ',' },
251
    { "TPCACC", false, true, ',' },
252
    { "TPCSIP", false, true, ',' },
253
    { "^EPR", true, true, ',' },
254
    { "^SCE", true, true, ',' },
255
    { "^SDR", true, true, ',' },
256
    { "^SHA", true, false, ',' },
257
    { "^SHD", true, true, ',' },
258
    { "^SSH", true, true, ',' },
259
    { "^STG", true, true, ',' },
260
    { "^MUT", false, true, ',' },
261
    { "", false, false, '\0' }
262
};
263
 
264
CMD_DEFINATIONS& findCmdDefines(const string& cmd)
265
{
266
    DECL_TRACER("findCmdDefines(const string& cmd)");
267
 
268
    int i = 0;
269
    string uCmd = cmd;
270
    uCmd = toUpper(uCmd);
271
 
272
    while (cmdDefinations[i].cmd.length() > 0)
273
    {
274
        if (cmdDefinations[i].cmd.compare(uCmd) == 0)
275
            return cmdDefinations[i];
276
 
277
        i++;
278
    }
279
 
280
    return cmdDefinations[i];
281
}
282
 
283
TAmxCommands::TAmxCommands()
284
{
285
    DECL_TRACER("TAmxCommands::TAmxCommands()");
286
}
287
 
288
TAmxCommands::~TAmxCommands()
289
{
290
    DECL_TRACER("TAmxCommands::~TAmxCommands()");
291
 
292
    if (mMap && mSystemMap && mMap != mSystemMap)
293
    {
294
        delete mMap;
295
        delete mSystemMap;
296
    }
297
    else if (mMap)
298
        delete mMap;
299
    else if (mSystemMap)
300
        delete mSystemMap;
301
}
302
 
486 andreas 303
bool TAmxCommands::readMap(bool tp5)
446 andreas 304
{
486 andreas 305
    DECL_TRACER("TAmxCommands::readMap(bool tp5)");
446 andreas 306
 
307
    bool err = false;
308
    string projectPath = TConfig::getProjectPath();
309
 
310
    if (fs::exists(projectPath + "/prj.xma"))
311
    {
486 andreas 312
        mMap = new TMap(projectPath, tp5);
446 andreas 313
        err = mMap->haveError();
314
    }
315
 
316
    projectPath += "/__system";
317
 
318
    if (fs::exists(projectPath + "/prj.xma"))
319
    {
486 andreas 320
        mSystemMap = new TMap(projectPath, tp5);
446 andreas 321
 
322
        if (!err)
323
            err = mSystemMap->haveError();
324
    }
325
 
326
    if (!mMap)
327
        mMap = mSystemMap;
328
 
329
    return !err;
330
}
331
 
332
vector<string> TAmxCommands::getFields(string& msg, char sep)
333
{
334
    DECL_TRACER("TAmxCommands::getFields(string& msg, char sep)");
335
 
336
    vector<string> flds;
337
    bool bStr = false;
338
    string part;
339
 
340
    for (size_t i = 0; i < msg.length(); i++)
341
    {
342
        if (msg.at(i) == sep && !bStr)
343
        {
344
            flds.push_back(part);
345
            part.clear();
346
            continue;
347
        }
348
        else if (msg.at(i) == '\'' && !bStr)
349
            bStr = true;
350
        else if (msg.at(i) == '\'' && bStr)
351
            bStr = false;
352
        else
353
            part.append(msg.substr(i, 1));
354
    }
355
 
356
    if (!part.empty())
357
        flds.push_back(part);
358
 
359
    if (flds.size() > 0 && TStreamError::checkFilter(HLOG_DEBUG))
360
    {
361
        MSG_DEBUG("Found fields:");
362
        vector<string>::iterator iter;
363
        int i = 1;
364
 
365
        for (iter = flds.begin(); iter != flds.end(); ++iter)
366
        {
367
            MSG_DEBUG("    " << i << ": " << *iter);
368
            i++;
369
        }
370
    }
371
 
372
    return flds;
373
}
374
 
375
vector<TMap::MAP_T> TAmxCommands::findButtons(int port, vector<int>& channels, TMap::MAP_TYPE mt)
376
{
377
    DECL_TRACER("TAmxCommands::findButtons(int port, vector<int>& channels, TMap::MAP_TYPE mt)");
378
 
379
    vector<TMap::MAP_T> map;
380
 
449 andreas 381
    map = mMap->findButtons(port, channels, mt);
446 andreas 382
    return map;
383
}
384
 
385
string TAmxCommands::findImage(int bt, int page, int instance)
386
{
387
    DECL_TRACER("TAmxCommands::findImage(int bt, int page, int instance)");
388
 
389
    if (page < SYSTEM_PAGE_START)
390
        return mMap->findImage(bt, page, instance);
391
 
392
    return mSystemMap->findImage(bt, page, instance);
393
}
394
 
395
string TAmxCommands::findImage(const string& name)
396
{
397
    DECL_TRACER("TAmxCommands::findImage(const string& name)");
398
 
399
    string str = mMap->findImage(name);
400
 
486 andreas 401
    if (str.empty() && mSystemMap)
446 andreas 402
        return mSystemMap->findImage(name);
403
 
404
    return str;
405
}
406
 
407
vector<TMap::MAP_T> TAmxCommands::findButtonByName(const string& name)
408
{
409
    DECL_TRACER("TAmxCommands::findButtonByName(const string& name)");
410
 
411
    vector<TMap::MAP_T> map = mMap->findButtonByName(name);
412
 
486 andreas 413
    if (map.empty() && mSystemMap)
446 andreas 414
        return mSystemMap->findButtonByName(name);
415
 
416
    return map;
417
}
418
 
419
vector<TMap::MAP_T> TAmxCommands::findBargraphs(int port, vector<int>& channels)
420
{
421
    DECL_TRACER("TAmxCommands::findBargraphs(int port, vector<int>& channels)");
422
 
423
    vector<TMap::MAP_T> map = mMap->findBargraphs(port, channels);
424
 
486 andreas 425
    if (map.empty() && mSystemMap)
446 andreas 426
        return mSystemMap->findBargraphs(port, channels);
427
 
428
    return map;
429
}
430
 
431
vector<string> TAmxCommands::findSounds()
432
{
433
    DECL_TRACER("TAmxCommands::findSounds()");
434
 
435
    return mMap->findSounds();      // This is enough because there are no sounds in the system settings
436
}
437
 
438
bool TAmxCommands::soundExist(const string& sname)
439
{
440
    DECL_TRACER("TAmxCommands::soundExist(const string sname)");
441
 
442
    return mMap->soundExist(sname);
443
}
444
 
445
bool TAmxCommands::parseCommand(int device, int port, const string& cmd)
446
{
447
    DECL_TRACER("TAmxCommands::parseCommand(int device, int port, const string& cmd)");
448
 
449
    vector<CMD_TABLE>::iterator iter;
450
    size_t pos = cmd.find_first_of("-");
451
    int system = TConfig::getSystem();
452
    string scmd = getCommand(cmd);
453
 
454
    MSG_TRACE("Parsing for device <" << device << ":" << port << ":" << system << "> the command: " << scmd);
455
 
456
    if (pos != string::npos)    // Command with parameters
457
    {
458
        string rest = cmd.substr(pos + 1);
459
 
460
        for (iter = mCmdTable.begin(); iter != mCmdTable.end(); ++iter)
461
        {
462
            iter->channels.clear();
463
            iter->pars.clear();
464
 
465
            if (iter->cmd.compare(scmd) == 0 && iter->command)
466
            {
467
                CMD_DEFINATIONS cdef = findCmdDefines(scmd);
468
 
469
                if (cdef.cmd.empty())
470
                {
471
                    MSG_WARNING("Command \"" << scmd << "\" not found in command table! Ignoring it.");
472
                    continue;
473
                }
474
 
475
                if (cdef.hasChannels || cdef.hasPars)
476
                {
477
                    vector<string> parts = getFields(rest, cdef.separator);
478
 
479
                    if (cdef.hasChannels && !parts.empty())
480
                        extractChannels(parts[0], &iter->channels);
481
                    else if (parts.empty())
482
                    {
483
                        MSG_WARNING("Malformed command " << scmd << ". Ignoring it!");
484
                        continue;
485
                    }
486
 
487
                    if (cdef.hasPars)
488
                    {
489
                        MSG_DEBUG("Command may have parameters. Found " << parts.size() << " parameters.");
490
 
491
                        if (parts.size() > 0)
492
                        {
493
                            vector<string>::iterator piter;
494
                            int cnt = 0;
495
 
496
                            for (piter = parts.begin(); piter != parts.end(); ++piter)
497
                            {
498
                                if (cdef.hasChannels && !cnt)
499
                                {
500
                                    cnt++;
501
                                    continue;
502
                                }
503
 
504
                                iter->pars.push_back(*piter);
505
                                cnt++;
506
                            }
507
                        }
508
                        else
509
                            iter->pars.push_back(rest);
510
                    }
511
                }
512
 
513
                iter->command(port, iter->channels, iter->pars);
514
                return true;
515
            }
516
        }
517
    }
518
    else        // Command without parameter
519
    {
520
        for (iter = mCmdTable.begin(); iter != mCmdTable.end(); ++iter)
521
        {
522
            if (iter->cmd.compare(scmd) == 0 && iter->command)
523
            {
524
                iter->command(port, iter->channels, iter->pars);
525
                return true;
526
            }
527
        }
528
    }
529
 
530
    MSG_WARNING("Command \"" << cmd << "\" currently not supported!");
531
#if TESTMODE == 1
532
    __done = true;
533
#endif
534
    return false;
535
}
536
 
537
bool TAmxCommands::extractChannels(const string& schan, vector<int>* ch)
538
{
539
    DECL_TRACER("TAmxCommands::extractChannels(const string& schan, vector<int>* ch)");
540
 
541
    if (!ch || schan.empty())
542
        return false;
543
 
544
    if (schan.find("&") == string::npos && schan.find(".") == string::npos)
545
    {
546
        int c = atoi(schan.c_str());
547
        ch->push_back(c);
548
        return true;
549
    }
550
 
551
    if (schan.find("&") != string::npos)
552
    {
553
        vector<string> parts = StrSplit(schan, "&");
554
        vector<string>::iterator iter;
555
 
556
        if (parts.size() > 0)
557
        {
558
            for (iter = parts.begin(); iter != parts.end(); ++iter)
559
            {
560
                if (iter->find(".") != string::npos)
561
                {
562
                    vector<string> p2 = StrSplit(*iter, ".");
563
 
564
                    if (p2.size() >= 2)
565
                    {
566
                        for (int i = atoi(p2[0].c_str()); i <= atoi(p2[1].c_str()); i++)
567
                            ch->push_back(i);
568
                    }
569
                    else if (p2.size() > 0)
570
                        ch->push_back(atoi(p2[0].c_str()));
571
                }
572
                else
573
                    ch->push_back(atoi(iter->c_str()));
574
            }
575
        }
576
    }
577
    else
578
    {
579
        vector<string> parts = StrSplit(schan, ".");
580
 
581
        if (parts.size() >= 2)
582
        {
583
            for (int i = atoi(parts[0].c_str()); i <= atoi(parts[1].c_str()); i++)
584
                ch->push_back(i);
585
        }
586
        else if (parts.size() > 0)
587
            ch->push_back(atoi(parts[0].c_str()));
588
    }
589
 
590
    return true;
591
}
592
 
593
void TAmxCommands::registerCommand(std::function<void (int port, vector<int>& channels, vector<string>& pars)> command, const string& name)
594
{
595
    DECL_TRACER("TAmxCommands::registerCommand(std::function<void (vector<int>& channels, vector<string>& pars)> command, const string& name)");
596
 
597
    vector<CMD_TABLE>::iterator iter;
598
 
599
    for (iter = mCmdTable.begin(); iter != mCmdTable.end(); ++iter)
600
    {
601
        if (iter->cmd.compare(name) == 0)
602
        {
603
            iter->command = command;
604
            iter->channels.clear();
605
            iter->pars.clear();
606
            return;
607
        }
608
    }
609
 
610
    CMD_TABLE ctbl;
611
    ctbl.cmd = name;
612
    ctbl.command = command;
613
    mCmdTable.push_back(ctbl);
614
}