Subversion Repositories tpanel

Rev

Rev 107 | Blame | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2020 to 2022 by Andreas Theofilu <andreas@theosys.at>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "tamxcommands.h"
#include "terror.h"
#include "tresources.h"
#include "tconfig.h"
#include "texpat++.h"

#include <string>
#include <vector>

using std::string;
using std::vector;
using namespace Expat;

typedef struct CMD_DEFINATIONS
{
    string cmd;
    bool hasChannels{false};
    bool hasPars{false};
    char separator{0};
}CMD_DEFINATIONS;

CMD_DEFINATIONS cmdDefinations[] = {
    { "@WLD", false, true, ',' },
    { "@AFP", false, true, ',' },
    { "@GCE", false, true, ',' },
    { "@APG", false, true, ';' },
    { "@CPG", false, true, ',' },
    { "@DPG", false, true, ';' },
    { "@PDR", false, true, ';' },
    { "@PHE", false, true, ';' },
    { "@PHP", false, true, ';' },
    { "@PHT", false, true, ';' },
    { "@PPA", false, true, ',' },
    { "^PPA", false, true, ',' },
    { "@PPF", false, true, ';' },
    { "^PPF", false, true, ';' },
    { "@PPG", false, true, ';' },
    { "^PPG", false, true, ';' },
    { "@PPK", false, true, ',' },
    { "^PPK", false, true, ',' },
    { "@PPM", false, true, ';' },
    { "^PPM", false, true, ';' },
    { "@PPN", false, true, ';' },
    { "^PPN", false, true, ';' },
    { "@PPT", false, true, ';' },
    { "^PPT", false, true, ';' },
    { "@PPX", false, false, '\0' },
    { "^PPX", false, false, '\0' },
    { "@PSE", false, true, ';' },
    { "@PSP", false, true, ';' },
    { "@PST", false, true, ';' },
    { "PAGE", false, true, ',' },
    { "^PGE", false, true, ',' },
    { "PPOF", false, true, ';' },
    { "PPOG", false, true, ';' },
    { "PPON", false, true, ';' },
    { "^ANI", true, true, ',' },
    { "^APF", true, true, ',' },
    { "^BAT", true, true, ',' },
    { "^BAU", true, true, ',' },
    { "^BCB", true, true, ',' },
    { "?BCB", true, true, ',' },
    { "^BCF", true, true, ',' },
    { "?BCF", true, true, ',' },
    { "^BCT", true, true, ',' },
    { "?BCT", true, true, ',' },
    { "^BDO", true, true, ',' },
    { "^BFB", true, true, ',' },
    { "^BIM", true, true, ',' },
    { "^BLN", true, true, ',' },
    { "^BMC", true, true, ',' },
    { "^BMF", true, true, ',' },
    { "^BMI", true, true, ',' },
    { "^BML", true, true, ',' },
    { "^BMP", true, true, ',' },
    { "?BMP", true, true, ',' },
    { "^BNC", true, true, ',' },
    { "^BNN", true, true, ',' },
    { "^BNT", true, true, ',' },
    { "^BOP", true, true, ',' },
    { "?BOP", true, true, ',' },
    { "^BOR", true, true, ',' },
    { "^BOS", true, true, ',' },
    { "^BPP", true, true, ',' },
    { "^BRD", true, true, ',' },
    { "?BRD", true, true, ',' },
    { "^BSF", true, true, ',' },
    { "^BSP", true, true, ',' },
    { "^BSM", true, false, ',' },
    { "^BSO", true, true, ',' },
    { "^BVL", true, true, ',' },
    { "^BVN", false, true, ',' },
    { "^BVP", true, true, ',' },
    { "^BVT", true, true, ',' },
    { "^BWW", true, true, ',' },
    { "?BWW", true, true, ',' },
    { "^CPF", true, false, ',' },
    { "^DLD", false, false, ',' },
    { "^DPF", true, true, ',' },
    { "^ENA", true, true, ',' },
    { "^FON", true, true, ',' },
    { "?FON", true, true, ',' },
    { "^GDI", true, true, ',' },
    { "^GIV", true, true, ',' },
    { "^GLH", true, true, ',' },
    { "^GLL", true, true, ',' },
    { "^GRD", true, true, ',' },
    { "^GRU", true, true, ',' },
    { "^GSC", true, true, ',' },
    { "^GSN", true, true, ',' },
    { "^ICO", true, true, ',' },
    { "?ICO", true, true, ',' },
    { "^IRM", false, true, ',' },
    { "^JSB", true, true, ',' },
    { "?JSB", true, true, ',' },
    { "^JSI", true, true, ',' },
    { "?JSI", true, true, ',' },
    { "^JST", true, true, ',' },
    { "?JST", true, true, ',' },
    { "^MBT", false, false, ',' },
    { "^MDC", false, false, ',' },
    { "^SHO", true, true, ',' },
    { "^TEC", true, true, ',' },
    { "?TEC", true, true, ',' },
    { "^TEF", true, true, ',' },
    { "?TEF", true, true, ',' },
    { "^TOP", false, true, ',' },
    { "^TXT", true, true, ',' },
    { "?TXT", true, true, ',' },
    { "^UNI", true, true, ',' },
    { "^UTF", true, true, ',' },
    { "^LPC", false, true, ',' },
    { "^LPR", false, true, ',' },
    { "^LPS", false, true, ',' },
    { "ABEEP", false, false, ',' },
    { "ADBEEP", false, false, ',' },
    { "@AKB", false, true, ';' },
    { "AKEYB", false, true, ',' },
    { "AKEYP", false, true, ',' },
    { "AKEYR", false, true, ',' },
    { "@AKP", false, true, ';' },
    { "@AKR", false, false, ',' },
    { "BEEP", false, false, ',' },
    { "^ABP", false, false, ',' },
    { "BRIT", false, true, ',' },
    { "@BRT", false, true, ',' },
    { "DBEEP", false, false, ',' },
    { "^ADP", false, false, ',' },
    { "@EKP", false, true, ';' },
    { "PKEYP", false, true, ',' },
    { "@PKB", false, true, ';' },
    { "^PKB", false, true, ';' },
    { "@PKP", false, true, ';' },
    { "^PKP", false, true, ';' },
    { "SETUP", false, false, ',' },
    { "^STP", false, false, ',' },
    { "SHUTDOWN", false, false, ',' },
    { "SLEEP", false, false, ',' },
    { "@SOU", false, true, ',' },
    { "^SOU", false, true, ',' },
    { "@TKP", false, true, ';' },
    { "^TKP", false, true, ';' },
    { "TPAGEON", false, false, ',' },
    { "TPAGEOFF", false, false, ',' },
    { "@VKB", false, false, ',' },
    { "^VKB", false, false, ',' },
    { "WAKE", false, false, ',' },
    { "^CAL", false, false, ',' },
    { "^KPS", false, true, ',' },
    { "^VKS", false, true, ',' },
    { "@PWD", false, true, ',' },
    { "^PWD", false, true, ',' },
    { "^BBR", true, true, ',' },
    { "^RAF", false, true, ',' },
    { "^RFR", false, true, ',' },
    { "^RMF", false, true, ',' },
    { "^RSR", false, true, ',' },
    { "^MODEL?", false, false, ',' },
    { "^ICS", false, true, ',' },
    { "^ICE", false, false, ',' },
    { "^ICM", false, true, ',' },
    { "^PHN", false, true, ',' },
    { "?PHN", false, true, ',' },
    { "LEVON", false, false, ',' },
    { "RXON", false, false, ',' },
    { "ON", false, true, ',' },
    { "OFF", false, true, ',' },
    { "LEVEL", false, true, ',' },
    { "BLINK", false, true, ',' },
    { "#FTR", false, true, ':' },
    { "", false, false, '\0' }
};

