diff --git a/include/effectengine/EffectDefinition.h b/include/effectengine/EffectDefinition.h new file mode 100644 index 00000000..d8c1ce27 --- /dev/null +++ b/include/effectengine/EffectDefinition.h @@ -0,0 +1,14 @@ +#pragma once + +// stl include +#include + +// json include +#include + +struct EffectDefinition +{ + std::string name; + std::string script; + Json::Value args; +}; diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 3e08f491..87474366 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -1,11 +1,17 @@ #pragma once +// Qt includes +#include + // Json includes #include // Hyperion includes #include +// Effect engine includes +#include + // pre-declarioation class Effect; typedef struct _ts PyThreadState; @@ -18,32 +24,28 @@ public: EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectConfig); virtual ~EffectEngine(); - std::list getEffects() const; + const std::list & getEffects() const; public slots: /// Run the specified effect on the given priority channel and optionally specify a timeout int runEffect(const std::string &effectName, int priority, int timeout = -1); + /// Run the specified effect on the given priority channel and optionally specify a timeout + int runEffect(const std::string &effectName, const Json::Value & args, int priority, int timeout = -1); + /// Clear any effect running on the provided channel void channelCleared(int priority); /// Clear all effects void allChannelsCleared(); -public: - struct EffectDefinition - { - std::string script; - Json::Value args; - }; - private slots: void effectFinished(Effect * effect); private: Hyperion * _hyperion; - std::map _availableEffects; + std::list _availableEffects; std::list _activeEffects; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 46b2245f..073a5458 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -15,6 +15,9 @@ #include #include +// Effect engine includes +#include + // Forward class declaration class HsvTransform; class ColorTransform; @@ -102,7 +105,7 @@ public: /// Get the list of available effects /// @return The list of available effects - std::list getEffects() const; + const std::list &getEffects() const; public slots: /// @@ -153,6 +156,12 @@ public slots: /// @param timout The timeout of the effect (after the timout, the effect will be cleared) int setEffect(const std::string & effectName, int priority, int timeout = -1); + /// Run the specified effect on the given priority channel and optionally specify a timeout + /// @param effectName Name of the effec to run + /// @param priority The priority channel of the effect + /// @param timout The timeout of the effect (after the timout, the effect will be cleared) + int setEffect(const std::string & effectName, const Json::Value & args, int priority, int timeout = -1); + public: static LedDevice * createDevice(const Json::Value & deviceConfig); static ColorOrder createColorOrder(const Json::Value & deviceConfig); diff --git a/libsrc/effectengine/CMakeLists.txt b/libsrc/effectengine/CMakeLists.txt index 5e50dee3..92afce2e 100644 --- a/libsrc/effectengine/CMakeLists.txt +++ b/libsrc/effectengine/CMakeLists.txt @@ -14,6 +14,7 @@ SET(EffectEngineQT_HEADERS ) SET(EffectEngineHEADERS + ${CURRENT_HEADER_DIR}/EffectDefinition.h ) SET(EffectEngineSOURCES diff --git a/libsrc/effectengine/Effect.h b/libsrc/effectengine/Effect.h index f162c860..765872b2 100644 --- a/libsrc/effectengine/Effect.h +++ b/libsrc/effectengine/Effect.h @@ -49,9 +49,9 @@ private: const int _timeout; - const std::string & _script; + const std::string _script; - const Json::Value & _args; + const Json::Value _args; int64_t _endTime; diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index 878edd62..a6e2a8a1 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -25,7 +25,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo for (const std::string & name : effectNames) { const Json::Value & info = jsonEffectConfig[name]; - _availableEffects[name] = {info["script"].asString(), info["args"]}; + _availableEffects.push_back({name, info["script"].asString(), info["args"]}); } // initialize the python interpreter @@ -43,22 +43,33 @@ EffectEngine::~EffectEngine() Py_Finalize(); } -std::list EffectEngine::getEffects() const +const std::list &EffectEngine::getEffects() const { - std::list effectNames; - foreach (auto entry, _availableEffects) { - effectNames.push_back(entry.first); - } - return effectNames; + return _availableEffects; } int EffectEngine::runEffect(const std::string &effectName, int priority, int timeout) +{ + return runEffect(effectName, Json::Value(Json::nullValue), priority, timeout); +} + +int EffectEngine::runEffect(const std::string &effectName, const Json::Value &args, int priority, int timeout) { std::cout << "run effect " << effectName << " on channel " << priority << std::endl; - if (_availableEffects.find(effectName) == _availableEffects.end()) + const EffectDefinition * effectDefinition = nullptr; + for (const EffectDefinition & e : _availableEffects) + { + if (e.name == effectName) + { + effectDefinition = &e; + break; + } + } + if (effectDefinition == nullptr) { // no such effect + std::cerr << "effect " << effectName << " not found" << std::endl; return -1; } @@ -66,8 +77,7 @@ int EffectEngine::runEffect(const std::string &effectName, int priority, int tim channelCleared(priority); // create the effect - const EffectDefinition & effectDefinition = _availableEffects[effectName]; - Effect * effect = new Effect(priority, timeout, effectDefinition.script, effectDefinition.args); + Effect * effect = new Effect(priority, timeout, effectDefinition->script, args.isNull() ? effectDefinition->args : args); connect(effect, SIGNAL(setColors(int,std::vector,int)), _hyperion, SLOT(setColors(int,std::vector,int)), Qt::QueuedConnection); connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*))); _activeEffects.push_back(effect); diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 2fd32e56..04cf756e 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -466,7 +466,7 @@ const Hyperion::InputInfo &Hyperion::getPriorityInfo(const int priority) const return _muxer.getInputInfo(priority); } -std::list Hyperion::getEffects() const +const std::list & Hyperion::getEffects() const { return _effectEngine->getEffects(); } @@ -476,6 +476,11 @@ int Hyperion::setEffect(const std::string &effectName, int priority, int timeout return _effectEngine->runEffect(effectName, priority, timeout); } +int Hyperion::setEffect(const std::string &effectName, const Json::Value &args, int priority, int timeout) +{ + return _effectEngine->runEffect(effectName, args, priority, timeout); +} + void Hyperion::update() { // Update the muxer, cleaning obsolete priorities diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index ae6e5527..f361041e 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -162,7 +162,14 @@ void JsonClientConnection::handleEffectCommand(const Json::Value &message) const std::string & effectName = effect["name"].asString(); // set output - _hyperion->setEffect(effectName, priority, duration); + if (effect.isMember("args")) + { + _hyperion->setEffect(effectName, effect["args"], priority, duration); + } + else + { + _hyperion->setEffect(effectName, priority, duration); + } // send reply sendSuccessReply(); @@ -212,11 +219,13 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &) // collect effect info Json::Value & effects = info["effects"] = Json::Value(Json::arrayValue); - std::list effectNames = _hyperion->getEffects(); - for (const std::string & name : effectNames) + const std::list & effectsDefinitions = _hyperion->getEffects(); + for (const EffectDefinition & effectDefinition : effectsDefinitions) { Json::Value effect; - effect["name"] = name; + effect["name"] = effectDefinition.name; + effect["script"] = effectDefinition.script; + effect["args"] = effectDefinition.args; effects.append(effect); } diff --git a/libsrc/jsonserver/schema/schema-effect.json b/libsrc/jsonserver/schema/schema-effect.json index e1b561cd..3a690d7d 100644 --- a/libsrc/jsonserver/schema/schema-effect.json +++ b/libsrc/jsonserver/schema/schema-effect.json @@ -22,6 +22,10 @@ "name" : { "type" : "string", "required" : true + }, + "args" : { + "type" : "object", + "required" : false } }, "additionalProperties": false diff --git a/src/hyperion-remote/JsonConnection.cpp b/src/hyperion-remote/JsonConnection.cpp index 4d96590b..4aae2b52 100644 --- a/src/hyperion-remote/JsonConnection.cpp +++ b/src/hyperion-remote/JsonConnection.cpp @@ -102,7 +102,7 @@ void JsonConnection::setImage(QImage image, int priority, int duration) parseReply(reply); } -void JsonConnection::setEffect(const std::string &effectName, int priority, int duration) +void JsonConnection::setEffect(const std::string &effectName, const std::string & effectArgs, int priority, int duration) { std::cout << "Start effect " << effectName << std::endl; @@ -112,6 +112,14 @@ void JsonConnection::setEffect(const std::string &effectName, int priority, int command["priority"] = priority; Json::Value & effect = command["effect"]; effect["name"] = effectName; + if (effectArgs.size() > 0) + { + Json::Reader reader; + if (!reader.parse(effectArgs, effect["args"], false)) + { + throw std::runtime_error("Error in effect arguments: " + reader.getFormattedErrorMessages()); + } + } if (duration > 0) { command["duration"] = duration; diff --git a/src/hyperion-remote/JsonConnection.h b/src/hyperion-remote/JsonConnection.h index ebc0292a..d7be9912 100644 --- a/src/hyperion-remote/JsonConnection.h +++ b/src/hyperion-remote/JsonConnection.h @@ -56,10 +56,11 @@ public: /// Start the given effect /// /// @param effect The name of the effect + /// @param effectArgs The arguments to use instead of the default ones /// @param priority The priority /// @param duration The duration in milliseconds /// - void setEffect(const std::string & effectName, int priority, int duration); + void setEffect(const std::string & effectName, const std::string &effectArgs, int priority, int duration); /// /// Retrieve a list of all occupied priority channels diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 8d9dcedb..d29ba7a2 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -43,6 +43,7 @@ int main(int argc, char * argv[]) ColorParameter & argColor = parameters.add ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name)"); ImageParameter & argImage = parameters.add ('i', "image" , "Set the leds to the colors according to the given image file"); StringParameter & argEffect = parameters.add ('e', "effect" , "Enable the effect with the given name"); + StringParameter & argEffectArgs = parameters.add (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string."); SwitchParameter<> & argServerInfo = parameters.add >('l', "list" , "List server info"); SwitchParameter<> & argClear = parameters.add >('x', "clear" , "Clear data for the priority channel provided by the -p option"); SwitchParameter<> & argClearAll = parameters.add >(0x0, "clearall" , "Clear data for all active priority channels"); @@ -59,6 +60,7 @@ int main(int argc, char * argv[]) argAddress.setDefault(defaultServerAddress.toStdString()); argPriority.setDefault(defaultPriority); argDuration.setDefault(-1); + argEffectArgs.setDefault(""); // parse all options optionParser.parse(argc, const_cast(argv)); @@ -108,7 +110,7 @@ int main(int argc, char * argv[]) } else if (argEffect.isSet()) { - connection.setEffect(argEffect.getValue(), argPriority.getValue(), argDuration.getValue()); + connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue()); } else if (argServerInfo.isSet()) {