Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,49 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(co_SRCS
xmlreader.cpp
xmlreader.h
handeleffects.cpp
handeleffects.h
handeleffect.cpp
handeleffect.h
)
SET(cot_SRCS
xmlreader.cpp
xmlreader.h
effect_test_main.cpp
)
NGL_ADD_LIBRARY2(AAR SV ControlEffects SHARED ${co_SRCS})
NGL_TARGET_LINK_LIBRARIES(ControlEffects
NGLLogger
Qt5::Concurrent
Qt5::Core
# Qt5::Test
Qt5::Xml
Qt5::Network
BuildingControlCommon
BuildingControlVwm
BuildingControlBsm
nglcoreapplication
)
NGL_ADD_EXECUTABLE(AAR SV ControlEffectsTest ${cot_SRCS})
NGL_TARGET_LINK_LIBRARIES(ControlEffectsTest
NGLLogger
Qt5::Concurrent
Qt5::Core
# Qt5::Test
Qt5::Xml
Qt5::Network
BuildingControlCommon
BuildingControlVwm
nglcoreapplication
)
NGL_INSTALL_TARGET(ControlEffectsTest)
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* Kleines Testprogramm für BSMs
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLSA, 2013-03-05
*/
#include <nglapplication/nglcoreapplication.h>
#include <QNetworkProxy>
#include <memory>
#include <settings.h>
#include "xmlreader.h"
#include "logger.h"
int main(int argc, char **argv)
{
// QString hostname = QStringLiteral("ubeecu0002");
NGLCoreApplication app(argc, argv);
QNetworkProxy::setApplicationProxy(QNetworkProxy());
app.createLogger("BCU");
BuildingControlUnit::XmlReader *xmlReader = new BuildingControlUnit::XmlReader();
//BuildingControlUnit::HandelEffects * handelEffects = new BuildingControlUnit::HandelEffects();
//BuildingControlUnit::HandleHttpRequest * handleHttpRequest = new BuildingControlUnit::HandleHttpRequest;
//QString filename = "C:/HomeOffice/Buildings/BuildingControlUnit/effect_config.xml";
//QString filename = "/shd/CTC/TOOLS/HomeOffice/Malte1/Personen/effect_config.xml";
//QString filename = "/shd/CTC/TOOLS/TDS/TDS-source/Lts/Proxies/Buildings/BuildingControlUnit/effect_config.xml";
QString filename = QStringLiteral("/tmp/effect_config.xml");
xmlReader->init(filename);
//handelEffects->init(filename, hostname);
//handelEffects->printEffects();
//qDebug() << "---------------------------------------------";
//qDebug() << "--- Start Effects: ---";
//qDebug() << "---------------------------------------------";
//handelEffects->startEffects(80);
return 0;
}
@@ -0,0 +1,139 @@
/**
* @RDE_HEADER@
* One Effect Handler. Destroyed/created on Range change
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLTE, 2021-05-31
*/
#include "handeleffect.h"
#include <QUrlQuery>
#include <QTimer>
namespace BuildingControlUnit
{
HandelEffect::HandelEffect(QObject *parent, const QString &loggerchannel)
: QObject(parent)
, m_logger(getLogger(QStringLiteral("HandelSingleEffect.") + loggerchannel))
{
}
bool HandelEffect::init(const QVector<Action *> &actions, int repetitions = 1)
{
if (actions.isEmpty())
return false;
m_actions = actions;
m_currentAction = nullptr;
m_repeatCount = repetitions; // Usually The action list is only worked over once.
return true;
}
//void handleEffect(const Effect *effect, QVector<Action *> actions, int damage, bool background);
void HandelEffect::handleEffect()
{
// No current Action? Start over or be ready
if (!m_currentAction)
{
// Infinite Repeat Actions must start with repeatCount = -1
if (m_repeatCount != 0)
{
m_currentAction = m_actions.first();
--m_repeatCount;
}
else
{
emit effectReady();
deleteLater();
return;
}
}
else
{
int index = m_actions.indexOf(m_currentAction);
++index;
m_currentAction = m_actions.value(index, nullptr);
if (!m_currentAction)
{
QTimer::singleShot(0, this, &HandelEffect::handleEffect);
return;
}
}
switch (m_currentAction->aclass)
{
case AC_Unset:
DEBUG_LOG(m_logger) << "---- This is unset ----";
break;
case AC_Standard:
{
DEBUG_LOG(m_logger) << "---- This is a standard Action ----";
if (!m_currentAction->url.isEmpty())
{
// Todo: Is there a standard action valid for BSM? If so, we need to consider!
DEBUG_LOG(m_logger) << "---- emit newHttpRequest(a->url):" << m_currentAction->url.toString();
emit newHttpRequest(m_currentAction->url);
}
break;
}
case AC_Repeat:
{
DEBUG_LOG(m_logger) << "---- This is a Repeat Action";
const RepeatAction *ra = static_cast<const RepeatAction *>(m_currentAction);
HandelEffect *he = new HandelEffect(this);
he->init(ra->actions, ra->count);
connect(he, &HandelEffect::effectReady, this, &HandelEffect::handleEffect);
connect(he, &HandelEffect::newHttpRequest, this, &HandelEffect::newHttpRequest);
connect(he, &HandelEffect::triggerAllFlashlights, this, &HandelEffect::triggerAllFlashlights);
QTimer::singleShot(0, he, &HandelEffect::handleEffect);
// To avoid the Timer call at the end of this method:
return;
// So this instance does nothing unless child-he is ready.
}
case AC_Pause:
{
const PauseAction *pa = static_cast<const PauseAction *>(m_currentAction);
DEBUG_LOG(m_logger) << "---- This is a Pause Action Pause=" << pa->pause
<< " comment=" << pa->comment;
QTimer::singleShot(pa->pause, this, &HandelEffect::handleEffect);
// To avoid the Timer call at the end of this method:
return;
// So this instance does nothing unless timer returns.
}
case AC_Bsm:
{
DEBUG_LOG(m_logger) << "---- This is a Bsm Action ";
const BsmAction *ba = static_cast<const BsmAction*>(m_currentAction);
if (!ba->isNull())
{
DEBUG_LOG(m_logger) << "emit triggerAllFlashlights ------ atype=" << m_currentAction->atype
<< " pause=" << ba->pause
<< " choplength=" << ba->choplength
<< " pulses=" << ba->pulses
<< " pulseduration=" << ba->pulseduration
<< " comment=" << ba->comment
<< " this: " << this;
emit triggerAllFlashlights(ba->pulses, ba->pause, ba->choplength, ba->pulseduration);
}
break;
}
default:
break;
}
DEBUG_LOG(m_logger) << "--- End Action ";
// Start next action with ending this method.
QTimer::singleShot(0, this, &HandelEffect::handleEffect);
} // end printActions
} //namespace BuildingControlUnit
@@ -0,0 +1,47 @@
/**
* @RDE_HEADER@
* One Effect Handler. Destroyed/created on Range change
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLTE, 2021-05-31
*/
#pragma once
#include "xmlreader.h"
#include <logger.h>
namespace BuildingControlUnit
{
class HandelEffect: public QObject
{
Q_OBJECT
public:
HandelEffect(QObject* parent = nullptr, const QString &loggerchannel = QString());
bool init(const QVector<Action*> &actions, int repetitions);
signals:
void newHttpRequest(const QUrl &url);
void triggerAllFlashlights(int numberOfFlashes,
int pauseDuration,
int choppedConfiguration,
int flashDuration);
void effectReady();
public slots:
void handleEffect();
private:
QVector<Action *> m_actions;
Action *m_currentAction{nullptr};
int m_repeatCount{1};
LOGGER_T m_logger;
};
}
@@ -0,0 +1,502 @@
/**
* @RDE_HEADER@
* Effects (plural) handler, handles all effects at one EGCU or BSM
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLTE, 2021-06-02
*/
#include "handeleffects.h"
#include "handeleffect.h"
#include "handle_http_request.h"
#include <QTimer>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDateTime>
#include <QJsonArray>
#include <QDateTime>
namespace BuildingControlUnit
{
HandelEffects::HandelEffects(QObject *parent, const QString &loggerchannel)
: QObject(parent)
, m_sounds(EffectOverride::EO_Auto)
, m_getRelayStateTimer(new QTimer(this))
, m_logger(getLogger(QStringLiteral("HandelEffects.") + loggerchannel))
{
DEBUG_LOG(m_logger) << "HandelEffects::init loggerchannel=" << loggerchannel;
// create a timer
// setup signal and slot
connect(m_getRelayStateTimer, &QTimer::timeout,
this, &HandelEffects::onRelayStateTimerTimeout);
}
HandelEffects::~HandelEffects()
{
}
void HandelEffects::init(const QString &controlEffectsCfg, const QString &hostname, EffectType et)
{
m_xmlReader.init(controlEffectsCfg);
DEBUG_LOG(m_logger) << "HandelEffects::init";
m_hostname = hostname;
m_lastDamage = -1;
m_effectType = et;
// msec
if (et == ET_Relay)
m_getRelayStateTimer->start(contents().updateRelayState);
}
void HandelEffects::startEffects(int damagePercent)
{
if (m_lastDamage == damagePercent)
{
DEBUG_LOG(m_logger) << "damagePercent=" << damagePercent
<< " m_lastDamage=" << m_lastDamage
<< " return";
return;
}
for (Effect *e : contents().constEffects())
{
// If this Instance does not handle BSM, skip BSM-Effects
if (e->effectType == ET_BsmFlash && m_effectType != ET_BsmFlash)
continue;
// If this Instance has BSM API, it is only for BSM, so skip sounds
if (e->effectType == ET_BackgroundSound && m_effectType == ET_BsmFlash)
continue;
// If this Instance has BSM API, it is only for BSM, so skip relays
if (e->effectType == ET_Relay && m_effectType == ET_BsmFlash)
continue;
DEBUG_LOG(m_logger) << "- HandelEffects::Starting Effect " << e << " of Type " << e->effectType << ". ----"
<< " name=" << e->name;
for (const Range *range : e->constRanges())
{
if (!range->fits(damagePercent))
continue;
if (range->fits(m_lastDamage) && !range->restartOnChange)
continue;
// We must restart the actions for that range. Either, because the range changed or because restart is commanded.
// Thus, we kill the old HandelEffect and start over.
createEffectHandler(e, range);
}
DEBUG_LOG(m_logger) << "- End Effect ";
}
m_lastDamage = damagePercent;
}
void HandelEffects::createEffectHandler(Effect *e, const Range *range)
{
HandelEffect *handelEffect = e->effectHandler;
delete handelEffect; // This also deletes the child handelEffect in case of Repeat actions!
handelEffect = new HandelEffect(this, m_hostname.isEmpty() ? QLatin1String("BSM") : m_hostname);
e->effectHandler = handelEffect;
if (e->override == EO_Auto)
{
if (m_effectType == ET_BsmFlash)
{
INFO_LOG(m_logger) << "setOverrides connect Flashes" ;
connect(handelEffect, &HandelEffect::triggerAllFlashlights, this, &HandelEffects::triggerAllFlashlights, Qt::UniqueConnection);
}
else
connect(handelEffect, &HandelEffect::newHttpRequest, this, &HandelEffects::sendHttpRequest, Qt::UniqueConnection);
}
connect(handelEffect, &HandelEffect::effectReady, this, &HandelEffects::onEffectReady);
DEBUG_LOG(m_logger) << "-- HandelEffects::Starting Range " << range->min << " - " << range->max
<< ". ----";
DEBUG_LOG(m_logger) << "-- Range has " << range->actions.count() << " actions";
handelEffect->init(range->actions, 1);
QTimer::singleShot(0, handelEffect, &HandelEffect::handleEffect);
DEBUG_LOG(m_logger) << "-- End Range ";
}
void HandelEffects::playHitSound(const int hitSoundId)
{
if (m_sounds == EffectOverride::EO_Off)
return;
QUrl url = XmlReader::createPreparedUrl(contents(), m_hostname);
url.setPath(QStringLiteral("/hitsound.php"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("sound"), QString::number(hitSoundId));
query.addQueryItem(QStringLiteral("vol"), QString::number(contents().hitSoundVolume));
url.setQuery(query);
emit newHttpRequest(url, HandleHttpQueueMessageStatus::MS_Single);
}
void HandelEffects::switchRelay(const SwitchRelayValues &switchRelayValues)
{
QUrl url = XmlReader::createPreparedUrl(contents(), m_hostname);
url.setPath(QStringLiteral("/relay.php"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("number"), QString::number(switchRelayValues.id));
query.addQueryItem(QStringLiteral("state"), QString::number(switchRelayValues.bcuRelayState));
if (switchRelayValues.bcuRelayState == BRS_Pulse)
{
query.addQueryItem(QStringLiteral("pulse"), QString::number(switchRelayValues.pulse));
query.addQueryItem(QStringLiteral("repeat"), QString::number(switchRelayValues.count-1));
query.addQueryItem(QStringLiteral("delay"), QString::number(switchRelayValues.delay));
}
url.setQuery(query);
emit newHttpRequest(url, HandleHttpQueueMessageStatus::MS_Single);
}
void HandelEffects::setOverrides(const QVector<BcuEffectState> &effectStates)
{
m_effectStates = effectStates; // Save effectStates to restore overrides
DEBUG_LOG(m_logger) << "In set overrides for " << m_hostname << ", effectStates.size: " << effectStates.size();
for(const auto &es : qAsConst(m_effectStates))
{
DEBUG_LOG(m_logger) << "m_effectStates: es.id=" << es.id
<< ", es.type=" << es.type
<< ", es.override=" << es.effectOverride;
}
m_sounds = EffectOverride::EO_Auto;
DEBUG_LOG(m_logger) << "Nun die Schleife:";
for(const auto &es : qAsConst(effectStates))
{
DEBUG_LOG(m_logger) << "es.id=" << es.id
<< ", es.type=" << es.type
<< ", es.override=" << es.effectOverride;
switch (es.type) {
case BET_BackgroundSound:
{
for (Effect *e : contents().constEffects())
{
if (e->effectType != ET_BackgroundSound)
continue;
EffectOverride eo = static_cast<EffectOverride>(es.effectOverride);
e->override = eo;
m_sounds = eo;
if (eo == EO_Auto)
createEffectHandler(e,e->getRangeByDamagePercent(m_lastDamage));
else
{
if (e->effectHandler)
{
disconnect(e->effectHandler, &HandelEffect::newHttpRequest, this, &HandelEffects::sendHttpRequest);
INFO_LOG(m_logger) << "setOverrides emit triggerAllFlashlights(0,0,0,0)" ;
emit triggerAllFlashlights(0,0,0,0);
}
}
}
if (es.effectOverride == BEO_Off )
{
DEBUG_LOG(m_logger) << "BEO_Off";
// Stop all Sounds:
QUrl url = XmlReader::createPreparedUrl(contents(), m_hostname);
QUrlQuery query;
url.setPath(QStringLiteral("/bgsound.php"));
query.addQueryItem(QStringLiteral("control"), QString::number(BCS_Stop_All_Soundfiles));
query.addQueryItem(QStringLiteral("category"), QStringLiteral("people")); // dummy for ICD
query.addQueryItem(QStringLiteral("sound"), QStringLiteral("0")); // dummy for ICD
url.setQuery(query);
sendHttpRequest(url);
}
break;
}
case BET_Relay:
{
for (Effect *e : contents().constEffects())
{
if (e->effectType != ET_Relay)
continue;
if (e->id != es.id)
continue;
EffectOverride eo = static_cast<EffectOverride>(es.effectOverride);
if (eo == e->override)
//continue;
e->override = eo;
if (es.effectOverride != BEO_Auto)
{
SwitchRelayValues relayValues;
relayValues.id = e->id;
relayValues.bcuRelayState = static_cast<BcuRelayState>(es.effectOverride);
switchRelay(relayValues);
if (e->effectHandler)
disconnect(e->effectHandler, &HandelEffect::newHttpRequest, this, &HandelEffects::sendHttpRequest);
}
else
createEffectHandler(e,e->getRangeByDamagePercent(m_lastDamage));
}
break;
}
case BET_BsmFlash:
{
if (m_effectType != ET_BsmFlash)
break;
for (Effect *e : contents().constEffects())
{
if (e->effectType != ET_BsmFlash)
continue;
EffectOverride eo = static_cast<EffectOverride>(es.effectOverride);
if (eo == e->override)
continue;
e->override = eo;
if (es.effectOverride != BEO_Off) // -1 for back to auto or 1 for permanently on which in this case shall also be "auto"...
createEffectHandler(e,e->getRangeByDamagePercent(m_lastDamage));
else
{
if (e->effectHandler)
{
INFO_LOG(m_logger) << "setOverrides disconnect Flashes" ;
disconnect (e->effectHandler, &HandelEffect::triggerAllFlashlights, this, &HandelEffects::triggerAllFlashlights);
}
}
}
break;
}
default:
break;
}
}
}
QVector<BcuEffectState> HandelEffects::getOverrides()
{
QVector<BcuEffectState> overrides;
for (Effect *effect : contents().constEffects())
{
BcuEffectState override;
override.id = effect->id;
switch (effect->effectType)
{
case BuildingControlUnit::ET_Unset:
override.type = BET_Unset;
break;
case BuildingControlUnit::ET_Relay:
override.type = BET_Relay;
break;
case BuildingControlUnit::ET_BackgroundSound:
override.type = BET_BackgroundSound;
break;
case BuildingControlUnit::ET_BsmFlash:
override.type = BET_BsmFlash;
break;
} // switch
switch (effect->override)
{
case EO_Auto:
override.effectOverride = BEO_Auto;
break;
case EO_Off:
override.effectOverride = BEO_Off;
break;
case EO_On:
override.effectOverride = BEO_On;
break;
} // switch
DEBUG_LOG(m_logger) << "getOverrides "
<< " id=" << override.id
<< " type=" << override.type
<< " override" << override.effectOverride;
overrides.append(override);
} // for
return overrides;
} // END getOverrides
void HandelEffects::onEffectReady()
{
// Effect will delete itself, but we have to remove the pointer from the corresponding effect.
for (Effect *e : contents().constEffects())
{
if (e->effectHandler != sender())
continue;
e->effectHandler = nullptr;
return;
}
}
void HandelEffects::sendHttpRequest(const QUrl &url)
{
QUrl copy = url;
copy.setHost(m_hostname);
DEBUG_LOG(m_logger) << "sendHttpRequest" << copy.toString();
emit newHttpRequest(copy, HandleHttpQueueMessageStatus::MS_Single);
}
void HandelEffects::onRelayStateTimerTimeout()
{
if ( m_offlineCounter == 3 )
{
m_offlineState = true;
NOTICE_LOG(m_logger) << "!!!!!!!!! Send Offline State !!!!!!!!!!!!!!!! m_offlineState=" << m_offlineState;
m_relayState.offlineTime = QDateTime::currentMSecsSinceEpoch();
emit sendRelayState(m_relayState);
m_overridesSend = false;
}
DEBUG_LOG(m_logger) << "send get relay-State contents()=" << contents().toString()
<< " m_offlineCounter=" << m_offlineCounter;
QUrl url = XmlReader::createPreparedUrl(contents(), m_hostname);
url.setPath(QStringLiteral("/relay.php"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("state"), QString::number(BRS_State));
url.setQuery(query);
emit newHttpRequest(url, HandleHttpQueueMessageStatus::MS_Cyclic);
m_offlineCounter++;
}
///////////////////////////////////////////////////
void HandelEffects::handleEgcuReplyObject(const QString &host, const QByteArray &data)
{
bool relayStateChanged = false;
TRACE_LOG(m_logger) << "handleEgcuReply Object:" << data
<< " m_offlineState=" << m_offlineState;
if (m_offlineState)
{
DEBUG_LOG(m_logger) << "State wiederherstellen !!!!!!!!!!! m_offlineState=" << m_offlineState;
m_offlineState = false;
setOverrides(m_effectStates);
}
RelayState lastRelayState = m_relayState;
m_relayState.relayStates.clear();
const QJsonObject jsonObject = QJsonDocument::fromJson(data).object();
const QJsonObject stateObj = jsonObject[QStringLiteral("State")].toObject();
for (auto it = stateObj.constBegin(); it != stateObj.constEnd(); ++it)
{
RelayStateInfo rsi;
rsi.id = it.key().toInt();
rsi.state = it.value().isString() ? (it.value().toString().compare(QLatin1String("on"), Qt::CaseInsensitive) == 0) : it.value().toBool(false);
m_relayState.relayStates.append(rsi);
TRACE_LOG(m_logger) << "id=" << rsi.id
<< " state=" << rsi.state;
}
if ( m_relayState.relayStates.size() == lastRelayState.relayStates.size())
{
for (int i = 0; i < m_relayState.relayStates.size(); ++i)
{
if (m_relayState.relayStates[i].state != lastRelayState.relayStates[i].state)
{
TRACE_LOG(m_logger) << "Values relayStateChanged = true";
relayStateChanged = true;
}
}
}
else
{
TRACE_LOG(m_logger) << "Size relayStateChanged = true";
relayStateChanged = true;
}
if ( m_offlineCounter > 1)
{
TRACE_LOG(m_logger) << "m_offlineCounter=" << m_offlineCounter;
relayStateChanged = true;
}
m_relayState.hostname = m_hostname;
m_relayState.stateTime = QDateTime::currentMSecsSinceEpoch();
m_relayState.offlineTime = 0;
m_offlineCounter=0;
DEBUG_LOG(m_logger) << "host=" << host.toLower()
<< " relayStateChanged=" << relayStateChanged
<< " m_overridesSend=" << m_overridesSend
<< " m_offlineState=" << m_offlineState;
// if ( m_relayState.hostname.toLower() == host.toLower() && relayStateChanged && m_overridesSend)
if ( m_offlineState)
{
NOTICE_LOG(m_logger) << "!!!!!!!!!!!!!!!! EGCU States wieder setzen !!!!!!!!!!!!!!!!!!";
m_offlineState = false;
}
if ( m_relayState.hostname.toLower() == host.toLower() && relayStateChanged)
{
DEBUG_LOG(m_logger) << "emit sendRelayState(m_relayState) relayStateChanged";
emit sendRelayState(m_relayState);
}
}
void HandelEffects::handleEgcuReplyArray(const QString &host, const QByteArray &data)
{
Q_UNUSED (host);
TRACE_LOG(m_logger) << "handleEgcuReply Array:" << data;
m_relayState.relayStates.clear();
//parse the reply JSON and send result to svcBuilding
const QJsonObject jsonObject = QJsonDocument::fromJson(data).object();
if (!jsonObject.contains(QStringLiteral("State")) )
return;
const QJsonArray stateArray = jsonObject[QStringLiteral("State")].toArray();
for (int i = 0; i < stateArray.size(); ++i)
{
RelayStateInfo rsi;
rsi.id = i;
rsi.state = stateArray[i].isString() ? (stateArray[i].toString().compare(QLatin1String("on"), Qt::CaseInsensitive)== 0 ): stateArray[i].toBool(false);
m_relayState.relayStates.append(rsi);
TRACE_LOG(m_logger) << "id=" << rsi.id
<< " state=" << rsi.state;
}
m_relayState.hostname = m_hostname;
m_relayState.stateTime = QDateTime::currentMSecsSinceEpoch();
m_relayState.offlineTime = 0;
m_offlineCounter=0;
// if ( m_relayState.hostname.toLower() == host.toLower() && relayStateChanged && m_overridesSend )
// emit sendRelayState(m_relayState);
}
void HandelEffects::handleEgcuReply(const QString &host, const QByteArray &data)
{
//parse the reply JSON and send result to svcBuilding
DEBUG_LOG(m_logger) << "HandelEffects::handleEgcuReply host=" << host
<< " m_hostname=" << m_hostname
<< " m_overridesSend=" << m_overridesSend;
if (host.toLower() != m_hostname.toLower())
return;
const QJsonObject jsonObject = QJsonDocument::fromJson(data).object();
if (!jsonObject.contains(QStringLiteral("State")) )
{
m_overridesSend = true;
TRACE_LOG(m_logger) << "No State return";
return;
}
const QJsonValue jsonValue = jsonObject[QStringLiteral("State")];
if (jsonValue.isArray())
{
TRACE_LOG(m_logger) << "data isArray\n";
handleEgcuReplyArray(host, data);
}
else
{
TRACE_LOG(m_logger) << "data isObject\n";
handleEgcuReplyObject(host, data);
}
}
} // BuildingControlUnit
@@ -0,0 +1,78 @@
/**
* @RDE_HEADER@
* Effects (plural) handler, handles all effects at one EGCU or BSM
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLTE, 2021-06-02
*/
#pragma once
#include "xmlreader.h"
#include <ControlEffects/controleffects_export.h>
#include <BSM/bsm_types.h>
#include <logger.h>
#include "handle_http_request.h"
class QTimer;
namespace BuildingControlUnit
{
class CONTROLEFFECTS_EXPORT HandelEffects: public QObject
{
Q_OBJECT
public:
explicit HandelEffects(QObject *parent = nullptr, const QString &loggerchannel = QString());
~HandelEffects();
void init(const QString &controlEffectsCfg, const QString &hostname, EffectType et);
void startEffects(int damagePercent);
void playHitSound(const int hitSoundId);
void switchRelay(const SwitchRelayValues &switchRelayValues);
QString hostname() const { return m_hostname; }
void setOverrides(const QVector<BcuEffectState> &effectStates);
QVector<BcuEffectState> getOverrides();
public slots:
void handleEgcuReply(const QString &host, const QByteArray &data);
private slots:
void onEffectReady();
void sendHttpRequest(const QUrl &url);
void onRelayStateTimerTimeout();
private:
void handleEgcuReplyObject(const QString &host, const QByteArray &data);
void handleEgcuReplyArray(const QString &host, const QByteArray &data);
signals:
void newHttpRequest(const QUrl &url, HandleHttpQueueMessageStatus status);
void triggerAllFlashlights(int numberOfFlashes,
int pauseDuration,
int choppedConfiguration,
int flashDuration);
void sendRelayState (const RelayState &relayState);
private:
XmlContents contents() const { return m_xmlReader.contents(); }
int m_lastDamage = -1;
XmlReader m_xmlReader;
EffectType m_effectType; // BSM or Sound/Relay [EGCU]
QString m_hostname;
EffectOverride m_sounds;
QTimer *m_getRelayStateTimer;
RelayState m_relayState;
int m_offlineCounter = 0;
LOGGER_T m_logger;
QVector<BcuEffectState> m_effectStates;
bool m_overridesSend = false;
//bool m_reconnected = false; // New Connection to EGCU => restore the target state of the EGCU
bool m_offlineState = true;
void createEffectHandler(Effect *e, const Range *range);
};
} // BuildingControlUnit
@@ -0,0 +1,30 @@
#include <QApplication>
#include "xmlreader.h"
#include "handeleffects.h"
#include <QString>
#include <QDebug>
int main(int argc, char *argv[])
{
HandelEffects *handelEffects = new HandelEffects();
QApplication a(argc, argv);
MainWindow w;
w.show();
//QString filename = "C:/HomeOffice/Buildings/BuildingControlUnit/effect_config.xml";
//QString filename = "/shd/CTC/TOOLS/HomeOffice/Malte1/Personen/effect_config.xml";
//QString filename = "/shd/CTC/TOOLS/TDS/TDS-source/Lts/Proxies/Buildings/BuildingControlUnit/effect_config.xml";
QString filename = "/shd/CTC/TOOLS/HomeOffice/Malte3/Personen1/effect_config.xml";
handelEffects->init(filename);
handelEffects->printEffects();
qDebug() << "---------------------------------------------";
qDebug() << "--- Start Effects: ---";
qDebug() << "---------------------------------------------";
handelEffects->startEffects(80);
//handelEffects->newSegmentDamage(80);
return a.exec();
}
@@ -0,0 +1,478 @@
/**
* @RDE_HEADER@
* Xml Reader for Effect Configuration
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLTE, 2021-06-02
*/
#include "xmlreader.h"
#include <QFile>
#include <QUrlQuery>
#include <QXmlStreamAttributes>
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<Category>(xml.readElementText().toInt());
}
if (xml.name() == QStringLiteral("override"))
{
e->override = static_cast<EffectOverride>(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: " <<r->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<ActionType>(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<ActionType>(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<ActionType>(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<const RepeatAction *>(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<Action *> &actions)
{
for (Action *a : actions)
generateUrl(e,a);
}
//QVector<Action *> 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
@@ -0,0 +1,207 @@
/**
* @RDE_HEADER@
* Xml Reader for Effect Configuration
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, SLTE, 2021-06-02
*/
#pragma once
//#include <ControlEffects/controleffects_export.h>
//#include <logger.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QXmlStreamReader>
#include <QVector>
#include <QUrl>
namespace BuildingControlUnit
{
class HandelEffect;
enum EffectType
{
// Keep in sync with EffectType from bcu_types.h
ET_Unset = 0,
ET_Relay = 1, // EGCU
ET_BackgroundSound = 2, // EGCU
ET_BsmFlash = 3, // BSM
};
enum EffectOverride
{
EO_Auto = -1,
EO_Off = 0,
EO_On = 1,
};
enum ActionType
{
AT_Off = 0,
AT_On = 1,
AT_Infinite = 2,
AT_Flash = 3
};
enum ActionClass
{
AC_Unset,
AC_Standard,
AC_Repeat,
AC_Pause,
AC_Bsm
};
enum Category
{
CT_Default = 0,
CT_BGS_Bahnhof = 1,
CT_BGS_Personen = 2,
CT_BGS_Sonstige = 3,
CT_BGS_Stadt = 4,
CT_BGS_Testsounds = 5,
CT_BGS_Aircrafts = 6,
CT_BGS_Battlefields = 7,
CT_BGS_Boats = 8,
CT_BGS_Explosions = 9,
CT_BGS_Helicopter = 10,
CT_BGS_Motors_and_urban_traffic = 11,
CT_BGS_InsideBuildings = 12,
CT_BGS_Vehicles = 13
// CT_RL_Testkategorie = 8 geht auch (RL=Relay): Dann kann man beide Namen je nach EffectType verwenden!
};
class Action
{
public:
Action(ActionClass ac = AC_Unset)
: aclass(ac)
{}
ActionClass aclass = AC_Unset;
ActionType atype = AT_Off;
QUrl url;
QString comment;
};
class RepeatAction : public Action
{
public:
RepeatAction()
: Action(AC_Repeat)
{}
// -1 => Infinite. So we can skip on (count == 0) in HandelEffect.
int count = -1;
QVector<Action *> actions;
};
class PauseAction : public Action
{
public:
PauseAction()
: Action(AC_Pause)
{}
int pause = 0;
};
class BsmAction : public Action
{
public:
bool isNull() const { return (count == 0 && pause == 0 && pulses == 0); }// Should be enough...
int count = 0;
int pause = 0;
int choplength = 0;
int pulses = 0;
int pulseduration = 0;
BsmAction() : Action(AC_Bsm) {}
};
class Range
{
public:
inline bool fits(int value) const { return (min <= value && value <= max); }
int min = 0;
int max = 100;
bool restartOnChange = false;
QVector<Action *> actions;
};
class Effect
{
public:
// QVector<Action *> getActionsByRange(int damagePercent) const;
Range* getRangeByDamagePercent(int damagePercent) const;
EffectType effectType = ET_Unset;
Category category = CT_Default;
int id = 0;
QString name;
QVector<Range *> ranges;
HandelEffect *effectHandler = nullptr;
EffectOverride override = EO_Auto;
const QVector<Range *> constRanges() const { return ranges; }
};
struct XmlContents
{
QVector<Effect *> effects;
int hitSoundVolume = 50;
int bgSoundVolume = 50;
QString username;
QString password;
int port = 80;
int updateRelayState = 1000;
const QVector<Effect *> constEffects() const { return effects; }
QString toString() const
{
QString str(QStringLiteral("User: %1, Password: %2, Port: %3, BGSound Vol: %4, HitSound Vol: %5, No of Effects: %6"));
return str.arg(username, password).arg(port).arg(bgSoundVolume).arg(hitSoundVolume).arg(effects.count());
}
};
class XmlReader : public QObject
{
Q_OBJECT
public:
explicit XmlReader(QObject *parent = nullptr);
~XmlReader() = default;
static QUrl createPreparedUrl(const XmlContents &contents, const QString &hostname = QString());
bool init(const QString &effectConfigFile);
XmlContents contents() const { return m_contents; }
private:
void createHttpRequests();
static QString categoryName(Category c);
Effect *parseEffect(QXmlStreamReader &xml);
Range *parseRange(QXmlStreamReader &xml);
Action *parseAction(QXmlStreamReader &xml);
PauseAction *parsePauseAction(QXmlStreamReader &xml);
RepeatAction *parseRepeatAction(QXmlStreamReader &xml);
BsmAction* parseBsmAction(QXmlStreamReader &xml);
Action *parseStandardAction(QXmlStreamReader &xml); // Can be BsmAction or Action alone
void generateUrls(const Effect *e, const QVector<Action *> &actions);
void generateUrl(const Effect *e, Action *a);
XmlContents m_contents;
//BuildingControlUnit::LOGGER_T m_logger;
NGL::INGLLogger &m_logger;
};
} // BuildingControlUnit