CMD_DEFINATIONS& findCmdDefines(const string& cmd)
{
    DECL_TRACER("findCmdDefines(const string& cmd)");

    int i = 0;
    string uCmd = cmd;
    uCmd = toUpper(uCmd);

    while (cmdDefinations[i].cmd.length() > 0)
    {
        if (cmdDefinations[i].cmd.compare(uCmd) == 0)
            return cmdDefinations[i];

        i++;
    }

    return cmdDefinations[i];
}

TAmxCommands::TAmxCommands()
{
    DECL_TRACER("TAmxCommands::TAmxCommands()");
    readMap();
}

TAmxCommands::~TAmxCommands()
{
    DECL_TRACER("TAmxCommands::~TAmxCommands()");
}

bool TAmxCommands::readMap()
{
    DECL_TRACER("TAmxCommands::readMap()");

    string path = makeFileName(TConfig::getProjectPath(), "map.xma");
    vector<string> elements = { "cm", "am", "lm", "bm", "sm", "strm", "pm" };

    if (!isValidFile())
    {
        MSG_ERROR("File \"" << path << "\" is not a regular readable file!");
        return false;
    }

    TExpat xml(path);
    xml.setEncoding(ENC_CP1250);

    if (!xml.parse())
        return false;

    int depth = 0;
    size_t index = 0;
    vector<string>::iterator mapIter;
    size_t oldIndex = 0;

    if (elements.size() == 0)
        return false;

    for (mapIter = elements.begin(); mapIter != elements.end(); ++mapIter)
    {
        if ((index = xml.getElementIndex(*mapIter, &depth)) == TExpat::npos)
        {
            MSG_WARNING("Element \"" << *mapIter << "\" was not found!");
            continue;
        }

        MAP_T map;
        MAP_BM_T mapBm;
        MAP_PM_T mapPm;
        string name, content;

        while ((index = xml.getNextElementFromIndex(index, &name, nullptr, nullptr)) != TExpat::npos)
        {
            string el = name;

            if (el.compare("me") == 0)
            {
                while ((index = xml.getNextElementFromIndex(index, &name, &content, nullptr)) != TExpat::npos)
                {
                    string e = name;

                    if (mapIter->compare("cm") == 0 || mapIter->compare("am") == 0 ||
                        mapIter->compare("lm") == 0 || mapIter->compare("strm") == 0)
                    {
                        if (e.compare("p") == 0)
                            map.p = xml.convertElementToInt(content);
                        else if (e.compare("c") == 0)
                            map.c = xml.convertElementToInt(content);
                        else if (e.compare("ax") == 0)
                            map.ax = xml.convertElementToInt(content);
                        else if (e.compare("pg") == 0)
                            map.pg = xml.convertElementToInt(content);
                        else if (e.compare("bt") == 0)
                            map.bt = xml.convertElementToInt(content);
                        else if (e.compare("pn") == 0)
                            map.pn = content;
                        else if (e.compare("bn") == 0)
                            map.bn = content;
                    }
                    else if (mapIter->compare("bm") == 0)
                    {
                        while ((index = xml.getNextElementFromIndex(index, &name, &content, nullptr)) != TExpat::npos)
                        {
                            string im = name;

                            if (im.compare("i") == 0)
                                mapBm.i = content;
                            else if (im.compare("id") == 0)
                                mapBm.id = xml.convertElementToInt(content);
                            else if (im.compare("rt") == 0)
                                mapBm.rt = xml.convertElementToInt(content);
                            else if (im.compare("pg") == 0)
                                mapBm.pg = xml.convertElementToInt(content);
                            else if (im.compare("bt") == 0)
                                mapBm.bt = xml.convertElementToInt(content);
                            else if (im.compare("st") == 0)
                                mapBm.st = xml.convertElementToInt(content);
                            else if (im.compare("sl") == 0)
                                mapBm.sl = xml.convertElementToInt(content);
                            else if (im.compare("pn") == 0)
                                mapBm.pn = content;
                            else if (im.compare("bn") == 0)
                                mapBm.bn = content;

                            oldIndex = index;
                        }

                        mMap.map_bm.push_back(mapBm);

                        if (index == TExpat::npos)
                            index = oldIndex + 1;
                    }
                    else if (mapIter->compare("sm") == 0)
                    {
                        if (e.compare("i") == 0)
                            mMap.map_sm.push_back(content);
                    }
                    else if (mapIter->compare("pm") == 0)
                    {
                        if (e.compare("a") == 0)
                            mapPm.a = xml.convertElementToInt(content);
                        else if (e.compare("t") == 0)
                            mapPm.t = content;
                        else if (e.compare("pg") == 0)
                            mapPm.pg = xml.convertElementToInt(content);
                        else if (e.compare("bt") == 0)
                            mapPm.bt = xml.convertElementToInt(content);
                        else if (e.compare("pn") == 0)
                            mapPm.pn = content;
                        else if (e.compare("bn") == 0)
                            mapPm.bn = content;
                    }

                    oldIndex = index;
                }

                if (mapIter->compare("cm") == 0)
                    mMap.map_cm.push_back(map);
                else if (mapIter->compare("am") == 0)
                    mMap.map_am.push_back(map);
                else if (mapIter->compare("lm") == 0)
                    mMap.map_lm.push_back(map);
                else if (mapIter->compare("strm") == 0)
                    mMap.map_strm.push_back(map);
                else if (mapIter->compare("pm") == 0)
                    mMap.map_pm.push_back(mapPm);

                if (index == TExpat::npos)
                    index = oldIndex + 1;
            }

            oldIndex = index;
        }
    }

    return true;
}

