effects included in hyperiond binary as qtResource (#237)

* implement effects included in hyperiond  binary

* cleanup

* remove install of effects dir. People who wants to develop effects has to copy them from github
effect params for initial effects can be changed in config permanently and other effect params can be changed
via json (currently only temporarily)

* fix schema of fadecandy
webui fix display of specific led options

* add leddevice write support

* cleanup

* webui: tune hue code

* when use json effect definition from putsiede hyperiond but want to use py script from inside hyperiond use ad a :
e.g. fade.py needs a fade.py near the json file, but :fade.py is taken from resource inside hyperiond

* add ability to di

* add abiloty to diable effcts via hyperion config

* use effect name instead of script in active effects and prio register

* finally solve open file handle during effect is playing. Now script is read before, then file closed and then t is run by python

* fix some webui things
- led config tabs
- inital loading screen

optimize qrc file generation
fix compile warning in hyperion.cpp

* cleanup

* more cleanup
This commit is contained in:
redPanther
2016-09-13 11:51:16 +02:00
committed by GitHub
parent d097995a74
commit adfe2a4b23
54 changed files with 205 additions and 8001 deletions

View File

@@ -26,7 +26,16 @@ SET(EffectEngineSOURCES
${CURRENT_SOURCE_DIR}/Effect.cpp
)
set(EffectEngine_RESOURCES ${CURRENT_SOURCE_DIR}/EffectEngine.qrc)
FILE ( GLOB effectFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/effects/* )
SET ( HYPERION_EFFECTS_RES "")
FOREACH( f ${effectFiles} )
GET_FILENAME_COMPONENT(fname ${f} NAME)
SET(HYPERION_EFFECTS_RES "${HYPERION_EFFECTS_RES}\n\t\t<file alias=\"/effects/${fname}\">${f}</file>")
ENDFOREACH()
CONFIGURE_FILE(${CURRENT_SOURCE_DIR}/EffectEngine.qrc.in ${CMAKE_BINARY_DIR}/EffectEngine.qrc )
SET(EffectEngine_RESOURCES ${CMAKE_BINARY_DIR}/EffectEngine.qrc)
QT5_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS})
qt5_add_resources(EffectEngine_RESOURCES_RCC ${EffectEngine_RESOURCES} OPTIONS "-no-compress")

View File

@@ -7,6 +7,7 @@
// Qt includes
#include <QDateTime>
#include <QFile>
// effect engin eincludes
#include "Effect.h"
@@ -50,12 +51,13 @@ void Effect::registerHyperionExtensionModule()
PyImport_AppendInittab("hyperion", &PyInit_hyperion);
}
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args)
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const QString & script, const QString & name, const Json::Value & args)
: QThread()
, _mainThreadState(mainThreadState)
, _priority(priority)
, _timeout(timeout)
, _script(script)
, _name(name)
, _args(args)
, _endTime(-1)
, _interpreterThreadState(nullptr)
@@ -70,6 +72,7 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const
// connect the finished signal
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
Q_INIT_RESOURCE(EffectEngine);
}
Effect::~Effect()
@@ -106,16 +109,22 @@ void Effect::run()
}
// Run the effect script
FILE* file = fopen(_script.c_str(), "r");
if (file != nullptr)
QFile file (_script);
QByteArray python_code;
if (file.open(QIODevice::ReadOnly))
{
PyRun_SimpleFile(file, _script.c_str());
python_code = file.readAll();
}
else
{
Error(Logger::getInstance("EFFECTENGINE"), "Unable to open script file %s", _script.c_str());
Error(Logger::getInstance("EFFECTENGINE"), "Unable to open script file %s", _script.toUtf8().constData());
}
file.close();
if (!python_code.isEmpty())
{
PyRun_SimpleString(python_code.constData());
}
fclose(file);
// Clean up the thread state
Py_EndInterpreter(_interpreterThreadState);

View File

@@ -14,14 +14,15 @@ class Effect : public QThread
Q_OBJECT
public:
Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
Effect(PyThreadState * mainThreadState, int priority, int timeout, const QString & script, const QString & name, const Json::Value & args = Json::Value());
virtual ~Effect();
virtual void run();
int getPriority() const;
std::string getScript() const { return _script; }
QString getScript() const { return _script; }
QString getName() const { return _name; }
int getTimeout() const {return _timeout; }
@@ -67,7 +68,8 @@ private:
const int _timeout;
const std::string _script;
const QString _script;
const QString _name;
const Json::Value _args;

View File

@@ -26,6 +26,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
, _mainThreadState(nullptr)
, _log(Logger::getInstance("EFFECTENGINE"))
{
Q_INIT_RESOURCE(EffectEngine);
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
// connect the Hyperion channel clear feedback
@@ -33,11 +34,26 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared()));
// read all effects
const Json::Value & paths = jsonEffectConfig["paths"];
const Json::Value & paths = jsonEffectConfig["paths"];
const Json::Value & disabledEfx = jsonEffectConfig["disable"];
QStringList efxPathList;
efxPathList << ":/effects/";
for (Json::UInt i = 0; i < paths.size(); ++i)
{
const std::string & path = paths[i].asString();
QDir directory(QString::fromStdString(path));
efxPathList << QString::fromStdString(paths[i].asString());
}
QStringList disableList;
for (Json::UInt i = 0; i < disabledEfx.size(); ++i)
{
disableList << QString::fromStdString(disabledEfx[i].asString());
}
std::map<std::string, EffectDefinition> availableEffects;
foreach (const QString & path, efxPathList )
{
QDir directory(path);
if (directory.exists())
{
int efxCount = 0;
@@ -45,16 +61,33 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
foreach (const QString & filename, filenames)
{
EffectDefinition def;
if (loadEffectDefinition(path, filename.toStdString(), def))
if (loadEffectDefinition(path, filename, def))
{
_availableEffects.push_back(def);
efxCount++;
if (availableEffects.find(def.name) != availableEffects.end())
{
Info(_log, "effect overload effect '%s' is now taken from %s'", def.name.c_str(), path.toUtf8().constData() );
}
if ( disableList.contains(QString::fromStdString(def.name)) )
{
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", def.name.c_str());
}
else
{
availableEffects[def.name] = def;
efxCount++;
}
}
}
Info(_log, "%d effects loaded from directory %s", efxCount, path.c_str());
Info(_log, "%d effects loaded from directory %s", efxCount, path.toUtf8().constData());
}
}
foreach(auto item, availableEffects)
{
_availableEffects.push_back(item.second);
}
if (_availableEffects.size() == 0)
{
Error(_log, "no effects found, check your effect directories");
@@ -88,7 +121,8 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
for (Effect * effect : _activeEffects)
{
ActiveEffectDefinition activeEffectDefinition;
activeEffectDefinition.script = effect->getScript();
activeEffectDefinition.script = effect->getScript().toStdString();
activeEffectDefinition.name = effect->getName().toStdString();
activeEffectDefinition.priority = effect->getPriority();
activeEffectDefinition.timeout = effect->getTimeout();
activeEffectDefinition.args = effect->getArgs();
@@ -98,26 +132,29 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
return _availableActiveEffects;
}
bool EffectEngine::loadEffectDefinition(const std::string &path, const std::string &effectConfigFile, EffectDefinition & effectDefinition)
bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effectConfigFile, EffectDefinition & effectDefinition)
{
std::string fileName = path + QDir::separator().toLatin1() + effectConfigFile;
std::ifstream file(fileName.c_str());
QString fileName = path + QDir::separator() + effectConfigFile;
QFile file(fileName);
Logger * log = Logger::getInstance("EFFECTENGINE");
if (!file.is_open())
if (!file.open(QIODevice::ReadOnly))
{
Error( log, "Effect file '%s' could not be loaded", fileName.c_str());
Error( log, "Effect file '%s' could not be loaded", fileName.toUtf8().constData());
return false;
}
QByteArray fileContent = file.readAll();
// Read the json config file
Json::Reader jsonReader;
Json::Value config;
if (!jsonReader.parse(file, config, false))
const char* fileContent_cStr = reinterpret_cast<const char *>(fileContent.constData());
if (! Json::Reader().parse(fileContent_cStr, fileContent_cStr+fileContent.size(), config, false) )
{
Error( log, "Error while reading effect '%s': %s", fileName.c_str(), jsonReader.getFormattedErrorMessages().c_str());
Error( log, "Error while reading effect '%s': %s", fileName.toUtf8().constData(), jsonReader.getFormattedErrorMessages().c_str());
return false;
}
file.close();
// Read the json schema file
QResource schemaData(":effect-schema");
@@ -129,14 +166,22 @@ bool EffectEngine::loadEffectDefinition(const std::string &path, const std::stri
{
const std::list<std::string> & errors = schemaChecker.getMessages();
foreach (const std::string & error, errors) {
Error( log, "Error while checking '%s':%s", fileName.c_str(), error.c_str());
Error( log, "Error while checking '%s':%s", fileName.toUtf8().constData(), error.c_str());
}
return false;
}
// setup the definition
std::string scriptName = config["script"].asString();
effectDefinition.name = config["name"].asString();
effectDefinition.script = path + QDir::separator().toLatin1() + config["script"].asString();
if (scriptName.empty())
return false;
if (scriptName[0] == ':' )
effectDefinition.script = ":/effects/"+scriptName.substr(1);
else
effectDefinition.script = path.toStdString() + QDir::separator().toLatin1() + scriptName;
effectDefinition.args = config["args"];
return true;
@@ -167,22 +212,22 @@ int EffectEngine::runEffect(const std::string &effectName, const Json::Value &ar
return -1;
}
return runEffectScript(effectDefinition->script, args.isNull() ? effectDefinition->args : args, priority, timeout);
return runEffectScript(effectDefinition->script, effectName, args.isNull() ? effectDefinition->args : args, priority, timeout);
}
int EffectEngine::runEffectScript(const std::string &script, const Json::Value &args, int priority, int timeout)
int EffectEngine::runEffectScript(const std::string &script, const std::string &name, const Json::Value &args, int priority, int timeout)
{
// clear current effect on the channel
channelCleared(priority);
// create the effect
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, args);
Effect * effect = new Effect(_mainThreadState, priority, timeout, QString::fromStdString(script), QString::fromStdString(name), args);
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool)), Qt::QueuedConnection);
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
_activeEffects.push_back(effect);
// start the effect
_hyperion->registerPriority("EFFECT: "+FileUtils::getBaseName(script), priority);
_hyperion->registerPriority("EFFECT: "+name, priority);
effect->start();
return 0;
@@ -227,5 +272,5 @@ void EffectEngine::effectFinished(Effect *effect)
// cleanup the effect
effect->deleteLater();
_hyperion->unRegisterPriority("EFFECT: " + FileUtils::getBaseName(effect->getScript()));
_hyperion->unRegisterPriority("EFFECT: " + effect->getName().toStdString());
}

View File

@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/">
<file alias="effect-schema">EffectDefinition.schema.json</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file alias="effect-schema">${CURRENT_SOURCE_DIR}/EffectDefinition.schema.json</file>
${HYPERION_EFFECTS_RES}
</qresource>
</RCC>