2013-11-26 21:38:24 +01:00
|
|
|
// Python includes
|
|
|
|
#include <Python.h>
|
2017-08-01 15:42:36 +02:00
|
|
|
#undef B0
|
2013-11-26 21:38:24 +01:00
|
|
|
|
2013-11-30 12:42:08 +01:00
|
|
|
// Qt includes
|
2013-12-11 21:58:59 +01:00
|
|
|
#include <QResource>
|
2013-11-30 12:42:08 +01:00
|
|
|
#include <QMetaType>
|
2013-12-11 21:58:59 +01:00
|
|
|
#include <QFile>
|
|
|
|
#include <QDir>
|
2017-08-04 12:01:45 +02:00
|
|
|
#include <QMap>
|
2013-12-11 21:58:59 +01:00
|
|
|
|
|
|
|
// hyperion util includes
|
2016-10-09 22:22:17 +02:00
|
|
|
#include <utils/jsonschema/QJsonSchemaChecker.h>
|
2017-10-12 11:55:03 +02:00
|
|
|
#include <utils/JsonUtils.h>
|
2016-10-10 18:29:54 +02:00
|
|
|
#include <utils/Components.h>
|
2013-11-30 12:42:08 +01:00
|
|
|
|
2013-11-26 21:38:24 +01:00
|
|
|
// effect engine includes
|
2013-11-24 16:10:48 +01:00
|
|
|
#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>
|
2016-06-12 22:27:24 +02:00
|
|
|
#include "HyperionConfig.h"
|
2013-11-24 16:10:48 +01:00
|
|
|
|
2018-12-31 15:48:29 +01:00
|
|
|
EffectEngine::EffectEngine(Hyperion * hyperion)
|
2016-08-08 00:17:00 +02:00
|
|
|
: _hyperion(hyperion)
|
|
|
|
, _availableEffects()
|
|
|
|
, _activeEffects()
|
|
|
|
, _log(Logger::getInstance("EFFECTENGINE"))
|
2018-12-31 15:48:29 +01:00
|
|
|
, _effectFileHandler(EffectFileHandler::getInstance())
|
2013-11-24 16:10:48 +01:00
|
|
|
{
|
2017-10-13 17:49:29 +02:00
|
|
|
|
2016-09-13 11:51:16 +02:00
|
|
|
Q_INIT_RESOURCE(EffectEngine);
|
2016-10-10 18:29:54 +02:00
|
|
|
qRegisterMetaType<hyperion::Components>("hyperion::Components");
|
2013-11-29 23:22:49 +01:00
|
|
|
|
2013-11-24 16:10:48 +01:00
|
|
|
// connect the Hyperion channel clear feedback
|
|
|
|
connect(_hyperion, SIGNAL(channelCleared(int)), this, SLOT(channelCleared(int)));
|
|
|
|
connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(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();
|
2013-11-24 16:10:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
EffectEngine::~EffectEngine()
|
|
|
|
{
|
2020-06-28 23:46:36 +02:00
|
|
|
for (Effect * effect : _activeEffects)
|
|
|
|
{
|
|
|
|
effect->wait();
|
|
|
|
delete effect;
|
|
|
|
}
|
2013-11-24 16:10:48 +01:00
|
|
|
}
|
|
|
|
|
2020-03-26 17:59:41 +01:00
|
|
|
QString EffectEngine::saveEffect(const QJsonObject& obj)
|
2018-12-31 15:48:29 +01:00
|
|
|
{
|
2020-03-26 17:59:41 +01:00
|
|
|
return _effectFileHandler->saveEffect(obj);
|
2018-12-31 15:48:29 +01:00
|
|
|
}
|
|
|
|
|
2020-03-26 17:59:41 +01:00
|
|
|
QString EffectEngine::deleteEffect(const QString& effectName)
|
2018-12-31 15:48:29 +01:00
|
|
|
{
|
2020-03-26 17:59:41 +01:00
|
|
|
return _effectFileHandler->deleteEffect(effectName);
|
2018-12-31 15:48:29 +01:00
|
|
|
}
|
|
|
|
|
2016-04-24 17:07:31 +02:00
|
|
|
const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
|
|
|
{
|
|
|
|
_availableActiveEffects.clear();
|
2017-09-04 23:12:59 +02:00
|
|
|
|
2016-04-24 17:07:31 +02:00
|
|
|
for (Effect * effect : _activeEffects)
|
|
|
|
{
|
|
|
|
ActiveEffectDefinition activeEffectDefinition;
|
2017-08-04 12:01:45 +02:00
|
|
|
activeEffectDefinition.script = effect->getScript();
|
|
|
|
activeEffectDefinition.name = effect->getName();
|
2016-04-24 17:07:31 +02:00
|
|
|
activeEffectDefinition.priority = effect->getPriority();
|
2017-08-04 12:01:45 +02:00
|
|
|
activeEffectDefinition.timeout = effect->getTimeout();
|
|
|
|
activeEffectDefinition.args = effect->getArgs();
|
2016-04-24 17:07:31 +02:00
|
|
|
_availableActiveEffects.push_back(activeEffectDefinition);
|
|
|
|
}
|
2017-09-04 23:12:59 +02:00
|
|
|
|
2016-04-24 17:07:31 +02:00
|
|
|
return _availableActiveEffects;
|
|
|
|
}
|
|
|
|
|
2018-12-31 15:48:29 +01:00
|
|
|
const std::list<EffectSchema> & EffectEngine::getEffectSchemas()
|
|
|
|
{
|
|
|
|
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()
|
2013-12-01 14:09:01 +01:00
|
|
|
{
|
2016-11-20 18:41:10 +01:00
|
|
|
_availableEffects.clear();
|
|
|
|
|
2020-02-10 15:21:58 +01:00
|
|
|
unsigned id = 2;
|
2018-12-31 15:48:29 +01:00
|
|
|
for (auto def : _effectFileHandler->getEffects())
|
2016-11-20 18:41:10 +01:00
|
|
|
{
|
2018-12-31 15:48:29 +01:00
|
|
|
// add smoothing configs to Hyperion
|
|
|
|
if (def.args["smoothing-custom-settings"].toBool())
|
2017-05-29 15:59:11 +02:00
|
|
|
{
|
2020-02-10 15:21:58 +01:00
|
|
|
def.smoothCfg = _hyperion->updateSmoothingConfig(
|
|
|
|
id,
|
2018-12-31 15:48:29 +01:00
|
|
|
def.args["smoothing-time_ms"].toInt(),
|
|
|
|
def.args["smoothing-updateFrequency"].toDouble(),
|
|
|
|
0 );
|
2020-02-10 15:21:58 +01:00
|
|
|
//Debug( _log, "Customs Settings: Update effect %s, script %s, file %s, smoothCfg [%u]", QSTRING_CSTR(def.name), QSTRING_CSTR(def.script), QSTRING_CSTR(def.file), def.smoothCfg);
|
2017-05-29 15:59:11 +02:00
|
|
|
}
|
2017-05-29 21:34:47 +02:00
|
|
|
else
|
2016-11-20 18:41:10 +01:00
|
|
|
{
|
2020-02-10 15:21:58 +01:00
|
|
|
def.smoothCfg = _hyperion->updateSmoothingConfig(id);
|
|
|
|
//Debug( _log, "Default Settings: Update effect %s, script %s, file %s, smoothCfg [%u]", QSTRING_CSTR(def.name), QSTRING_CSTR(def.script), QSTRING_CSTR(def.file), def.smoothCfg);
|
2016-11-20 18:41:10 +01:00
|
|
|
}
|
2018-12-31 15:48:29 +01:00
|
|
|
_availableEffects.push_back(def);
|
2016-11-20 18:41:10 +01:00
|
|
|
}
|
2018-12-27 23:11:32 +01:00
|
|
|
emit effectListUpdated();
|
2013-12-01 14:09:01 +01:00
|
|
|
}
|
|
|
|
|
2017-03-01 15:23:53 +01:00
|
|
|
int EffectEngine::runEffect(const QString &effectName, int priority, int timeout, const QString &origin)
|
|
|
|
{
|
|
|
|
return runEffect(effectName, QJsonObject(), priority, timeout, "", origin);
|
|
|
|
}
|
|
|
|
|
2019-01-06 19:49:56 +01:00
|
|
|
int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, unsigned smoothCfg, const QString &imageData)
|
2013-11-24 16:10:48 +01:00
|
|
|
{
|
2020-02-10 15:21:58 +01:00
|
|
|
Info( _log, "Run effect \"%s\" on channel %d", QSTRING_CSTR(effectName), priority);
|
2013-11-26 21:38:24 +01:00
|
|
|
|
2017-03-01 15:23:53 +01:00
|
|
|
if (pythonScript.isEmpty())
|
2013-12-01 14:09:01 +01:00
|
|
|
{
|
2019-01-06 19:49:56 +01:00
|
|
|
const EffectDefinition *effectDefinition = nullptr;
|
|
|
|
for (const EffectDefinition &e : _availableEffects)
|
2013-12-01 14:09:01 +01:00
|
|
|
{
|
2016-10-30 22:59:45 +01:00
|
|
|
if (e.name == effectName)
|
|
|
|
{
|
|
|
|
effectDefinition = &e;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (effectDefinition == nullptr)
|
|
|
|
{
|
|
|
|
// no such effect
|
2018-12-27 23:11:32 +01:00
|
|
|
Error(_log, "Effect %s not found", QSTRING_CSTR(effectName));
|
2016-10-30 22:59:45 +01:00
|
|
|
return -1;
|
2013-12-01 14:09:01 +01:00
|
|
|
}
|
2013-11-30 00:28:04 +01:00
|
|
|
|
2017-08-04 12:01:45 +02:00
|
|
|
return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin, effectDefinition->smoothCfg);
|
2017-03-01 15:23:53 +01:00
|
|
|
}
|
2019-01-06 19:49:56 +01:00
|
|
|
return runEffectScript(pythonScript, effectName, args, priority, timeout, origin, smoothCfg, imageData);
|
2013-12-01 16:35:45 +01:00
|
|
|
}
|
|
|
|
|
2019-01-06 19:49:56 +01:00
|
|
|
int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString &origin, unsigned smoothCfg, const QString &imageData)
|
2013-12-01 16:35:45 +01:00
|
|
|
{
|
2013-11-26 21:38:24 +01:00
|
|
|
// clear current effect on the channel
|
|
|
|
channelCleared(priority);
|
|
|
|
|
|
|
|
// create the effect
|
2019-08-02 21:12:13 +02:00
|
|
|
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);
|
2017-10-12 11:55:03 +02:00
|
|
|
connect(effect, &QThread::finished, this, &EffectEngine::effectFinished);
|
2019-08-03 15:11:28 +02:00
|
|
|
connect(_hyperion, &Hyperion::finished, effect, &Effect::requestInterruption, Qt::DirectConnection);
|
2013-11-26 21:38:24 +01:00
|
|
|
_activeEffects.push_back(effect);
|
|
|
|
|
|
|
|
// start the effect
|
2020-02-10 15:21:58 +01:00
|
|
|
Debug(_log, "Start the effect: name [%s], smoothCfg [%u]", QSTRING_CSTR(name), smoothCfg);
|
2018-12-27 23:11:32 +01:00
|
|
|
_hyperion->registerInput(priority, hyperion::COMP_EFFECT, origin, name ,smoothCfg);
|
2013-11-26 21:38:24 +01:00
|
|
|
effect->start();
|
|
|
|
|
2013-11-24 16:10:48 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EffectEngine::channelCleared(int priority)
|
|
|
|
{
|
2013-11-26 21:38:24 +01:00
|
|
|
for (Effect * effect : _activeEffects)
|
|
|
|
{
|
2019-08-03 15:11:28 +02:00
|
|
|
if (effect->getPriority() == priority && !effect->isInterruptionRequested())
|
2013-11-26 21:38:24 +01:00
|
|
|
{
|
2019-08-03 15:11:28 +02:00
|
|
|
effect->requestInterruption();
|
2013-11-26 21:38:24 +01:00
|
|
|
}
|
|
|
|
}
|
2013-11-24 16:10:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void EffectEngine::allChannelsCleared()
|
|
|
|
{
|
2013-11-26 21:38:24 +01:00
|
|
|
for (Effect * effect : _activeEffects)
|
|
|
|
{
|
2019-08-03 15:11:28 +02:00
|
|
|
if (effect->getPriority() != 254 && !effect->isInterruptionRequested())
|
2017-10-12 11:55:03 +02:00
|
|
|
{
|
2019-08-03 15:11:28 +02:00
|
|
|
effect->requestInterruption();
|
2017-10-12 11:55:03 +02:00
|
|
|
}
|
2013-11-26 21:38:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-12 11:55:03 +02:00
|
|
|
void EffectEngine::effectFinished()
|
2013-11-26 21:38:24 +01:00
|
|
|
{
|
2017-10-12 11:55:03 +02:00
|
|
|
Effect* effect = qobject_cast<Effect*>(sender());
|
2019-08-03 15:11:28 +02:00
|
|
|
if (!effect->isInterruptionRequested())
|
2013-11-30 15:54:47 +01:00
|
|
|
{
|
|
|
|
// effect stopped by itself. Clear the channel
|
|
|
|
_hyperion->clear(effect->getPriority());
|
|
|
|
}
|
|
|
|
|
2016-06-23 13:48:49 +02:00
|
|
|
Info( _log, "effect finished");
|
2013-11-26 21:38:24 +01:00
|
|
|
for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt)
|
|
|
|
{
|
|
|
|
if (*effectIt == effect)
|
|
|
|
{
|
|
|
|
_activeEffects.erase(effectIt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanup the effect
|
|
|
|
effect->deleteLater();
|
2013-11-24 16:10:48 +01:00
|
|
|
}
|