vector<string> TAmxCommands::getFields(string& msg, char sep)
{
    DECL_TRACER("TAmxCommands::getFields(string& msg, char sep)");

    vector<string> flds;
    bool bStr = false;
    string part;

    for (size_t i = 0; i < msg.length(); i++)
    {
        if (msg.at(i) == sep && !bStr)
        {
            flds.push_back(part);
            part.clear();
            continue;
        }
        else if (msg.at(i) == '\'' && !bStr)
            bStr = true;
        else if (msg.at(i) == '\'' && bStr)
            bStr = false;
        else
            part.append(msg.substr(i, 1));
    }

    if (!part.empty())
        flds.push_back(part);

    if (flds.size() > 0 && TStreamError::checkFilter(HLOG_DEBUG))
    {
        MSG_DEBUG("Found fields:");
        vector<string>::iterator iter;
        int i = 1;

        for (iter = flds.begin(); iter != flds.end(); ++iter)
        {
            MSG_DEBUG("    " << i << ": " << *iter);
            i++;
        }
    }

    return flds;
}

vector<MAP_T> TAmxCommands::findButtons(int port, vector<int>& channels, MAP_TYPE mt)
{
    DECL_TRACER("TAmxCommands::findButtons(int port, vector<int>& channels, MAP_TYPE mt)");

    vector<MAP_T> map;
    vector<int>::iterator iter;

    if (channels.empty())
    {
        MSG_WARNING("Got empty channel list!");
        return map;
    }

    vector<MAP_T> localMap;

    switch (mt)
    {
        case TYPE_AM:   localMap = mMap.map_am; break;
        case TYPE_CM:   localMap = mMap.map_cm; break;
        case TYPE_LM:   localMap = mMap.map_lm; break;
    }

    if (localMap.empty())
    {
        MSG_WARNING("The internal list of elements is empty!")
        return map;
    }

    for (iter = channels.begin(); iter != channels.end(); ++iter)
    {
        vector<MAP_T>::iterator mapIter;

        for (mapIter = localMap.begin(); mapIter != localMap.end(); ++mapIter)
        {
            if (mapIter->p == port && mapIter->c == *iter)
                map.push_back(*mapIter);
        }
    }

    MSG_DEBUG("Found " << map.size() << " buttons.");
    return map;
}

