/** * @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 #include #include #include #include #include #include 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 &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(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(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(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(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 HandelEffects::getOverrides() { QVector 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