From c9d9a0294a4d5667d7e3d0c7047e6c10e6e2cf55 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:40:40 +0100 Subject: [PATCH 001/112] Thread Refactoring & SmartPointers --- CMakeLists.txt | 10 +- include/api/API.h | 3 +- include/effectengine/Effect.h | 7 + include/effectengine/EffectEngine.h | 17 +- include/hyperion/BGEffectHandler.h | 110 +-------- include/hyperion/Hyperion.h | 42 ++-- include/hyperion/HyperionIManager.h | 17 +- include/hyperion/LinearColorSmoothing.h | 3 +- include/hyperion/PriorityMuxer.h | 6 + include/leddevice/LedDevice.h | 31 ++- include/leddevice/LedDeviceWrapper.h | 86 ++++--- include/mdns/MdnsProvider.h | 5 + include/utils/WaitTime.h | 14 +- include/utils/settings.h | 52 ++--- libsrc/api/API.cpp | 37 ++-- libsrc/api/JsonAPI.cpp | 52 ++--- libsrc/effectengine/Effect.cpp | 9 +- libsrc/effectengine/EffectEngine.cpp | 99 +++++++-- libsrc/hyperion/BGEffectHandler.cpp | 98 ++++++++ libsrc/hyperion/CMakeLists.txt | 1 + libsrc/hyperion/Hyperion.cpp | 126 +++++------ libsrc/hyperion/HyperionIManager.cpp | 133 +++++++---- libsrc/hyperion/ImageProcessor.cpp | 1 - libsrc/hyperion/LinearColorSmoothing.cpp | 5 +- libsrc/hyperion/PriorityMuxer.cpp | 8 + libsrc/leddevice/LedDevice.cpp | 55 ++--- libsrc/leddevice/LedDeviceWrapper.cpp | 157 +++++++------ .../leddevice/dev_net/LedDevicePhilipsHue.cpp | 3 +- libsrc/leddevice/dev_net/LedDeviceRazer.cpp | 2 +- libsrc/leddevice/dev_other/LedDeviceFile.cpp | 17 +- libsrc/leddevice/dev_other/LedDeviceFile.h | 13 +- libsrc/mdns/MdnsProvider.cpp | 9 +- libsrc/protoserver/ProtoServer.cpp | 2 +- src/hyperiond/hyperiond.cpp | 209 +++++++++++------- src/hyperiond/hyperiond.h | 30 ++- src/hyperiond/main.cpp | 11 +- src/hyperiond/systray.h | 3 +- 37 files changed, 896 insertions(+), 587 deletions(-) create mode 100644 libsrc/hyperion/BGEffectHandler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f2ca768e5..14fbacaee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,12 +263,12 @@ if(HYPERION_LIGHT) # Disable Services SET ( DEFAULT_EFFECTENGINE OFF ) - #SET ( DEFAULT_EXPERIMENTAL OFF ) + SET ( DEFAULT_EXPERIMENTAL OFF ) #SET ( DEFAULT_MDNS OFF ) - #SET ( DEFAULT_REMOTE_CTL OFF ) - - #SET ( ENABLE_JSONCHECKS OFF ) - #SET ( ENABLE_DEPLOY_DEPENDENCIES OFF ) + SET ( DEFAULT_REMOTE_CTL OFF ) + + SET ( ENABLE_JSONCHECKS ON ) + SET ( ENABLE_DEPLOY_DEPENDENCIES ON ) endif() message(STATUS "Grabber options:") diff --git a/include/api/API.h b/include/api/API.h index 8b6476e60..4b52e7c54 100644 --- a/include/api/API.h +++ b/include/api/API.h @@ -13,6 +13,7 @@ // qt includes #include +#include class JsonCallbacks; class HyperionIManager; @@ -379,7 +380,7 @@ class API : public QObject HyperionIManager *_instanceManager; Logger *_log; - Hyperion *_hyperion; + QSharedPointer _hyperion; signals: /// diff --git a/include/effectengine/Effect.h b/include/effectengine/Effect.h index 294f5efc5..3d21e8fe5 100644 --- a/include/effectengine/Effect.h +++ b/include/effectengine/Effect.h @@ -69,6 +69,13 @@ class Effect : public QThread QJsonObject getArgs() const { return _args; } +public slots: + + /// + /// @brief Stop the effect (triggers its interruption) + /// + void stop(); + signals: void setInput(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect); void setInputImage(int priority, const Image& image, int timeout_ms, bool clearEffect); diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 5756345bb..57a44633b 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -7,6 +7,7 @@ #include #include #include +#include // Hyperion includes #include @@ -66,10 +67,18 @@ class EffectEngine : public QObject /// void startCachedEffects(); + /// + /// @brief Stop all effects + /// + void stopAllEffects(); + signals: - /// Emit when the effect list has been updated + /// Emits when the effect list has been updated void effectListUpdated(); + /// Emits when all effevts were stopped + void isStopCompleted(); + public slots: /// Run the specified effect on the given priority channel and optionally specify a timeout int runEffect(const QString &effectName, int priority, int timeout = PriorityMuxer::ENDLESS, const QString &origin="System"); @@ -93,6 +102,7 @@ public slots: private slots: void effectFinished(); + void onEffectFinished(); /// /// @brief is called whenever the EffectFileHandler emits updated effect list @@ -111,6 +121,8 @@ private slots: , const QString &imageData = "" ); + void waitForEffectsToStop(); + private: Hyperion * _hyperion; @@ -124,4 +136,7 @@ private slots: // The global effect file handler EffectFileHandler* _effectFileHandler; + + QEventLoop _eventLoop; + int _remainingEffects; }; diff --git a/include/hyperion/BGEffectHandler.h b/include/hyperion/BGEffectHandler.h index f3ee80879..560c50d45 100644 --- a/include/hyperion/BGEffectHandler.h +++ b/include/hyperion/BGEffectHandler.h @@ -4,8 +4,9 @@ #include #include #include -#include -#include + +#include +#include /// /// @brief Handle the background Effect settings, reacts on runtime to settings changes @@ -15,49 +16,20 @@ class BGEffectHandler : public QObject Q_OBJECT public: - BGEffectHandler(Hyperion* hyperion) - : QObject(hyperion) - , _hyperion(hyperion) - , _prioMuxer(_hyperion->getMuxerInstance()) - , _isBgEffectEnabled(false) - , _isSuspended(false) - { - QString subComponent = parent()->property("instance").toString(); - _log = Logger::getInstance("HYPERION", subComponent); - - // listen for config changes - connect(_hyperion, &Hyperion::settingsChanged, this, [=] (settings::type type, const QJsonDocument& config) { - this->handleSettingsUpdate(type, config); - }); - - connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, [=] { - this->handlePriorityUpdate(); - }); - - // listen for suspend/resume requests, to not start a background effect when system goes into suspend mode - connect(_hyperion, &Hyperion::suspendRequest, this, [=] (bool isSuspended) { - _isSuspended = isSuspended; - }); - - // initialization - handleSettingsUpdate(settings::BGEFFECT, _hyperion->getSetting(settings::BGEFFECT)); - } + BGEffectHandler(Hyperion* hyperion = nullptr); /// /// @brief Disconnect from connected signals - /// Disconnect should be done before other priorities invoke methods during shutdown + /// Disconnect should be done before other priorities invoke methods + /// during shutdown /// - void disconnect() - { - QObject::disconnect(_prioMuxer, &PriorityMuxer::prioritiesChanged, nullptr, nullptr); - QObject::disconnect(_hyperion, nullptr, nullptr, nullptr); - } + void disconnect(); /// /// @brief Check, if background effect processing is enabled. /// @return True, background effect processing is enabled. /// - bool _isEnabled () const { return _isBgEffectEnabled; } + bool _isEnabled () const; private slots: /// @@ -65,81 +37,19 @@ private slots: /// @param type settingType from enum /// @param config configuration object /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config) - { - if(type == settings::BGEFFECT) - { - _bgEffectConfig = config; - - const QJsonObject& BGEffectConfig = _bgEffectConfig.object(); - #define BGCONFIG_ARRAY bgColorConfig.toArray() - // clear background priority - if (_hyperion->getCurrentPriority() == PriorityMuxer::BG_PRIORITY) - { - _hyperion->clear(PriorityMuxer::BG_PRIORITY); - } - _isBgEffectEnabled = BGEffectConfig["enable"].toBool(true); - - // initial background effect/color - if (_isBgEffectEnabled) - { - #if defined(ENABLE_EFFECTENGINE) - const QString bgTypeConfig = BGEffectConfig["type"].toString("effect"); - const QString bgEffectConfig = BGEffectConfig["effect"].toString("Warm mood blobs"); - #else - const QString bgTypeConfig = "color"; - #endif - const QJsonValue bgColorConfig = BGEffectConfig["color"]; - if (bgTypeConfig.contains("color")) - { - std::vector bg_color = { - ColorRgb { - static_cast(BGCONFIG_ARRAY.at(0).toInt(0)), - static_cast(BGCONFIG_ARRAY.at(1).toInt(0)), - static_cast(BGCONFIG_ARRAY.at(2).toInt(0)) - } - }; - _hyperion->setColor(PriorityMuxer::BG_PRIORITY, bg_color); - Info(_log,"Initial background color set (%d %d %d)",bg_color.at(0).red, bg_color.at(0).green, bg_color.at(0).blue); - } - #if defined(ENABLE_EFFECTENGINE) - else - { - int result = _hyperion->setEffect(bgEffectConfig, PriorityMuxer::BG_PRIORITY, PriorityMuxer::ENDLESS); - Info(_log,"Initial background effect '%s' %s", QSTRING_CSTR(bgEffectConfig), ((result == 0) ? "started" : "failed")); - } - #endif - } - #undef BGCONFIG_ARRAY - } - } + void handleSettingsUpdate(settings::type type, const QJsonDocument& config); /// /// @brief Handle priority updates. /// In case the background effect is not current priority, stop BG-effect to save resources; otherwise start effect again. /// - void handlePriorityUpdate() - { - if (_prioMuxer->getCurrentPriority() < PriorityMuxer::BG_PRIORITY && _prioMuxer->hasPriority(PriorityMuxer::BG_PRIORITY)) - { - Debug(_log,"Stop background (color-) effect as it moved out of scope"); - _hyperion->clear(PriorityMuxer::BG_PRIORITY); - } - else if (!_isSuspended && _prioMuxer->getCurrentPriority() == PriorityMuxer::LOWEST_PRIORITY && _isBgEffectEnabled) - { - Debug(_log,"Start background (color-) effect as it moved in scope"); - emit handleSettingsUpdate (settings::BGEFFECT, _bgEffectConfig); - } - } + void handlePriorityUpdate(int currentPriority); private: /// Hyperion instance pointer Hyperion* _hyperion; Logger * _log; - /// priority muxer instance - PriorityMuxer* _prioMuxer; - QJsonDocument _bgEffectConfig; bool _isBgEffectEnabled; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 6a3772055..a1cb015d8 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -11,6 +11,7 @@ #include #include #include +#include // hyperion-utils includes #include @@ -75,12 +76,7 @@ class Hyperion : public QObject /// ~Hyperion() override; - /// - /// free all alocated objects, should be called only from constructor or before restarting hyperion - /// - void freeObjects(); - - ImageProcessor* getImageProcessor() const { return _imageProcessor; } + ImageProcessor* getImageProcessor() const { return _imageProcessor.get(); } /// /// @brief Get instance index of this instance @@ -206,7 +202,7 @@ public slots: /// @brief Get a pointer to the effect engine /// @return EffectEngine instance pointer /// - EffectEngine* getEffectEngineInstance() const { return _effectEngine; } + EffectEngine* getEffectEngineInstance() const { return _effectEngine.get(); } /// /// @brief Save an effect @@ -261,7 +257,7 @@ public slots: /// @brief Get a pointer to the priorityMuxer instance /// @return PriorityMuxer instance pointer /// - PriorityMuxer* getMuxerInstance() { return _muxer; } + PriorityMuxer* getMuxerInstance() { return _muxer.get(); } /// /// @brief enable/disable automatic/priorized source selection @@ -328,7 +324,7 @@ public slots: /// gets the current json config object from SettingsManager /// @return json config - QJsonObject getQJsonConfig() const; + QJsonObject getQJsonConfig(quint8 inst) const; /// /// @brief Save a complete json config @@ -343,7 +339,7 @@ public slots: /// @brief Get the component Register /// return Component register pointer /// - ComponentRegister* getComponentRegister() const { return _componentRegister; } + ComponentRegister* getComponentRegister() const { return _componentRegister.get(); } /// /// @brief Called from components to update their current state. DO NOT CALL FROM USERS @@ -508,6 +504,8 @@ public slots: /// void started(); + void stopEffects(); + private slots: /// /// @brief Handle whenever the visible component changed @@ -547,39 +545,39 @@ private slots: const quint8 _instIndex; /// Settings manager of this instance - SettingsManager* _settingsManager; + QScopedPointer _settingsManager; /// Register that holds component states - ComponentRegister* _componentRegister; + QSharedPointer _componentRegister; /// The specifiation of the led frame construction and picture integration LedString _ledString; /// Image Processor - ImageProcessor* _imageProcessor; + QSharedPointer _imageProcessor; std::vector _ledStringColorOrder; /// The priority muxer - PriorityMuxer* _muxer; + QScopedPointer _muxer; /// The adjustment from raw colors to led colors - MultiColorAdjustment * _raw2ledAdjustment; + QScopedPointer _raw2ledAdjustment; /// The actual LedDeviceWrapper - LedDeviceWrapper* _ledDeviceWrapper; + QScopedPointer _ledDeviceWrapper; /// The smoothing LedDevice - LinearColorSmoothing * _deviceSmooth; + QScopedPointer _deviceSmooth; #if defined(ENABLE_EFFECTENGINE) /// Effect engine - EffectEngine * _effectEngine; + QScopedPointer _effectEngine; #endif #if defined(ENABLE_FORWARDER) // Message forwarder - MessageForwarder * _messageForwarder; + QScopedPointer _messageForwarder; #endif /// Logger instance @@ -591,9 +589,9 @@ private slots: QSize _ledGridSize; /// Background effect instance, kept active to react on setting changes - BGEffectHandler* _BGEffectHandler; + QScopedPointer _BGEffectHandler; /// Capture control for Daemon native capture - CaptureCont* _captureCont; + QScopedPointer _captureCont; /// buffer for leds (with adjustment) std::vector _ledBuffer; @@ -602,6 +600,6 @@ private slots: #if defined(ENABLE_BOBLIGHT_SERVER) /// Boblight instance - BoblightServer* _boblightServer; + QScopedPointer _boblightServer; #endif }; diff --git a/include/hyperion/HyperionIManager.h b/include/hyperion/HyperionIManager.h index 60a170764..bc025338e 100644 --- a/include/hyperion/HyperionIManager.h +++ b/include/hyperion/HyperionIManager.h @@ -6,9 +6,12 @@ #include #include #include +#include // qt #include +#include +#include class Hyperion; class InstanceTable; @@ -39,6 +42,8 @@ class HyperionIManager : public QObject static HyperionIManager* getInstance() { return HIMinstance; } static HyperionIManager* HIMinstance; + ~HyperionIManager() override; + public slots: /// /// @brief Is given instance running? @@ -52,7 +57,7 @@ public slots: /// @param intance the index /// @return Hyperion instance, if index is not found returns instance 0 /// - Hyperion* getHyperionInstance(quint8 instance = 0); + QSharedPointer getHyperionInstance(quint8 instance = 0); /// /// @brief Get instance data of all instances in db + running state @@ -142,6 +147,10 @@ public slots: /// void startInstanceResponse(QObject *caller, const int &tan); + /// + /// @brief Emits when all instances were stopped + /// + void areAllInstancesStopped(); signals: /////////////////////////////////////// @@ -220,10 +229,10 @@ private slots: private: Logger* _log; - InstanceTable* _instanceTable; - QMap _runningInstances; + QScopedPointer _instanceTable; + QMap> _runningInstances; + QMap> _startingInstances; - QList _startQueue; /// All pending requests QMap _pendingRequests; }; diff --git a/include/hyperion/LinearColorSmoothing.h b/include/hyperion/LinearColorSmoothing.h index d22c5da5e..66c5b9cce 100644 --- a/include/hyperion/LinearColorSmoothing.h +++ b/include/hyperion/LinearColorSmoothing.h @@ -7,6 +7,7 @@ // Qt includes #include +#include // hyperion includes #include @@ -180,7 +181,7 @@ private slots: int64_t _settlingTime; /// The Qt timer object - QTimer *_timer; + QScopedPointer _timer; /// The timestamp at which the target data should be fully applied int64_t _targetTime; diff --git a/include/hyperion/PriorityMuxer.h b/include/hyperion/PriorityMuxer.h index d307843b9..0648ef5d3 100644 --- a/include/hyperion/PriorityMuxer.h +++ b/include/hyperion/PriorityMuxer.h @@ -81,6 +81,12 @@ class PriorityMuxer : public QObject /// ~PriorityMuxer() override; + + /// + /// @brief Stop the PriorityMuxer and its timers + /// + void stop(); + /// /// @brief Start/Stop the PriorityMuxer update timer; On disabled no priority and timeout updates will be performend /// @param enable The new state diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index b17a0fc9f..bd4ed880f 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -9,6 +9,8 @@ #include #include #include +#include + // STL includes #include @@ -284,11 +286,23 @@ public slots: signals: /// - /// @brief Emits whenever the LED-Device switches between on/off. + /// @brief Emits whenever the LED-Device is enabled/disabled. + /// + /// @param[in] isEnabled The new state of the device + /// + void isEnabledChanged(bool isEnabled); + + /// + /// @brief Emits whenever the LED-Device switches on/off. + /// + /// @param[in] isOn The new state of the device /// - /// @param[in] newState The new state of the device + void isOnChanged(bool isOn); + + /// + /// @brief Emits whenever the LED-Device is stopped. /// - void enableStateChanged(bool newState); + void isStopped(); protected: @@ -422,7 +436,7 @@ public slots: /// Timer object which makes sure that LED data is written at a minimum rate /// e.g. some devices will switch off when they do not receive data at least every 15 seconds - QTimer* _refreshTimer; + QScopedPointer _refreshTimer; // Device configuration parameters @@ -486,6 +500,13 @@ protected slots: /// virtual void setInError( const QString& errorMsg, bool isRecoverable=true); +private slots: + + /// + /// @brief Retry to enable the LED-device + /// + void retryEnable(); + private: /// @brief Start a new refresh cycle @@ -495,7 +516,7 @@ protected slots: void stopRefreshTimer(); /// Timer that enables a device (used to retry enablement, if enabled failed before) - QTimer* _enableAttemptsTimer; + QScopedPointer _enableAttemptsTimer; // Device configuration parameters diff --git a/include/leddevice/LedDeviceWrapper.h b/include/leddevice/LedDeviceWrapper.h index b86c76192..c5a5fa041 100644 --- a/include/leddevice/LedDeviceWrapper.h +++ b/include/leddevice/LedDeviceWrapper.h @@ -6,11 +6,8 @@ #include #include -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - #include -#else - #include -#endif +#include +#include class LedDevice; class Hyperion; @@ -19,7 +16,7 @@ typedef LedDevice* ( *LedDeviceCreateFuncType ) ( const QJsonObject& ); typedef std::map LedDeviceRegistry; /// -/// @brief Creates and destroys LedDevice instances with LedDeviceFactory and moves the device to a thread. Pipes all signal/slots and methods to LedDevice instance +/// @brief Creates and destroys LedDevice instances with LedDeviceFactory and moves the device to an own thread. Pipes all signal/slots and methods to an Led-device instance /// class LedDeviceWrapper : public QObject { @@ -51,20 +48,25 @@ class LedDeviceWrapper : public QObject static const LedDeviceRegistry& getDeviceMap(); /// - /// @brief Get the current latch time of the ledDevice + /// @brief Get the current latch time of the LED-device /// @ return latch time in ms /// int getLatchTime() const; /// - /// @brief Get the current active ledDevice type + /// @brief Get the current active LED-device type /// QString getActiveDeviceType() const; /// /// @brief Return the last enable state /// - bool enabled() const; + bool isEnabled() const; + + /// + /// @brief Return the last ON state + /// + bool isOn() const; /// /// @brief Get the current colorOrder from device @@ -74,7 +76,7 @@ class LedDeviceWrapper : public QObject /// /// @brief Get the number of LEDs from device /// - unsigned int getLedCount() const; + int getLedCount() const; public slots: /// @@ -84,6 +86,11 @@ public slots: /// void handleComponentState(hyperion::Components component, bool state); + /// + /// @brief Disables the LED-device and stops the device's thread + /// + void stopDevice(); + signals: /// /// PIPER signal for Hyperion -> LedDevice @@ -103,40 +110,61 @@ public slots: /// @brief Switch the LEDs off. /// void switchOff(); - - void stopLedDevice(); + + /// + /// @brief Enable the LED-device. + /// + void enable(); + + /// + /// @brief Disable the LED-device. + /// + void disable(); + + /// + /// @brief Stop the LED-device. + /// + void stop(); + + /// + /// @brief Emits when the LED-device stopped. + /// + void isStopped(); private slots: /// - /// @brief Is called whenever the led device switches between on/off. The led device can disable it's component state + /// @brief Is called whenever the LED-device is enabled/disabled. /// The signal comes from the LedDevice /// @param newState The new state of the device /// - void handleInternalEnableState(bool newState); + void onIsEnabledChanged(bool isEnabled); + /// + /// @brief Is called whenever the LED-device switches between on/off. + /// The signal comes from the LedDevice + /// @param newState The new state of the device + /// + void onIsOnChanged(bool isOn); protected: /// contains all available led device constructors static LedDeviceRegistry _ledDeviceMap; - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - static QRecursiveMutex _ledDeviceMapLock; -#else - static QMutex _ledDeviceMapLock; -#endif -private: - /// - /// @brief switchOff() the device and Stops the device thread - /// - void stopDeviceThread(); + static QMutex _ledDeviceMapLock; private: + /// The common Logger instance for all LED-devices + Logger * _log; + // parent Hyperion Hyperion* _hyperion; - // Pointer of current led device - LedDevice* _ledDevice; - // the enable state - bool _enabled; + + // Pointer to the current LED-device & its thread + QScopedPointer _ledDevice; + QScopedPointer _ledDeviceThread; + + // LED-Device's states + bool _isEnabled; + bool _isOn; }; #endif // LEDEVICEWRAPPER_H diff --git a/include/mdns/MdnsProvider.h b/include/mdns/MdnsProvider.h index c3e328c71..3282d7eb4 100644 --- a/include/mdns/MdnsProvider.h +++ b/include/mdns/MdnsProvider.h @@ -31,6 +31,11 @@ public slots: /// void init(); + /// + /// @brief Stop MdnsProvider to cleanup objects with thread affinity + /// + void stop(); + void publishService (const QString& serviceType, quint16 servicePort, const QByteArray& serviceName = ""); private slots: diff --git a/include/utils/WaitTime.h b/include/utils/WaitTime.h index 0521ed5b3..e6140f01f 100644 --- a/include/utils/WaitTime.h +++ b/include/utils/WaitTime.h @@ -9,18 +9,20 @@ inline void wait(std::chrono::milliseconds millisecondsWait) { QEventLoop loop; - QTimer t; - t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit); - t.start(millisecondsWait.count()); + QTimer timer; + timer.setTimerType(Qt::PreciseTimer); + QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(millisecondsWait.count()); loop.exec(); } inline void wait(int millisecondsWait) { QEventLoop loop; - QTimer t; - t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit); - t.start(millisecondsWait); + QTimer timer; + timer.setTimerType(Qt::PreciseTimer); + QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(millisecondsWait); loop.exec(); } diff --git a/include/utils/settings.h b/include/utils/settings.h index 10cd879b0..8a36006da 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -81,31 +81,31 @@ namespace settings { /// inline type stringToType(const QString& type) { - if (type == "backgroundEffect") return BGEFFECT; - else if (type == "foregroundEffect") return FGEFFECT; - else if (type == "blackborderdetector") return BLACKBORDER; - else if (type == "boblightServer") return BOBLSERVER; - else if (type == "color") return COLOR; - else if (type == "device") return DEVICE; - else if (type == "effects") return EFFECTS; - else if (type == "forwarder") return NETFORWARD; - else if (type == "framegrabber") return SYSTEMCAPTURE; - else if (type == "general") return GENERAL; - else if (type == "grabberV4L2") return V4L2; - else if (type == "grabberAudio") return AUDIO; - else if (type == "jsonServer") return JSONSERVER; - else if (type == "ledConfig") return LEDCONFIG; - else if (type == "leds") return LEDS; - else if (type == "logger") return LOGGER; - else if (type == "smoothing") return SMOOTHING; - else if (type == "webConfig") return WEBSERVER; - else if (type == "instCapture") return INSTCAPTURE; - else if (type == "network") return NETWORK; - else if (type == "flatbufServer") return FLATBUFSERVER; - else if (type == "protoServer") return PROTOSERVER; - else if (type == "osEvents") return OSEVENTS; - else if (type == "cecEvents") return CECEVENTS; - else if (type == "schedEvents") return SCHEDEVENTS; - else return INVALID; + if (type == "backgroundEffect") return BGEFFECT; + if (type == "foregroundEffect") return FGEFFECT; + if (type == "blackborderdetector") return BLACKBORDER; + if (type == "boblightServer") return BOBLSERVER; + if (type == "color") return COLOR; + if (type == "device") return DEVICE; + if (type == "effects") return EFFECTS; + if (type == "forwarder") return NETFORWARD; + if (type == "framegrabber") return SYSTEMCAPTURE; + if (type == "general") return GENERAL; + if (type == "grabberV4L2") return V4L2; + if (type == "grabberAudio") return AUDIO; + if (type == "jsonServer") return JSONSERVER; + if (type == "ledConfig") return LEDCONFIG; + if (type == "leds") return LEDS; + if (type == "logger") return LOGGER; + if (type == "smoothing") return SMOOTHING; + if (type == "webConfig") return WEBSERVER; + if (type == "instCapture") return INSTCAPTURE; + if (type == "network") return NETWORK; + if (type == "flatbufServer") return FLATBUFSERVER; + if (type == "protoServer") return PROTOSERVER; + if (type == "osEvents") return OSEVENTS; + if (type == "cecEvents") return CECEVENTS; + if (type == "schedEvents") return SCHEDEVENTS; + return INVALID; } } diff --git a/libsrc/api/API.cpp b/libsrc/api/API.cpp index b34ec5083..d98a8bbbd 100644 --- a/libsrc/api/API.cpp +++ b/libsrc/api/API.cpp @@ -13,6 +13,7 @@ #include #include #include +#include // hyperion includes #include @@ -102,7 +103,8 @@ void API::setColor(int priority, const std::vector &ledColors, int time { fledColors.emplace_back(ColorRgb{ledColors[i], ledColors[i + 1], ledColors[i + 2]}); } - QMetaObject::invokeMethod(_hyperion, "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin)); + + QMetaObject::invokeMethod(_hyperion.get(), "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin)); } } @@ -192,8 +194,8 @@ bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &reply Image image(data.width, data.height); memcpy(image.memptr(), data.data.data(), static_cast(data.data.size())); - QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName)); - QMetaObject::invokeMethod(_hyperion, "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image, image), Q_ARG(int64_t, data.duration)); + QMetaObject::invokeMethod(_hyperion.get(), "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName)); + QMetaObject::invokeMethod(_hyperion.get(), "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image, image), Q_ARG(int64_t, data.duration)); return true; } @@ -202,7 +204,7 @@ bool API::clearPriority(int priority, QString &replyMsg, hyperion::Components /* { if (priority < 0 || (priority > 0 && priority < PriorityMuxer::BG_PRIORITY)) { - QMetaObject::invokeMethod(_hyperion, "clear", Qt::QueuedConnection, Q_ARG(int, priority)); + QMetaObject::invokeMethod(_hyperion.get(), "clear", Qt::QueuedConnection, Q_ARG(int, priority)); } else { @@ -218,7 +220,7 @@ bool API::setComponentState(const QString &comp, bool &compState, QString &reply if (component != COMP_INVALID) { - QMetaObject::invokeMethod(_hyperion, "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState)); + QMetaObject::invokeMethod(_hyperion.get(), "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState)); return true; } replyMsg = QString("Unknown component name: %1").arg(comp); @@ -227,12 +229,12 @@ bool API::setComponentState(const QString &comp, bool &compState, QString &reply void API::setLedMappingType(int type, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion, "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type)); + QMetaObject::invokeMethod(_hyperion.get(), "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type)); } void API::setVideoMode(VideoMode mode, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion, "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode)); + QMetaObject::invokeMethod(_hyperion.get(), "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode)); } #if defined(ENABLE_EFFECTENGINE) @@ -241,11 +243,11 @@ bool API::setEffect(const EffectCmdData &dat, hyperion::Components /*callerComp* int isStarted; if (!dat.args.isEmpty()) { - QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data)); + QMetaObject::invokeMethod(_hyperion.get(), "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data)); } else { - QMetaObject::invokeMethod(_hyperion, "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin)); + QMetaObject::invokeMethod(_hyperion.get(), "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin)); } return isStarted >= 0; @@ -254,12 +256,12 @@ bool API::setEffect(const EffectCmdData &dat, hyperion::Components /*callerComp* void API::setSourceAutoSelect(bool state, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion, "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state)); + QMetaObject::invokeMethod(_hyperion.get(), "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state)); } void API::setVisiblePriority(int priority, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion, "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority)); + QMetaObject::invokeMethod(_hyperion.get(), "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority)); } void API::registerInput(int priority, hyperion::Components component, const QString &origin, const QString &owner, hyperion::Components callerComp) @@ -271,7 +273,7 @@ void API::registerInput(int priority, hyperion::Components component, const QStr _activeRegisters.insert({priority, registerData{component, origin, owner, callerComp}}); - QMetaObject::invokeMethod(_hyperion, "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner)); + QMetaObject::invokeMethod(_hyperion.get(), "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner)); } void API::unregisterInput(int priority) @@ -296,16 +298,17 @@ bool API::setHyperionInstance(quint8 inst) return false; } - disconnect(_hyperion, nullptr, this, nullptr); - QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::DirectConnection, Q_RETURN_ARG(Hyperion *, _hyperion), Q_ARG(quint8, inst)); + disconnect(_hyperion.get(), nullptr, this, nullptr); + QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::DirectConnection, Q_RETURN_ARG(QSharedPointer, _hyperion), Q_ARG(quint8, inst)); _currInstanceIndex = inst; + return true; } bool API::isHyperionEnabled() { int isEnabled; - QMetaObject::invokeMethod(_hyperion, "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isEnabled), Q_ARG(hyperion::Components, hyperion::COMP_ALL)); + QMetaObject::invokeMethod(_hyperion.get(), "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isEnabled), Q_ARG(hyperion::Components, hyperion::COMP_ALL)); return isEnabled > 0; } @@ -373,7 +376,7 @@ QString API::deleteEffect(const QString &name) if (_adminAuthorized) { QString res; - QMetaObject::invokeMethod(_hyperion, "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name)); + QMetaObject::invokeMethod(_hyperion.get(), "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name)); return res; } return NO_AUTHORIZATION; @@ -384,7 +387,7 @@ QString API::saveEffect(const QJsonObject &data) if (_adminAuthorized) { QString res; - QMetaObject::invokeMethod(_hyperion, "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data)); + QMetaObject::invokeMethod(_hyperion.get(), "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data)); return res; } return NO_AUTHORIZATION; diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 52e4c6af8..37d7b363c 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -104,8 +104,8 @@ void JsonAPI::initialize() if (_hyperion != nullptr) { // Initialise jsonCB with current instance - _jsonCB->setSubscriptionsTo(_hyperion); - connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage); + _jsonCB->setSubscriptionsTo(_hyperion.get()); + connect(this, &JsonAPI::forwardJsonMessage, _hyperion.get(), &Hyperion::forwardJsonMessage); } //notify eventhadler on suspend/resume/idle requests @@ -118,7 +118,7 @@ bool JsonAPI::handleInstanceSwitch(quint8 inst, bool /*forced*/) { Debug(_log, "Client '%s' switch to Hyperion instance %d", QSTRING_CSTR(_peerAddress), inst); // the JsonCB creates json messages you can subscribe to e.g. data change events - _jsonCB->setSubscriptionsTo(_hyperion); + _jsonCB->setSubscriptionsTo(_hyperion.get()); return true; } return false; @@ -245,14 +245,21 @@ void JsonAPI::handleMessage(const QString &messageString, const QString &httpAut void JsonAPI::handleInstanceCommand(const JsonApiCommand& cmd, const QJsonObject &message) { - const QJsonValue instanceElement = message.value("instance"); QJsonArray instances; - if (instanceElement.isDouble()) + const QJsonValue instanceElement = message.value("instance"); + if (instanceElement.isUndefined() || instanceElement.isNull()) { - instances.append(instanceElement); - } else if (instanceElement.isArray()) + instances.append(getCurrentInstanceIndex()); + } + else { - instances = instanceElement.toArray(); + if (instanceElement.isDouble()) + { + instances.append(instanceElement); + } else if (instanceElement.isArray()) + { + instances = instanceElement.toArray(); + } } QList runningInstanceIdxs = _instanceManager->getRunningInstanceIdx(); @@ -288,7 +295,6 @@ void JsonAPI::handleInstanceCommand(const JsonApiCommand& cmd, const QJsonObject return; } - quint8 currentInstanceIdx = getCurrentInstanceIndex(); if (instanceIdxList.size() > 1) { if (cmd.isInstanceCmd != InstanceCmd::Multi) @@ -305,8 +311,6 @@ void JsonAPI::handleInstanceCommand(const JsonApiCommand& cmd, const QJsonObject handleCommand(cmd, message); } } - - setHyperionInstance(currentInstanceIdx); } void JsonAPI::handleCommand(const JsonApiCommand& cmd, const QJsonObject &message) @@ -472,7 +476,7 @@ void JsonAPI::handleDeleteEffectCommand(const QJsonObject &message, const JsonAp void JsonAPI::handleSysInfoCommand(const QJsonObject & /*unused*/, const JsonApiCommand& cmd) { - sendSuccessDataReply(JsonInfo::getSystemInfo(_hyperion), cmd); + sendSuccessDataReply(JsonInfo::getSystemInfo(_hyperion.get()), cmd); } void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const JsonApiCommand& cmd) @@ -483,28 +487,28 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const JsonApiC switch (cmd.getSubCommand()) { case SubCommand::Empty: case SubCommand::GetInfo: - info["priorities"] = JsonInfo::getPrioritiestInfo(_hyperion); + info["priorities"] = JsonInfo::getPrioritiestInfo(_hyperion.get()); info["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled(); - info["adjustment"] = JsonInfo::getAdjustmentInfo(_hyperion, _log); + info["adjustment"] = JsonInfo::getAdjustmentInfo(_hyperion.get(), _log); info["ledDevices"] = JsonInfo::getAvailableLedDevices(); - info["grabbers"] = JsonInfo::getGrabbers(_hyperion); + info["grabbers"] = JsonInfo::getGrabbers(_hyperion.get()); info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode())); info["cec"] = JsonInfo::getCecInfo(); info["services"] = JsonInfo::getServices(); - info["components"] = JsonInfo::getComponents(_hyperion); + info["components"] = JsonInfo::getComponents(_hyperion.get()); info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType()); info["instance"] = JsonInfo::getInstanceInfo(); info["leds"] = _hyperion->getSetting(settings::LEDS).array(); - info["activeLedColor"] = JsonInfo::getActiveColors(_hyperion); + info["activeLedColor"] = JsonInfo::getActiveColors(_hyperion.get()); #if defined(ENABLE_EFFECTENGINE) - info["effects"] = JsonInfo::getEffects(_hyperion); - info["activeEffects"] = JsonInfo::getActiveEffects(_hyperion); + info["effects"] = JsonInfo::getEffects(_hyperion.get()); + info["activeEffects"] = JsonInfo::getActiveEffects(_hyperion.get()); #endif // BEGIN | The following entries are deprecated but used to ensure backward compatibility with hyperion Classic or up to Hyperion 2.0.16 info["hostname"] = QHostInfo::localHostName(); - info["transform"] = JsonInfo::getTransformationInfo(_hyperion); + info["transform"] = JsonInfo::getTransformationInfo(_hyperion.get()); if (!_noListener && message.contains("subscribe")) { @@ -705,7 +709,7 @@ void JsonAPI::handleConfigCommand(const QJsonObject& message, const JsonApiComma break; case SubCommand::GetConfigOld: - sendSuccessDataReply(_hyperion->getQJsonConfig(), cmd); + sendSuccessDataReply(_hyperion->getQJsonConfig(this->getCurrentInstanceIndex()), cmd); break; case SubCommand::SetConfig: @@ -781,7 +785,7 @@ void JsonAPI::handleConfigSetCommand(const QJsonObject &message, const JsonApiCo i.next(); quint8 idx = i.key(); - Hyperion* instance = HyperionIManager::getInstance()->getHyperionInstance(idx); + QSharedPointer instance = HyperionIManager::getInstance()->getHyperionInstance(idx); QPair isSaved = instance->saveSettings(i.value()); errorDetails.append(isSaved.second); @@ -1379,7 +1383,7 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const JsonApiCo } QJsonObject config { { "type", devType } }; - LedDevice* ledDevice = LedDeviceFactory::construct(config); + QScopedPointer ledDevice(LedDeviceFactory::construct(config)); switch (cmd.subCommand) { case SubCommand::Discover: @@ -1397,8 +1401,6 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const JsonApiCo default: break; } - - delete ledDevice; } void JsonAPI::handleLedDeviceDiscover(LedDevice& ledDevice, const QJsonObject& message, const JsonApiCommand& cmd) diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index c01f26432..fc35061c5 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -42,9 +42,6 @@ Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString& scr Effect::~Effect() { - requestInterruption(); - wait(); - delete _painter; _imageStack.clear(); } @@ -155,3 +152,9 @@ void Effect::run() } file.close(); } + +void Effect::stop() +{ + requestInterruption(); + Debug(_log,"Effect \"%s\" stopped", QSTRING_CSTR(_name)); +} diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index 2c5ab5214..d4a113e72 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -41,11 +41,6 @@ EffectEngine::EffectEngine(Hyperion * hyperion) EffectEngine::~EffectEngine() { - for (Effect * effect : _activeEffects) - { - effect->wait(); - delete effect; - } } QString EffectEngine::saveEffect(const QJsonObject& obj) @@ -210,11 +205,10 @@ int EffectEngine::runEffectScript(const QString &script, const QString &name, co 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)); + Debug(_log, "Start the effect: \"%s\"", QSTRING_CSTR(name)); _hyperion->registerInput(priority, hyperion::COMP_EFFECT, origin, name ,smoothCfg); effect->start(); @@ -243,25 +237,92 @@ void EffectEngine::allChannelsCleared() } } +void EffectEngine::stopAllEffects() +{ + _remainingEffects = _activeEffects.size(); + + for (auto it = _activeEffects.begin(); it != _activeEffects.end();) + { + Effect* effect = *it; + + Debug(_log, "Currently active effects: %d", _remainingEffects); + + if (effect) + { + connect(effect, &Effect::finished, this, &EffectEngine::onEffectFinished, Qt::QueuedConnection); + QMetaObject::invokeMethod(effect, "stop", Qt::QueuedConnection); + + effect->requestInterruption(); + + // Wait for the thread to finish + if (effect->isRunning()) + { + effect->wait(); + } + + // Check if the effect has already been cleaned up in effectFinished + if (std::find(_activeEffects.begin(), _activeEffects.end(), effect) != _activeEffects.end()) + { + // Remove from list, cleanup handled in effectFinished + it = _activeEffects.erase(it); + } + else + { + ++it; // Effect was already handled, move to the next + } + } + else + { + ++it; + } + } + + // Wait until all instances have finished + waitForEffectsToStop(); + + Debug(_log, "All effects are stopped"); +} + void EffectEngine::effectFinished() { Effect* effect = qobject_cast(sender()); - if (!effect->isInterruptionRequested()) + if (!effect) + return; + + Info(_log, "Effect \"%s\" finished", QSTRING_CSTR(effect->getName())); + + auto it = std::find(_activeEffects.begin(), _activeEffects.end(), effect); + if (it != _activeEffects.end()) { - // effect stopped by itself. Clear the channel - _hyperion->clear(effect->getPriority()); + _activeEffects.erase(it); + effect->deleteLater(); } +} - Info( _log, "Effect [%s] finished", QSTRING_CSTR(effect->getName())); - for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt) +void EffectEngine::onEffectFinished() +{ + // Decrement the count of remaining effects + if (--_remainingEffects == 0) { - if (*effectIt == effect) - { - _activeEffects.erase(effectIt); - break; - } + // All effects have finished, exit the loop + _eventLoop.quit(); + } - // cleanup the effect - effect->deleteLater(); + emit isStopCompleted(); +} + +void EffectEngine::waitForEffectsToStop() +{ + // Only wait if there are instances to wait for + if (_remainingEffects > 0) + { + Debug(_log, "%d effect(s) still running. Wait for completion...", _remainingEffects); + _eventLoop.exec(); // Blocks until all effects are done + } + else + { + Debug(_log, "No effects currently running"); + emit isStopCompleted(); + } } diff --git a/libsrc/hyperion/BGEffectHandler.cpp b/libsrc/hyperion/BGEffectHandler.cpp new file mode 100644 index 000000000..e6bddc5d0 --- /dev/null +++ b/libsrc/hyperion/BGEffectHandler.cpp @@ -0,0 +1,98 @@ +#include + +#include + +BGEffectHandler::BGEffectHandler(Hyperion* hyperion) + : QObject(hyperion) + , _hyperion(hyperion) + , _isBgEffectEnabled(false) + , _isSuspended(false) +{ + QString subComponent = parent()->property("instance").toString(); + _log = Logger::getInstance("HYPERION", subComponent); + + QObject::connect(_hyperion, &Hyperion::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); + QObject::connect(_hyperion->getMuxerInstance(), &PriorityMuxer::prioritiesChanged, this, &BGEffectHandler::handlePriorityUpdate); + + // listen for suspend/resume requests, to not start a background effect when system goes into suspend mode + connect(_hyperion, &Hyperion::suspendRequest, this, [=] (bool isSuspended) { + _isSuspended = isSuspended; + }); + + // initialization + handleSettingsUpdate(settings::BGEFFECT, _hyperion->getSetting(settings::BGEFFECT)); +} + +void BGEffectHandler::disconnect() +{ + QObject::disconnect(_hyperion->getMuxerInstance(), &PriorityMuxer::prioritiesChanged, this, &BGEffectHandler::handlePriorityUpdate); + QObject::disconnect(_hyperion, &Hyperion::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); +} + +bool BGEffectHandler::_isEnabled () const +{ + return _isBgEffectEnabled; +} + +void BGEffectHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if(type == settings::BGEFFECT) + { + _bgEffectConfig = config; + + const QJsonObject& BGEffectConfig = _bgEffectConfig.object(); +#define BGCONFIG_ARRAY bgColorConfig.toArray() + // clear background priority + if (_hyperion->getCurrentPriority() == PriorityMuxer::BG_PRIORITY) + { + _hyperion->clear(PriorityMuxer::BG_PRIORITY); + } + _isBgEffectEnabled = BGEffectConfig["enable"].toBool(true); + + // initial background effect/color + if (_isBgEffectEnabled) + { +#if defined(ENABLE_EFFECTENGINE) + const QString bgTypeConfig = BGEffectConfig["type"].toString("effect"); + const QString bgEffectConfig = BGEffectConfig["effect"].toString("Warm mood blobs"); +#else + const QString bgTypeConfig = "color"; +#endif + const QJsonValue bgColorConfig = BGEffectConfig["color"]; + if (bgTypeConfig.contains("color")) + { + std::vector bg_color = { + ColorRgb { + static_cast(BGCONFIG_ARRAY.at(0).toInt(0)), + static_cast(BGCONFIG_ARRAY.at(1).toInt(0)), + static_cast(BGCONFIG_ARRAY.at(2).toInt(0)) + } + }; + _hyperion->setColor(PriorityMuxer::BG_PRIORITY, bg_color); + Info(_log,"Initial background color set (%d %d %d)",bg_color.at(0).red, bg_color.at(0).green, bg_color.at(0).blue); + } +#if defined(ENABLE_EFFECTENGINE) + else + { + int result = _hyperion->setEffect(bgEffectConfig, PriorityMuxer::BG_PRIORITY, PriorityMuxer::ENDLESS); + Info(_log,"Initial background effect '%s' %s", QSTRING_CSTR(bgEffectConfig), ((result == 0) ? "started" : "failed")); + } +#endif + } +#undef BGCONFIG_ARRAY + } +} + +void BGEffectHandler::handlePriorityUpdate(int currentPriority) +{ + if (currentPriority < PriorityMuxer::BG_PRIORITY && _hyperion->getMuxerInstance()->hasPriority(PriorityMuxer::BG_PRIORITY)) + { + Debug(_log,"Stop background (color-) effect as it moved out of scope"); + _hyperion->clear(PriorityMuxer::BG_PRIORITY); + } + else if (!_isSuspended && currentPriority == PriorityMuxer::LOWEST_PRIORITY && _isBgEffectEnabled) + { + Debug(_log,"Start background (color-) effect as it moved in scope"); + emit handleSettingsUpdate (settings::BGEFFECT, _bgEffectConfig); + } +} diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index b073dcf4b..903463d56 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(hyperion ${CMAKE_SOURCE_DIR}/libsrc/hyperion/AuthManager.cpp # Background Effect Handler ${CMAKE_SOURCE_DIR}/include/hyperion/BGEffectHandler.h + ${CMAKE_SOURCE_DIR}/libsrc/hyperion/BGEffectHandler.cpp # Capture Control class ${CMAKE_SOURCE_DIR}/include/hyperion/CaptureCont.h ${CMAKE_SOURCE_DIR}/libsrc/hyperion/CaptureCont.cpp diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index db9703141..c59ad756b 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -52,12 +52,12 @@ Hyperion::Hyperion(quint8 instance) : QObject() , _instIndex(instance) - , _settingsManager(new SettingsManager(instance, this)) + , _settingsManager(nullptr) , _componentRegister(nullptr) - , _ledString(LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object()))) + , _ledString() , _imageProcessor(nullptr) , _muxer(nullptr) - , _raw2ledAdjustment(hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), getSetting(settings::COLOR).object())) + , _raw2ledAdjustment(nullptr) , _ledDeviceWrapper(nullptr) , _deviceSmooth(nullptr) #if defined(ENABLE_EFFECTENGINE) @@ -68,10 +68,10 @@ Hyperion::Hyperion(quint8 instance) #endif , _log(nullptr) , _hwLedCount() - , _ledGridSize(hyperion::getLedLayoutGridSize(getSetting(settings::LEDS).array())) + , _ledGridSize() , _BGEffectHandler(nullptr) , _captureCont(nullptr) - , _ledBuffer(_ledString.leds().size(), ColorRgb::BLACK) + , _ledBuffer() #if defined(ENABLE_BOBLIGHT_SERVER) , _boblightServer(nullptr) #endif @@ -83,20 +83,24 @@ Hyperion::Hyperion(quint8 instance) _log= Logger::getInstance("HYPERION", subComponent); - _componentRegister = new ComponentRegister(this); - _imageProcessor = new ImageProcessor(_ledString, this); - _muxer = new PriorityMuxer(static_cast(_ledString.leds().size()), this); + _settingsManager.reset(new SettingsManager(instance, this)); + _ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())); + _raw2ledAdjustment.reset(hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), getSetting(settings::COLOR).object())); + _ledGridSize = hyperion::getLedLayoutGridSize(getSetting(settings::LEDS).array()); + _ledBuffer = {_ledString.leds().size(), ColorRgb::BLACK}; + + _componentRegister.reset(new ComponentRegister(this)); + _imageProcessor.reset(new ImageProcessor(_ledString, this)); } Hyperion::~Hyperion() { - freeObjects(); } void Hyperion::start() { // forward settings changed to Hyperion - connect(_settingsManager, &SettingsManager::settingsChanged, this, &Hyperion::settingsChanged); + connect(_settingsManager.get(), &SettingsManager::settingsChanged, this, &Hyperion::settingsChanged); // get newVideoMode from HyperionIManager connect(this, &Hyperion::newVideoMode, this, &Hyperion::handleNewVideoMode); @@ -115,17 +119,19 @@ void Hyperion::start() _ledStringColorOrder.push_back(led.colorOrder); } + _muxer.reset(new PriorityMuxer(static_cast(_ledString.leds().size()), this)); + // connect Hyperion::update with Muxer visible priority changes as muxer updates independent - connect(_muxer, &PriorityMuxer::visiblePriorityChanged, this, &Hyperion::update); - connect(_muxer, &PriorityMuxer::visiblePriorityChanged, this, &Hyperion::handleSourceAvailability); - connect(_muxer, &PriorityMuxer::visibleComponentChanged, this, &Hyperion::handleVisibleComponentChanged); + connect(_muxer.get(), &PriorityMuxer::visiblePriorityChanged, this, &Hyperion::update); + connect(_muxer.get(), &PriorityMuxer::visiblePriorityChanged, this, &Hyperion::handleSourceAvailability); + connect(_muxer.get(), &PriorityMuxer::visibleComponentChanged, this, &Hyperion::handleVisibleComponentChanged); // listen for suspend/resume, idle requests to perform core activation/deactivation actions connect(this, &Hyperion::suspendRequest, this, &Hyperion::setSuspend); connect(this, &Hyperion::idleRequest, this, &Hyperion::setIdle); // listen for settings updates of this instance (LEDS & COLOR) - connect(_settingsManager, &SettingsManager::settingsChanged, this, &Hyperion::handleSettingsUpdate); + connect(_settingsManager.get(), &SettingsManager::settingsChanged, this, &Hyperion::handleSettingsUpdate); #if 0 // set color correction activity state @@ -136,14 +142,15 @@ void Hyperion::start() QJsonObject ledDevice = getSetting(settings::DEVICE).object(); ledDevice["currentLedCount"] = _hwLedCount; // Inject led count info - _ledDeviceWrapper = new LedDeviceWrapper(this); - connect(this, &Hyperion::compStateChangeRequest, _ledDeviceWrapper, &LedDeviceWrapper::handleComponentState); - connect(this, &Hyperion::ledDeviceData, _ledDeviceWrapper, &LedDeviceWrapper::updateLeds); - _ledDeviceWrapper->createLedDevice(ledDevice); + _ledDeviceWrapper.reset(new LedDeviceWrapper(this)); + connect(this, &Hyperion::compStateChangeRequest, _ledDeviceWrapper.get(), &LedDeviceWrapper::handleComponentState); + connect(this, &Hyperion::ledDeviceData, _ledDeviceWrapper.get(), &LedDeviceWrapper::updateLeds); + + _ledDeviceWrapper->createLedDevice(ledDevice); // smoothing - _deviceSmooth = new LinearColorSmoothing(getSetting(settings::SMOOTHING), this); - connect(this, &Hyperion::settingsChanged, _deviceSmooth, &LinearColorSmoothing::handleSettingsUpdate); + _deviceSmooth.reset(new LinearColorSmoothing(getSetting(settings::SMOOTHING), this)); + connect(this, &Hyperion::settingsChanged, _deviceSmooth.get(), &LinearColorSmoothing::handleSettingsUpdate); //Start in pause mode, a new priority will activate smoothing (either start-effect or grabber) _deviceSmooth->setPause(true); @@ -152,7 +159,7 @@ void Hyperion::start() // create the message forwarder only on main instance if (_instIndex == 0) { - _messageForwarder = new MessageForwarder(this); + _messageForwarder.reset(new MessageForwarder(this)); _messageForwarder->handleSettingsUpdate(settings::NETFORWARD, getSetting(settings::NETFORWARD)); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) connect(GlobalSignals::getInstance(), &GlobalSignals::setBufferImage, this, &Hyperion::forwardBufferMessage); @@ -162,17 +169,19 @@ void Hyperion::start() #if defined(ENABLE_EFFECTENGINE) // create the effect engine; needs to be initialized after smoothing! - _effectEngine = new EffectEngine(this); - connect(_effectEngine, &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated); + _effectEngine.reset(new EffectEngine(this)); + connect(_effectEngine.get(), &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated); + + connect(this, &Hyperion::stopEffects, _effectEngine.get(), &EffectEngine::stopAllEffects); #endif // initial startup effect hyperion::handleInitialEffect(this, getSetting(settings::FGEFFECT).object()); // handle background effect - _BGEffectHandler = new BGEffectHandler(this); + _BGEffectHandler.reset(new BGEffectHandler(this)); // create the Daemon capture interface - _captureCont = new CaptureCont(this); + _captureCont.reset(new CaptureCont(this)); // forwards global signals to the corresponding slots connect(GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput, this, &Hyperion::registerInput); @@ -185,8 +194,8 @@ void Hyperion::start() #if defined(ENABLE_BOBLIGHT_SERVER) // boblight, can't live in global scope as it depends on layout - _boblightServer = new BoblightServer(this, getSetting(settings::BOBLSERVER)); - connect(this, &Hyperion::settingsChanged, _boblightServer, &BoblightServer::handleSettingsUpdate); + _boblightServer.reset(new BoblightServer(this, getSetting(settings::BOBLSERVER))); + connect(this, &Hyperion::settingsChanged, _boblightServer.get(), &BoblightServer::handleSettingsUpdate); #endif // instance initiated, enter thread event loop @@ -195,44 +204,23 @@ void Hyperion::start() void Hyperion::stop() { - emit finished(); - thread()->wait(); -} + Debug(_log, "Hyperion instance %d is stopping", _instIndex); -void Hyperion::freeObjects() -{ //Disconnect Background effect first that it does not kick in when other priorities are stopped _BGEffectHandler->disconnect(); - //Remove all priorities to switch off all leds - clear(-1,true); - - // delete components on exit of hyperion core - - delete _BGEffectHandler; - -#if defined(ENABLE_BOBLIGHT_SERVER) - delete _boblightServer; -#endif - - delete _captureCont; + //Remove all priorities + _muxer->clearAll(true); #if defined(ENABLE_EFFECTENGINE) - delete _effectEngine; -#endif + _effectEngine->stopAllEffects(); + #endif - delete _raw2ledAdjustment; - -#if defined(ENABLE_FORWARDER) - delete _messageForwarder; -#endif + _ledDeviceWrapper->stopDevice(); - delete _settingsManager; - delete _ledDeviceWrapper; + _muxer->stop(); - delete _imageProcessor; - delete _muxer; - delete _componentRegister; + emit finished(); } void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& config) @@ -241,8 +229,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co { const QJsonObject obj = config.object(); // change in color recreate ledAdjustments - delete _raw2ledAdjustment; - _raw2ledAdjustment = hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), obj); + _raw2ledAdjustment.reset(hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), obj)); if (!_raw2ledAdjustment->verifyAdjustments()) { @@ -264,7 +251,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co _muxer->updateLedColorsLength(static_cast(_ledString.leds().size())); _ledGridSize = hyperion::getLedLayoutGridSize(leds); - std::vector color(_ledString.leds().size(), ColorRgb{0,0,0}); + std::vector color(_ledString.leds().size(), ColorRgb::BLACK); _ledBuffer = color; _ledStringColorOrder.clear(); @@ -277,8 +264,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co _hwLedCount = getSetting(settings::DEVICE).object()["hardwareLedCount"].toInt(getLedCount()); // change in leds are also reflected in adjustment - delete _raw2ledAdjustment; - _raw2ledAdjustment = hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), getSetting(settings::COLOR).object()); + _raw2ledAdjustment.reset(hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), getSetting(settings::COLOR).object())); #if defined(ENABLE_EFFECTENGINE) // start cached effects @@ -322,9 +308,9 @@ QJsonDocument Hyperion::getSetting(settings::type type) const } // TODO: Remove function, if UI is able to handle full configuration -QJsonObject Hyperion::getQJsonConfig() const +QJsonObject Hyperion::getQJsonConfig(quint8 inst) const { - const QJsonObject instanceConfig = _settingsManager->getSettings(); + const QJsonObject instanceConfig = _settingsManager->getSettings(inst); const QJsonObject globalConfig = _settingsManager->getSettings({},QStringList()); return JsonUtils::mergeJsonObjects(instanceConfig, globalConfig); } @@ -655,8 +641,15 @@ void Hyperion::handleSourceAvailability(int priority) { if ( previousPriority == PriorityMuxer::LOWEST_PRIORITY ) { - Debug(_log,"new source available -> Resume output processing and switch LED-Device on"); - emit _ledDeviceWrapper->switchOn(); + if(_ledDeviceWrapper->isEnabled()) + { + Debug(_log,"new source available -> LED-Device is enabled, switch LED-device on and resume output processing"); + emit _ledDeviceWrapper->switchOn(); + } + else + { + Debug(_log,"new source available -> LED-Device not enabled, cannot switch on LED-device"); + } emit _deviceSmooth->setPause(false); } } @@ -735,7 +728,8 @@ void Hyperion::update() } // Write the data to the device - if (_ledDeviceWrapper->enabled()) + //if (_ledDeviceWrapper->isEnabled()) + if (_ledDeviceWrapper->isOn()) { // Smoothing is disabled if (! _deviceSmooth->enabled()) diff --git a/libsrc/hyperion/HyperionIManager.cpp b/libsrc/hyperion/HyperionIManager.cpp index 254d1bf09..e93a03dd6 100644 --- a/libsrc/hyperion/HyperionIManager.cpp +++ b/libsrc/hyperion/HyperionIManager.cpp @@ -12,18 +12,24 @@ HyperionIManager* HyperionIManager::HIMinstance; HyperionIManager::HyperionIManager(QObject* parent) : QObject(parent) , _log(Logger::getInstance("HYPERION-INSTMGR")) - , _instanceTable( new InstanceTable()) + , _instanceTable(new InstanceTable(this)) { HIMinstance = this; qRegisterMetaType("InstanceState"); _instanceTable->createDefaultInstance(); } -Hyperion* HyperionIManager::getHyperionInstance(quint8 instance) +HyperionIManager::~HyperionIManager() { - Hyperion* pInstance {nullptr}; +} + +QSharedPointer HyperionIManager::getHyperionInstance(quint8 instance) +{ + QSharedPointer pInstance {nullptr}; if(_runningInstances.contains(instance)) + { return _runningInstances.value(instance); + } if (!_runningInstances.isEmpty()) { @@ -39,7 +45,7 @@ QVector HyperionIManager::getInstanceData() const for( auto & entry : instances) { // add running state - entry["running"] = _runningInstances.contains(entry["instance"].toInt()); + entry["running"] = _runningInstances.contains(static_cast(entry["instance"].toInt())); } return instances; } @@ -62,7 +68,7 @@ QList HyperionIManager::getInstanceIds() const void HyperionIManager::startAll() { - const QVector instances = _instanceTable->getAllInstances(true); + QVector const instances = _instanceTable->getAllInstances(true); if (instances.isEmpty()) { Error(_log, "No enabled instances found to be started"); @@ -71,17 +77,20 @@ void HyperionIManager::startAll() for(const auto & entry : instances) { - startInstance(entry["instance"].toInt()); + startInstance(static_cast(entry["instance"].toInt())); } } void HyperionIManager::stopAll() { - // copy the instances due to loop corruption, even with .erase() return next iter - QMap instCopy = _runningInstances; - for(auto *const instance : instCopy) + Debug(_log, "Running instances: %d, starting instances: %d" + , _runningInstances.size() + , _startingInstances.size()); + + QMap> const instCopy = _runningInstances; + for(auto const &instance : instCopy) { - instance->stop(); + QMetaObject::invokeMethod(instance.get(), "stop", Qt::QueuedConnection); } } @@ -113,8 +122,7 @@ void HyperionIManager::handleEvent(Event event) void HyperionIManager::toggleSuspend(bool isSuspend) { Info(_log,"Put all instances in %s state", isSuspend ? "suspend" : "working"); - QMap instCopy = _runningInstances; - for(const auto instance : instCopy) + for(const auto& instance : std::as_const(_runningInstances)) { emit instance->suspendRequest(isSuspend); } @@ -123,8 +131,7 @@ void HyperionIManager::toggleSuspend(bool isSuspend) void HyperionIManager::toggleIdle(bool isIdle) { Info(_log,"Put all instances in %s state", isIdle ? "idle" : "working"); - QMap instCopy = _runningInstances; - for(const auto instance : instCopy) + for(const auto& instance : std::as_const(_runningInstances)) { emit instance->idleRequest(isIdle); } @@ -133,8 +140,7 @@ void HyperionIManager::toggleIdle(bool isIdle) void HyperionIManager::toggleStateAllInstances(bool enable) { // copy the instances due to loop corruption, even with .erase() return next iter - QMap instCopy = _runningInstances; - for(const auto instance : instCopy) + for(const auto& instance : std::as_const(_runningInstances)) { emit instance->compStateChangeRequest(hyperion::COMP_ALL, enable); } @@ -144,27 +150,28 @@ bool HyperionIManager::startInstance(quint8 inst, bool block, QObject* caller, i { if(_instanceTable->instanceExist(inst)) { - if(!_runningInstances.contains(inst) && !_startQueue.contains(inst)) + if(!_runningInstances.contains(inst) && !_startingInstances.contains(inst)) { - QThread* hyperionThread = new QThread(); + QSharedPointer const hyperion(new Hyperion(inst), &QObject::deleteLater); + + QThread* hyperionThread = new QThread(this); hyperionThread->setObjectName("HyperionThread"); - Hyperion* hyperion = new Hyperion(inst); + hyperion->moveToThread(hyperionThread); // setup thread management - connect(hyperionThread, &QThread::started, hyperion, &Hyperion::start); - connect(hyperion, &Hyperion::started, this, &HyperionIManager::handleStarted); - connect(hyperion, &Hyperion::finished, this, &HyperionIManager::handleFinished); - connect(hyperion, &Hyperion::finished, hyperionThread, &QThread::quit, Qt::DirectConnection); + connect(hyperionThread, &QThread::started, hyperion.get(), &Hyperion::start); + connect(hyperion.get(), &Hyperion::started, this, &HyperionIManager::handleStarted); + connect(hyperion.get(), &Hyperion::finished, this, &HyperionIManager::handleFinished); // setup further connections // from Hyperion - connect(hyperion, &Hyperion::settingsChanged, this, &HyperionIManager::settingsChanged); - connect(hyperion, &Hyperion::videoMode, this, &HyperionIManager::requestVideoMode); + connect(hyperion.get(), &Hyperion::settingsChanged, this, &HyperionIManager::settingsChanged); + connect(hyperion.get(), &Hyperion::videoMode, this, &HyperionIManager::requestVideoMode); // to Hyperion - connect(this, &HyperionIManager::newVideoMode, hyperion, &Hyperion::newVideoMode); + connect(this, &HyperionIManager::newVideoMode, hyperion.get(), &Hyperion::newVideoMode); // add to queue and start - _startQueue << inst; + _startingInstances.insert(inst, hyperion); hyperionThread->start(); // update db @@ -178,7 +185,7 @@ bool HyperionIManager::startInstance(quint8 inst, bool block, QObject* caller, i if (!_pendingRequests.contains(inst) && caller != nullptr) { - PendingRequests newDef{caller, tan}; + PendingRequests const newDef{caller, tan}; _pendingRequests[inst] = newDef; } @@ -193,9 +200,11 @@ bool HyperionIManager::startInstance(quint8 inst, bool block, QObject* caller, i bool HyperionIManager::stopInstance(quint8 inst) { - // inst 0 can't be stopped + // instance 0 cannot be stopped if(!isInstAllowed(inst)) + { return false; + } if(_instanceTable->instanceExist(inst)) { @@ -203,7 +212,7 @@ bool HyperionIManager::stopInstance(quint8 inst) { // notify a ON_STOP rather sooner than later, queued signal listener should have some time to drop the pointer before it's deleted emit instanceStateChanged(InstanceState::H_ON_STOP, inst); - Hyperion* hyperion = _runningInstances.value(inst); + QSharedPointer const hyperion = _runningInstances.value(inst); hyperion->stop(); // update db @@ -228,7 +237,10 @@ bool HyperionIManager::createInstance(const QString& name, bool start) emit change(); if(start) + { startInstance(inst); + } + return true; } return false; @@ -269,33 +281,60 @@ bool HyperionIManager::saveName(quint8 inst, const QString& name) void HyperionIManager::handleFinished() { Hyperion* hyperion = qobject_cast(sender()); - quint8 instance = hyperion->getInstanceIndex(); + if (hyperion != nullptr) + { + quint8 const instance = hyperion->getInstanceIndex(); + _startingInstances.remove(instance); + _runningInstances.remove(instance); - Info(_log,"Hyperion instance '%s' stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + // Manually stop the thread and cleanup + QThread* thread = hyperion->thread(); + if (thread != nullptr) + { + thread->quit(); + thread->wait(); + } + + emit instanceStateChanged(InstanceState::H_STOPPED, instance); + emit change(); - _runningInstances.remove(instance); - hyperion->thread()->deleteLater(); - hyperion->deleteLater(); - emit instanceStateChanged(InstanceState::H_STOPPED, instance); - emit change(); + Info(_log,"Hyperion instance '%s' stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + + if ( _runningInstances.size() == 0 ) + { + Info(_log,"All Hyperion instances are stopped"); + emit areAllInstancesStopped(); + } + } } void HyperionIManager::handleStarted() { Hyperion* hyperion = qobject_cast(sender()); - quint8 instance = hyperion->getInstanceIndex(); + quint8 const instanceId = hyperion->getInstanceIndex(); - Info(_log,"Hyperion instance '%s' has been started", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + if (_startingInstances.contains(instanceId)) + { + Info(_log,"Hyperion instance '%s' has been started", QSTRING_CSTR(_instanceTable->getNamebyIndex(instanceId))); + + QSharedPointer const hyperionInstance = _startingInstances.value(instanceId); + _runningInstances.insert(instanceId, hyperionInstance); + _startingInstances.remove(instanceId); - _startQueue.removeAll(instance); - _runningInstances.insert(instance, hyperion); - emit instanceStateChanged(InstanceState::H_STARTED, instance); - emit change(); + emit instanceStateChanged(InstanceState::H_STARTED, instanceId); + emit change(); - if (_pendingRequests.contains(instance)) + if (_pendingRequests.contains(instanceId)) + { + PendingRequests const def = _pendingRequests.take(instanceId); + emit startInstanceResponse(def.caller, def.tan); + _pendingRequests.remove(instanceId); + } + } + else { - PendingRequests def = _pendingRequests.take(instance); - emit startInstanceResponse(def.caller, def.tan); - _pendingRequests.remove(instance); + Error(_log, "Could not find instance '%s (index: %i)' in the starting list", + QSTRING_CSTR(_instanceTable->getNamebyIndex(instanceId)), instanceId); } + } diff --git a/libsrc/hyperion/ImageProcessor.cpp b/libsrc/hyperion/ImageProcessor.cpp index 6f9c62221..54fd90706 100644 --- a/libsrc/hyperion/ImageProcessor.cpp +++ b/libsrc/hyperion/ImageProcessor.cpp @@ -142,7 +142,6 @@ void ImageProcessor::setSize(int width, int height) void ImageProcessor::setLedString(const LedString& ledString) { - Debug(_log,""); if ( !_imageToLedColors.isNull() ) { _ledString = ledString; diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 06edc4982..bb0a99d4c 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -78,7 +78,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion _log= Logger::getInstance("SMOOTHING", subComponent); // timer - _timer = new QTimer(this); + _timer.reset(new QTimer(this)); _timer->setTimerType(Qt::PreciseTimer); // init cfg (default) @@ -91,7 +91,7 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion // listen for comp changes connect(_hyperion, &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange); - connect(_timer, &QTimer::timeout, this, &LinearColorSmoothing::updateLeds); + connect(_timer.get(), &QTimer::timeout, this, &LinearColorSmoothing::updateLeds); connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, [=] (int priority){ const PriorityMuxer::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority); @@ -105,7 +105,6 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion LinearColorSmoothing::~LinearColorSmoothing() { - delete _timer; } void LinearColorSmoothing::handleSettingsUpdate(settings::type type, const QJsonDocument &config) diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index 91024744c..3bf46696b 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -64,6 +64,14 @@ PriorityMuxer::~PriorityMuxer() { } +void PriorityMuxer::stop() +{ + _timer->stop(); + _updateTimer->stop(); + _blockTimer->stop(); + Debug(_log, "Priority-Muxer stopped"); +} + void PriorityMuxer::setEnable(bool enable) { enable ? _updateTimer->start() : _updateTimer->stop(); diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index 5772ce27f..49ce628d0 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -11,6 +11,7 @@ #include "hyperion/Hyperion.h" #include +#include //std includes #include @@ -101,6 +102,7 @@ void LedDevice::stop() this->disable(); this->stopRefreshTimer(); Info(_log, "Stopped LedDevice '%s'", QSTRING_CSTR(_activeDeviceType)); + emit isStopped(); } int LedDevice::open() @@ -132,7 +134,7 @@ void LedDevice::setInError(const QString& errorMsg, bool isRecoverable) _isDeviceRecoverable = isRecoverable; } Error(_log, "Device disabled, device '%s' signals error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(errorMsg)); - emit enableStateChanged(_isEnabled); + emit isEnabledChanged(_isEnabled); } void LedDevice::enable() @@ -167,28 +169,32 @@ void LedDevice::enable() stopEnableAttemptsTimer(); _isEnabled = true; isEnableFailed = false; - emit enableStateChanged(_isEnabled); + emit isEnabledChanged(_isEnabled); Info(_log, "LedDevice '%s' enabled", QSTRING_CSTR(_activeDeviceType)); } } if (isEnableFailed) { - emit enableStateChanged(false); - - if (_maxEnableAttempts > 0 && _isDeviceRecoverable) - { - Debug(_log, "Device's enablement failed - Start retry timer. Retried already done [%d], isEnabled: [%d]", _enableAttempts, _isEnabled); - startEnableAttemptsTimer(); - } - else - { - Debug(_log, "Device's enablement failed"); - } + emit isEnabledChanged(false); + QMetaObject::invokeMethod(this, "retryEnable", Qt::QueuedConnection); } } } +void LedDevice::retryEnable() +{ + if (_maxEnableAttempts > 0 && _isDeviceRecoverable) + { + Debug(_log, "Device's enablement failed - Start retry timer. Retried already done [%d], isEnabled: [%d]", _enableAttempts, _isEnabled); + startEnableAttemptsTimer(); + } + else + { + Debug(_log, "Device's enablement failed"); + } +} + void LedDevice::disable() { Debug(_log, "Disable device %s'", QSTRING_CSTR(_activeDeviceType)); @@ -201,7 +207,7 @@ void LedDevice::disable() switchOff(); close(); - emit enableStateChanged(_isEnabled); + emit isEnabledChanged(_isEnabled); } } @@ -235,9 +241,9 @@ void LedDevice::startRefreshTimer() // setup refreshTimer if (_refreshTimer == nullptr) { - _refreshTimer = new QTimer(this); + _refreshTimer.reset(new QTimer(this)); _refreshTimer->setTimerType(Qt::PreciseTimer); - connect(_refreshTimer, &QTimer::timeout, this, &LedDevice::rewriteLEDs); + connect(_refreshTimer.get(), &QTimer::timeout, this, &LedDevice::rewriteLEDs); } _refreshTimer->setInterval(_refreshTimerInterval_ms); _refreshTimer->start(); @@ -254,8 +260,6 @@ void LedDevice::stopRefreshTimer() if (_refreshTimer != nullptr) { _refreshTimer->stop(); - delete _refreshTimer; - _refreshTimer = nullptr; } } @@ -272,9 +276,9 @@ void LedDevice::startEnableAttemptsTimer() // setup enable retry timer if (_enableAttemptsTimer == nullptr) { - _enableAttemptsTimer = new QTimer(this); + _enableAttemptsTimer.reset(new QTimer(this)); _enableAttemptsTimer->setTimerType(Qt::PreciseTimer); - connect(_enableAttemptsTimer, &QTimer::timeout, this, &LedDevice::enable); + connect(_enableAttemptsTimer.get(), &QTimer::timeout, this, &LedDevice::enable); } _enableAttemptsTimer->setInterval(static_cast(_enableAttemptTimerInterval.count() * 1000)); //NOLINT @@ -296,8 +300,6 @@ void LedDevice::stopEnableAttemptsTimer() { Debug(_log, "Stopping enable retry timer"); _enableAttemptsTimer->stop(); - delete _enableAttemptsTimer; - _enableAttemptsTimer = nullptr; _enableAttempts = 0; } } @@ -366,16 +368,14 @@ int LedDevice::writeBlack(int numberOfWrites) int LedDevice::writeColor(const ColorRgb& color, int numberOfWrites) { + Debug(_log,"Writes: [%d]", numberOfWrites); int rc = -1; for (int i = 0; i < numberOfWrites; i++) { if (_latchTime_ms > 0) { - // Wait latch time before writing black - QEventLoop loop; - QTimer::singleShot(_latchTime_ms, &loop, &QEventLoop::quit); - loop.exec(); + wait(_latchTime_ms); } _lastLedValues = std::vector(static_cast(_ledCount), color); rc = write(_lastLedValues); @@ -403,7 +403,6 @@ bool LedDevice::switchOn() { Info(_log, "Device %s is ON", QSTRING_CSTR(_activeDeviceType)); _isOn = true; - emit enableStateChanged(_isEnabled); rc = true; } else @@ -412,6 +411,7 @@ bool LedDevice::switchOn() } } } + emit isOnChanged(_isOn); } return rc; } @@ -455,6 +455,7 @@ bool LedDevice::switchOff() } } } + emit isOnChanged(_isOn); } return rc; } diff --git a/libsrc/leddevice/LedDeviceWrapper.cpp b/libsrc/leddevice/LedDeviceWrapper.cpp index 535320745..909611b91 100644 --- a/libsrc/leddevice/LedDeviceWrapper.cpp +++ b/libsrc/leddevice/LedDeviceWrapper.cpp @@ -11,71 +11,70 @@ #include // qt +#include #include #include #include +#include LedDeviceRegistry LedDeviceWrapper::_ledDeviceMap {}; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QRecursiveMutex LedDeviceWrapper::_ledDeviceMapLock; -#else - QMutex LedDeviceWrapper::_ledDeviceMapLock{ QMutex::Recursive }; -#endif +QMutex LedDeviceWrapper::_ledDeviceMapLock{}; LedDeviceWrapper::LedDeviceWrapper(Hyperion* hyperion) : QObject(hyperion) + , _log(nullptr) , _hyperion(hyperion) , _ledDevice(nullptr) - , _enabled(false) + , _isEnabled(false) { // prepare the device constructor map - #define REGISTER(className) LedDeviceWrapper::addToDeviceMap(QString(#className).toLower(), LedDevice##className::construct); +#define REGISTER(className) LedDeviceWrapper::addToDeviceMap(QString(#className).toLower(), LedDevice##className::construct); // the REGISTER() calls are auto-generated by cmake. - #include "LedDevice_register.cpp" +#include "LedDevice_register.cpp" - #undef REGISTER +#undef REGISTER + + QString const subComponent = parent()->property("instance").toString(); + _log = Logger::getInstance("LEDDEVICE", subComponent); _hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, false); } LedDeviceWrapper::~LedDeviceWrapper() { - stopDeviceThread(); } void LedDeviceWrapper::createLedDevice(const QJsonObject& config) { - if(_ledDevice != nullptr) + if (_ledDevice != nullptr) { - stopDeviceThread(); + stopDevice(); } - // create thread and device - QThread* thread = new QThread(this); - thread->setObjectName("LedDeviceThread"); - _ledDevice = LedDeviceFactory::construct(config); + _ledDeviceThread.reset(new QThread()); + _ledDeviceThread->setObjectName("LedDeviceThread"); + _ledDevice.reset(LedDeviceFactory::construct(config)); - QString subComponent = parent()->property("instance").toString(); + QString const subComponent = parent()->property("instance").toString(); _ledDevice->setLogger(Logger::getInstance("LEDDEVICE", subComponent)); - _ledDevice->moveToThread(thread); - // setup thread management - connect(thread, &QThread::started, _ledDevice, &LedDevice::start); - - // further signals - connect(this, &LedDeviceWrapper::updateLeds, _ledDevice, &LedDevice::updateLeds, Qt::BlockingQueuedConnection); - - connect(this, &LedDeviceWrapper::switchOn, _ledDevice, &LedDevice::switchOn, Qt::BlockingQueuedConnection); - connect(this, &LedDeviceWrapper::switchOff, _ledDevice, &LedDevice::switchOff, Qt::BlockingQueuedConnection); + _ledDevice->moveToThread(_ledDeviceThread.get()); - connect(this, &LedDeviceWrapper::stopLedDevice, _ledDevice, &LedDevice::stop, Qt::BlockingQueuedConnection); + connect(_ledDeviceThread.get(), &QThread::started, _ledDevice.get(), &LedDevice::start); + connect(this, &LedDeviceWrapper::updateLeds, _ledDevice.get(), &LedDevice::updateLeds); + connect(this, &LedDeviceWrapper::switchOn, _ledDevice.get(), &LedDevice::switchOn); + connect(this, &LedDeviceWrapper::switchOff, _ledDevice.get(), &LedDevice::switchOff); + connect(this, &LedDeviceWrapper::enable, _ledDevice.get(), &LedDevice::enable); + connect(this, &LedDeviceWrapper::disable, _ledDevice.get(), &LedDevice::disable); + connect(this, &LedDeviceWrapper::stop, _ledDevice.get(), &LedDevice::stop ); - connect(_ledDevice, &LedDevice::enableStateChanged, this, &LedDeviceWrapper::handleInternalEnableState, Qt::QueuedConnection); + //Handle LED-device state changes + connect(_ledDevice.get(), &LedDevice::isEnabledChanged, this, &LedDeviceWrapper::onIsEnabledChanged); + connect(_ledDevice.get(), &LedDevice::isOnChanged, this, &LedDeviceWrapper::onIsOnChanged); - // start the thread - thread->start(); + _ledDeviceThread->start(); } void LedDeviceWrapper::handleComponentState(hyperion::Components component, bool state) @@ -84,82 +83,102 @@ void LedDeviceWrapper::handleComponentState(hyperion::Components component, bool { if (state) { - QMetaObject::invokeMethod(_ledDevice, "enable", Qt::BlockingQueuedConnection); + emit enable(); } else { - QMetaObject::invokeMethod(_ledDevice, "disable", Qt::BlockingQueuedConnection); + emit disable(); } - - QMetaObject::invokeMethod(_ledDevice, "componentState", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, _enabled)); } } -void LedDeviceWrapper::handleInternalEnableState(bool newState) +void LedDeviceWrapper::onIsEnabledChanged(bool isEnabled) { - _hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, newState); - _enabled = newState; + _hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, isEnabled); + _isEnabled = isEnabled; +} - if (_enabled) +void LedDeviceWrapper::onIsOnChanged(bool isOn) +{ + _isOn = isOn; + if (_isOn) { _hyperion->update(); } } -void LedDeviceWrapper::stopDeviceThread() +void LedDeviceWrapper::stopDevice() { - // turns the LEDs off & stop refresh timers - emit stopLedDevice(); - - // get current thread - QThread* oldThread = _ledDevice->thread(); - disconnect(oldThread, nullptr, nullptr, nullptr); - oldThread->quit(); - oldThread->wait(); - delete oldThread; + Debug(_log, "Stop LED-Device thread"); + + //Disable updates to the LedDevice + disconnect(this, &LedDeviceWrapper::updateLeds, _ledDevice.get(), &LedDevice::updateLeds); + disconnect(this, &LedDeviceWrapper::switchOff, _ledDevice.get(), &LedDevice::switchOff); + disconnect(this, &LedDeviceWrapper::disable, _ledDevice.get(), &LedDevice::disable); + disconnect(this, &LedDeviceWrapper::enable, _ledDevice.get(), &LedDevice::enable); + disconnect(this, &LedDeviceWrapper::switchOn, _ledDevice.get(), &LedDevice::switchOn); + + // Create a local event loop to wait for the LedDevice isStopped signal + QEventLoop loop; + connect(_ledDevice.get(), &LedDevice::isStopped, &loop, &QEventLoop::quit); + // Turn the LEDs off & stops refresh timers + emit stop(); + loop.exec(); + + if (!_ledDeviceThread.isNull()) + { + if (_ledDeviceThread->isRunning()) + { + _ledDeviceThread->quit(); + _ledDeviceThread->wait(); + } + } - disconnect(_ledDevice, nullptr, nullptr, nullptr); - delete _ledDevice; - _ledDevice = nullptr; + Debug(_log, "LED-Device thread stopped"); + emit isStopped(); } QString LedDeviceWrapper::getActiveDeviceType() const { - QString value = 0; - QMetaObject::invokeMethod(_ledDevice, "getActiveDeviceType", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value)); + QString value = nullptr; + QMetaObject::invokeMethod(_ledDevice.get(), "getActiveDeviceType", Q_RETURN_ARG(QString, value)); return value; } -unsigned int LedDeviceWrapper::getLedCount() const +int LedDeviceWrapper::getLedCount() const { - int value = 0; - QMetaObject::invokeMethod(_ledDevice, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value)); - return value; + int count = 0; + QMetaObject::invokeMethod(_ledDevice.get(), "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, count)); + return count; } QString LedDeviceWrapper::getColorOrder() const { QString value; - QMetaObject::invokeMethod(_ledDevice, "getColorOrder", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value)); + QMetaObject::invokeMethod(_ledDevice.get(), "getColorOrder", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, value)); return value; } int LedDeviceWrapper::getLatchTime() const { int value = 0; - QMetaObject::invokeMethod(_ledDevice, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value)); + QMetaObject::invokeMethod(_ledDevice.get(), "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, value)); return value; } -bool LedDeviceWrapper::enabled() const +bool LedDeviceWrapper::isEnabled() const { - return _enabled; + return _isEnabled; +} + +bool LedDeviceWrapper::isOn() const +{ + return _isOn; } int LedDeviceWrapper::addToDeviceMap(QString name, LedDeviceCreateFuncType funcPtr) { QMutexLocker lock(&_ledDeviceMapLock); - _ledDeviceMap.emplace(name,funcPtr); return 0; @@ -168,7 +187,6 @@ int LedDeviceWrapper::addToDeviceMap(QString name, LedDeviceCreateFuncType funcP const LedDeviceRegistry& LedDeviceWrapper::getDeviceMap() { QMutexLocker lock(&_ledDeviceMapLock); - return _ledDeviceMap; } @@ -178,13 +196,14 @@ QJsonObject LedDeviceWrapper::getLedDeviceSchemas() Q_INIT_RESOURCE(LedDeviceSchemas); // read the JSON schema from the resource - QDir dir(":/leddevices/"); - QJsonObject result, schemaJson; + QDir const dir(":/leddevices/"); + QJsonObject result; + QJsonObject schemaJson; for(QString &item : dir.entryList()) { - QString schemaPath(QString(":/leddevices/")+item); - QString devName = item.remove("schema-"); + QString const schemaPath(QString(":/leddevices/")+item); + QString const devName = item.remove("schema-"); QString data; if(!FileUtils::readFile(schemaPath, data, Logger::getInstance("LEDDEVICE"))) @@ -193,10 +212,10 @@ QJsonObject LedDeviceWrapper::getLedDeviceSchemas() } QJsonObject schema; - QPair parsingResult = JsonUtils::parse(schemaPath, data, schema, Logger::getInstance("LEDDEVICE")); + QPair const parsingResult = JsonUtils::parse(schemaPath, data, schema, Logger::getInstance("LEDDEVICE")); if (!parsingResult.first) { - QStringList errorList = parsingResult.second; + QStringList const errorList = parsingResult.second; for (const auto& errorMessage : errorList) { Debug(Logger::getInstance("LEDDEVICE"), "JSON parse error: %s ", QSTRING_CSTR(errorMessage)); } diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 54d7bd612..db1a1000a 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -1561,7 +1561,6 @@ bool PhilipsHueLight::isBusy() void PhilipsHueLight::setBlack() { - Debug(_log,""); CiColor black; black.bri = 0; black.x = 0; @@ -2647,7 +2646,7 @@ bool LedDevicePhilipsHue::switchOn() if (_isOn) { Info(_log, "Device %s is ON", QSTRING_CSTR(_activeDeviceType)); - emit enableStateChanged(_isEnabled); + emit isEnabledChanged(_isEnabled); rc =true; } else diff --git a/libsrc/leddevice/dev_net/LedDeviceRazer.cpp b/libsrc/leddevice/dev_net/LedDeviceRazer.cpp index 26a116a82..f80c4b31a 100644 --- a/libsrc/leddevice/dev_net/LedDeviceRazer.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceRazer.cpp @@ -60,7 +60,7 @@ bool LedDeviceRazer::init(const QJsonObject& deviceConfig) { bool isInitOK = false; setRewriteTime(HEARTBEAT_INTERVALL.count()); - connect(_refreshTimer, &QTimer::timeout, this, &LedDeviceRazer::rewriteLEDs); + connect(_refreshTimer.get(), &QTimer::timeout, this, &LedDeviceRazer::rewriteLEDs); // Initialise sub-class if (LedDevice::init(deviceConfig)) diff --git a/libsrc/leddevice/dev_other/LedDeviceFile.cpp b/libsrc/leddevice/dev_other/LedDeviceFile.cpp index cc7ddd2d3..d84fc9307 100644 --- a/libsrc/leddevice/dev_other/LedDeviceFile.cpp +++ b/libsrc/leddevice/dev_other/LedDeviceFile.cpp @@ -3,6 +3,7 @@ // Qt includes #include #include +#include LedDeviceFile::LedDeviceFile(const QJsonObject &deviceConfig) : LedDevice(deviceConfig) @@ -13,7 +14,6 @@ LedDeviceFile::LedDeviceFile(const QJsonObject &deviceConfig) LedDeviceFile::~LedDeviceFile() { - delete _file; } LedDevice* LedDeviceFile::construct(const QJsonObject &deviceConfig) @@ -45,7 +45,7 @@ void LedDeviceFile::initFile(const QString &fileName) { if ( _file == nullptr ) { - _file = new QFile(fileName, this); + _file.reset(new QFile(fileName)); } } @@ -89,9 +89,20 @@ int LedDeviceFile::close() return retval; } +bool LedDeviceFile::powerOff() +{ + // Simulate power-off by writing a final "Black" to have a defined outcome + bool rc = false; + if ( writeBlack( 5 ) >= 0 ) + { + rc = true; + } + return rc; +} + int LedDeviceFile::write(const std::vector & ledValues) { - QTextStream out(_file); + QTextStream out(_file.get()); if ( _printTimeStamp ) { QDateTime now = QDateTime::currentDateTime(); diff --git a/libsrc/leddevice/dev_other/LedDeviceFile.h b/libsrc/leddevice/dev_other/LedDeviceFile.h index 950170073..dc80d7860 100644 --- a/libsrc/leddevice/dev_other/LedDeviceFile.h +++ b/libsrc/leddevice/dev_other/LedDeviceFile.h @@ -6,7 +6,7 @@ // Qt includes #include -#include +#include /// /// Implementation of the LedDevice that write the LED-colors to an @@ -59,6 +59,15 @@ class LedDeviceFile : public LedDevice /// int close() override; + /// + /// @brief Power-/turn off a file-device + /// + /// The off-state is simulated by writing "Black to LED" + /// + /// @return True, if success + /// + bool powerOff() override; + /// /// @brief Writes the RGB-Color values to the LEDs. /// @@ -68,7 +77,7 @@ class LedDeviceFile : public LedDevice int write(const std::vector & ledValues) override; /// The outputstream - QFile* _file; + QScopedPointer _file; private: diff --git a/libsrc/mdns/MdnsProvider.cpp b/libsrc/mdns/MdnsProvider.cpp index eaaffb7f9..b5df49dbe 100644 --- a/libsrc/mdns/MdnsProvider.cpp +++ b/libsrc/mdns/MdnsProvider.cpp @@ -22,6 +22,10 @@ MdnsProvider::MdnsProvider(QObject* parent) { } +MdnsProvider::~MdnsProvider() +{ +} + void MdnsProvider::init() { _server.reset(new QMdnsEngine::Server()); @@ -31,9 +35,11 @@ void MdnsProvider::init() DebugIf(verboseProvider, _log, "Hostname [%s], isRegistered [%d]", _hostname->hostname().constData(), _hostname->isRegistered()); } -MdnsProvider::~MdnsProvider() +void MdnsProvider::stop() { _providedServiceTypes.clear(); + _server.reset(); + _hostname.reset(); Info(_log, "mDNS info service stopped"); } @@ -49,7 +55,6 @@ void MdnsProvider::publishService(const QString& serviceType, quint16 servicePor { QSharedPointer newProvider = QSharedPointer::create(_server.data(), _hostname.data()); _providedServiceTypes.insert(type, newProvider); - } QSharedPointer provider = _providedServiceTypes.value(type); diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp index aa8061259..b01271fcf 100644 --- a/libsrc/protoserver/ProtoServer.cpp +++ b/libsrc/protoserver/ProtoServer.cpp @@ -123,6 +123,6 @@ void ProtoServer::stopServer() client->forceClose(); } _server->close(); - Info(_log, "Stopped"); + Info(_log, "ProtocolBuffer-Server stopped"); } } diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 731001e12..4c9e29521 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -1,5 +1,8 @@ +#include +#include #include +#include #include #include #include @@ -9,10 +12,21 @@ #include #include #include - +#include +#include +#include + +#include "db/SettingsTable.h" +#include "events/EventScheduler.h" +#include "events/OsEventHandler.h" +#include #include -#include #include +#include +#include "utils/ColorRgb.h" +#include "utils/Logger.h" +#include "utils/VideoMode.h" +#include "utils/global_defines.h" #include // Required to determine the cmake options @@ -107,7 +121,7 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo _instanceManager->startAll(); //Cleaning up Hyperion before quit - connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &HyperionDaemon::stoppServices); + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &HyperionDaemon::stopServices); //Handle services dependent on the on first instance's availability connect(_instanceManager.get(), &HyperionIManager::instanceStateChanged, this, &HyperionDaemon::handleInstanceStateChange); @@ -128,9 +142,6 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo HyperionDaemon::~HyperionDaemon() { -#if defined(ENABLE_EFFECTENGINE) - delete _pyInit; -#endif Info(_log, "Hyperion daemon stopped"); } @@ -175,18 +186,25 @@ QJsonDocument HyperionDaemon::getSetting(settings::type type) const return _settingsManager->getSetting(type); } -void HyperionDaemon::stoppServices() +void HyperionDaemon::stopServices() { - Info(_log, "Stopping Hyperion services."); + Info(_log, "Stopping Hyperion services..."); - QObject::disconnect(_instanceManager.get(), nullptr); - QObject::disconnect(this, nullptr); - - stopGrabberServices(); stopEventServices(); - stopNetworkServices(); + stopGrabberServices(); + // Ensure that all Instances and their threads are stopped + QEventLoop loopLedDevice; + QObject::connect(_instanceManager.get(), &HyperionIManager::areAllInstancesStopped, &loopLedDevice, &QEventLoop::quit); _instanceManager->stopAll(); + loopLedDevice.exec(); + + stopNetworkServices(); + +#if defined(ENABLE_EFFECTENGINE) + // Finalize Python environment when all sub-interpreters were stopped, i.e. all must have been stopped before + delete _pyInit; +#endif } void HyperionDaemon::createNetworkServices() @@ -201,128 +219,154 @@ void HyperionDaemon::createNetworkServices() #ifdef ENABLE_MDNS // Create mDNS-Provider in own thread to allow publishing other services + _mDnsThread.reset(new QThread()); + _mDnsThread->setObjectName("mDNSProviderThread"); _mDNSProvider.reset(new MdnsProvider()); - QThread* mDnsThread = new QThread(); - mDnsThread->setObjectName("mDNSProviderThread"); - connect(mDnsThread, &QThread::started, _mDNSProvider.get(), &MdnsProvider::init); - connect(mDnsThread, &QThread::finished, _mDNSProvider.get(), &MdnsProvider::deleteLater); - _mDNSProvider->moveToThread(mDnsThread); + _mDNSProvider->moveToThread(_mDnsThread.get()); + connect(_mDnsThread.get(), &QThread::started, _mDNSProvider.get(), &MdnsProvider::init); #endif // Create JSON server in own thread + _jsonServerThread.reset(new QThread()); + _jsonServerThread->setObjectName("JSONServerThread"); _jsonServer.reset(new JsonServer(getSetting(settings::JSONSERVER))); - QThread* jsonThread = new QThread(); - jsonThread->setObjectName("JSONServerThread"); - connect(jsonThread, &QThread::started, _jsonServer.get(), &JsonServer::initServer); - connect(jsonThread, &QThread::finished, _jsonServer.get(), &JsonServer::deleteLater); + _jsonServer->moveToThread(_jsonServerThread.get()); + connect(_jsonServerThread.get(), &QThread::started, _jsonServer.get(), &JsonServer::initServer); connect(this, &HyperionDaemon::settingsChanged, _jsonServer.get(), &JsonServer::handleSettingsUpdate); #ifdef ENABLE_MDNS connect(_jsonServer.get(), &JsonServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); #endif - _jsonServer->moveToThread(jsonThread); // Create Webserver in own thread - _webserver.reset(new WebServer(getSetting(settings::WEBSERVER), false)); - QThread* wsThread = new QThread(); - wsThread->setObjectName("WebServerThread"); - connect(wsThread, &QThread::started, _webserver.get(), &WebServer::initServer); - connect(wsThread, &QThread::finished, _webserver.get(), &WebServer::deleteLater); - connect(this, &HyperionDaemon::settingsChanged, _webserver.get(), &WebServer::handleSettingsUpdate); + _webServerThread.reset(new QThread()); + _webServerThread->setObjectName("WebServerThread"); + _webServer.reset(new WebServer(getSetting(settings::WEBSERVER), false)); + _webServer->moveToThread(_webServerThread.get()); + connect(_webServerThread.get(), &QThread::started, _webServer.get(), &WebServer::initServer); + connect(this, &HyperionDaemon::settingsChanged, _webServer.get(), &WebServer::handleSettingsUpdate); #ifdef ENABLE_MDNS - connect(_webserver.get(), &WebServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); + connect(_webServer.get(), &WebServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); #endif - _webserver->moveToThread(wsThread); // Create SSL Webserver in own thread - _sslWebserver.reset(new WebServer(getSetting(settings::WEBSERVER), true)); - QThread* sslWsThread = new QThread(); - sslWsThread->setObjectName("SSLWebServerThread"); - connect(sslWsThread, &QThread::started, _sslWebserver.get(), &WebServer::initServer); - connect(sslWsThread, &QThread::finished, _sslWebserver.get(), &WebServer::deleteLater); - connect(this, &HyperionDaemon::settingsChanged, _sslWebserver.get(), &WebServer::handleSettingsUpdate); + _sslWebServerThread.reset(new QThread()); + _sslWebServerThread->setObjectName("SSLWebServerThread"); + _sslWebServer.reset(new WebServer(getSetting(settings::WEBSERVER), true)); + _sslWebServer->moveToThread(_sslWebServerThread.get()); + connect(_sslWebServerThread.get(), &QThread::started, _sslWebServer.get(), &WebServer::initServer); + connect(this, &HyperionDaemon::settingsChanged, _sslWebServer.get(), &WebServer::handleSettingsUpdate); #ifdef ENABLE_MDNS - connect(_sslWebserver.get(), &WebServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); + connect(_sslWebServer.get(), &WebServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); #endif - _sslWebserver->moveToThread(sslWsThread); // Create SSDP server - _ssdp.reset(new SSDPHandler(_webserver.get(), + _ssdpHandlerThread.reset(new QThread()); + _ssdpHandlerThread->setObjectName("SSDPThread"); + _ssdHandler.reset(new SSDPHandler(_webServer.get(), getSetting(settings::FLATBUFSERVER).object()["port"].toInt(), getSetting(settings::PROTOSERVER).object()["port"].toInt(), getSetting(settings::JSONSERVER).object()["port"].toInt(), getSetting(settings::WEBSERVER).object()["sslPort"].toInt(), getSetting(settings::GENERAL).object()["name"].toString())); - QThread* ssdpThread = new QThread(); - ssdpThread->setObjectName("SSDPThread"); - connect(ssdpThread, &QThread::started, _ssdp.get(), &SSDPHandler::initServer); - connect(ssdpThread, &QThread::finished, _ssdp.get(), &SSDPHandler::deleteLater); - connect(_webserver.get(), &WebServer::stateChange, _ssdp.get(), &SSDPHandler::handleWebServerStateChange); - connect(this, &HyperionDaemon::settingsChanged, _ssdp.get(), &SSDPHandler::handleSettingsUpdate); - _ssdp->moveToThread(ssdpThread); + _ssdHandler->moveToThread(_ssdpHandlerThread.get()); + connect(_ssdpHandlerThread.get(), &QThread::started, _ssdHandler.get(), &SSDPHandler::initServer); + connect(_webServer.get(), &WebServer::stateChange, _ssdHandler.get(), &SSDPHandler::handleWebServerStateChange); + connect(this, &HyperionDaemon::settingsChanged, _ssdHandler.get(), &SSDPHandler::handleSettingsUpdate); #if defined(ENABLE_FLATBUF_SERVER) // Create FlatBuffer server in thread + _flatBufferServerThread.reset(new QThread()); + _flatBufferServerThread->setObjectName("FlatBufferServerThread"); _flatBufferServer.reset(new FlatBufferServer(getSetting(settings::FLATBUFSERVER))); - QThread* fbThread = new QThread(); - fbThread->setObjectName("FlatBufferServerThread"); - connect(fbThread, &QThread::started, _flatBufferServer.get(), &FlatBufferServer::initServer); - connect(fbThread, &QThread::finished, _flatBufferServer.get(), &FlatBufferServer::deleteLater); + _flatBufferServer->moveToThread(_flatBufferServerThread.get()); + connect(_flatBufferServerThread.get(), &QThread::started, _flatBufferServer.get(), &FlatBufferServer::initServer); connect(this, &HyperionDaemon::settingsChanged, _flatBufferServer.get(), &FlatBufferServer::handleSettingsUpdate); #ifdef ENABLE_MDNS connect(_flatBufferServer.get(), &FlatBufferServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); #endif - _flatBufferServer->moveToThread(fbThread); #endif #if defined(ENABLE_PROTOBUF_SERVER) // Create Proto server in thread + _protoServerThread.reset(new QThread()); + _protoServerThread->setObjectName("ProtoServerThread"); _protoServer.reset(new ProtoServer(getSetting(settings::PROTOSERVER))); - QThread* pThread = new QThread(); - pThread->setObjectName("ProtoServerThread"); - connect(pThread, &QThread::started, _protoServer.get(), &ProtoServer::initServer); - connect(pThread, &QThread::finished, _protoServer.get(), &ProtoServer::deleteLater); + _protoServer->moveToThread(_protoServerThread.get()); + connect(_protoServerThread.get(), &QThread::started, _protoServer.get(), &ProtoServer::initServer); connect(this, &HyperionDaemon::settingsChanged, _protoServer.get(), &ProtoServer::handleSettingsUpdate); #ifdef ENABLE_MDNS connect(_protoServer.get(), &ProtoServer::publishService, _mDNSProvider.get(), &MdnsProvider::publishService); #endif - _protoServer->moveToThread(pThread); #endif } void HyperionDaemon::startNetworkServices() { - _jsonServer->thread()->start(); - _webserver->thread()->start(); - _sslWebserver->thread()->start(); + _jsonServerThread->start(); + _webServerThread->start(); + _sslWebServerThread->start(); #if defined(ENABLE_MDNS) - _mDNSProvider->thread()->start(); + _mDnsThread->start(); #endif - _ssdp->thread()->start(); + _ssdpHandlerThread->start(); #if defined(ENABLE_FLATBUF_SERVER) - _flatBufferServer->thread()->start(); + _flatBufferServerThread->start(); #endif #if defined(ENABLE_PROTOBUF_SERVER) - _protoServer->thread()->start(); + _protoServerThread->start(); #endif } void HyperionDaemon::stopNetworkServices() { #if defined(ENABLE_PROTOBUF_SERVER) - _protoServer.reset(nullptr); + if (_protoServerThread->isRunning()) { + _protoServerThread->quit(); + _protoServerThread->wait(); + } #endif + #if defined(ENABLE_FLATBUF_SERVER) - _flatBufferServer.reset(nullptr); + if (_flatBufferServerThread->isRunning()) { + _flatBufferServerThread->quit(); + _flatBufferServerThread->wait(); + } #endif + #if defined(ENABLE_MDNS) - _mDNSProvider.reset(nullptr); + QMetaObject::invokeMethod(_mDNSProvider.get(), &MdnsProvider::stop, Qt::QueuedConnection); + if (_mDnsThread->isRunning()) { + _mDnsThread->quit(); + _mDnsThread->wait(); + } #endif - _ssdp.reset(nullptr); - _sslWebserver.reset(nullptr); - _webserver.reset(nullptr); + // Delete SSDP Server, as it is currently depended on the WebServer + if (_ssdpHandlerThread->isRunning()) { + _ssdpHandlerThread->quit(); + _ssdpHandlerThread->wait(); + } + _ssdHandler.reset(nullptr); + + if (_webServerThread->isRunning()) { + _webServerThread->quit(); + _webServerThread->wait(); + } + _webServer.reset(nullptr); + + if (_sslWebServerThread->isRunning()) { + _sslWebServerThread->quit(); + _sslWebServerThread->wait(); + } + _sslWebServer.reset(nullptr); + + if (_jsonServerThread->isRunning()) { + _jsonServerThread->quit(); + _jsonServerThread->wait(); + } _jsonServer.reset(nullptr); + } void HyperionDaemon::startEventServices() @@ -338,17 +382,15 @@ void HyperionDaemon::startEventServices() connect(this, &HyperionDaemon::settingsChanged, _osEventHandler.get(), &OsEventHandler::handleSettingsUpdate); #if defined(ENABLE_CEC) + _cecHandlerThread.reset(new QThread()); + _cecHandlerThread->setObjectName("CECThread"); _cecHandler.reset(new CECHandler(getSetting(settings::CECEVENTS))); - QThread* cecHandlerThread = new QThread(); - cecHandlerThread->setObjectName("CECThread"); - connect(cecHandlerThread, &QThread::started, _cecHandler.get(), &CECHandler::start); - connect(cecHandlerThread, &QThread::finished, _cecHandler.get(), &CECHandler::stop); - connect(cecHandlerThread, &QThread::finished, _cecHandler.get(), &CECHandler::deleteLater); + _cecHandler->moveToThread(_cecHandlerThread.get()); + connect(_cecHandlerThread.get(), &QThread::started, _cecHandler.get(), &CECHandler::start); + connect(_cecHandlerThread.get(), &QThread::finished, _cecHandler.get(), &CECHandler::stop); connect(this, &HyperionDaemon::settingsChanged, _cecHandler.get(), &CECHandler::handleSettingsUpdate); Info(_log, "CEC event handler created"); - - _cecHandler->moveToThread(cecHandlerThread); - cecHandlerThread->start(); + _cecHandlerThread->start(); #else Debug(_log, "The CEC handler is not supported on this platform"); #endif @@ -357,7 +399,10 @@ void HyperionDaemon::startEventServices() void HyperionDaemon::stopEventServices() { #if defined(ENABLE_CEC) - _cecHandler.reset(nullptr); + if (_cecHandlerThread->isRunning()) { + _cecHandlerThread->quit(); + _cecHandlerThread->wait(); + } #endif _osEventHandler.reset(nullptr); _eventScheduler.reset(nullptr); @@ -388,7 +433,7 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs { const QJsonObject& logConfig = config.object(); - std::string level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug + std::string const level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug if (level == "silent") { Logger::setLogLevel(Logger::OFF); @@ -568,7 +613,7 @@ QString HyperionDaemon::evalScreenGrabberType() { type = "amlogic"; - QString amlDevice("/dev/amvideocap0"); + QString const amlDevice("/dev/amvideocap0"); if (!QFile::exists(amlDevice)) { Error(_log, "grabber device '%s' for type amlogic not found!", QSTRING_CSTR(amlDevice)); @@ -577,7 +622,7 @@ QString HyperionDaemon::evalScreenGrabberType() else { // x11 -> if DISPLAY is set - QByteArray envDisplay = qgetenv("DISPLAY"); + QByteArray const envDisplay = qgetenv("DISPLAY"); if (!envDisplay.isEmpty()) { #if defined(ENABLE_X11) diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index e2f95ece6..14e952b3d 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -120,7 +120,7 @@ class HyperionDaemon : public QObject /// /// @brief Get webserver pointer (systray) /// - WebServer *getWebServerInstance() { return _webserver.data(); } + WebServer *getWebServerInstance() { return _webServer.data(); } /// /// @brief Get the current videoMode @@ -136,7 +136,7 @@ class HyperionDaemon : public QObject static HyperionDaemon* daemon; public slots: - void stoppServices(); + void stopServices(); signals: /////////////////////////////////////// @@ -238,28 +238,35 @@ private slots: Logger* _log; /// Core services - QScopedPointer _instanceManager; + QScopedPointer _instanceManager; QScopedPointer _settingsManager; #if defined(ENABLE_EFFECTENGINE) - PythonInit* _pyInit; + PythonInit* _pyInit; #endif /// Network services QScopedPointer _authManager; QScopedPointer _netOrigin; - QScopedPointer _jsonServer; - QScopedPointer _webserver; - QScopedPointer _sslWebserver; - QScopedPointer _ssdp; + QScopedPointer _jsonServer; + QScopedPointer _jsonServerThread; + QScopedPointer _webServer; + QScopedPointer _webServerThread; + QScopedPointer _sslWebServer; + QScopedPointer _sslWebServerThread; + QScopedPointer _ssdHandler; + QScopedPointer _ssdpHandlerThread; #ifdef ENABLE_MDNS - QScopedPointer _mDNSProvider; + QScopedPointer _mDNSProvider; + QScopedPointer _mDnsThread; #endif #if defined(ENABLE_FLATBUF_SERVER) - QScopedPointer _flatBufferServer; + QScopedPointer _flatBufferServer; + QScopedPointer _flatBufferServerThread; #endif #if defined(ENABLE_PROTOBUF_SERVER) - QScopedPointer _protoServer; + QScopedPointer _protoServer; + QScopedPointer _protoServerThread; #endif /// Event services @@ -268,6 +275,7 @@ private slots: QScopedPointer _eventScheduler; #ifdef ENABLE_CEC QScopedPointer _cecHandler; + QScopedPointer _cecHandlerThread; #endif /// Grabber services diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index a9dffe9fc..53ff0c898 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "HyperionConfig.h" @@ -431,10 +432,10 @@ int main(int argc, char** argv) Warning(log,"The database file '%s' is set not writeable. Hyperion starts in read-only mode. Configuration updates will not be persisted!", QSTRING_CSTR(dbFile.absoluteFilePath())); } - HyperionDaemon* hyperiond = nullptr; + QScopedPointer hyperiond; try { - hyperiond = new HyperionDaemon(userDataDirectory.absolutePath(), qApp, bool(logLevelCheck)); + hyperiond.reset(new HyperionDaemon(userDataDirectory.absolutePath(), qApp, bool(logLevelCheck))); } catch (std::exception& e) { @@ -447,7 +448,7 @@ int main(int argc, char** argv) { Info(log, "Start Systray menu"); QApplication::setQuitOnLastWindowClosed(false); - SysTray tray(hyperiond); + SysTray tray(hyperiond.get()); tray.hide(); rc = (qobject_cast(app.data()))->exec(); } @@ -455,8 +456,6 @@ int main(int argc, char** argv) { rc = app->exec(); } - Info(log, "Application closed with code %d", rc); - delete hyperiond; } catch (std::exception& e) { @@ -470,5 +469,7 @@ int main(int argc, char** argv) } #endif + Info(log, "Application ended with code %d", rc); + return rc; } diff --git a/src/hyperiond/systray.h b/src/hyperiond/systray.h index 137ee129c..a4b3ed658 100644 --- a/src/hyperiond/systray.h +++ b/src/hyperiond/systray.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,7 @@ private slots: QMenu *_trayIconSystemMenu; QColorDialog _colorDlg; HyperionDaemon *_hyperiond; - Hyperion *_hyperion; + QSharedPointer _hyperion; HyperionIManager *_instanceManager; quint16 _webPort; }; From 8fbdd18a7b3c8e9d3366fa58b6a4bec04745fe5d Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:00:48 +0100 Subject: [PATCH 002/112] Further refactor --- include/commandline/ColorsOption.h | 2 +- include/forwarder/MessageForwarder.h | 2 + include/hyperion/Hyperion.h | 16 ++-- include/hyperion/LinearColorSmoothing.h | 8 ++ include/hyperion/PriorityMuxer.h | 10 +- include/utils/hyperion.h | 4 +- include/utils/settings.h | 2 +- libsrc/boblightserver/BoblightServer.cpp | 4 +- libsrc/commandline/ColorsOption.cpp | 2 +- libsrc/forwarder/MessageForwarder.cpp | 8 +- libsrc/hyperion/Hyperion.cpp | 92 ++++++++++-------- libsrc/hyperion/HyperionIManager.cpp | 9 +- libsrc/hyperion/LinearColorSmoothing.cpp | 115 +++++++++++++---------- libsrc/hyperion/PriorityMuxer.cpp | 37 +++++--- libsrc/leddevice/LedDeviceWrapper.cpp | 4 +- libsrc/webserver/QtHttpServer.cpp | 20 ++-- libsrc/webserver/WebServer.cpp | 7 +- src/hyperion-remote/hyperion-remote.cpp | 4 +- src/hyperiond/hyperiond.cpp | 10 +- test/TestImage2LedsMap.cpp | 3 +- 20 files changed, 222 insertions(+), 137 deletions(-) diff --git a/include/commandline/ColorsOption.h b/include/commandline/ColorsOption.h index 299d44bf5..edd8a1b70 100644 --- a/include/commandline/ColorsOption.h +++ b/include/commandline/ColorsOption.h @@ -35,7 +35,7 @@ class ColorsOption: public Option {} virtual bool validate(Parser & parser, QString & value) override; - QList getColors(Parser &parser) const { return _colors; } + QList getColors(Parser & /*parser*/) const { return _colors; } }; } diff --git a/include/forwarder/MessageForwarder.h b/include/forwarder/MessageForwarder.h index d17add09c..eb616122c 100644 --- a/include/forwarder/MessageForwarder.h +++ b/include/forwarder/MessageForwarder.h @@ -58,6 +58,8 @@ public slots: /// void handleSettingsUpdate(settings::type type, const QJsonDocument &config); + void stop(); + private slots: /// /// @brief Handle component state change MessageForwarder diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index a1cb015d8..0ab471e9b 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -40,6 +40,7 @@ // Forward class declaration class HyperionDaemon; +class HyperionIManager; class ImageProcessor; #if defined(ENABLE_FORWARDER) class MessageForwarder; @@ -87,7 +88,7 @@ class Hyperion : public QObject /// /// @brief Return the size of led grid /// - QSize getLedGridSize() const { return _ledGridSize; } + QSize getLedGridSize() const { return _layoutGridSize; } /// gets the methode how image is maped to leds int getLedMappingType() const; @@ -112,6 +113,8 @@ public slots: /// void update(); + void refreshUpdate(); + /// /// Returns the number of attached leds /// @@ -180,7 +183,7 @@ public slots: /// Returns the ColorAdjustment with the given identifier /// @return The adjustment with the given identifier (or nullptr if the identifier does not exist) /// - ColorAdjustment * getAdjustment(const QString& id) const; + ColorAdjustment * getAdjustment(const QString& identifier) const; /// Tell Hyperion that the corrections have changed and the leds need to be updated void adjustmentsUpdated(); @@ -533,13 +536,14 @@ private slots: private: friend class HyperionDaemon; - friend class HyperionIManager; /// /// @brief Constructs the Hyperion instance, just accessible for HyperionIManager /// @param instance The instance index /// - Hyperion(quint8 instance); + explicit Hyperion(quint8 instance, QObject* parent = nullptr); + + friend class HyperionIManager; // Grant HyperionIManager access to private members /// instance index const quint8 _instIndex; @@ -585,8 +589,8 @@ private slots: /// count of hardware leds int _hwLedCount; - - QSize _ledGridSize; + QString _colorOrder; + QSize _layoutGridSize; /// Background effect instance, kept active to react on setting changes QScopedPointer _BGEffectHandler; diff --git a/include/hyperion/LinearColorSmoothing.h b/include/hyperion/LinearColorSmoothing.h index 66c5b9cce..00d4c66ee 100644 --- a/include/hyperion/LinearColorSmoothing.h +++ b/include/hyperion/LinearColorSmoothing.h @@ -136,6 +136,9 @@ public slots: /// void handleSettingsUpdate(settings::type type, const QJsonDocument &config); + void start(); + void stop(); + private slots: /// Timer callback which writes updated led values to the led device void updateLeds(); @@ -147,6 +150,11 @@ private slots: /// void componentStateChange(hyperion::Components component, bool state); + /// + /// @brief Handle priority updates. + /// + void handlePriorityUpdate(int priority); + private: /** * Pushes the colors into the output queue and popping the head to the led-device diff --git a/include/hyperion/PriorityMuxer.h b/include/hyperion/PriorityMuxer.h index 0648ef5d3..43abe7bc9 100644 --- a/include/hyperion/PriorityMuxer.h +++ b/include/hyperion/PriorityMuxer.h @@ -81,6 +81,10 @@ class PriorityMuxer : public QObject /// ~PriorityMuxer() override; + /// + /// @brief Start the PriorityMuxer and its timers + /// + void start(); /// /// @brief Stop the PriorityMuxer and its timers @@ -286,8 +290,8 @@ private slots: bool _sourceAutoSelectEnabled; // Timer to update Muxer times independent - QTimer* _updateTimer; + QScopedPointer _updateTimer; - QTimer* _timer; - QTimer* _blockTimer; + QScopedPointer _timer; + QScopedPointer _blockTimer; }; diff --git a/include/utils/hyperion.h b/include/utils/hyperion.h index eeb5be5fb..ba93925dc 100644 --- a/include/utils/hyperion.h +++ b/include/utils/hyperion.h @@ -63,9 +63,9 @@ namespace hyperion { #undef FGCONFIG_ARRAY } - static ColorOrder createColorOrder(const QJsonObject &deviceConfig) + static ColorOrder createColorOrder(const QString& colorOrder = "rgb") { - return stringToColorOrder(deviceConfig["colorOrder"].toString("rgb")); + return stringToColorOrder(colorOrder); } static RgbTransform createRgbTransform(const QJsonObject& colorConfig) diff --git a/include/utils/settings.h b/include/utils/settings.h index 8a36006da..2db7d5c09 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -92,7 +92,7 @@ namespace settings { if (type == "framegrabber") return SYSTEMCAPTURE; if (type == "general") return GENERAL; if (type == "grabberV4L2") return V4L2; - if (type == "grabberAudio") return AUDIO; + if (type == "grabberAudio") return AUDIO; if (type == "jsonServer") return JSONSERVER; if (type == "ledConfig") return LEDCONFIG; if (type == "leds") return LEDS; diff --git a/libsrc/boblightserver/BoblightServer.cpp b/libsrc/boblightserver/BoblightServer.cpp index f6acccf82..1cd413456 100644 --- a/libsrc/boblightserver/BoblightServer.cpp +++ b/libsrc/boblightserver/BoblightServer.cpp @@ -52,7 +52,7 @@ void BoblightServer::start() if (NetUtils::portAvailable(_port, _log)) _server->listen(QHostAddress::Any, _port); - Info(_log, "Started on port: %d", _port); + Info(_log, "Boblight service started on port: %d", _port); _hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); } @@ -67,7 +67,7 @@ void BoblightServer::stop() _server->close(); - Info(_log, "Stopped"); + Info(_log, "Boblight service stopped"); _hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); } diff --git a/libsrc/commandline/ColorsOption.cpp b/libsrc/commandline/ColorsOption.cpp index ab26b5854..ceec57798 100644 --- a/libsrc/commandline/ColorsOption.cpp +++ b/libsrc/commandline/ColorsOption.cpp @@ -38,7 +38,7 @@ bool ColorsOption::validate(Parser & parser, QString & value) return true; } - _error = QString("Invalid color. A color is specified by a six lettered RRGGBB hex getColors or one of the following names:\n\t- %1").arg(QColor::colorNames().join("\n\t- ")); + _error = QString("Invalid color. A color is specified by a six lettered RRGGBB hex value string or one of the following names:\n\t- %1").arg(QColor::colorNames().join("\n\t- ")); return false; } diff --git a/libsrc/forwarder/MessageForwarder.cpp b/libsrc/forwarder/MessageForwarder.cpp index 88322b294..8be588fba 100644 --- a/libsrc/forwarder/MessageForwarder.cpp +++ b/libsrc/forwarder/MessageForwarder.cpp @@ -63,11 +63,17 @@ MessageForwarder::MessageForwarder(Hyperion* hyperion) } MessageForwarder::~MessageForwarder() +{ + stop(); +} + +void MessageForwarder::stop() { stopJsonTargets(); stopFlatbufferTargets(); -} + Info(_log, "Forwarding service stopped"); +} void MessageForwarder::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index c59ad756b..bd53ca825 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -1,6 +1,5 @@ // STL includes -#include -#include +#include // QT includes #include @@ -23,6 +22,7 @@ #include #include #include +#include "utils/WaitTime.h" // LedDevice includes #include @@ -49,8 +49,8 @@ #include #endif -Hyperion::Hyperion(quint8 instance) - : QObject() +Hyperion::Hyperion(quint8 instance, QObject* parent) + : QObject(parent) , _instIndex(instance) , _settingsManager(nullptr) , _componentRegister(nullptr) @@ -67,8 +67,9 @@ Hyperion::Hyperion(quint8 instance) , _messageForwarder(nullptr) #endif , _log(nullptr) - , _hwLedCount() - , _ledGridSize() + , _hwLedCount(0) + , _colorOrder("rgb") + , _layoutGridSize() , _BGEffectHandler(nullptr) , _captureCont(nullptr) , _ledBuffer() @@ -78,15 +79,16 @@ Hyperion::Hyperion(quint8 instance) { qRegisterMetaType("ComponentList"); - QString subComponent = "I"+QString::number(instance); - this->setProperty("instance", (QString) subComponent); + QString const subComponent = "I"+QString::number(_instIndex); + this->setProperty("instance", QVariant::fromValue(subComponent)); _log= Logger::getInstance("HYPERION", subComponent); _settingsManager.reset(new SettingsManager(instance, this)); - _ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(getSetting(settings::DEVICE).object())); + _colorOrder = getSetting(settings::DEVICE).object()["colorOrder"].toString("rgb"); + _ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(_colorOrder)); _raw2ledAdjustment.reset(hyperion::createLedColorsAdjustment(static_cast(_ledString.leds().size()), getSetting(settings::COLOR).object())); - _ledGridSize = hyperion::getLedLayoutGridSize(getSetting(settings::LEDS).array()); + _layoutGridSize = hyperion::getLedLayoutGridSize(getSetting(settings::LEDS).array()); _ledBuffer = {_ledString.leds().size(), ColorRgb::BLACK}; _componentRegister.reset(new ComponentRegister(this)); @@ -99,6 +101,8 @@ Hyperion::~Hyperion() void Hyperion::start() { + Debug(_log, "Hyperion instance starting..."); + // forward settings changed to Hyperion connect(_settingsManager.get(), &SettingsManager::settingsChanged, this, &Hyperion::settingsChanged); @@ -139,21 +143,22 @@ void Hyperion::start() #endif // initialize LED-devices - QJsonObject ledDevice = getSetting(settings::DEVICE).object(); - ledDevice["currentLedCount"] = _hwLedCount; // Inject led count info + QJsonObject const ledDeviceSettings = getSetting(settings::DEVICE).object(); + ledDeviceSettings["currentLedCount"] = _hwLedCount; // Inject led count info + _colorOrder = getSetting(settings::DEVICE).object()["colorOrder"].toString("rgb"); - _ledDeviceWrapper.reset(new LedDeviceWrapper(this)); + _ledDeviceWrapper.reset(new LedDeviceWrapper(this)); connect(this, &Hyperion::compStateChangeRequest, _ledDeviceWrapper.get(), &LedDeviceWrapper::handleComponentState); connect(this, &Hyperion::ledDeviceData, _ledDeviceWrapper.get(), &LedDeviceWrapper::updateLeds); - _ledDeviceWrapper->createLedDevice(ledDevice); + _ledDeviceWrapper->createLedDevice(ledDeviceSettings); // smoothing _deviceSmooth.reset(new LinearColorSmoothing(getSetting(settings::SMOOTHING), this)); connect(this, &Hyperion::settingsChanged, _deviceSmooth.get(), &LinearColorSmoothing::handleSettingsUpdate); - //Start in pause mode, a new priority will activate smoothing (either start-effect or grabber) - _deviceSmooth->setPause(true); + _deviceSmooth->start(); + _muxer->start(); #if defined(ENABLE_FORWARDER) // create the message forwarder only on main instance @@ -171,7 +176,6 @@ void Hyperion::start() // create the effect engine; needs to be initialized after smoothing! _effectEngine.reset(new EffectEngine(this)); connect(_effectEngine.get(), &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated); - connect(this, &Hyperion::stopEffects, _effectEngine.get(), &EffectEngine::stopAllEffects); #endif // initial startup effect @@ -190,7 +194,7 @@ void Hyperion::start() connect(GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage, this, &Hyperion::setInputImage); // if there is no startup / background effect and no sending capture interface we probably want to push once BLACK (as PrioMuxer won't emit a priority change) - update(); + refreshUpdate(); #if defined(ENABLE_BOBLIGHT_SERVER) // boblight, can't live in global scope as it depends on layout @@ -208,6 +212,7 @@ void Hyperion::stop() //Disconnect Background effect first that it does not kick in when other priorities are stopped _BGEffectHandler->disconnect(); + _boblightServer.get()->stop(); //Remove all priorities _muxer->clearAll(true); @@ -217,7 +222,7 @@ void Hyperion::stop() #endif _ledDeviceWrapper->stopDevice(); - + _deviceSmooth->stop(); _muxer->stop(); emit finished(); @@ -246,10 +251,11 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co #endif // ledstring, img processor, muxer, ledGridSize (effect-engine image based effects), _ledBuffer and ByteOrder of ledstring - _ledString = LedString::createLedString(leds, hyperion::createColorOrder(getSetting(settings::DEVICE).object())); + _colorOrder = getSetting(settings::DEVICE).object()["colorOrder"].toString("rgb"); + _ledString = LedString::createLedString(leds, hyperion::createColorOrder(_colorOrder)); _imageProcessor->setLedString(_ledString); _muxer->updateLedColorsLength(static_cast(_ledString.leds().size())); - _ledGridSize = hyperion::getLedLayoutGridSize(leds); + _layoutGridSize = hyperion::getLedLayoutGridSize(leds); std::vector color(_ledString.leds().size(), ColorRgb::BLACK); _ledBuffer = color; @@ -270,6 +276,8 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co // start cached effects _effectEngine->startCachedEffects(); #endif + + refreshUpdate(); } else if(type == settings::DEVICE) { @@ -279,9 +287,10 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co _hwLedCount = dev["hardwareLedCount"].toInt(getLedCount()); // force ledString update, if device ByteOrder changed - if(_ledDeviceWrapper->getColorOrder() != dev["colorOrder"].toString("rgb")) + QString colorOrder = getSetting(settings::DEVICE).object()["colorOrder"].toString("rgb"); + if(_ledDeviceWrapper->getColorOrder() !=colorOrder) { - _ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(dev)); + _ledString = LedString::createLedString(getSetting(settings::LEDS).array(), hyperion::createColorOrder(colorOrder)); _imageProcessor->setLedString(_ledString); _ledStringColorOrder.clear(); @@ -299,7 +308,7 @@ void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& co } // update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer color - update(); + refreshUpdate(); } QJsonDocument Hyperion::getSetting(settings::type type) const @@ -372,7 +381,7 @@ int Hyperion::isComponentEnabled(hyperion::Components comp) const void Hyperion::setSuspend(bool isSuspend) { - bool enable = !isSuspend; + bool const enable = !isSuspend; emit compStateChangeRequestAll(enable); } @@ -380,7 +389,7 @@ void Hyperion::setIdle(bool isIdle) { clear(-1); - bool enable = !isIdle; + bool const enable = !isIdle; emit compStateChangeRequestAll(enable, {hyperion::COMP_LEDDEVICE, hyperion::COMP_SMOOTHING} ); } @@ -404,7 +413,7 @@ bool Hyperion::setInput(int priority, const std::vector& ledColors, in // if this priority is visible, update immediately if(priority == _muxer->getCurrentPriority()) { - update(); + refreshUpdate(); } return true; @@ -433,7 +442,7 @@ bool Hyperion::setInputImage(int priority, const Image& image, int64_t // if this priority is visible, update immediately if(priority == _muxer->getCurrentPriority()) { - update(); + refreshUpdate(); } return true; @@ -457,7 +466,7 @@ void Hyperion::setColor(int priority, const std::vector &ledColors, in #endif // create full led vector from single/multiple colors - size_t size = _ledString.leds().size(); + size_t const size = _ledString.leds().size(); std::vector newLedColors; while (true) { @@ -484,15 +493,15 @@ QStringList Hyperion::getAdjustmentIds() const return _raw2ledAdjustment->getAdjustmentIds(); } -ColorAdjustment * Hyperion::getAdjustment(const QString& id) const +ColorAdjustment * Hyperion::getAdjustment(const QString& identifier) const { - return _raw2ledAdjustment->getAdjustment(id); + return _raw2ledAdjustment->getAdjustment(identifier); } void Hyperion::adjustmentsUpdated() { emit adjustmentChanged(); - update(); + refreshUpdate(); } bool Hyperion::clear(int priority, bool forceClearAll) @@ -625,7 +634,7 @@ void Hyperion::handleVisibleComponentChanged(hyperion::Components comp) void Hyperion::handleSourceAvailability(int priority) { - int previousPriority = _muxer->getPreviousPriority(); + int const previousPriority = _muxer->getPreviousPriority(); if ( priority == PriorityMuxer::LOWEST_PRIORITY) { @@ -634,7 +643,7 @@ void Hyperion::handleSourceAvailability(int priority) { Debug(_log,"No source left -> Pause output processing and switch LED-Device off"); emit _ledDeviceWrapper->switchOff(); - emit _deviceSmooth->setPause(true); + _deviceSmooth->setPause(true); } } else @@ -650,15 +659,21 @@ void Hyperion::handleSourceAvailability(int priority) { Debug(_log,"new source available -> LED-Device not enabled, cannot switch on LED-device"); } - emit _deviceSmooth->setPause(false); + _deviceSmooth->setPause(false); } } } +void Hyperion::refreshUpdate() +{ + wait(_ledDeviceWrapper->getLatchTime()); + update(); +} + void Hyperion::update() { // Obtain the current priority channel - int priority = _muxer->getCurrentPriority(); + int const priority = _muxer->getCurrentPriority(); const PriorityMuxer::InputInfo priorityInfo = _muxer->getInputInfo(priority); // copy image & process OR copy ledColors from muxer @@ -674,7 +689,7 @@ void Hyperion::update() if (_ledString.hasBlackListedLeds()) { - for (unsigned long id : _ledString.blacklistedLedIds()) + for (unsigned long const id : _ledString.blacklistedLedIds()) { if (id > _ledBuffer.size()-1) { @@ -718,13 +733,12 @@ void Hyperion::update() std::swap(color.green, color.blue); break; } - i++; } // fill additional hardware LEDs with black if ( _hwLedCount > static_cast(_ledBuffer.size()) ) { - _ledBuffer.resize(_hwLedCount, ColorRgb::BLACK); + _ledBuffer.resize(static_cast(_hwLedCount), ColorRgb::BLACK); } // Write the data to the device diff --git a/libsrc/hyperion/HyperionIManager.cpp b/libsrc/hyperion/HyperionIManager.cpp index e93a03dd6..4d6870600 100644 --- a/libsrc/hyperion/HyperionIManager.cpp +++ b/libsrc/hyperion/HyperionIManager.cpp @@ -284,8 +284,6 @@ void HyperionIManager::handleFinished() if (hyperion != nullptr) { quint8 const instance = hyperion->getInstanceIndex(); - _startingInstances.remove(instance); - _runningInstances.remove(instance); // Manually stop the thread and cleanup QThread* thread = hyperion->thread(); @@ -295,11 +293,14 @@ void HyperionIManager::handleFinished() thread->wait(); } + Info(_log,"Hyperion instance '%s' stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); + + _startingInstances.remove(instance); + _runningInstances.remove(instance); + emit instanceStateChanged(InstanceState::H_STOPPED, instance); emit change(); - Info(_log,"Hyperion instance '%s' stopped", QSTRING_CSTR(_instanceTable->getNamebyIndex(instance))); - if ( _runningInstances.size() == 0 ) { Info(_log,"All Hyperion instances are stopped"); diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index bb0a99d4c..b2d5d2a25 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -60,27 +60,23 @@ using namespace hyperion; LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion *hyperion) : QObject(hyperion) - , _log(nullptr) - , _hyperion(hyperion) - , _prioMuxer(_hyperion->getMuxerInstance()) - , _updateInterval(DEFAULT_UPDATEINTERVALL.count()) - , _settlingTime(DEFAULT_SETTLINGTIME) - , _timer(nullptr) - , _outputDelay(DEFAULT_OUTPUTDEPLAY) - , _pause(false) - , _currentConfigId(SmoothingConfigID::SYSTEM) - , _enabled(false) - , _enabledSystemCfg(false) - , _smoothingType(SmoothingType::Linear) - , tempValues(std::vector()) + , _log(nullptr) + , _hyperion(hyperion) + , _prioMuxer(_hyperion->getMuxerInstance()) + , _updateInterval(DEFAULT_UPDATEINTERVALL.count()) + , _settlingTime(DEFAULT_SETTLINGTIME) + , _timer(nullptr) + , _outputDelay(DEFAULT_OUTPUTDEPLAY) + , _pause(false) + , _currentConfigId(SmoothingConfigID::SYSTEM) + , _enabled(false) + , _enabledSystemCfg(false) + , _smoothingType(SmoothingType::Linear) + , tempValues(std::vector()) { QString subComponent = hyperion->property("instance").toString(); _log= Logger::getInstance("SMOOTHING", subComponent); - // timer - _timer.reset(new QTimer(this)); - _timer->setTimerType(Qt::PreciseTimer); - // init cfg (default) updateConfig(SmoothingConfigID::SYSTEM, DEFAULT_SETTLINGTIME, DEFAULT_UPDATEFREQUENCY, DEFAULT_OUTPUTDEPLAY); handleSettingsUpdate(settings::SMOOTHING, config); @@ -88,23 +84,36 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonDocument &config, Hyperion // add pause on cfg 1 SmoothingCfg cfg {true, 0, 0}; _cfgList.append(std::move(cfg)); +} +LinearColorSmoothing::~LinearColorSmoothing() +{ +} + +void LinearColorSmoothing::start() +{ + Info(_log, "LinearColorSmoothing starting..."); + + _timer.reset(new QTimer(this)); + _timer->setTimerType(Qt::PreciseTimer); + + //Start in pause mode, a new priority will activate smoothing (either start-effect or grabber) + setPause(true); // listen for comp changes - connect(_hyperion, &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange); + QObject::connect(_hyperion, &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange); + QObject::connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &LinearColorSmoothing::handlePriorityUpdate); connect(_timer.get(), &QTimer::timeout, this, &LinearColorSmoothing::updateLeds); - - connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, [=] (int priority){ - const PriorityMuxer::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority); - int smooth_cfg = priorityInfo.smooth_cfg; - if (smooth_cfg != _currentConfigId || smooth_cfg == SmoothingConfigID::EFFECT_DYNAMIC) - { - this->selectConfig(smooth_cfg, false); - } - }); } -LinearColorSmoothing::~LinearColorSmoothing() +void LinearColorSmoothing::stop() { + Debug(_log, "LinearColorSmoothing stopping..."); + + QObject::disconnect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &LinearColorSmoothing::handlePriorityUpdate); + setEnable(false); + _timer->stop(); + + Info(_log, "LinearColorSmoothing stopped"); } void LinearColorSmoothing::handleSettingsUpdate(settings::type type, const QJsonDocument &config) @@ -148,6 +157,16 @@ void LinearColorSmoothing::handleSettingsUpdate(settings::type type, const QJson } } +void LinearColorSmoothing::handlePriorityUpdate(int priority) +{ + const PriorityMuxer::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority); + int smooth_cfg = priorityInfo.smooth_cfg; + if (smooth_cfg != _currentConfigId || smooth_cfg == SmoothingConfigID::EFFECT_DYNAMIC) + { + this->selectConfig(smooth_cfg, false); + } +} + int LinearColorSmoothing::write(const std::vector &ledValues) { _targetTime = micros() + (MS_PER_MICRO * _settlingTime); @@ -418,12 +437,12 @@ void LinearColorSmoothing::performDecay(const int64_t now) { if ((now > (_renderedStatTime + 30 * 1000000)) && (_renderedCounter > _renderedStatCounter)) { Debug(_log, "decay - rendered frames [%d] (%f/s), interpolated frames [%d] (%f/s) in [%f ms]" - , _renderedCounter - _renderedStatCounter - , (1.0F * (_renderedCounter - _renderedStatCounter) / ((now - _renderedStatTime) / 1000000.0F)) - , _interpolationCounter - _interpolationStatCounter - , (1.0F * (_interpolationCounter - _interpolationStatCounter) / ((now - _renderedStatTime) / 1000000.0F)) - , (now - _renderedStatTime) / 1000.0F - ); + , _renderedCounter - _renderedStatCounter + , (1.0F * (_renderedCounter - _renderedStatCounter) / ((now - _renderedStatTime) / 1000000.0F)) + , _interpolationCounter - _interpolationStatCounter + , (1.0F * (_interpolationCounter - _interpolationStatCounter) / ((now - _renderedStatTime) / 1000000.0F)) + , (now - _renderedStatTime) / 1000.0F + ); _renderedStatTime = now; _renderedStatCounter = _renderedCounter; _interpolationStatCounter = _interpolationCounter; @@ -467,12 +486,12 @@ void LinearColorSmoothing::updateLeds() { case SmoothingType::Decay: performDecay(now); - break; + break; case SmoothingType::Linear: default: performLinear(now); - break; + break; } } @@ -748,22 +767,22 @@ QString LinearColorSmoothing::getConfig(int cfgID) } LinearColorSmoothing::SmoothingCfg::SmoothingCfg() : - _pause(false), - _settlingTime(DEFAULT_SETTLINGTIME), - _updateInterval(DEFAULT_UPDATEFREQUENCY), - _type(SmoothingType::Linear) + _pause(false), + _settlingTime(DEFAULT_SETTLINGTIME), + _updateInterval(DEFAULT_UPDATEFREQUENCY), + _type(SmoothingType::Linear) { } LinearColorSmoothing::SmoothingCfg::SmoothingCfg(bool pause, int64_t settlingTime, int updateInterval, SmoothingType type, double interpolationRate, unsigned outputDelay, bool dithering, double decay) : - _pause(pause), - _settlingTime(settlingTime), - _updateInterval(updateInterval), - _type(type), - _interpolationRate(interpolationRate), - _outputDelay(outputDelay), - _dithering(dithering), - _decay(decay) + _pause(pause), + _settlingTime(settlingTime), + _updateInterval(updateInterval), + _type(type), + _interpolationRate(interpolationRate), + _outputDelay(outputDelay), + _dithering(dithering), + _decay(decay) { } diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index 3bf46696b..185b5104e 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -1,5 +1,4 @@ // STL includes -#include #include // qt incl @@ -47,29 +46,43 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) _lowestPriorityInfo.smooth_cfg = 0; _activeInputs[PriorityMuxer::LOWEST_PRIORITY] = _lowestPriorityInfo; +} + +PriorityMuxer::~PriorityMuxer() +{ +} + +void PriorityMuxer::start() +{ + Info(_log, "Priority-Muxer starting..."); // adapt to 1s interval for COLOR and EFFECT timeouts > -1 (endless) - connect(_timer, &QTimer::timeout, this, &PriorityMuxer::timeTrigger); + _timer.reset(new QTimer(this)); + connect(_timer.get(), &QTimer::timeout, this, &PriorityMuxer::timeTrigger); _timer->setSingleShot(true); + + _blockTimer.reset(new QTimer(this)); _blockTimer->setSingleShot(true); + connect(this, &PriorityMuxer::signalTimeTrigger, this, &PriorityMuxer::timeTrigger); // start muxer timer - connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::updatePriorities); + _updateTimer.reset(new QTimer(this)); + connect(_updateTimer.get(), &QTimer::timeout, this, &PriorityMuxer::updatePriorities); _updateTimer->setInterval(250); _updateTimer->start(); } -PriorityMuxer::~PriorityMuxer() -{ -} - void PriorityMuxer::stop() { + Debug(_log, "Priority-Muxer is stopping..."); + + setEnable(false); _timer->stop(); - _updateTimer->stop(); _blockTimer->stop(); - Debug(_log, "Priority-Muxer stopped"); + _updateTimer->stop(); + + Info(_log, "Priority-Muxer stopped"); } void PriorityMuxer::setEnable(bool enable) @@ -359,7 +372,7 @@ void PriorityMuxer::updatePriorities() _activeInputs.contains(0) ? newPriority = 0 : newPriority = PriorityMuxer::LOWEST_PRIORITY; - bool timeTrigger {false}; + bool timeElapsed {false}; QMutableMapIterator i(_activeInputs); while (i.hasNext()) { i.next(); @@ -401,13 +414,13 @@ void PriorityMuxer::updatePriorities() ) ) { - timeTrigger = true; + timeElapsed = true; } } } } - if (timeTrigger) + if (timeElapsed) { emit signalTimeTrigger(); // signal to prevent Threading issues } diff --git a/libsrc/leddevice/LedDeviceWrapper.cpp b/libsrc/leddevice/LedDeviceWrapper.cpp index 909611b91..667ab4a1f 100644 --- a/libsrc/leddevice/LedDeviceWrapper.cpp +++ b/libsrc/leddevice/LedDeviceWrapper.cpp @@ -56,9 +56,7 @@ void LedDeviceWrapper::createLedDevice(const QJsonObject& config) _ledDeviceThread.reset(new QThread()); _ledDeviceThread->setObjectName("LedDeviceThread"); _ledDevice.reset(LedDeviceFactory::construct(config)); - - QString const subComponent = parent()->property("instance").toString(); - _ledDevice->setLogger(Logger::getInstance("LEDDEVICE", subComponent)); + _ledDevice->setLogger(_log); _ledDevice->moveToThread(_ledDeviceThread.get()); diff --git a/libsrc/webserver/QtHttpServer.cpp b/libsrc/webserver/QtHttpServer.cpp index ff8fe207c..3916fc50c 100644 --- a/libsrc/webserver/QtHttpServer.cpp +++ b/libsrc/webserver/QtHttpServer.cpp @@ -38,6 +38,7 @@ QtHttpServer::QtHttpServer (QObject * parent) , m_useSsl (false) , m_serverName (QStringLiteral ("The Qt6 HTTP Server")) , m_netOrigin (NetOrigin::getInstance()) + , m_sockServer (nullptr) { m_sockServer = new QtHttpServerWrapper (this); connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected); @@ -55,17 +56,20 @@ void QtHttpServer::start (quint16 port) void QtHttpServer::stop (void) { - if (m_sockServer->isListening ()) + if (m_sockServer != nullptr) { - m_sockServer->close (); - // disconnect clients - const QList socks = m_socksClientsHash.keys(); - for(auto sock : socks) + if (m_sockServer->isListening ()) { - sock->close(); - } + m_sockServer->close (); + // disconnect clients + const QList socks = m_socksClientsHash.keys(); + for(auto *sock : socks) + { + sock->close(); + } - emit stopped (); + emit stopped (); + } } } diff --git a/libsrc/webserver/WebServer.cpp b/libsrc/webserver/WebServer.cpp index c40aa6a0c..5f4d5c569 100644 --- a/libsrc/webserver/WebServer.cpp +++ b/libsrc/webserver/WebServer.cpp @@ -31,6 +31,8 @@ WebServer::WebServer(const QJsonDocument& config, bool useSsl, QObject* parent) , _useSsl(useSsl) , _log(Logger::getInstance("WEBSERVER")) , _port(WEBSERVER_DEFAULT_PORT) + , _staticFileServing (nullptr) + , _server(nullptr) { } @@ -226,7 +228,10 @@ void WebServer::start() void WebServer::stop() { - _server->stop(); + if (_server != nullptr) + { + _server->stop(); + } } void WebServer::setSSDPDescription(const QString& desc) diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 277f1b2af..eee3bc378 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -102,7 +102,7 @@ int main(int argc, char * argv[]) Option & argInstance = parser.add