vector<MAP_T> TAmxCommands::findButtonByName(const string& name)
{
    DECL_TRACER("TAmxCommands::findButtonByName(const string& name)");

    vector<MAP_T> map;

    if (mMap.map_cm.empty())
    {
        MSG_WARNING("The internal list of elements is empty!")
        return map;
    }

    vector<MAP_T>::iterator mapIter;

    for (mapIter = mMap.map_cm.begin(); mapIter != mMap.map_cm.end(); ++mapIter)
    {
        if (mapIter->bn == name)
            map.push_back(*mapIter);
    }

    MSG_DEBUG("Found " << map.size() << " buttons.");
    return map;
}

vector<MAP_T> TAmxCommands::findBargraphs(int port, vector<int>& channels)
{
    DECL_TRACER("TAmxCommands::findBargraphs(int port, vector<int>& channels)");

    vector<MAP_T> map;
    vector<int>::iterator iter;

    if (channels.size() == 0)
        return map;

    for (iter = channels.begin(); iter != channels.end(); ++iter)
    {
        vector<MAP_T>::iterator mapIter;

        if (mMap.map_lm.size() > 0)
        {
            for (mapIter = mMap.map_lm.begin(); mapIter != mMap.map_lm.end(); ++mapIter)
            {
                if (mapIter->p == port && mapIter->c == *iter)
                    map.push_back(*mapIter);
            }
        }
    }

    MSG_DEBUG("Found " << map.size() << " buttons.");
    return map;
}

vector<string> TAmxCommands::findSounds()
{
    DECL_TRACER("TAmxCommands::findSounds()");

    return mMap.map_sm;
}

bool TAmxCommands::soundExist(const string& sname)
{
    DECL_TRACER("TAmxCommands::soundExist(const string sname)");

    if (mMap.map_sm.size() == 0)
        return false;

    vector<string>::iterator iter;

    for (iter = mMap.map_sm.begin(); iter != mMap.map_sm.end(); ++iter)
    {
        if (iter->compare(sname) == 0)
            return true;
    }

    return false;
}

