/** * @RDE_HEADER@ * Xml Reader for Effect Configuration * $Id$ * $HeadURL$ * $Author$ * Created by Gerlinde Rudolphus, SLTE, 2021-06-02 */ #include "xmlreader.h" #include #include #include namespace BuildingControlUnit { XmlReader::XmlReader(QObject *parent) : QObject(parent) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("XmlReader")) { } bool XmlReader::init(const QString &effectConfigFile) { m_contents.effects.clear(); QFile file(effectConfigFile); DEBUG_LOG(m_logger) << "open effectConfigFile: " << effectConfigFile; if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { WARNING_LOG(m_logger) << "Couldn't open EffectConfigFile: " << effectConfigFile; return false; } QXmlStreamReader xml(file.readAll()); while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (token == QXmlStreamReader::StartDocument) continue; else if (token == QXmlStreamReader::StartElement) { if (xml.name() == QStringLiteral("effects")) continue; else if (xml.name() == QStringLiteral("backgroundSoundVolume")) m_contents.bgSoundVolume = xml.readElementText().toInt(); else if (xml.name() == QStringLiteral("hitSoundVolume")) m_contents.hitSoundVolume = xml.readElementText().toInt(); else if (xml.name() == QStringLiteral("password")) m_contents.password = xml.readElementText(); else if (xml.name() == QStringLiteral("port")) m_contents.port = xml.readElementText().toInt(); else if (xml.name() == QStringLiteral("username")) m_contents.username = xml.readElementText(); else if (xml.name() == QStringLiteral("updateRelayState")) m_contents.updateRelayState = xml.readElementText().toInt(); if (xml.name() == QStringLiteral("effect")) { Effect *e = this->parseEffect(xml); if (e) m_contents.effects.append(e); } } } if (xml.hasError()) { ERROR_LOG(m_logger) << "Effect Config File had XML Errors:\n" << xml.errorString(); return false; } DEBUG_LOG(m_logger) << "Effect Config File Contents: " << m_contents.toString(); createHttpRequests(); return true; } Effect *XmlReader::parseEffect(QXmlStreamReader &xml) { DEBUG_LOG(m_logger) << "- parseEffect Start"; if (xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != QStringLiteral("effect")) return {}; Effect *e = new Effect(); QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute(QStringLiteral("type"))) { QString typeString = attributes.value(QStringLiteral("type")).toString(); if (typeString == QStringLiteral("relayAction")) e->effectType = ET_Relay; else if (typeString == QStringLiteral("backgroundSoundAction")) e->effectType = ET_BackgroundSound; else if (typeString == QStringLiteral("bsmFlashAction")) e->effectType = ET_BsmFlash; } xml.readNext(); while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == QStringLiteral("effect"))) { if (xml.tokenType() == QXmlStreamReader::StartElement) { if (xml.name() == QStringLiteral("id")) { QXmlStreamAttributes attributesName = xml.attributes(); if (attributesName.hasAttribute(QStringLiteral("name"))) { e->name = attributesName.value(QStringLiteral("name")).toString(); } e->id = xml.readElementText().toInt(); } if (xml.name() == QStringLiteral("category")) { e->category = static_cast(xml.readElementText().toInt()); } if (xml.name() == QStringLiteral("override")) { e->override = static_cast(xml.readElementText().toInt()); } if (xml.name() == QStringLiteral("range")) e->ranges.append(parseRange(xml)); } xml.readNext(); } DEBUG_LOG(m_logger) << "- parseEffect: Name: " << e->id << ", Type: " << e->effectType << ", Category: " << e->category << ", Name: " << e->name << ", override: " << e->override; DEBUG_LOG(m_logger) << "- parseEffect End. It has " << e->ranges.size() << " ranges"; return e; } Range *XmlReader::parseRange(QXmlStreamReader &xml) { if (xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != QStringLiteral("range")) return {}; DEBUG_LOG(m_logger) << "-- parseRange Start"; Range *r = new Range(); QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute(QStringLiteral("min"))) r->min = attributes.value(QStringLiteral("min")).toInt(); if (attributes.hasAttribute(QStringLiteral("max"))) r->max = attributes.value(QStringLiteral("max")).toInt(); if (attributes.hasAttribute(QStringLiteral("restartOnChange"))) r->restartOnChange = attributes.value(QLatin1String("restartOnChange")).toString().toLower() == QLatin1String("true"); xml.readNext(); /* * We're going to loop over the things because the order might change. * We'll continue the loop until we hit an EndElement named person. */ while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == QLatin1String("range"))) { Action *a = parseAction(xml); if (a && a->aclass != AC_Unset) r->actions << a; xml.readNext(); } DEBUG_LOG(m_logger) << "-- parseRange: [" << r->min << "-" << r->max << "]" << ", restartOnChange: " <restartOnChange << ", ActionCount: " << r->actions.count(); DEBUG_LOG(m_logger) << "-- parseRange End"; return r; } Action *XmlReader::parseAction(QXmlStreamReader &xml) { if (xml.tokenType() != QXmlStreamReader::StartElement) return {}; if (xml.name() == QStringLiteral("action")) return parseStandardAction(xml);; if (xml.name() == QStringLiteral("pause")) return parsePauseAction(xml); if (xml.name() == QStringLiteral("repeat")) return parseRepeatAction(xml); if (xml.name() == QStringLiteral("bsm")) return parseBsmAction(xml); WARNING_LOG(m_logger) << "--- parseUnknownAction with name '" << xml.name().string() << "'cannot be handled."; return {}; } PauseAction *XmlReader::parsePauseAction(QXmlStreamReader &xml) { if (xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != QLatin1String("pause")) return {}; DEBUG_LOG(m_logger) << "--- parsePauseAction Start"; QXmlStreamAttributes attributes = xml.attributes(); PauseAction *p = new PauseAction(); p->atype = static_cast(attributes.value(QStringLiteral("type")).toInt()); if (attributes.hasAttribute(QStringLiteral("comment"))) p->comment = attributes.value(QStringLiteral("comment")).toString(); p->pause = xml.readElementText().toInt(); DEBUG_LOG(m_logger) << "--- parsePauseAction: " << " Pause: " << p->pause << ", Comment: " << p->comment; DEBUG_LOG(m_logger) << "--- parsePauseAction End"; return p; } RepeatAction *XmlReader::parseRepeatAction(QXmlStreamReader &xml) { if (xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != QStringLiteral("repeat")) return {}; DEBUG_LOG(m_logger) << "--- parseRepeatAction Start"; RepeatAction *ra = new RepeatAction(); QXmlStreamAttributes attributes = xml.attributes(); if (attributes.hasAttribute(QStringLiteral("count"))) ra->count = attributes.value(QStringLiteral("count")).toInt(); if (attributes.hasAttribute(QStringLiteral("comment"))) ra->comment = attributes.value(QStringLiteral("comment")).toString(); if (ra->count == 0) ra->count = -1; xml.readNext(); while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == QStringLiteral("repeat"))) { Action *a = parseAction(xml); if (a && a->aclass != AC_Unset) ra->actions << a; xml.readNext(); } QString repetitions = QString::number(ra->count); if (ra->count < 0) repetitions = QLatin1String("Infinite"); DEBUG_LOG(m_logger) << "--- parseRepeatAction: Repetitions: " << repetitions << ", Comment: " << ra->comment; DEBUG_LOG(m_logger) << "--- parseRepeatAction End"; return ra; } Action *XmlReader::parseStandardAction(QXmlStreamReader &xml) { if (xml.tokenType() != QXmlStreamReader::StartElement || xml.name() != QStringLiteral("action")) return {}; DEBUG_LOG(m_logger) << "--- parseStandardAction Start"; QXmlStreamAttributes attributes = xml.attributes(); if (!attributes.hasAttribute(QStringLiteral("type"))) return {}; Action *a = new Action(); // set explicitely, as Action itself is "unset" a->aclass = AC_Standard; a->atype = static_cast(attributes.value(QStringLiteral("type")).toInt()); if (attributes.hasAttribute(QStringLiteral("comment"))) a->comment = attributes.value(QStringLiteral("comment")).toString(); DEBUG_LOG(m_logger) << "--- parseStandardAction: Type: " << a->atype << ", Comment: " << a->comment; DEBUG_LOG(m_logger) << "--- parseStandardAction End"; return a; } BsmAction *XmlReader::parseBsmAction(QXmlStreamReader &xml) { QXmlStreamAttributes attributes = xml.attributes(); DEBUG_LOG(m_logger) << "--- parseBsmAction Start"; BsmAction *a = new BsmAction(); a->atype = static_cast(attributes.value(QStringLiteral("type")).toInt()); if (attributes.hasAttribute(QStringLiteral("comment"))) a->comment = attributes.value(QStringLiteral("comment")).toString(); xml.readNext(); while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == QStringLiteral("bsm"))) { // What about count? if (xml.name() == QStringLiteral("pause")) a->pause = xml.readElementText().toInt(); if (xml.name() == QStringLiteral("choplength")) a->choplength = xml.readElementText().toInt(); if (xml.name() == QStringLiteral("pulses")) a->pulses = xml.readElementText().toInt(); if (xml.name() == QStringLiteral("pulseduration")) a->pulseduration = xml.readElementText().toInt(); xml.readNext(); } DEBUG_LOG(m_logger) << "--- parseBsmAction: Type: " << a->atype << ", Pause: " << a->pause << ", Choplength: " << a->choplength << ", Count: " << a->count << ", Pulses: " << a->pulses << ", Pulseduration: " << a->pulseduration << ", Comment: " << a->comment; DEBUG_LOG(m_logger) << "--- parseBsmAction End"; return a; } void XmlReader::createHttpRequests() { for (const Effect *e : m_contents.constEffects()) { for (const Range *r : e->constRanges()) { generateUrls(e, r->actions); } } } QUrl XmlReader::createPreparedUrl(const XmlContents &contents, const QString &hostname) { QUrl url; url.setScheme(QStringLiteral("http")); url.setUserName(contents.username); url.setPassword(contents.password); if (!hostname.isEmpty()) url.setHost(hostname); url.setPort(contents.port); return url; } void XmlReader::generateUrl(const Effect *e, Action *a) { switch (a->aclass) { case AC_Standard: { QUrl url = createPreparedUrl(m_contents); QUrlQuery query; switch (e->effectType) { case ET_Relay: { url.setPath(QStringLiteral("/relay.php")); query.addQueryItem(QStringLiteral("state"), QString::number(a->atype)); query.addQueryItem(QStringLiteral("number"), QString::number(e->id)); break; } case ET_BackgroundSound: { url.setPath(QStringLiteral("/bgsound.php")); query.addQueryItem(QStringLiteral("category"), categoryName(e->category)); query.addQueryItem(QStringLiteral("sound"), QString::number(e->id)); query.addQueryItem(QStringLiteral("vol"), QString::number(m_contents.bgSoundVolume)); switch (a->atype) { case AT_Off: query.addQueryItem(QStringLiteral("control"), QStringLiteral("2")); break; case AT_On: query.addQueryItem(QStringLiteral("control"), QStringLiteral("1")); break; case AT_Infinite: query.addQueryItem(QStringLiteral("control"), QStringLiteral("3")); break; case AT_Flash: default: break; } break; } default: break; } url.setQuery(query); a->url = url; DEBUG_LOG(m_logger) << "Effect " << e->name << ", Action '" << a->comment << "' now has URL: " << url.toString(); break; } case AC_Repeat: { const RepeatAction *ra = static_cast(a); generateUrls(e, ra->actions); break; } case AC_Unset: case AC_Pause: case AC_Bsm: default: DEBUG_LOG(m_logger) << "Effect " << e->name << ", Action '" << a->comment << "' of Type " << a->aclass << " needs no URL"; break; } } void XmlReader::generateUrls(const Effect *e, const QVector &actions) { for (Action *a : actions) generateUrl(e,a); } //QVector Effect::getActionsByRange(int damagePercent) const //{ // for (const Range *r : ranges) // { // if (r->fits(damagePercent)) // return r->actions; // } // return {}; //} Range* Effect::getRangeByDamagePercent(int damagePercent) const { for (Range *r : ranges) { if (r->fits(damagePercent)) return r; } return nullptr; } QString XmlReader::categoryName(Category c) { switch (c) { case CT_BGS_Bahnhof: return QStringLiteral("bahnhof"); case CT_BGS_Personen: return QStringLiteral("personen"); case CT_BGS_Sonstige: return QStringLiteral("sonstige"); case CT_BGS_Stadt: return QStringLiteral("stadt"); case CT_BGS_Testsounds: return QStringLiteral("testsounds"); case CT_BGS_Aircrafts: return QStringLiteral("Aircrafts"); case CT_BGS_Battlefields: return QStringLiteral("Battlefields"); case CT_BGS_Boats: return QStringLiteral("Boats"); case CT_BGS_Explosions: return QStringLiteral("Explosions"); case CT_BGS_Helicopter: return QStringLiteral("Helicopter"); case CT_BGS_Motors_and_urban_traffic: return QStringLiteral("Motors_and_urban_traffic"); case CT_BGS_InsideBuildings: return QStringLiteral("Inside_buildings"); case CT_BGS_Vehicles: return QStringLiteral("Vehicles"); case CT_Default: default: return QStringLiteral("Default"); break; } } } // BuildingControlUnit