hyperion.ng/libsrc/effectengine/EffectEngine.cpp

255 lines
7.5 KiB
C++
Raw Normal View History

// Python includes
#include <Python.h>
#undef B0
// Qt includes
#include <QResource>
// hyperion util includes
#include <utils/jsonschema/QJsonSchemaChecker.h>
#include <utils/JsonUtils.h>
#include <utils/Components.h>
// effect engine includes
#include <effectengine/EffectEngine.h>
2018-12-27 23:11:32 +01:00
#include <effectengine/Effect.h>
#include <effectengine/EffectModule.h>
2018-12-31 15:48:29 +01:00
#include <effectengine/EffectFileHandler.h>
#include "HyperionConfig.h"
2018-12-31 15:48:29 +01:00
EffectEngine::EffectEngine(Hyperion * hyperion)
: _hyperion(hyperion)
, _log(nullptr)
2018-12-31 15:48:29 +01:00
, _effectFileHandler(EffectFileHandler::getInstance())
{
QString subComponent = hyperion->property("instance").toString();
_log= Logger::getInstance("EFFECTENGINE", subComponent);
Q_INIT_RESOURCE(EffectEngine);
qRegisterMetaType<hyperion::Components>("hyperion::Components");
// connect the Hyperion channel clear feedback
connect(_hyperion, &Hyperion::channelCleared, this, &EffectEngine::channelCleared);
connect(_hyperion, &Hyperion::allChannelsCleared, this, &EffectEngine::allChannelsCleared);
2018-12-31 15:48:29 +01:00
// get notifications about refreshed effect list
connect(_effectFileHandler, &EffectFileHandler::effectListChanged, this, &EffectEngine::handleUpdatedEffectList);
// register smooth cfgs and fill available effects
handleUpdatedEffectList();
}
EffectEngine::~EffectEngine()
{
for (Effect * effect : _activeEffects)
{
effect->wait();
delete effect;
}
}
QString EffectEngine::saveEffect(const QJsonObject& obj)
2018-12-31 15:48:29 +01:00
{
return _effectFileHandler->saveEffect(obj);
2018-12-31 15:48:29 +01:00
}
QString EffectEngine::deleteEffect(const QString& effectName)
2018-12-31 15:48:29 +01:00
{
return _effectFileHandler->deleteEffect(effectName);
2018-12-31 15:48:29 +01:00
}
std::list<ActiveEffectDefinition> EffectEngine::getActiveEffects() const
{
std::list<ActiveEffectDefinition> availableActiveEffects;
for (Effect * effect : _activeEffects)
{
ActiveEffectDefinition activeEffectDefinition;
activeEffectDefinition.script = effect->getScript();
activeEffectDefinition.name = effect->getName();
activeEffectDefinition.priority = effect->getPriority();
activeEffectDefinition.timeout = effect->getTimeout();
activeEffectDefinition.args = effect->getArgs();
availableActiveEffects.push_back(activeEffectDefinition);
}
return availableActiveEffects;
}
std::list<EffectSchema> EffectEngine::getEffectSchemas() const
2018-12-31 15:48:29 +01:00
{
return _effectFileHandler->getEffectSchemas();
}
2018-12-27 23:11:32 +01:00
void EffectEngine::cacheRunningEffects()
{
_cachedActiveEffects.clear();
for (Effect * effect : _activeEffects)
{
ActiveEffectDefinition activeEffectDefinition;
activeEffectDefinition.script = effect->getScript();
activeEffectDefinition.name = effect->getName();
activeEffectDefinition.priority = effect->getPriority();
activeEffectDefinition.timeout = effect->getTimeout();
activeEffectDefinition.args = effect->getArgs();
_cachedActiveEffects.push_back(activeEffectDefinition);
channelCleared(effect->getPriority());
}
}
void EffectEngine::startCachedEffects()
{
for (const auto & def : _cachedActiveEffects)
{
// the smooth cfg AND origin are ignored for this start!
runEffect(def.name, def.args, def.priority, def.timeout, def.script);
}
_cachedActiveEffects.clear();
}
2018-12-31 15:48:29 +01:00
void EffectEngine::handleUpdatedEffectList()
{
_availableEffects.clear();
//Add smoothing config entry to support dynamic effects done in configurator
_hyperion->updateSmoothingConfig(SmoothingConfigID::EFFECT_DYNAMIC);
unsigned specificId = SmoothingConfigID::EFFECT_SPECIFIC;
2018-12-31 15:48:29 +01:00
for (auto def : _effectFileHandler->getEffects())
{
// add smoothing configurations to Hyperion
2018-12-31 15:48:29 +01:00
if (def.args["smoothing-custom-settings"].toBool())
{
Disentangle LedDevice/LinearColorSmoothing, Bug Fixes & Test support (#654) * Handle Exceptions in main & Pythoninit * Have SSDPDiscover generic again * Have SSDPDiscover generic again * Change Info- to Debug logs as technical service messages * Nanoleaf - When switched on, ensure UDP mode * Include SQL Database in Cross-Compile instructions * Fix Clazy (QT code checker) and clang Warnings * Stop LedDevice:write for disabled device * Nanoleaf: Fix uint printfs * NanoLeaf: Fix indents to tabs * NanoLeaf - Add debug verbosity switches * Device switchability support, FileDevice with timestamp support * Nanoleaf Light Panels now support External Control V2 * Enhance LedDeviceFile by Timestamp + fix readyness * Stop color stream, if LedDevice disabled * Nanoleaf - remove switchability * Fix MultiColorAdjustment, if led-range is greater lednum * Fix logging * LedFileDevice/LedDevice - add testing support * New "Led Test" effect * LedDeviceFile - Add chrono include + Allow Led rewrites for testing * Stabilize Effects for LedDevices where latchtime = 0 * Update LedDeviceFile, allow latchtime = 0 * Distangle LinearColorSmoothing and LEDDevice, Fix Effect configuration updates * Updates LedDeviceFile - Initialize via Open * Updates LedDeviceNanoleaf - Initialize via Open, Remove throwing exceptions * Updates ProviderUDP - Remove throwing exceptions * Framebuffer - Use precise timer * TestSpi - Align to LedDevice updates * Pretty Print CrossCompileHowTo as markdown-file * Ensure that output is only written when LedDevice is ready * Align APA102 Device to new device staging * Logger - Remove clang warnings on extra semicolon * Devices SPI - Align to Device stages and methods * Fix cppcheck and clang findings * Add Code-Template for new Devices * Align devices to stages and methods, clean-up some code * Allow to reopen LedDevice without restart * Revert change "Remove Connect (PriorityMuxer::visiblePriorityChanged -> Hyperion::update) due to double writes" * Remove visiblePriorityChanged from LedDevice to decouple LedDevice from hyperion logic * Expose LedDevice getLedCount and align signedness
2020-02-10 15:21:58 +01:00
def.smoothCfg = _hyperion->updateSmoothingConfig(
++specificId,
2018-12-31 15:48:29 +01:00
def.args["smoothing-time_ms"].toInt(),
def.args["smoothing-updateFrequency"].toDouble(),
0 );
}
2017-05-29 21:34:47 +02:00
else
{
def.smoothCfg = SmoothingConfigID::SYSTEM;
}
2018-12-31 15:48:29 +01:00
_availableEffects.push_back(def);
}
2018-12-27 23:11:32 +01:00
emit effectListUpdated();
}
int EffectEngine::runEffect(const QString &effectName, int priority, int timeout, const QString &origin)
{
unsigned smoothCfg = SmoothingConfigID::SYSTEM;
for (const auto &def : _availableEffects)
{
if (def.name == effectName)
{
smoothCfg = def.smoothCfg;
break;
}
}
return runEffect(effectName, QJsonObject(), priority, timeout, "", origin, smoothCfg);
}
int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, unsigned smoothCfg, const QString &imageData)
{
//In case smoothing information is provided dynamically use temp smoothing config item (2)
if (smoothCfg == SmoothingConfigID::SYSTEM && args["smoothing-custom-settings"].toBool())
{
smoothCfg = _hyperion->updateSmoothingConfig(
SmoothingConfigID::EFFECT_DYNAMIC,
args["smoothing-time_ms"].toInt(),
args["smoothing-updateFrequency"].toDouble(),
0 );
}
if (pythonScript.isEmpty())
{
const EffectDefinition *effectDefinition = nullptr;
for (const EffectDefinition &e : _availableEffects)
{
if (e.name == effectName)
{
effectDefinition = &e;
break;
}
}
if (effectDefinition == nullptr)
{
// no such effect
Error(_log, "Effect \"%s\" not found", QSTRING_CSTR(effectName));
return -1;
}
Info( _log, "Run effect \"%s\" on channel %d", QSTRING_CSTR(effectName), priority);
return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin, effectDefinition->smoothCfg);
}
Info( _log, "Run effect \"%s\" on channel %d", QSTRING_CSTR(effectName), priority);
return runEffectScript(pythonScript, effectName, args, priority, timeout, origin, smoothCfg, imageData);
}
int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString &origin, unsigned smoothCfg, const QString &imageData)
{
// clear current effect on the channel
channelCleared(priority);
// create the effect
Effect *effect = new Effect(_hyperion, priority, timeout, script, name, args, imageData);
2018-12-27 23:11:32 +01:00
connect(effect, &Effect::setInput, _hyperion, &Hyperion::setInput, Qt::QueuedConnection);
connect(effect, &Effect::setInputImage, _hyperion, &Hyperion::setInputImage, Qt::QueuedConnection);
connect(effect, &QThread::finished, this, &EffectEngine::effectFinished);
connect(_hyperion, &Hyperion::finished, effect, &Effect::requestInterruption, Qt::DirectConnection);
_activeEffects.push_back(effect);
// start the effect
Debug(_log, "Start the effect: name [%s]", QSTRING_CSTR(name));
2018-12-27 23:11:32 +01:00
_hyperion->registerInput(priority, hyperion::COMP_EFFECT, origin, name ,smoothCfg);
effect->start();
return 0;
}
void EffectEngine::channelCleared(int priority)
{
for (Effect * effect : _activeEffects)
{
if (effect->getPriority() == priority && !effect->isInterruptionRequested())
{
effect->requestInterruption();
}
}
}
void EffectEngine::allChannelsCleared()
{
for (Effect * effect : _activeEffects)
{
if (effect->getPriority() != PriorityMuxer::BG_PRIORITY && !effect->isInterruptionRequested())
{
effect->requestInterruption();
}
}
}
void EffectEngine::effectFinished()
{
Effect* effect = qobject_cast<Effect*>(sender());
if (!effect->isInterruptionRequested())
{
// effect stopped by itself. Clear the channel
_hyperion->clear(effect->getPriority());
}
Info( _log, "Effect [%s] finished", QSTRING_CSTR(effect->getName()));
for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt)
{
if (*effectIt == effect)
{
_activeEffects.erase(effectIt);
break;
}
}
// cleanup the effect
effect->deleteLater();
}