bool TAmxCommands::parseCommand(int device, int port, const string& cmd)
{
    DECL_TRACER("TAmxCommands::parseCommand(int device, int port, const string& cmd)");
/*
    if (device != TConfig::getChannel())    // This happens on a filetransfer only!
    {
        MSG_WARNING("Command is for device " << device << ", but this device is " << TConfig::getChannel());
    }
*/
    vector<CMD_TABLE>::iterator iter;
    size_t pos = cmd.find_first_of("-");

    MSG_TRACE("Parsing for device <" << device << ":" << port << ":" << TConfig::getSystem() << "> the command: " << getCommand(cmd));

    if (pos != string::npos)    // Command with parameters
    {
        string bef = cmd.substr(0, pos);
        string rest = cmd.substr(pos + 1);

        for (iter = mCmdTable.begin(); iter != mCmdTable.end(); ++iter)
        {
            iter->channels.clear();
            iter->pars.clear();

            if (iter->cmd.compare(bef) == 0 && iter->command)
            {
                CMD_DEFINATIONS cdef = findCmdDefines(bef);

                if (cdef.cmd.empty())
                {
                    MSG_WARNING("Command \"" << bef << "\" not found in command table! Ignoring it.");
                    continue;
                }

                if (cdef.hasChannels || cdef.hasPars)
                {
                    vector<string> parts = getFields(rest, cdef.separator);

                    if (cdef.hasChannels)
                        extractChannels(parts[0], &iter->channels);

                    if (cdef.hasPars)
                    {
                        MSG_DEBUG("Command may have parameters. Found " << parts.size() << " parameters.");

                        if (parts.size() > 0)
                        {
                            vector<string>::iterator piter;
                            int cnt = 0;

                            for (piter = parts.begin(); piter != parts.end(); ++piter)
                            {
                                if (cdef.hasChannels && !cnt)
                                {
                                    cnt++;
                                    continue;
                                }

                                iter->pars.push_back(*piter);
                                cnt++;
                            }
                        }
                        else
                            iter->pars.push_back(rest);
                    }
                }

                iter->command(port, iter->channels, iter->pars);
                return true;
            }
        }
    }
    else        // Command without parameter
    {
        for (iter = mCmdTable.begin(); iter != mCmdTable.end(); ++iter)
        {
            if (iter->cmd.compare(cmd) == 0 && iter->command)
            {
                iter->command(port, iter->channels, iter->pars);
                return true;
            }
        }
    }

    MSG_WARNING("Command \"" << cmd << "\" currently not supported!");
    return false;
}

bool TAmxCommands::extractChannels(const string& schan, vector<int>* ch)
{
    DECL_TRACER("TAmxCommands::extractChannels(const string& schan, vector<int>* ch)");

    if (!ch || schan.empty())
        return false;

    if (schan.find("&") == string::npos && schan.find(".") == string::npos)
    {
        int c = atoi(schan.c_str());
        ch->push_back(c);
        return true;
    }

    if (schan.find("&") != string::npos)
    {
        vector<string> parts = StrSplit(schan, "&");
        vector<string>::iterator iter;

        if (parts.size() > 0)
        {
            for (iter = parts.begin(); iter != parts.end(); ++iter)
            {
                if (iter->find(".") != string::npos)
                {
                    vector<string> p2 = StrSplit(*iter, ".");

                    if (p2.size() >= 2)
                    {
                        for (int i = atoi(p2[0].c_str()); i <= atoi(p2[1].c_str()); i++)
                            ch->push_back(i);
                    }
                    else if (p2.size() > 0)
                        ch->push_back(atoi(p2[0].c_str()));
                }
                else
                    ch->push_back(atoi(iter->c_str()));
            }
        }
    }
    else
    {
        vector<string> parts = StrSplit(schan, ".");

        if (parts.size() >= 2)
        {
            for (int i = atoi(parts[0].c_str()); i <= atoi(parts[1].c_str()); i++)
                ch->push_back(i);
        }
        else if (parts.size() > 0)
            ch->push_back(atoi(parts[0].c_str()));
    }

    return true;
}

void TAmxCommands::registerCommand(std::function<void (int port, vector<int>& channels, vector<string>& pars)> command, const string& name)
{
    DECL_TRACER("TAmxCommands::registerCommand(std::function<void (vector<int>& channels, vector<string>& pars)> command, const string& name)");

    vector<CMD_TABLE>::iterator iter;

    for (iter = mCmdTable.begin(); iter != mCmdTable.end(); ++iter)
    {
        if (iter->cmd.compare(name) == 0)
        {
            iter->command = command;
            iter->channels.clear();
            iter->pars.clear();
            return;
        }
    }

    CMD_TABLE ctbl;
    ctbl.cmd = name;
    ctbl.command = command;
    mCmdTable.push_back(ctbl);
}