From 5454ddb375d8d55cd7eee37339a101576aff2ae6 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 24 Nov 2013 16:10:48 +0100 Subject: [PATCH] Added skeleton for effect engine development Former-commit-id: e1fb69fd4de4b8968075660e3ba7f7add021c152 --- include/effectengine/EffectEngine.h | 29 ++++++++++++++ include/hyperion/Hyperion.h | 24 ++++++++++++ libsrc/CMakeLists.txt | 1 + libsrc/effectengine/CMakeLists.txt | 31 +++++++++++++++ libsrc/effectengine/Effect.cpp | 10 +++++ libsrc/effectengine/Effect.h | 12 ++++++ libsrc/effectengine/EffectEngine.cpp | 42 +++++++++++++++++++++ libsrc/hyperion/CMakeLists.txt | 1 + libsrc/hyperion/Hyperion.cpp | 26 +++++++++++++ libsrc/jsonserver/JsonClientConnection.cpp | 30 ++++++++++++++- libsrc/jsonserver/JsonClientConnection.h | 7 ++++ libsrc/jsonserver/JsonSchemas.qrc | 19 +++++----- libsrc/jsonserver/schema/schema-effect.json | 31 +++++++++++++++ libsrc/jsonserver/schema/schema.json | 2 +- src/hyperion-remote/JsonConnection.cpp | 21 +++++++++++ src/hyperion-remote/JsonConnection.h | 9 +++++ src/hyperion-remote/hyperion-remote.cpp | 8 +++- src/hyperiond/CMakeLists.txt | 1 + src/hyperiond/hyperiond.cpp | 12 ++++++ 19 files changed, 304 insertions(+), 12 deletions(-) create mode 100644 include/effectengine/EffectEngine.h create mode 100644 libsrc/effectengine/CMakeLists.txt create mode 100644 libsrc/effectengine/Effect.cpp create mode 100644 libsrc/effectengine/Effect.h create mode 100644 libsrc/effectengine/EffectEngine.cpp create mode 100644 libsrc/jsonserver/schema/schema-effect.json diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h new file mode 100644 index 00000000..6b81049a --- /dev/null +++ b/include/effectengine/EffectEngine.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class EffectEngine : public QObject +{ + Q_OBJECT + +public: + EffectEngine(Hyperion * hyperion); + virtual ~EffectEngine(); + + 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); + + /// Clear any effect running on the provided channel + void channelCleared(int priority); + + /// Clear all effects + void allChannelsCleared(); + +private: + Hyperion * _hyperion; + + std::map _availableEffects; +}; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 5026ef08..f03c9db6 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -18,6 +18,7 @@ // Forward class declaration class HsvTransform; class ColorTransform; +class EffectEngine; /// /// The main class of Hyperion. This gives other 'users' access to the attached LedDevice through @@ -141,6 +142,17 @@ public: /// const InputInfo& getPriorityInfo(const int priority) const; + /// Get the list of available effects + /// @return The list of available effects + std::list getEffects() const; + + /// 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, int priority, int timeout = -1); + +public: static LedDevice * createDevice(const Json::Value & deviceConfig); static ColorOrder createColorOrder(const Json::Value & deviceConfig); static LedString createLedString(const Json::Value & ledsConfig); @@ -148,6 +160,15 @@ public: static ColorTransform * createColorTransform(const Json::Value & colorConfig); static LedDevice * createColorSmoothing(const Json::Value & smoothingConfig, LedDevice * ledDevice); +signals: + /// Signal which is emitted when a priority channel is actively cleared + /// This signal will not be emitted when a priority channel time out + void channelCleared(int priority); + + /// Signal which is emitted when all priority channels are actively cleared + /// This signal will not be emitted when a priority channel time out + void allChannelsCleared(); + private slots: /// /// Updates the priority muxer with the current time and (re)writes the led color with applied @@ -185,6 +206,9 @@ private: /// The actual LedDevice LedDevice * _device; + /// Effect engine + EffectEngine * _effectEngine; + /// The timer for handling priority channel timeouts QTimer _timer; }; diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index d97e6368..4e2470d3 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(protoserver) add_subdirectory(boblightserver) add_subdirectory(utils) add_subdirectory(xbmcvideochecker) +add_subdirectory(effectengine) if (ENABLE_DISPMANX) add_subdirectory(dispmanx-grabber) diff --git a/libsrc/effectengine/CMakeLists.txt b/libsrc/effectengine/CMakeLists.txt new file mode 100644 index 00000000..e234f3ba --- /dev/null +++ b/libsrc/effectengine/CMakeLists.txt @@ -0,0 +1,31 @@ + +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/effectengine) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/effectengine) + +# Group the headers that go through the MOC compiler +SET(EffectEngineQT_HEADERS + ${CURRENT_HEADER_DIR}/EffectEngine.h + ${CURRENT_SOURCE_DIR}/Effect.h +) + +SET(EffectEngineHEADERS +) + +SET(EffectEngineSOURCES + ${CURRENT_SOURCE_DIR}/EffectEngine.cpp + ${CURRENT_SOURCE_DIR}/Effect.cpp +) + +QT4_WRAP_CPP(EffectEngineHEADERS_MOC ${EffectEngineQT_HEADERS}) + +add_library(effectengine + ${EffectEngineHEADERS} + ${EffectEngineQT_HEADERS} + ${EffectEngineHEADERS_MOC} + ${EffectEngineSOURCES} +) + +target_link_libraries(effectengine + hyperion + ${QT_LIBRARIES}) diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp new file mode 100644 index 00000000..4090a976 --- /dev/null +++ b/libsrc/effectengine/Effect.cpp @@ -0,0 +1,10 @@ +#include "Effect.h" + +Effect::Effect() +{ +} + +Effect::~Effect() +{ +} + diff --git a/libsrc/effectengine/Effect.h b/libsrc/effectengine/Effect.h new file mode 100644 index 00000000..76257815 --- /dev/null +++ b/libsrc/effectengine/Effect.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class Effect : public QObject +{ + Q_OBJECT + +public: + Effect(); + virtual ~Effect(); +}; diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp new file mode 100644 index 00000000..5f0ddc3c --- /dev/null +++ b/libsrc/effectengine/EffectEngine.cpp @@ -0,0 +1,42 @@ +#include + +EffectEngine::EffectEngine(Hyperion * hyperion) : + _hyperion(hyperion), + _availableEffects() +{ + // connect the Hyperion channel clear feedback + connect(_hyperion, SIGNAL(channelCleared(int)), this, SLOT(channelCleared(int))); + connect(_hyperion, SIGNAL(allChannelsCleared()), this, SLOT(allChannelsCleared())); + + // read all effects + _availableEffects["test"] = "test.py"; +} + +EffectEngine::~EffectEngine() +{ +} + +std::list EffectEngine::getEffects() const +{ + std::list effectNames; + foreach (auto entry, _availableEffects) { + effectNames.push_back(entry.first); + } + return effectNames; +} + +int EffectEngine::runEffect(const std::string &effectName, int priority, int timeout) +{ + std::cout << "run effect " << effectName << " on channel " << priority << std::endl; + return 0; +} + +void EffectEngine::channelCleared(int priority) +{ + std::cout << "clear effect on channel " << priority << std::endl; +} + +void EffectEngine::allChannelsCleared() +{ + std::cout << "clear effect on every channel" << std::endl; +} diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index f87438d6..1740e432 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -77,6 +77,7 @@ add_library(hyperion target_link_libraries(hyperion hyperion-utils + effectengine serialport ${QT_LIBRARIES} ${LIBUSB_1_LIBRARIES} diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index dbfd123d..dbb8c991 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -27,6 +27,9 @@ #include #include +// effect engine includes +#include + LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) { std::cout << "Device configuration: " << deviceConfig << std::endl; @@ -252,6 +255,7 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) : _blueTransform(createColorTransform(jsonConfig["color"]["blue"])), _colorOrder(createColorOrder(jsonConfig["device"])), _device(createDevice(jsonConfig["device"])), + _effectEngine(nullptr), _timer() { // initialize the image processor factory @@ -264,6 +268,9 @@ Hyperion::Hyperion(const Json::Value &jsonConfig) : _timer.setSingleShot(true); QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); + // create the effect engine + _effectEngine = new EffectEngine(this); + // initialize the leds update(); } @@ -275,6 +282,9 @@ Hyperion::~Hyperion() clearall(); _device->switchOff(); + // delete the effect engine + delete _effectEngine; + // Delete the Led-String delete _device; @@ -382,6 +392,9 @@ void Hyperion::clear(int priority) { update(); } + + // send clear signal to the effect engine + _effectEngine->channelCleared(priority); } } @@ -391,6 +404,9 @@ void Hyperion::clearall() // update leds update(); + + // send clearall signal to the effect engine + _effectEngine->allChannelsCleared(); } double Hyperion::getTransform(Hyperion::Transform transform, Hyperion::Color color) const @@ -448,6 +464,16 @@ const Hyperion::InputInfo &Hyperion::getPriorityInfo(const int priority) const return _muxer.getInputInfo(priority); } +std::list Hyperion::getEffects() const +{ + return _effectEngine->getEffects(); +} + +int Hyperion::setEffect(const std::string &effectName, int priority, int timeout) +{ + return _effectEngine->runEffect(effectName, priority, timeout); +} + void Hyperion::update() { // Update the muxer, cleaning obsolete priorities diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index 1c99b13d..2862de82 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -94,6 +94,8 @@ void JsonClientConnection::handleMessage(const std::string &messageString) handleColorCommand(message); else if (command == "image") handleImageCommand(message); + else if (command == "effect") + handleEffectCommand(message); else if (command == "serverinfo") handleServerInfoCommand(message); else if (command == "clear") @@ -151,7 +153,22 @@ void JsonClientConnection::handleImageCommand(const Json::Value &message) sendSuccessReply(); } -void JsonClientConnection::handleServerInfoCommand(const Json::Value &message) +void JsonClientConnection::handleEffectCommand(const Json::Value &message) +{ + // extract parameters + int priority = message["priority"].asInt(); + int duration = message.get("duration", -1).asInt(); + const Json::Value & effect = message["effect"]; + const std::string & effectName = effect["name"].asString(); + + // set output + _hyperion->setEffect(effectName, priority, duration); + + // send reply + sendSuccessReply(); +} + +void JsonClientConnection::handleServerInfoCommand(const Json::Value &) { // create result Json::Value result; @@ -193,6 +210,17 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &message) whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::GREEN)); whitelevel.append(_hyperion->getTransform(Hyperion::WHITELEVEL, Hyperion::BLUE)); + // collect effect info + Json::Value & effects = info["effects"]; + std::list effectNames = _hyperion->getEffects(); + for (const std::string & name : effectNames) + { + Json::Value effect; + effect["name"] = name; + + effects.append(effect); + } + // send the result sendMessage(result); } diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index 5381baff..dc80deba 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -78,6 +78,13 @@ private: /// void handleImageCommand(const Json::Value & message); + /// + /// Handle an incoming JSON Effect message + /// + /// @param message the incoming message + /// + void handleEffectCommand(const Json::Value & message); + /// /// Handle an incoming JSON Server info message /// diff --git a/libsrc/jsonserver/JsonSchemas.qrc b/libsrc/jsonserver/JsonSchemas.qrc index 5c2f1afb..7736e530 100644 --- a/libsrc/jsonserver/JsonSchemas.qrc +++ b/libsrc/jsonserver/JsonSchemas.qrc @@ -1,11 +1,12 @@ - - schema/schema.json - schema/schema-color.json - schema/schema-image.json - schema/schema-serverinfo.json - schema/schema-clear.json - schema/schema-clearall.json - schema/schema-transform.json - + + schema/schema.json + schema/schema-color.json + schema/schema-image.json + schema/schema-serverinfo.json + schema/schema-clear.json + schema/schema-clearall.json + schema/schema-transform.json + schema/schema-effect.json + diff --git a/libsrc/jsonserver/schema/schema-effect.json b/libsrc/jsonserver/schema/schema-effect.json new file mode 100644 index 00000000..e1b561cd --- /dev/null +++ b/libsrc/jsonserver/schema/schema-effect.json @@ -0,0 +1,31 @@ +{ + "type":"object", + "required":true, + "properties":{ + "command": { + "type" : "string", + "required" : true, + "enum" : ["effect"] + }, + "priority": { + "type": "integer", + "required": true + }, + "duration": { + "type": "integer", + "required": false + }, + "effect": { + "type": "object", + "required": true, + "properties" :{ + "name" : { + "type" : "string", + "required" : true + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/libsrc/jsonserver/schema/schema.json b/libsrc/jsonserver/schema/schema.json index 1cbbbb06..e931f7b7 100644 --- a/libsrc/jsonserver/schema/schema.json +++ b/libsrc/jsonserver/schema/schema.json @@ -5,7 +5,7 @@ "command": { "type" : "string", "required" : true, - "enum" : ["color", "image", "serverinfo", "clear", "clearall", "transform"] + "enum" : ["color", "image", "effect", "serverinfo", "clear", "clearall", "transform"] } } } diff --git a/src/hyperion-remote/JsonConnection.cpp b/src/hyperion-remote/JsonConnection.cpp index d742daaa..4d96590b 100644 --- a/src/hyperion-remote/JsonConnection.cpp +++ b/src/hyperion-remote/JsonConnection.cpp @@ -102,6 +102,27 @@ void JsonConnection::setImage(QImage image, int priority, int duration) parseReply(reply); } +void JsonConnection::setEffect(const std::string &effectName, int priority, int duration) +{ + std::cout << "Start effect " << effectName << std::endl; + + // create command + Json::Value command; + command["command"] = "effect"; + command["priority"] = priority; + Json::Value & effect = command["effect"]; + effect["name"] = effectName; + if (duration > 0) + { + command["duration"] = duration; + } + + // send command message + Json::Value reply = sendMessage(command); + + // parse reply message + parseReply(reply); +} QString JsonConnection::getServerInfo() { diff --git a/src/hyperion-remote/JsonConnection.h b/src/hyperion-remote/JsonConnection.h index 33fc5b4a..ebc0292a 100644 --- a/src/hyperion-remote/JsonConnection.h +++ b/src/hyperion-remote/JsonConnection.h @@ -52,6 +52,15 @@ public: /// void setImage(QImage image, int priority, int duration); + /// + /// Start the given effect + /// + /// @param effect The name of the effect + /// @param priority The priority + /// @param duration The duration in milliseconds + /// + void setEffect(const std::string & effectName, 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 bbe4f408..8d9dcedb 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -42,6 +42,7 @@ int main(int argc, char * argv[]) IntParameter & argDuration = parameters.add ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]"); 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"); 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"); @@ -73,12 +74,13 @@ int main(int argc, char * argv[]) bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet(); // check that exactly one command was given - int commandCount = count({argColor.isSet(), argImage.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); + int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform}); if (commandCount != 1) { std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl; std::cerr << " " << argColor.usageLine() << std::endl; std::cerr << " " << argImage.usageLine() << std::endl; + std::cerr << " " << argEffect.usageLine() << std::endl; std::cerr << " " << argServerInfo.usageLine() << std::endl; std::cerr << " " << argClear.usageLine() << std::endl; std::cerr << " " << argClearAll.usageLine() << std::endl; @@ -104,6 +106,10 @@ int main(int argc, char * argv[]) { connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue()); } + else if (argEffect.isSet()) + { + connection.setEffect(argEffect.getValue(), argPriority.getValue(), argDuration.getValue()); + } else if (argServerInfo.isSet()) { QString info = connection.getServerInfo(); diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 646a8575..a6c35c33 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -6,6 +6,7 @@ target_link_libraries(hyperiond bootsequence hyperion xbmcvideochecker + effectengine jsonserver protoserver boblightserver) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index bc789691..331f5342 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -26,6 +26,9 @@ // XBMC Video checker includes #include +// Effect engine includes +#include + // JsonServer includes #include @@ -150,6 +153,13 @@ int main(int argc, char** argv) } #endif + // Create the effect engine + EffectEngine * effectEngine = nullptr; + if (true) + { + effectEngine = new EffectEngine(&hyperion); + } + // Create Json server if configuration is present JsonServer * jsonServer = nullptr; if (config.isMember("jsonServer")) @@ -187,8 +197,10 @@ int main(int argc, char** argv) delete dispmanx; #endif delete xbmcVideoChecker; + delete effectEngine; delete jsonServer; delete protoServer; + delete boblightServer; // leave application return rc;