From 2a77f6f0122691fe1961259178c1ce9d0caca053 Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Fri, 28 Dec 2018 18:12:45 +0100 Subject: [PATCH] even more changes Signed-off-by: Paulchen-Panther --- CMakeLists.txt | 6 +- CompileHowto.md | 15 +- README.md | 2 +- assets/webconfig/i18n/de.json | 18 +- assets/webconfig/i18n/en.json | 22 +- assets/webconfig/js/content_network.js | 23 +- assets/webconfig/js/hyperion.js | 2 +- assets/webconfig/js/ledsim.js | 10 + bin/service/hyperion.systemd | 5 +- cmake/debian/postinst | 47 +- cmake/debian/preinst | 95 +- cmake/debian/prerm | 40 + cmake/packages.cmake | 4 +- config/hyperion.config.json.commented | 21 +- config/hyperion.config.json.default | 10 +- dependencies/CMakeLists.txt | 42 + include/api/JsonAPI.h | 254 ++++ include/api/JsonCB.h | 112 ++ include/boblightserver/BoblightServer.h | 2 +- include/bonjour/bonjourserviceregister.h | 5 + include/flatbufserver/FlatBufferConnection.h | 8 +- include/flatbufserver/FlatBufferServer.h | 2 - include/grabber/DispmanxFrameGrabber.h | 2 +- include/grabber/V4L2Grabber.h | 12 +- include/grabber/V4L2Wrapper.h | 1 - include/grabber/X11Grabber.h | 2 +- include/hyperion/CaptureCont.h | 7 + include/hyperion/ComponentRegister.h | 4 +- include/hyperion/Grabber.h | 6 +- include/hyperion/GrabberWrapper.h | 9 +- include/hyperion/Hyperion.h | 15 +- include/hyperion/PriorityMuxer.h | 7 + include/hyperion/SettingsManager.h | 3 - include/jsonserver/JsonServer.h | 13 - include/protoserver/ProtoServer.h | 25 - include/udplistener/UDPListener.h | 30 +- include/utils/Components.h | 7 +- include/utils/NetUtils.h | 34 + include/utils/Stats.h | 8 + include/utils/settings.h | 3 + include/webserver/WebServer.h | 7 +- libsrc/CMakeLists.txt | 1 + libsrc/api/CMakeLists.txt | 21 + libsrc/api/JsonAPI.cpp | 1069 +++++++++++++++++ libsrc/api/JsonCB.cpp | 304 +++++ libsrc/blackborder/BlackBorderProcessor.cpp | 12 +- .../BoblightClientConnection.cpp | 21 +- .../boblightserver/BoblightClientConnection.h | 12 +- libsrc/boblightserver/BoblightServer.cpp | 6 +- libsrc/bonjour/bonjourserviceregister.cpp | 8 +- libsrc/bonjour/bonjourserviceresolver.cpp | 2 +- libsrc/effectengine/CMakeLists.txt | 2 +- libsrc/effectengine/Effect.cpp | 2 +- libsrc/flatbufserver/FlatBufferClient.cpp | 10 +- libsrc/flatbufserver/FlatBufferConnection.cpp | 106 +- libsrc/flatbufserver/FlatBufferServer.cpp | 7 - .../grabber/dispmanx/DispmanxFrameGrabber.cpp | 22 +- libsrc/grabber/dispmanx/DispmanxWrapper.cpp | 2 +- libsrc/grabber/osx/OsxFrameGrabber.cpp | 9 +- libsrc/grabber/v4l2/V4L2Grabber.cpp | 76 +- libsrc/grabber/v4l2/V4L2Wrapper.cpp | 4 +- libsrc/grabber/x11/X11Grabber.cpp | 6 +- libsrc/hyperion/CMakeLists.txt | 1 + libsrc/hyperion/CaptureCont.cpp | 18 +- libsrc/hyperion/ComponentRegister.cpp | 4 +- libsrc/hyperion/Grabber.cpp | 13 +- libsrc/hyperion/GrabberWrapper.cpp | 16 +- libsrc/hyperion/Hyperion.cpp | 22 +- libsrc/hyperion/LinearColorSmoothing.cpp | 2 +- libsrc/hyperion/PriorityMuxer.cpp | 6 + libsrc/hyperion/SettingsManager.cpp | 19 +- libsrc/hyperion/hyperion.schema.json | 4 + libsrc/hyperion/resource.qrc | 1 + libsrc/hyperion/schema/schema-device.json | 13 +- .../hyperion/schema/schema-grabberV4L2.json | 42 +- .../hyperion/schema/schema-instCapture.json | 4 +- libsrc/hyperion/schema/schema-webConfig.json | 8 - libsrc/jsonserver/JsonServer.cpp | 56 +- libsrc/protoserver/ProtoClientConnection.cpp | 7 +- libsrc/protoserver/ProtoClientConnection.h | 5 +- libsrc/protoserver/ProtoServer.cpp | 59 +- libsrc/python/CMakeLists.txt | 2 +- libsrc/udplistener/UDPListener.cpp | 41 +- libsrc/utils/Stats.cpp | 50 +- libsrc/webserver/CgiHandler.cpp | 9 +- libsrc/webserver/CgiHandler.h | 5 +- libsrc/webserver/QtHttpServer.h | 76 +- libsrc/webserver/StaticFileServing.cpp | 5 +- libsrc/webserver/StaticFileServing.h | 12 +- libsrc/webserver/WebJsonRpc.h | 4 + libsrc/webserver/WebServer.cpp | 37 +- libsrc/webserver/WebSocketClient.cpp | 9 +- src/hyperion-v4l2/hyperion-v4l2.cpp | 4 +- src/hyperion-x11/X11Wrapper.cpp | 3 +- src/hyperion-x11/X11Wrapper.h | 3 + src/hyperiond/CMakeLists.txt | 4 +- src/hyperiond/hyperiond.cpp | 69 +- src/hyperiond/hyperiond.h | 6 +- src/hyperiond/main.cpp | 2 +- 99 files changed, 2610 insertions(+), 673 deletions(-) create mode 100644 cmake/debian/prerm create mode 100644 include/api/JsonAPI.h create mode 100644 include/api/JsonCB.h create mode 100644 include/utils/NetUtils.h create mode 100644 libsrc/api/CMakeLists.txt create mode 100644 libsrc/api/JsonAPI.cpp create mode 100644 libsrc/api/JsonCB.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index facbd343..bdb31224 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ SET ( DEFAULT_X11 OFF ) SET ( DEFAULT_WS281XPWM OFF ) SET ( DEFAULT_USE_SHARED_AVAHI_LIBS ON ) SET ( DEFAULT_USE_SYSTEM_PROTO_LIBS OFF ) +SET ( DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS OFF ) SET ( DEFAULT_TESTS OFF ) IF ( ${CMAKE_SYSTEM} MATCHES "Linux" ) @@ -163,6 +164,9 @@ message(STATUS "ENABLE_PROFILER = ${ENABLE_PROFILER}") SET ( PROTOBUF_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/proto ) SET ( PROTOBUF_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/proto ) +SET ( FLATBUFFERS_INSTALL_BIN_DIR ${CMAKE_BINARY_DIR}/flatbuf ) +SET ( FLATBUFFERS_INSTALL_LIB_DIR ${CMAKE_BINARY_DIR}/flatbuf ) + # check all json files FILE ( GLOB_RECURSE HYPERION_SCHEMAS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libsrc/*schema*.json ) SET( JSON_FILES @@ -246,7 +250,7 @@ if (UNIX AND NOT APPLE) endif () # add QT5 dependency -SET(QT_MIN_VERSION "5.2.0") +SET(QT_MIN_VERSION "5.5.0") find_package(Qt5 COMPONENTS Core Gui Network SerialPort REQUIRED) message( STATUS "Found Qt Version: ${Qt5Core_VERSION}" ) IF ( "${Qt5Core_VERSION}" VERSION_LESS "${QT_MIN_VERSION}" ) diff --git a/CompileHowto.md b/CompileHowto.md index 25c6e2a5..dcbb869d 100644 --- a/CompileHowto.md +++ b/CompileHowto.md @@ -6,24 +6,13 @@ sudo apt-get update sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev ``` -### Ubuntu 14.04 specific -You need a newer version of cmake (minimum 3.0.0). Install it from the ppa or website -``` -sudo apt-get install software-properties-common -sudo add-apt-repository ppa:george-edison55/cmake-3.x -sudo apt-get update && sudo apt-get upgrade -``` **on RPI you need the videocore IV headers** ``` sudo apt-get install libraspberrypi-dev ``` -**OSMC** -libraspberrypi-dev is not available, use this instead -``` -sudo apt-get install rbp-userland-dev-osmc -``` + **ATTENTION Win10LinuxSubsystem** we do not (/we can't) support using hyperion in linux subsystem of MS Windows 10, albeit some users tested it with success. Keep in mind to disable all linux specific led and grabber hardware via cmake. Because we use QT as framework in hyperion, serialport leds and network driven devices could work. @@ -71,7 +60,7 @@ sudo make install/strip sudo make uninstall # ... or run it from compile directory bin/hyperiond -# webui is located on localhost:8090 +# webui is located on localhost:8099 ``` diff --git a/README.md b/README.md index 4bc70795..4b9d9354 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ If you need further support please open a topic at the our new forum! [Hyperion webpage/forum](https://www.hyperion-project.org). ## Requirements -* Debian 8, Ubuntu 14.04 or higher. Windows is not supported currently. +* Debian 9, Ubuntu 16.04 or higher. Windows is not supported currently. ## Building See [Compilehowto](CompileHowto.md) and [CrossCompileHowto](CrossCompileHowto.txt). diff --git a/assets/webconfig/i18n/de.json b/assets/webconfig/i18n/de.json index 469a5e21..730dc199 100644 --- a/assets/webconfig/i18n/de.json +++ b/assets/webconfig/i18n/de.json @@ -27,6 +27,7 @@ "general_col_green" : "grün", "general_col_blue" : "blau", "general_button_savesettings" : "Einstellungen speichern", + "general_btn_yes" : "Ja", "general_btn_ok" : "OK", "general_btn_cancel" : "Abbrechen", "general_btn_continue" : "Fortfahren", @@ -346,7 +347,7 @@ "edt_dev_enum_sub_min_warm_adjust" : "Minimale Anpassung: warm", "edt_dev_enum_white_off" : "Weiß ist aus", "edt_dev_general_heading_title" : "Allgemeine Einstellungen", - "edt_dev_general_ledCount_title" : "Anzahl Hardware LEDs", + "edt_dev_general_hardwareLedCount_title" : "Anzahl Hardware LEDs", "edt_dev_general_colorOrder_title" : "RGB Byte Reihenfolge", "edt_dev_general_rewriteTime_title" : "Aktualisierungszeit", "edt_dev_spec_header_title" : "Spezifische Einstellungen", @@ -482,13 +483,11 @@ "edt_conf_smooth_continuousOutput_expl" : "Aktualisiere die LEDs, auch wenn das Bild sich nicht geändert hat.", "edt_conf_v4l2_heading_title" : "USB Aufnahme", "edt_conf_v4l2_device_title" : "Gerät", - "edt_conf_v4l2_device_expl" : "Der Pfad zum USB Aufnahmegerät.", - "edt_conf_v4l2_input_title" : "Eingang", - "edt_conf_v4l2_input_expl" : "Der Eingang des Pfades.", + "edt_conf_v4l2_device_expl" : "Der Pfad zum USB (v4l) Aufnahmegerät. Wähle 'auto' für automatische Erkennung. Beispiel: '/dev/video0'", "edt_conf_v4l2_standard_title" : "Videoformat", - "edt_conf_v4l2_standard_expl" : "Wähle das passende Videoformat deiner Region.", + "edt_conf_v4l2_standard_expl" : "Wähle das passende Videoformat deiner Region. Auf 'Auto' wird der gewählte Modus vom v4l interface beibehalten.", "edt_conf_v4l2_sizeDecimation_title" : "Bildverkleinerung Faktor", - "edt_conf_v4l2_sizeDecimation_expl" : "Der Faktor der Bildverkleinerung ausgehend der von der ursprünglichen Größe, 1 bedeutet keine Änderung (originales Bild).", + "edt_conf_v4l2_sizeDecimation_expl" : "Der Faktor der Bildverkleinerung ausgehend von der ursprünglichen Größe, 1 bedeutet keine Änderung (originales Bild).", "edt_conf_v4l2_cropLeft_title" : "Entferne links", "edt_conf_v4l2_cropLeft_expl" : "Anzahl der Pixel auf der linken Seite die vom Bild entfernt werden.", "edt_conf_v4l2_cropRight_title" : "Entferne rechts", @@ -498,7 +497,7 @@ "edt_conf_v4l2_cropBottom_title" : "Entferne unten", "edt_conf_v4l2_cropBottom_expl" : "Anzahl der Pixel auf der unteren Seite die vom Bild entfernt werden.", "edt_conf_v4l2_signalDetection_title" : "Signal Erkennung", - "edt_conf_v4l2_signalDetection_expl" : "Wenn aktiviert, wird die USB Aufnahme temporär bei \"kein Signal\" abgeschalten.", + "edt_conf_v4l2_signalDetection_expl" : "Wenn aktiviert, wird die USB Aufnahme temporär bei \"kein Signal\" abgeschalten. Das Bild muss dazu 4 Sekunden lang unter die Schwellwerte fallen.", "edt_conf_v4l2_redSignalThreshold_title" : "Rote Signalschwelle", "edt_conf_v4l2_redSignalThreshold_expl" : "Je höher die rote Schwelle je eher wird abgeschalten bei entsprechendem rot-Anteil.", "edt_conf_v4l2_greenSignalThreshold_title" : "Grüne Signalschwelle", @@ -513,6 +512,11 @@ "edt_conf_v4l2_sDVOffsetMax_expl" : "Signal Erkennungs-Bereich vertikal maximum (0.0-1.0)", "edt_conf_v4l2_sDHOffsetMax_title" : "Signal Erkennung HMax", "edt_conf_v4l2_sDHOffsetMax_expl" : "Signal Erkennungs-Bereich horizontal maximum (0.0-1.0)", + "edt_conf_instCapture_heading_title" : "Instance Aufnahme", + "edt_conf_instC_systemEnable_title" : "Aktiviere Plattform Aufnahme", + "edt_conf_instC_systemEnable_expl" : "Aktiviert die Plattform Aufnahme für diese LED Hardware Instanz", + "edt_conf_instC_v4lEnable_title" : "Aktiviere USB Aufnahme", + "edt_conf_instC_v4lEnable_expl" : "Aktiviert die USB Aufnahme für diese LED Hardware Instanz", "edt_conf_fg_heading_title" : "Plattform Aufnahme", "edt_conf_fg_type_title" : "Typ", "edt_conf_fg_type_expl" : "Art der Plattform Aufnahme, standard ist 'auto'", diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 5c2ae968..51067818 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -27,6 +27,7 @@ "general_col_green" : "green", "general_col_blue" : "blue", "general_button_savesettings" : "Save settings", + "general_btn_yes" : "Yes", "general_btn_ok" : "OK", "general_btn_cancel" : "Cancel", "general_btn_continue" : "Continue", @@ -347,7 +348,7 @@ "edt_dev_enum_white_off" : "White off", "edt_dev_general_heading_title" : "General Settings", "edt_dev_general_name_title" : "Configuration name", - "edt_dev_general_ledCount_title" : "Count of all hardware LEDs", + "edt_dev_general_hardwareLedCount_title" : "Hardware LED count", "edt_dev_general_colorOrder_title" : "RGB byte order", "edt_dev_general_rewriteTime_title" : "Refresh time", "edt_dev_spec_header_title" : "Specific Settings", @@ -483,17 +484,9 @@ "edt_conf_smooth_continuousOutput_expl" : "Update the leds even there is no changed picture.", "edt_conf_v4l2_heading_title" : "USB Capture", "edt_conf_v4l2_device_title" : "Device", - "edt_conf_v4l2_device_expl" : "The path to the usb capture.", - "edt_conf_v4l2_input_title" : "Input", - "edt_conf_v4l2_input_expl" : "Input of this path.", + "edt_conf_v4l2_device_expl" : "The path to the usb capture interface. Set to 'auto' for auto detection. Example: '/dev/video0'", "edt_conf_v4l2_standard_title" : "Video standard", - "edt_conf_v4l2_standard_expl" : "Select the video standard for your region.", - "edt_conf_v4l2_width_title" : "Width", - "edt_conf_v4l2_width_expl" : "The width of the picture. (-1 = auto width)", - "edt_conf_v4l2_height_title" : "Height", - "edt_conf_v4l2_height_expl" : "The height of the picture. (-1 = auto height)", - "edt_conf_v4l2_frameDecimation_title" : "Frame decimation", - "edt_conf_v4l2_frameDecimation_expl" : "The factor of frame decimation", + "edt_conf_v4l2_standard_expl" : "Select the video standard for your region. 'Auto' keeps the chosen one from v4l interface", "edt_conf_v4l2_sizeDecimation_title" : "Size decimation", "edt_conf_v4l2_sizeDecimation_expl" : "The factor of size decimation. 1 means no decimation (keep original size)", "edt_conf_v4l2_cropLeft_title" : "Crop left", @@ -505,7 +498,7 @@ "edt_conf_v4l2_cropBottom_title" : "Crop bottom", "edt_conf_v4l2_cropBottom_expl" : "Count of pixels on the bottom side that are removed from the picture.", "edt_conf_v4l2_signalDetection_title" : "Signal detection", - "edt_conf_v4l2_signalDetection_expl" : "If enabled, usb capture will be temporarily disabled when no signal was found.", + "edt_conf_v4l2_signalDetection_expl" : "If enabled, usb capture will be temporarily disabled when no signal was found. This will happen when the picture fall below the threshold value for a period of 4 seconds.", "edt_conf_v4l2_redSignalThreshold_title" : "Red signal threshold", "edt_conf_v4l2_redSignalThreshold_expl" : "Darkens low red values (recognized as black)", "edt_conf_v4l2_greenSignalThreshold_title" : "Green signal threshold", @@ -520,6 +513,11 @@ "edt_conf_v4l2_sDVOffsetMax_expl" : "Signal detection area vertical maximum (0.0-1.0)", "edt_conf_v4l2_sDHOffsetMax_title" : "Signal Detection HMax", "edt_conf_v4l2_sDHOffsetMax_expl" : "Signal detection area horizontal maximum (0.0-1.0)", + "edt_conf_instCapture_heading_title" : "Instance Capture", + "edt_conf_instC_systemEnable_title" : "Enable platform capture", + "edt_conf_instC_systemEnable_expl" : "Enables the platform capture for this led hardware instance", + "edt_conf_instC_v4lEnable_title" : "Enable USB capture", + "edt_conf_instC_v4lEnable_expl" : "Enables the USB capture for this led hardware instance", "edt_conf_fg_heading_title" : "Platform Capture", "edt_conf_fg_type_title" : "Type", "edt_conf_fg_type_expl" : "Type of platform capture, default is 'auto'", diff --git a/assets/webconfig/js/content_network.js b/assets/webconfig/js/content_network.js index 5c176e2f..eb276c08 100644 --- a/assets/webconfig/js/content_network.js +++ b/assets/webconfig/js/content_network.js @@ -4,6 +4,7 @@ $(document).ready( function() { var conf_editor_net = null; var conf_editor_json = null; var conf_editor_proto = null; + var conf_editor_fbs = null; var conf_editor_bobl = null; var conf_editor_udpl = null; var conf_editor_forw = null; @@ -20,6 +21,11 @@ $(document).ready( function() { $('#conf_cont_proto').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_ps_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver')); $('#conf_cont_proto').append(createHelpTable(schema.protoServer.properties, $.i18n("edt_conf_ps_heading_title"))); + //flatbufserver + $('#conf_cont').append(createRow('conf_cont_flatbuf')) + $('#conf_cont_flatbuf').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fbs_heading_title"), 'editor_container_fbserver', 'btn_submit_fbserver')); + $('#conf_cont_flatbuf').append(createHelpTable(schema.flatbufServer.properties, $.i18n("edt_conf_fbs_heading_title"))); + //boblight $('#conf_cont').append(createRow('conf_cont_bobl')) $('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver')); @@ -43,6 +49,7 @@ $(document).ready( function() { $('#conf_cont').addClass('row'); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_js_heading_title"), 'editor_container_jsonserver', 'btn_submit_jsonserver')); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_ps_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver')); + $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fbs_heading_title"), 'editor_container_fbserver', 'btn_submit_fbserver')); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver')); $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_udpl_heading_title"), 'editor_container_udplistener', 'btn_submit_udplistener')); if(storedAccess != 'default') @@ -62,7 +69,7 @@ $(document).ready( function() { requestWriteConfig(conf_editor_json.getValue()); }); - //proto + //protobuffer conf_editor_proto = createJsonEditor('editor_container_protoserver', { protoServer : schema.protoServer }, true, true); @@ -75,6 +82,19 @@ $(document).ready( function() { requestWriteConfig(conf_editor_proto.getValue()); }); + //flatbuffer + conf_editor_fbs = createJsonEditor('editor_container_fbserver', { + flatbufServer : schema.flatbufServer + }, true, true); + + conf_editor_fbs.on('change',function() { + conf_editor_fbs.validate().length ? $('#btn_submit_fbserver').attr('disabled', true) : $('#btn_submit_fbserver').attr('disabled', false); + }); + + $('#btn_submit_fbserver').off().on('click',function() { + requestWriteConfig(conf_editor_fbs.getValue()); + }); + //boblight conf_editor_bobl = createJsonEditor('editor_container_boblightserver', { boblightServer : schema.boblightServer @@ -122,6 +142,7 @@ $(document).ready( function() { { createHint("intro", $.i18n('conf_network_json_intro'), "editor_container_jsonserver"); createHint("intro", $.i18n('conf_network_proto_intro'), "editor_container_protoserver"); + createHint("intro", $.i18n('conf_network_fbs_intro'), "editor_container_fbserver"); createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver"); createHint("intro", $.i18n('conf_network_udpl_intro'), "editor_container_udplistener"); createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder"); diff --git a/assets/webconfig/js/hyperion.js b/assets/webconfig/js/hyperion.js index 78273a59..1ec7c7c8 100644 --- a/assets/webconfig/js/hyperion.js +++ b/assets/webconfig/js/hyperion.js @@ -173,7 +173,7 @@ function sendToHyperion(command, subcommand, msg) // also used for watchdog function requestServerInfo() { - sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update"]'); + sendToHyperion("serverinfo","",'"subscribe":["components-update","sessions-update","priorities-update", "imageToLedMapping-update", "adjustment-update", "videomode-update", "effects-update", "settings-update"]'); } function requestSysInfo() diff --git a/assets/webconfig/js/ledsim.js b/assets/webconfig/js/ledsim.js index 698fc92c..0b3666e2 100644 --- a/assets/webconfig/js/ledsim.js +++ b/assets/webconfig/js/ledsim.js @@ -244,4 +244,14 @@ $(document).ready(function() { }; image.src ='data:image/png;base64,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LV199dXo37//58ePH/9pYWHhk0Z5bm7uXdu2bdvCGNuW6B92paCsC4vbiRLBWQDoeEodHocydzpvpaY3sGnq1AlXrbaSJwz/WQvKjjjH2Lp1a1dtmySRoiiUlpbGR44cyadNm2b3foiz/2932xN6gfH9tCuuuKLmyJEj9xQVFf1NkqQsvZP94Fe/+tXm0aNHvzhixAhWVlZ2RgaLyy+/nNra2vYR0QnGWBYAKTMzc8qdd9456FNMvgwAABKUSURBVMYbbzyFbjrI28na+neeCD1gFwBqamqqWLduXbUuRnT7vV577TUA4E8++eQL995771y/33+lzhX0Hz58+M+feOKJhbfddltDvH8tOfQiikdd4gWqhihz2oDaavUyDpslDgw5FvH9gc11chvTSLJrp9gqWvRwidBZ2UTUyfK7/oeAs0vas2cPBg4cuLmmpmaZWD5ixIgVr7/++tiysrIzGtxs3bp1xzjnW4zv4vP5Si699NKrJEnyjh49+ox+HM65jFi0hjRDSbV///4tn332WS1jrEcAnTRpEgDQI4880rply5almqYdFWTVK6+44orrAUi6g0sSAJXiKomcTjWDSTtr53trV2bpjysqkZA4wgMXlA+2czgTyNeOJpJLkmRHjZ14PYkgxX9aMsJT9u/f/6lgMPiS8M6ZX/va1/5nxowZmWdKgVNdXY1rr71WPXXq1HOITcIGABQWFv74o48+mrpnzx5at25djymnHjgbjLH7AHQ4sCiKUn355Zf/VVfi9Ihab9u2rWP/sssu233ixIkHxfPFxcUPrFmzZvz+/fu5PQVlJrbWXiZ1OsHaLoaQVRQFOARDF4DaRC6wXbvFanaITo3h4H261G8FJJ1lhgPK20Xm/N+YZH0mrBIbNmx4QFXVDsdSn8837aWXXroFgMwYM6hGt1O/fv0wffp05OXlfdzW1vasMBh4J0+e/MaHH3444corr+zxt9K5lZsA3CeWL1++/N5QKBRkjLXjDNtei4uLVwWDweeFd0q78sorX927d28O51yzBSgJwLQJBBRvcnVCQNmwx/HqgQ07SZxzZmKbrahtF/DX1dVZUnid9XW6ZGDcZMQEgvWqZ5achsHW/qdST/P3WrlyZXlNTc0jBnVhjHn69u3746NHj04CIIlUoztp+fLl2LhxIwAgEAj8RFGU7UKHTr344ov/dvjw4YvEb3XVVVc5ppzGdyaiGwCsEM9/8sknv7v33nt3yrIcIqLQmQJonz59AAC33nor+8lPfrJYUZRdgpZ6wLx585ZrmuaxBKimt0LVswZLh81kl/DrdB8/bYDjDrW4ZCODMhN7a2VqsWTBDx48CDsWVxiNE2mp48qghkklnpxpBc5kgMn+zSj++9//Tq+++uqfI5FIR6wgl8uV36dPn0eWLFmSOXDgwB61b8mSJQCAuXPnAgBft27d9/SwJAZIC4qLi98Ih8PX68IY1q5da5hLbEEpADOTiH6BmJN/BygqKyvXX3fddW8ACGua1ngmlVENDQ0AgN///vf07LPPniorK7uTc24EjZJSUlK+4na7L7AEqFkQ1Kz1904WjLVVEiHx6mN21JBby/Xcrh5bJdQPfvADy8nkNu2DA3bXDBwoikKqqnIn4OyuzFlfX68gNrOj2zxeT9jDG2+8EYsXLw6/8cYbSzRNOy6YKi6566677jpy5EiPOYLs7Gy88847ICK6+uqrKw4ePHibOI1LkqR8j8fzTDQa/Wtra+uMsrIyn05xLf+L/s4ZRHQ9gP8HYBkE18uGhoYdP//5z3938ODBWsScQEI4iyassWPHbmpubv4dOsfxspwc7rILX+C2lj/tNLnA6VkmZj6f4CyuUaIl4DtRPQsZtDuO/KI3UzxzSEKAAqBQKAQTNba8x/hO3aGeK1asGDl+/PiMKVOmtIvR7ZMwn7Qyxg51t3etWrUKAPCd73zn2Pnnn7/oggsueNs4l5ubu3jr1q2bGGPvDBkyBBUVFd16xqlTp0RwaSNGjFj/8ccfLxw/fvwzbre7SD/ndblcX3e5XDOHDRtWpmna+wB2McYqEVvdTgKQhdjc0gsBzAXQH6ao9DU1NR9+73vfe2T9+vXHAdQhZvs9K26FQqzc6PLly3+7bNmyOR6PZ0K8e1yaBepsAqc6XQS3E5hEdz4H99sBVSSfVnU6Xdm6y+RvVVW54eWDxAsUU6KPrweOtgW8GDsoWQrDGHP/8Ic//JMYSSHJ5Caif+paS9YTKjF69Gg2ZsyYdxoaGn6dnZ292JBHL7zwwsevvvrqS1577bVaU6dMOnm9XkQiERowYAAvLS19b9GiRV999NFHH8vIyJgtqDIDjLGJjLGJ+nFYYE99sI+KwHfs2LGytLR0taqqjYyxen1pSBVnKYnf4eGHH67/+te/fnNpaelWxFmbRjJYWlEGVRMriAAHc0L1zsiROIqf5TP0SO5k0uBynY3s6RQ4IiKy8CRKBE7bTh2JRMSpaJbg7Abr10kGlyQpwBhLg75uaJI5RVGUbJ2KuM31J8P67tmzhwDQG2+88WtFUTpmCbnd7qFPP/30vYZ815NwK0bkwqNHj/Jp06bxlStXVmRmZl5XVla2NBQK2YUo8CFm00yzAicRRRsaGrY/88wzP5o4ceJKVVUbGGMniciJ3BlPgZlUuvvuu9lFF130aV1d3e3xntMBUHPm1iBNZANl+qwAQ3ngMYXJTMbEwvXIaX7h56cxxpge8tEp5SRB3gsIbQu43W5JVVWzqSUROMW6mF6X3+fzSZqmoa2tzVL73F0ziiRJgTM5iuueQGmGzMMYyzbOeTyetGQ73qJFi2qbm5vvJSJDQ8Oys7N/dPLkye/feOONVpQhS3ie43f76KOPKDU1VQFwauTIkY8/9dRT127ZsuXumpqad0Oh0HF95o21bUhVW4LB4MGKioo1zz777JKvfvWrixctWvQBYpPYT+iUM6FSSPxWbrc7oK8/2630q1/9imbOnMnGjBmzqqWl5UXxMR6PJ02WZdZJBk1gU3BCpSQAwVOnTi2vqqrq297ennr48OGDbrebxYm2HpeNZIxRU1PTurq6uoa6ujp3JBLR6uvrw0OGDImndbUsIyJfe3v7M1VVVf86efJkemtra2TXrl2nsrKy4ilzLOVhIkJLS8sLNTU1h2pqatJDoRBfs2ZNtcvlourq6rhsbbKptrb2xfr6+qra2lqvpmlST8DJGGPNzc11iMWsbdUVJLcfP368qKmpKa0hpm70JglSGjRo0PubNm26KRQKjQ6FQh5ZlqXm5mb6/PPP0xCLJdTxHRsbG+88duzYgKampoy6urr6ZNrf1tYGfeYM3XXXXXtkWT5SWlq6pri4uHDGjBmD8vLyclNSUtIZYx7d7BUJBoPN+/fvr928efPx8vLy5mPHjrUjFvGhhYjadGDGfd8xY8bg888/R3Nz89LKysphdXV16S0tLW07d+40ZkF1S1zYsGEDAYi+/fbb9w8ePHhfMBjMVlVVqqysPF5RUREFQF0AaiODOmIhJUkKHTp06G/vvvtuZk1Njc/r9WLkyJHtKSkpGhK7zlnZItnx48e3v/DCC/srKirSVVVlgwcPDhcUFKgJnOWtBpaUHTt2fLhs2bJ/HT16NItzLhcXF0dGjhypeDwecgpOYwCdMWPGP3fu3LkXscWK3R6PRykoKIiaPJbIrE1MNuXn528CsFt/jnwmtIiSJEU55/zqq69mOTk5zwHoq7PAYIyFicixHFZQUABVVdULL7xwDYAPcTqUi4bT0/+5oKF9QXiexBhrJyLHDum6SKIhNpEi+tFHH7UCqHvppZf2Cay7MTmdCRxbVJdPwwCMd3TEf3/++ed48cUXWWZm5l8A9BH+RY/iJOXl5WHWrFl8wYIFRwD8QecuDK4jCIDYCUIKB4oBjOTASAJGMeSOyMXaYV5clAKgFsBtAI7oH9uWEhIRBYNBVlVV5Wpra2OSJFFWVlY0Pz8/Ki4+hPgeQJ3Ot7a24sCBA+6amhpJVVVKT0/nQ4cOVfLy8lRhDUxHVLShoYFt375dLisrc4XDYRYIBNQJEyYokyZNUoVAYYnAyYkIW7duZU8++aRcUVHhaW9vZ5qmqf369VMeeughtbS0tJPM2VOuVOh0Es5MiuL0fFIJp6MlAKcjK/JuttMljPFWdYnXGWBTeqg5ZegcPFYSyrmFBZF6+C+MdzTeTzsD/8Sly+3Gt9MAKOwYIYWAYjIBtB/WDvN1Bugh/eXjmUc6fG8NCmewt4LW0axMiuvFI0zoJiIiWZbBOSeL9T5FYNmxzhSNRjvmlDLGyOVykR6gGjZKMFgBV1EUYoyhsbGRKisrkZqaSllZWejbt28n171zwDuoN/0HJ5dqQg0XyKQDJZHl4j0WGthklqHvVL9QHwksWjyvobh1ut1uOxY7nkNCl2OPxwMAlJubi9zc3C4O773g7E1nBKDmkOwWAHUUPc+GkiWSMYHELoRWoEsWnIlU5fHMKHbHXbYiIHvB2ZvOGAVNANBE4LEDS7xZJonmXXKbuhPdn0jJ42RRo25te6lmb/p3A9TJtDAnbG0iZY6dgiZpu6dDzTEcsLVxI+L3grM3nVWAmlciMgGUCZ2ZOWRxndg9ndhFeQLgJhsFwammthecvek/i4LGURLZRT5IRFntANYdcCbraxs31hESx0cC4jvOM9EX9j8dnOfQfNPe5JCCMopN4zYoqBexpQNkJJgYbQO4ePIgR+J4tE6ngCWzwFNSfrboOqvmXOntxBhr6e3qXwIKqvvgktwZoBmIBVUK2bC7tp3YivKgq2Y1kauUGTx2ywBY1UMO25lwQaFz9P+SpoIAzOvt6ucoQDWANIATEOWxrBBYlNBha/QCmNT7qc7NxGMTkHvTuQpQ3VOYa0CEgDYNaJHBT4VR29aOwzIHSIgiRgBYPK2JldDKYR/Kr2sSvdlELtu8z0zXG95eLE6WQADS4JJy4HFBkLlVEE6AK7zTc6WudNSpe0OiWAyJPIednAcSWm+V6FlfFaw3nW0KygFFA8IEtHCgPopTlf/CD2XAk60BjIMMZ0aKHVtPUTNyGLF1CVr1rZjb9RxCzAFTE8DTeSEw86JahgukR8hefevT98WtsR/LDD5ocGl9kOFZiQsG5cDjaoIWfRvB+muR2c8FhlcROvVLhCqz4HFxuNnpdkgAZ7GXE9cBN46t1gY3rxsuHmumcnEirhrnfvP19pkMeYXxXoCe6zKoxoEIB4IcOMWBFA2aK4QTYRVII8Bt1QcM2VXsW4oOvFY9t+jboABWYxu1pJwuEzjdQvZYgDPFBMgUIYtlUQaE1Exkel7H+bMnIsOvgLR7UPnxi2ipzYF7/GVIPe/HSM2LQqq/B23/AiRvJ6uSoQFlnfnHTgK8ZgFGxQTcqA2Q7cCZ6HozYKPCj4m1T+vt5uc+QNs50EKAVwUkHitrIcCnxdwBGbdgV8VjVQBnUKCSbfo2oufTUxuMOEnmSQh2VNNtAqfXRDXF7BGALQMA74+MlE2Y851BSM3iIP48atevQu32CNx0D2qPDkHh/IFwFy2Bf3g65GO3Ibw1pi/jp1EpCc028+0uxNcHi4yC+MqKvo0K45OqNz2agCprNhS482jaC9BzGaA8xrZGtBh7Cw1QOdCuAQEOeDRA1lnbjjk7qg1L22aillasbbiDrZVNIDVTUNm07zKVSxbXiGHxjS3RpSjIXoUZtxbDX8hB0W0I/v1O7H8lAi8Ahu1ojS5Dw/EnkLskDVL/m+CZ6QM7+hNEN7YZSGM6FZUFwdUlUE/ZNHK5HcqlENgJZiNOW5VJ+o9gNudOZ+rt5ucwQK9iwJ8JKoBWDnBdHg1ynXpqALMDZ9QEzlYdhGaAhrqA07zOhBl0ZnnUDOJ4YI3VzyCBcKTtKSwYPR/jbs2G53wClAq0rSzF5lU6GywDBAku9iec3J8B94GHkf1LP9gFN8Lzf86HS7sWkecOg6sMkocYqFPzufBoUfslm1gM25iCCf4OWYATpnHIDFZjvxegX4rUIVG9QGA8BkiXBrj1fUmMUSTKnapAFVtNAG0TtiE9xxaQNPN4ThRDogzqFlhYY5tiUhQZ/hS/aC1H7ZUD0edxN6R8AsI1CN9egL++DqSDIV0ioT4ZHmhQtd+jMO9GBB7zxMI0ohlYcy+0258AgoDs7jRKqTayoyGQW5VbbbU4siVPcF/UxNpGLRQG77BeU8u5SkH/TMB1DAjHBK6oCkQJCGsmImD+54oOuoiwNbIi6EYUoQ/FN4GYeTUpTplIfWEqf4DAAI349yWw3yAWnqKFgBveQd06IMRdyINqsl9oIHjgx634W+NCfPu7AHsSwH9lAFc9Djn7UeA7KYxV5WrEap2+hhTndcRxCiZqzIXyqOl6s6zL4SzW3KUEvNfr6teb/g3JWNlb388gol8SkaIHYThORJc4rYufrkcmoseFesqIaDoRyeZn9qbe1JvigFPYH01E7wkrFP6diIqSBZSpzu8Q0Um9vhAR3U9EGVbX9qbe1JvsgXQDER0VwPkIEQW6CyJT3aVEtFOoez0Rje4FaW/qTYmBlElELxIR18HTTETXnAnwmECaQkRPCiANEdFig+XtTb3pbKT/D85E97Jm8YYnAAAAAElFTkSuQmCC'; } + + // ------------------------------------------------------------------ + $(hyperion).on("cmd-settings-update",function(event){ + var obj = event.response.data + Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { + serverInfo[val] = obj[val]; + }); + leds = serverConfig.leds + updateLedLayout(); + }); }); diff --git a/bin/service/hyperion.systemd b/bin/service/hyperion.systemd index 8d9f1f2c..35817d1b 100644 --- a/bin/service/hyperion.systemd +++ b/bin/service/hyperion.systemd @@ -1,13 +1,14 @@ [Unit] -Description=Hyperion ambient light systemd service +Description=Hyperion ambient light systemd service for user %i After=network.target [Service] ExecStart=/usr/bin/hyperiond WorkingDirectory=/usr/share/hyperion/bin +User=%i TimeoutStopSec=5 KillMode=mixed -Restart=always +Restart=on-failure RestartSec=2 [Install] diff --git a/cmake/debian/postinst b/cmake/debian/postinst index a49b4795..aa7e5e1e 100644 --- a/cmake/debian/postinst +++ b/cmake/debian/postinst @@ -6,6 +6,7 @@ install_file() dest="$2" if [ ! -e "$dest" ] + then cp "$src" "${dest}" return 1 else @@ -15,10 +16,7 @@ install_file() } -echo "--- hyperion ambient light postinstall ---" -echo "- install configuration template" -mkdir -p /etc/hyperion -mkdir -p /usr/share/hyperion/custom-effects +echo "---Hyperion ambient light postinstall ---" #check system CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835' /proc/cpuinfo` @@ -27,25 +25,29 @@ CPU_X32X64=`uname -m | grep 'x86_32\|i686\|x86_64' | wc -l` #Check for a bootloader as Berryboot BOOT_BERRYBOOT=$(grep -m1 -c '\(/var/media\|/media/pi\)/berryboot' /etc/mtab) -#get current system ip + add default port -address=$(ip -o -4 a | awk '$2 == "eth0" { gsub(/\/.*/, "", $4); print $4 }')":8099" +#get current system ip +NET_IF=`netstat -rn | awk '/^0.0.0.0/ {thif=substr($0,74,10); print thif;} /^default.*UG/ {thif=substr($0,65,10); print thif;}'` +NET_IP=`ifconfig ${NET_IF} | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1'` #check if hyperion is running HYPERION_RUNNING=false pgrep hyperiond > /dev/null 2>&1 && HYPERION_RUNNING=true +# search for users in system, returns first entry +FOUND_USR=`who | grep -o '^\w*\b'` || "root" + start_msg="" restart_msg="" -SERVICE_POSTFIX="" if grep -m1 systemd /proc/1/comm > /dev/null then echo "--> init deamon: systemd" # systemd $HYPERION_RUNNING && systemctl stop hyperiond 2> /dev/null - install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperiond.service && systemctl -q enable hyperiond.service - start_msg="--> systemctl start hyperiond" - systemctl start hyperiond + install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperiond@.service + systemctl enable hyperiond"@${FOUND_USR}".service + start_msg="--> systemctl start hyperiond for user ${FOUND_USR}" + systemctl start hyperiond"@${FOUND_USR}" elif [ -e /sbin/initctl ] then @@ -100,9 +102,12 @@ if [ $CPU_RPI -eq 1 ]; then fi fi +echo ${start_msg} + echo "-----------------------------------------------------------------------------" echo "--> Hyperion has been installed/updated!" -echo "--> For configuration, visit with your browser: ${address}" +echo "--> For configuration, visit with your browser: ${NET_IP}:8090" +echo "--> or if already used by another service try: ${NET_IP}:8091" $REBOOTMESSAGE echo "-----------------------------------------------------------------------------" echo "Webpage: www.hyperion-project.org" @@ -110,28 +115,12 @@ echo "Wiki: wiki.hyperion-project.org" echo "Forum: forum.hyperion-project.org" echo "-----------------------------------------------------------------------------" -# try to open the browser for desktops. TODO: add headless detection(?) -if [ $CPU_X32X64 -eq 1] -echo "--> Will open browser with target: ${address}" - if [[ -e /usr/bin/xdg-open ]] - then - xdg-open http://"$address" - elif [[ -e /usr/bin/x-www-browser ]] - then - x-www-browser http://"$address" - elif [[ -e /usr/bin/www-browser ]] - then - www-browser http://"$address" - fi -fi - if [ -e /opt/hyperion/ ] then echo echo "---------------------------------------------------------------------------------" - echo "- It seemd that you have an older version of hyperion installed in /opt/hyerion -" - echo "- please remove it and check your config to avoid problems -" + echo "- It seemd that you have an older version of hyperion installed in /opt/hyperion -" + echo "- please remove it to avoid problems -" echo "---------------------------------------------------------------------------------" fi - diff --git a/cmake/debian/preinst b/cmake/debian/preinst index 335aa696..515b5200 100644 --- a/cmake/debian/preinst +++ b/cmake/debian/preinst @@ -1,51 +1,56 @@ #!/bin/sh -# check which init script we should use -USE_SYSTEMD=`grep -m1 -c systemd /proc/1/comm` -USE_INITCTL=`which /sbin/initctl | wc -l` -USE_SERVICE=`which /usr/sbin/service | wc -l` +echo "---Hyperion ambient light preinst ---" -#check for hyperion install -if [ -d /usr/share/hyperion/bin ];then - if [ -e /etc/hyperion/hyperion.config.json ];then - file=`grep -m1 -c '"general"' /etc/hyperion/hyperion.config.json` - if [ $file -ne 1 ]; then - echo "--> It seems you are running an old version of Hyperion (1.X). Will create a backup at /usr/share/hyperion/Backup_Hyperion_1.0 and reset configuration / system service" - - # Stop hyperion daemon if it is running - echo '---> Stop Hyperion, if necessary' - if [ $USE_SYSTEMD -eq 1 ]; then - service hyperion stop 2>/dev/null - elif [ $USE_INITCTL -eq 1 ]; then - /sbin/initctl stop hyperion 2>/dev/null - elif [ $USE_SERVICE -eq 1 ]; then - /usr/sbin/service hyperion stop 2>/dev/null - fi - - #Backup - echo "--> Move old config(s) and files to /usr/share/hyperion/Backup_Hyperion_1.0" - mkdir /usr/share/hyperion/Backup_Hyperion_1.0 - mv /usr/share/hyperion /usr/share/hyperion/Backup_Hyperion_1.0 - mv /etc/hyperion/* /usr/share/hyperion/Backup_Hyperion_1.0 +# search for users in system, returns first entry +FOUND_USR=`who | grep -o '^\w*\b'` || "root" - #Disabling and delete service files - if [ $USE_SYSTEMD -eq 1 ]; then - # Delete and disable Hyperion systemd script - echo '---> Delete and disable Hyperion systemd service' - systemctl disable hyperion.service - rm -v /etc/systemd/system/hyperion* 2>/dev/null - elif [ $USE_INITCTL -eq 1 ]; then - echo '---> Delete and disable Hyperion initctl script' - rm -v /etc/init/hyperion* 2>/dev/null - initctl reload-configuration - elif [ $USE_SERVICE -eq 1 ]; then - # Delete and disable Hyperion init.d script - echo '---> Delete and disable Hyperion init.d script' - update-rc.d -f hyperion remove - rm /etc/init.d/hyperion* 2>/dev/null - fi - - echo "--> Hyperion 1.0 installation has been moved" - fi +# stop running daemon before we install +if pgrep hyperiond > /dev/null 2>&1 +then + if grep -m1 systemd /proc/1/comm > /dev/null + then + echo "--> stop init deamon: systemd" + # systemd + systemctl stop hyperiond"@${FOUND_USR}" 2> /dev/null + + elif [ -e /sbin/initctl ] + then + echo "--> stop init deamon: upstart" + # upstart + initctl stop hyperiond + + else + echo "--> stop init deamon: sysV" + # sysV + service hyperiond stop 2>/dev/null fi fi + + +#$USR=hyperionIS; + +#addToGroup() +##{ +# getent group $1 && adduser $USR $1; +#} + +#check if user exists +#if id $USR >/dev/null 2>&1; then +# echo "--> hyperion user exists, skip creation"; +#else + ## create user +# echo "--> Create Hyperion user"; +# adduser --system --group $USR; +#fi + +# add user to groups if required +## secondary user groups that are required to access system things +#addToGroup(dialout); +#addToGroup(video); +#addToGroup(audio); +#addToGroup(systemd-journal); +# platform specific groups +#addToGroup(i2c); +#addToGroup(spi); +#addToGroup(gpio); diff --git a/cmake/debian/prerm b/cmake/debian/prerm new file mode 100644 index 00000000..748ed9e5 --- /dev/null +++ b/cmake/debian/prerm @@ -0,0 +1,40 @@ +#!/bin/sh + +echo "---Hyperion ambient light prerm ---" + +# search for users in system, returns first entry +FOUND_USR=`who | grep -o '^\w*\b'` || "root" + +# stop running daemon before we delete it +HYPERION_RUNNING=false +pgrep hyperiond > /dev/null 2>&1 && HYPERION_RUNNING=true + +if grep -m1 systemd /proc/1/comm > /dev/null +then + echo "--> stop init deamon: systemd" + # systemd + $HYPERION_RUNNING && systemctl stop hyperiond"@${FOUND_USR}" 2> /dev/null + # disable user specific symlink + echo "--> Disable service and remove entry" + systemctl -q disable hyperiond"@${FOUND_USR}" + rm -v /etc/systemd/system/hyperiond@.service 2>/dev/null + +elif [ -e /sbin/initctl ] +then + echo "--> stop init deamon: upstart" + # upstart + $HYPERION_RUNNING && initctl stop hyperiond + echo "--> Remove upstart service" + rm -v /etc/init/hyperion* 2>/dev/null + initctl reload-configuration + +else + echo "--> stop init deamon: sysV" + # sysV + $HYPERION_RUNNING && service hyperiond stop 2>/dev/null + echo "--> Remove sysV service" + update-rc.d -f hyperion remove + rm /etc/init.d/hyperion* 2>/dev/null +fi + +return 0 diff --git a/cmake/packages.cmake b/cmake/packages.cmake index 7fef5d09..0d016880 100644 --- a/cmake/packages.cmake +++ b/cmake/packages.cmake @@ -13,9 +13,9 @@ SET ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" ) SET ( CPACK_DEBIAN_PACKAGE_MAINTAINER "Hyperion Team") SET ( CPACK_DEBIAN_PACKAGE_NAME "Hyperion" ) -SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/preinst" ) +SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/preinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/prerm" ) SET ( CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.hyperion-project.org" ) -SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5core5a (>= 5.2.0), libqt5network5 (>= 5.2.0), libqt5gui5 (>= 5.2.0), libqt5serialport5 (>= 5.2.0), libavahi-core7 (>= 0.6.31), libavahi-compat-libdnssd1 (>= 0.6.31), libusb-1.0-0, libpython3.4, libc6" ) +SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libqt5core5a (>= 5.5.0), libqt5network5 (>= 5.5.0), libqt5gui5 (>= 5.5.0), libqt5serialport5 (>= 5.5.0), libqt5sql5 (>= 5.5.0), libavahi-core7 (>= 0.6.31), libavahi-compat-libdnssd1 (>= 0.6.31), libusb-1.0-0, libpython3.5, libc6" ) SET ( CPACK_DEBIAN_PACKAGE_SECTION "Miscellaneous" ) SET ( CPACK_RPM_PACKAGE_NAME "Hyperion" ) diff --git a/config/hyperion.config.json.commented b/config/hyperion.config.json.commented index 30375404..c5313fca 100644 --- a/config/hyperion.config.json.commented +++ b/config/hyperion.config.json.commented @@ -20,14 +20,14 @@ /// Device configuration contains the following fields: /// * 'name' : The user friendly name of the device (only used for display purposes) - /// * 'type' : The type of the device or leds (known types for now are - /// APA102, WS2801, P9813, LPD6803, LPD8806, ---------PWM---------, WS2812b (just RPi1), WS281X (RPi1, RPi2, RPi3), --------OTHER--------, PhilipsHUE, AtmoOrb, PiBlaster, Tinkerforge, FadeCandy, RawHID (USB), UDP, SEDU, TPM2, USBASP-WS2801, USBASP-WS2812, ------3rd PARTY------, Adalight, AdalightAPA102, Atmo, Lightpack, Multi-Lightpack, Paintpack, Test (file), None) + /// * 'type' : The type of the device /// * [device type specific configuration] /// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.). /// * 'rewriteTime': in ms. Data is resend to leds, if no new data is available in thistime. 0 means no refresh "device" : { "type" : "file", + "hardwareLedCount" : 1, "output" : "/dev/null", "rate" : 1000000, "colorOrder" : "rgb", @@ -98,8 +98,7 @@ }, /// Configuration for the embedded V4L2 grabber - /// * device : V4L2 Device to use [default="/dev/video0"] - /// * input : V4L2 input to use [default=0] + /// * device : V4L2 Device to use [default="auto"] (Auto detection) /// * standard : Video standard (PAL/NTSC/SECAM/NO_CHANGE) [default="NO_CHANGE"] /// * sizeDecimation : Size decimation factor [default=8] /// * cropLeft : Cropping from the left [default=0] @@ -118,7 +117,6 @@ [ { "device" : "auto", - "input" : 0, "standard" : "NO_CHANGE", "sizeDecimation" : 8, "priority" : 240, @@ -233,13 +231,22 @@ "port" : 19444 }, - /// The configuration of the Proto server which enables the protobuffer remote interface + /// The configuration of the Protobuffer server which enables the Protobuffer remote interface /// * port : Port at which the protobuffer server is started "protoServer" : { "port" : 19445 }, + /// The configuration of the Flatbuffer server which enables the Flatbuffer remote interface + /// * port : Port at which the flatbuffer server is started + "flatbufServer" : + { + "enable" : true, + "port" : 19400, + "timeout" : 5 + }, + /// The configuration of the boblight server which enables the boblight remote interface /// * enable : Enable or disable the boblight server (true/false) /// * port : Port at which the boblight server is started @@ -269,12 +276,10 @@ }, /// Configuration of the Hyperion webserver - /// * enable : enable or disable the webserver (true/false) /// * document_root : path to hyperion webapp files (webconfig developer only) /// * port : the port where hyperion webapp is accasible "webConfig" : { - "enable" : true, "document_root" : "/path/to/files", "port" : 8090 }, diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 33f27ece..7ced8462 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -12,6 +12,7 @@ "device" : { "type" : "file", + "hardwareLedCount" : 1, "output" : "/dev/null", "rate" : 1000000, "colorOrder" : "rgb", @@ -58,7 +59,6 @@ [ { "device" : "auto", - "input" : 0, "standard" : "NO_CHANGE", "sizeDecimation" : 8, "cropLeft" : 0, @@ -135,6 +135,13 @@ "port" : 19445 }, + "flatbufServer" : + { + "enable" : true, + "port" : 19400, + "timeout" : 5 + }, + "boblightServer" : { "enable" : false, @@ -154,7 +161,6 @@ "webConfig" : { - "enable" : true, "document_root" : "", "port" : 8090 }, diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index e0443059..9d91a34e 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -9,6 +9,48 @@ if(ENABLE_WS281XPWM) external/rpi_ws281x/rpihw.c) endif() +set(USE_SYSTEM_FLATBUFFERS_LIBS ${DEFAULT_USE_SYSTEM_FLATBUFFERS_LIBS} CACHE BOOL "use flatbuffers library from system") + +if (USE_SYSTEM_FLATBUFFERS_LIBS) + find_package(flatbuffers REQUIRED) + include_directories(${FLATBUFFERS_INCLUDE_DIRS}) +else () + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared flatbuffers library") + set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "Build Flatbuffers with tests") + add_subdirectory(external/flatbuffers) + + if(CMAKE_CROSSCOMPILING) + # when crosscompiling import the flatc executable targets from a file generated by a native build + option(IMPORT_FLATC "flatc export file (flatc_export.cmake) from a native build" "IMPORT_FLATC-FILE_NOT_FOUND") + include(${IMPORT_FLATC}) + else() + # export the flatc compiler so it can be used when cross compiling + export(TARGETS flatc FILE "${CMAKE_BINARY_DIR}/flatc_export.cmake") + endif() + + # define the include for the flatbuffers library at the parent scope + set(FLATBUFFERS_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/flatbuffers/include") + set(FLATBUFFERS_INCLUDE_DIRS ${FLATBUFFERS_INCLUDE_DIRS} PARENT_SCOPE) + + # define the flatc executable at the parent scope + get_property(FLATBUFFERS_FLATC_EXECUTABLE TARGET flatc PROPERTY LOCATION) + set(FLATBUFFERS_FLATC_EXECUTABLE ${FLATBUFFERS_FLATC_EXECUTABLE} PARENT_SCOPE) +endif() + +message(STATUS "Using flatbuffers compiler: " ${FLATBUFFERS_FLATC_EXECUTABLE}) + + +function(compile_flattbuffer_schema SRC_FBS OUTPUT_DIR) + string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS}) + add_custom_command( + OUTPUT ${GEN_HEADER} + COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable + --gen-object-api + -o "${OUTPUT_DIR}" + "${SRC_FBS}" + DEPENDS flatc) +endfunction() + set(USE_SYSTEM_PROTO_LIBS ${DEFAULT_USE_SYSTEM_PROTO_LIBS} CACHE BOOL "use protobuf library from system") if (USE_SYSTEM_PROTO_LIBS) diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h new file mode 100644 index 00000000..580975d2 --- /dev/null +++ b/include/api/JsonAPI.h @@ -0,0 +1,254 @@ +#pragma once + +// hyperion includes +#include +#include +#include +#include + +// qt includess +#include +#include +#include +#include + +// createEffect helper +struct find_schema: std::unary_function +{ + QString pyFile; + find_schema(QString pyFile):pyFile(pyFile) { } + bool operator()(EffectSchema const& schema) const + { + return schema.pyFile == pyFile; + } +}; + +// deleteEffect helper +struct find_effect: std::unary_function +{ + QString effectName; + find_effect(QString effectName) :effectName(effectName) { } + bool operator()(EffectDefinition const& effectDefinition) const + { + return effectDefinition.name == effectName; + } +}; + +class JsonCB; + +class JsonAPI : public QObject +{ + Q_OBJECT + +public: + /// + /// Constructor + /// + /// @param peerAddress provide the Address of the peer + /// @param log The Logger class of the creator + /// @param parent Parent QObject + /// @param noListener if true, this instance won't listen for hyperion push events + /// + JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener = false); + + /// + /// Handle an incoming JSON message + /// + /// @param message the incoming message as string + /// + void handleMessage(const QString & message, const QString& httpAuthHeader = ""); + +public slots: + /// _timer_ledcolors requests ledcolor updates (if enabled) + void streamLedcolorsUpdate(); + + /// push images whenever hyperion emits (if enabled) + void setImage(const Image & image); + + /// process and push new log messages from logger (if enabled) + void incommingLogMessage(Logger::T_LOG_MESSAGE); + +signals: + /// + /// Signal emits with the reply message provided with handleMessage() + /// + void callbackMessage(QJsonObject); + + /// + /// Signal emits whenever a jsonmessage should be forwarded + /// + void forwardJsonMessage(QJsonObject); + +private: + + // The JsonCB instance which handles data subscription/notifications + JsonCB* _jsonCB; + // true if further callbacks are forbidden (http) + bool _noListener; + /// The peer address of the client + QString _peerAddress; + + /// Log instance + Logger* _log; + + /// Hyperion instance + Hyperion* _hyperion; + + /// timer for ledcolors streaming + QTimer _timer_ledcolors; + + // streaming buffers + QJsonObject _streaming_leds_reply; + QJsonObject _streaming_image_reply; + QJsonObject _streaming_logging_reply; + + /// flag to determine state of log streaming + bool _streaming_logging_activated; + + /// mutex to determine state of image streaming + QMutex _image_stream_mutex; + + /// timeout for live video refresh + volatile qint64 _image_stream_timeout; + + /// + /// Handle an incoming JSON Color message + /// + /// @param message the incoming message + /// + void handleColorCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Image message + /// + /// @param message the incoming message + /// + void handleImageCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Effect message + /// + /// @param message the incoming message + /// + void handleEffectCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Effect message (Write JSON Effect) + /// + /// @param message the incoming message + /// + void handleCreateEffectCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Effect message (Delete JSON Effect) + /// + /// @param message the incoming message + /// + void handleDeleteEffectCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON System info message + /// + /// @param message the incoming message + /// + void handleSysInfoCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Server info message + /// + /// @param message the incoming message + /// + void handleServerInfoCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Clear message + /// + /// @param message the incoming message + /// + void handleClearCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Adjustment message + /// + /// @param message the incoming message + /// + void handleAdjustmentCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON SourceSelect message + /// + /// @param message the incoming message + /// + void handleSourceSelectCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON GetConfig message and check subcommand + /// + /// @param message the incoming message + /// + void handleConfigCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON GetConfig message from handleConfigCommand() + /// + /// @param message the incoming message + /// + void handleSchemaGetCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON SetConfig message from handleConfigCommand() + /// + /// @param message the incoming message + /// + void handleConfigSetCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON Component State message + /// + /// @param message the incoming message + /// + void handleComponentStateCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON Led Colors message + /// + /// @param message the incoming message + /// + void handleLedColorsCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON Logging message + /// + /// @param message the incoming message + /// + void handleLoggingCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON Proccessing message + /// + /// @param message the incoming message + /// + void handleProcessingCommand(const QJsonObject & message, const QString &command, const int tan); + + /// Handle an incoming JSON VideoMode message + /// + /// @param message the incoming message + /// + void handleVideoModeCommand(const QJsonObject & message, const QString &command, const int tan); + + /// + /// Handle an incoming JSON message of unknown type + /// + void handleNotImplemented(); + + /// + /// Send a standard reply indicating success + /// + void sendSuccessReply(const QString &command="", const int tan=0); + + /// + /// Send a standard reply indicating success with data + /// + void sendSuccessDataReply(const QJsonDocument &doc, const QString &command="", const int &tan=0); + + /// + /// Send an error message back to the client + /// + /// @param error String describing the error + /// + void sendErrorReply(const QString & error, const QString &command="", const int tan=0); +}; diff --git a/include/api/JsonCB.h b/include/api/JsonCB.h new file mode 100644 index 00000000..98d1c3bb --- /dev/null +++ b/include/api/JsonCB.h @@ -0,0 +1,112 @@ +#pragma once + +// qt incl +#include +#include + +// components def +#include +// bonjour +#include +// videModes +#include +// settings +#include + +class Hyperion; +class ComponentRegister; +class BonjourBrowserWrapper; +class PriorityMuxer; + +class JsonCB : public QObject +{ + Q_OBJECT + +public: + JsonCB(QObject* parent); + + /// + /// @brief Subscribe to future data updates given by cmd + /// @param cmd The cmd which will be subscribed for + /// @return True on success, false if not found + /// + bool subscribeFor(const QString& cmd); + + /// + /// @brief Get all possible commands to subscribe for + /// @return The list of commands + /// + QStringList getCommands() { return _availableCommands; }; + /// + /// @brief Get all subscribed commands + /// @return The list of commands + /// + QStringList getSubscribedCommands() { return _subscribedCommands; }; +signals: + /// + /// @brief Emits whenever a new json mesage callback is ready to send + /// @param The JsonObject message + /// + void newCallback(QJsonObject); + +private slots: + /// + /// @brief handle component state changes + /// + void handleComponentState(const hyperion::Components comp, const bool state); + + /// + /// @brief handle emits from bonjour wrapper + /// @param bRegisters The full register map + /// + void handleBonjourChange(const QMap& bRegisters); + + /// + /// @brief handle emits from PriorityMuxer + /// + void handlePriorityUpdate(); + + /// + /// @brief Handle imageToLedsMapping updates + /// + void handleImageToLedsMappingChange(const int& mappingType); + + /// + /// @brief Handle the adjustment update + /// + void handleAdjustmentChange(); + + /// + /// @brief Handle video mode change + /// @param mode The new videoMode + /// + void handleVideoModeChange(const VideoMode& mode); + + /// + /// @brief Handle effect list change + /// + void handleEffectListChange(); + + /// + /// @brief Handle a config part change. This does NOT include (global) changes from other hyperion instances + /// @param type The settings type from enum + /// @param data The data as QJsonDocument + /// + void handleSettingsChange(const settings::type& type, const QJsonDocument& data); + +private: + /// pointer of Hyperion instance + Hyperion* _hyperion; + /// pointer of comp register + ComponentRegister* _componentRegister; + /// Bonjour instance + BonjourBrowserWrapper* _bonjour; + /// priority muxer instance + PriorityMuxer* _prioMuxer; + /// contains all available commands + QStringList _availableCommands; + /// contains active subscriptions + QStringList _subscribedCommands; + /// construct callback msg + void doCallback(const QString& cmd, const QVariant& data); +}; diff --git a/include/boblightserver/BoblightServer.h b/include/boblightserver/BoblightServer.h index 3b711816..d0e1567b 100644 --- a/include/boblightserver/BoblightServer.h +++ b/include/boblightserver/BoblightServer.h @@ -31,7 +31,7 @@ public: /// @param hyperion Hyperion instance /// @param port port number on which to start listening for connections /// - BoblightServer(const QJsonDocument& config); + BoblightServer(Hyperion* hyperion, const QJsonDocument& config); ~BoblightServer(); /// diff --git a/include/bonjour/bonjourserviceregister.h b/include/bonjour/bonjourserviceregister.h index 6e3e14ec..d0c65d87 100755 --- a/include/bonjour/bonjourserviceregister.h +++ b/include/bonjour/bonjourserviceregister.h @@ -47,6 +47,8 @@ public: void registerService(const BonjourRecord &record, quint16 servicePort, std::vector> txt = std::vector>()); inline BonjourRecord registeredRecord() const {return finalRecord; } + const quint16 & getPort() { return _port; }; + signals: void error(DNSServiceErrorType error); void serviceRegistered(const BonjourRecord &record); @@ -62,6 +64,9 @@ private: DNSServiceRef dnssref; QSocketNotifier *bonjourSocket; BonjourRecord finalRecord; + + // current port + quint16 _port = 0; }; #endif // BONJOURSERVICEREGISTER_H diff --git a/include/flatbufserver/FlatBufferConnection.h b/include/flatbufserver/FlatBufferConnection.h index a4c06a59..a9cc9eed 100644 --- a/include/flatbufserver/FlatBufferConnection.h +++ b/include/flatbufserver/FlatBufferConnection.h @@ -19,7 +19,7 @@ #include "hyperion_request_generated.h" /// -/// Connection class to setup an connection to the hyperion server and execute commands. Used from standalone capture binaries (x11/dispamnx/...) +/// Connection class to setup an connection to the hyperion server and execute commands. /// class FlatBufferConnection : public QObject { @@ -40,7 +40,7 @@ public: ~FlatBufferConnection(); /// Do not read reply messages from Hyperion if set to true - void setSkipReply(bool skip); + void setSkipReply(const bool& skip); /// /// Set all leds to the specified color @@ -116,8 +116,8 @@ private: /// Host port uint16_t _port; - /// Skip receiving reply messages from Hyperion if set - bool _skipReply; + /// buffer for reply + QByteArray _receiveBuffer; QTimer _timer; QAbstractSocket::SocketState _prevSocketState; diff --git a/include/flatbufserver/FlatBufferServer.h b/include/flatbufserver/FlatBufferServer.h index 799e3d92..eb751213 100644 --- a/include/flatbufserver/FlatBufferServer.h +++ b/include/flatbufserver/FlatBufferServer.h @@ -9,7 +9,6 @@ class QTcpServer; class FlatBufferClient; -class NetOrigin; /// /// @brief A TcpServer to receive images of different formats with Google Flatbuffer @@ -57,7 +56,6 @@ private: private: QTcpServer* _server; - NetOrigin* _netOrigin; Logger* _log; int _timeout; quint16 _port; diff --git a/include/grabber/DispmanxFrameGrabber.h b/include/grabber/DispmanxFrameGrabber.h index cd885015..82b14b35 100644 --- a/include/grabber/DispmanxFrameGrabber.h +++ b/include/grabber/DispmanxFrameGrabber.h @@ -42,7 +42,7 @@ public: /// ///@brief Set new width and height for dispmanx, overwrite Grabber.h impl - virtual void setWidthHeight(int width, int height); + virtual bool setWidthHeight(int width, int height); private: /// diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index 7277b69a..b4026131 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -14,6 +14,8 @@ #include #include +class QTimer; + /// Capture class for V4L2 devices /// /// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html @@ -23,7 +25,6 @@ class V4L2Grabber : public Grabber public: V4L2Grabber(const QString & device, - int input, VideoStandard videoStandard, PixelFormat pixelFormat, int pixelDecimation @@ -71,7 +72,7 @@ public: /// /// @brief overwrite Grabber.h implementation /// - virtual void setInputVideoStandard(int input, VideoStandard videoStandard); + virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard); public slots: @@ -86,6 +87,11 @@ signals: private slots: int read_frame(); + /// + /// @brief Is called whenever the _readFrameAdaptTimer emits to unlock read_frame() through _readFrame bool + /// + void unlockReadFrame() { _readFrame = true; }; + private: void getV4Ldevices(); @@ -161,4 +167,6 @@ private: bool _initialized; bool _deviceAutoDiscoverEnabled; + QTimer* _readFrameAdaptTimer; + bool _readFrame = false; }; diff --git a/include/grabber/V4L2Wrapper.h b/include/grabber/V4L2Wrapper.h index dfa14014..e013e0ef 100644 --- a/include/grabber/V4L2Wrapper.h +++ b/include/grabber/V4L2Wrapper.h @@ -9,7 +9,6 @@ class V4L2Wrapper : public GrabberWrapper public: V4L2Wrapper(const QString & device, - int input, VideoStandard videoStandard, PixelFormat pixelFormat, int pixelDecimation ); diff --git a/include/grabber/X11Grabber.h b/include/grabber/X11Grabber.h index af833f33..ab7adf45 100755 --- a/include/grabber/X11Grabber.h +++ b/include/grabber/X11Grabber.h @@ -42,7 +42,7 @@ public: /// /// @brief Apply new width/height values, overwrite Grabber.h implementation as X11 doesn't use width/height, just pixelDecimation to calc dimensions /// - virtual void setWidthHeight(int width, int height); + virtual bool setWidthHeight(int width, int height) { return true; }; /// /// @brief Apply new pixelDecimation diff --git a/include/hyperion/CaptureCont.h b/include/hyperion/CaptureCont.h index 4322776c..dd15e122 100644 --- a/include/hyperion/CaptureCont.h +++ b/include/hyperion/CaptureCont.h @@ -6,6 +6,7 @@ #include class Hyperion; +class QTimer; /// /// @brief Capture Control class which is a interface to the HyperionDaemon native capture classes. @@ -48,6 +49,11 @@ private slots: /// void handleV4lImage(const Image & image); + /// + /// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive + /// + void setV4lInactive(); + private: /// Hyperion instance Hyperion* _hyperion; @@ -59,4 +65,5 @@ private: /// Reflect state of v4l capture and prio bool _v4lCaptEnabled; quint8 _v4lCaptPrio; + QTimer* _v4lInactiveTimer; }; diff --git a/include/hyperion/ComponentRegister.h b/include/hyperion/ComponentRegister.h index 91bd7228..4f1a13c6 100644 --- a/include/hyperion/ComponentRegister.h +++ b/include/hyperion/ComponentRegister.h @@ -33,9 +33,9 @@ public: /// /// @brief Check if a component is currently enabled /// @param comp The component from enum - /// @return True if component is running else false + /// @return True if component is running else false. Not found is -1 /// - bool isComponentEnabled(const hyperion::Components& comp) const; + int isComponentEnabled(const hyperion::Components& comp) const; /// contains all components and their state std::map getRegister() { return _componentStates; }; diff --git a/include/hyperion/Grabber.h b/include/hyperion/Grabber.h index 88207e03..16e2ee5e 100644 --- a/include/hyperion/Grabber.h +++ b/include/hyperion/Grabber.h @@ -36,7 +36,7 @@ public: /// /// @brief Apply new width/height values, on errors (collide with cropping) reject the values /// - virtual void setWidthHeight(int width, int height); + virtual bool setWidthHeight(int width, int height); /// /// @brief Apply new pixelDecimation (used from x11) @@ -66,9 +66,9 @@ public: virtual void setSignalDetectionEnable(bool enable) {}; /// - /// @brief Apply input and videoStanded (used from v4l) + /// @brief Apply device and videoStanded (used from v4l) /// - virtual void setInputVideoStandard(int input, VideoStandard videoStandard) {}; + virtual void setDeviceVideoStandard(QString device, VideoStandard videoStandard) {}; /// /// @brief Apply display index (used from x11) diff --git a/include/hyperion/GrabberWrapper.h b/include/hyperion/GrabberWrapper.h index ca70a436..2fc45f48 100644 --- a/include/hyperion/GrabberWrapper.h +++ b/include/hyperion/GrabberWrapper.h @@ -1,12 +1,13 @@ #pragma once #include +#include +#include #include #include #include #include -#include #include #include #include @@ -98,9 +99,6 @@ protected: QString _grabberName; - /// Pointer to Hyperion for writing led values - Hyperion * _hyperion; - /// The timer for generating events with the specified update rate QTimer* _timer; @@ -110,9 +108,6 @@ protected: /// The Logger instance Logger * _log; - // forwarding enabled - bool _forward; - Grabber *_ggrabber; /// The image used for grabbing frames diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index c39dce72..e981e1b5 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -49,6 +49,7 @@ class ColorAdjustment; class SettingsManager; class BGEffectHandler; class CaptureCont; +class BoblightServer; /// /// The main class of Hyperion. This gives other 'users' access to the attached LedDevice through @@ -105,6 +106,8 @@ public: /// PriorityMuxer* getMuxerInstance() { return &_muxer; }; + ImageProcessor* getImageProcessor() { return _imageProcessor; }; + /// /// @brief Get a setting by settings::type from SettingsManager /// @param type The settingsType from enum @@ -145,7 +148,7 @@ public: bool isCurrentPriority(const int priority) const; /// - /// Returns a list of active priorities + /// Returns a list of all registered priorities /// /// @return The list with priorities /// @@ -279,6 +282,13 @@ public slots: /// const bool setInputImage(const int priority, const Image& image, int64_t timeout_ms = -1, const bool& clearEffect = true); + /// + /// @brief Set the given priority to inactive + /// @param priority The priority + /// @return True on success false if not found + /// + const bool setInputInactive(const quint8& priority); + /// /// Writes a single color to all the leds for the given time and priority /// Registers comp color or provided type against muxer @@ -540,4 +550,7 @@ private: std::vector _ledBuffer; /// buffer for leds (without adjustment) std::vector _rawLedBuffer; + + /// Boblight instance + BoblightServer* _boblightServer; }; diff --git a/include/hyperion/PriorityMuxer.h b/include/hyperion/PriorityMuxer.h index 21af614b..e6a4b0ff 100644 --- a/include/hyperion/PriorityMuxer.h +++ b/include/hyperion/PriorityMuxer.h @@ -162,6 +162,13 @@ public: /// const bool setInputImage(const int priority, const Image& image, int64_t timeout_ms = -1); + /// + /// @brief Set the given priority to inactive + /// @param priority The priority + /// @return True on success false if not found + /// + const bool setInputInactive(const quint8& priority); + /// /// Clears the specified priority channel and update _currentPriority on success /// diff --git a/include/hyperion/SettingsManager.h b/include/hyperion/SettingsManager.h index f3fa1d63..2394cc7b 100644 --- a/include/hyperion/SettingsManager.h +++ b/include/hyperion/SettingsManager.h @@ -6,7 +6,6 @@ // qt incl #include -class SettingsTable; class Hyperion; /// @@ -63,8 +62,6 @@ private: Hyperion* _hyperion; /// Logger instance Logger* _log; - /// instance of database table interface - SettingsTable* _sTable; /// the schema static QJsonObject schemaJson; /// the current config of this instance diff --git a/include/jsonserver/JsonServer.h b/include/jsonserver/JsonServer.h index e0c64206..a6c55053 100644 --- a/include/jsonserver/JsonServer.h +++ b/include/jsonserver/JsonServer.h @@ -8,12 +8,10 @@ #include #include -class Hyperion; class QTcpServer; class QTcpSocket; class JsonClientConnection; class BonjourServiceRegister; -class ComponentRegister; class NetOrigin; /// @@ -50,12 +48,7 @@ private slots: /// void closedConnection(void); - /// forward message to all json slaves - void forwardJsonMessage(const QJsonObject &message); - public slots: - /// process current forwarder state - void componentStateChanged(const hyperion::Components component, bool enable); /// /// forward message to a single json slaves @@ -75,18 +68,12 @@ private: /// The TCP server object QTcpServer * _server; - /// Link to Hyperion to get config state emiter - Hyperion * _hyperion; - /// List with open connections QSet _openConnections; /// the logger instance Logger * _log; - /// Component Register pointer - ComponentRegister* _componentRegister; - NetOrigin* _netOrigin; /// port diff --git a/include/protoserver/ProtoServer.h b/include/protoserver/ProtoServer.h index e285a6cb..9ad9afa0 100644 --- a/include/protoserver/ProtoServer.h +++ b/include/protoserver/ProtoServer.h @@ -25,8 +25,6 @@ class ProtoConnection; class QTcpServer; class Hyperion; class BonjourServiceRegister; -class ComponentRegister; -class NetOrigin; namespace proto { class HyperionRequest; @@ -55,8 +53,6 @@ public: uint16_t getPort() const; public slots: - void sendImageToProtoSlaves(int priority, const Image & image, int duration_ms); - void componentStateChanged(const hyperion::Components component, bool enable); /// /// @brief Handle settings update from Hyperion Settingsmanager emit or this constructor @@ -65,12 +61,6 @@ public slots: /// void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); -signals: - /// - /// Forwarding videoMode - /// - void videoMode(const VideoMode VideoMode); - private slots: /// /// Slot which is called when a client tries to create a new connection @@ -83,8 +73,6 @@ private slots: /// void closedConnection(ProtoClientConnection * connection); - void newMessage(const proto::HyperionRequest * message); - private: /// Hyperion instance Hyperion * _hyperion; @@ -94,26 +82,13 @@ private: /// List with open connections QSet _openConnections; - QStringList _forwardClients; - - /// Hyperion proto connection object for forwarding - QList _proxy_connections; /// Logger instance Logger * _log; - /// Component Register - ComponentRegister* _componentRegister; - - /// Network Origin Check - NetOrigin* _netOrigin; - /// Service register BonjourServiceRegister * _serviceRegister = nullptr; - /// flag if forwarder is enabled - bool _forwarder_enabled; - uint16_t _port = 0; /// Start server diff --git a/include/udplistener/UDPListener.h b/include/udplistener/UDPListener.h index bf294df6..0ab01d92 100644 --- a/include/udplistener/UDPListener.h +++ b/include/udplistener/UDPListener.h @@ -11,15 +11,13 @@ // Hyperion includes #include #include +#include // settings #include -class Hyperion; -class UDPClientConnection; class BonjourServiceRegister; class QUdpSocket; -class NetOrigin; /// /// This class creates a UDP server which accepts connections from boblight clients. @@ -67,6 +65,22 @@ public slots: /// void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config); +signals: + /// + /// @brief forward register data to HyperionDaemon + /// + void registerGlobalInput(const int priority, const hyperion::Components& component, const QString& origin = "System", const QString& owner = "", unsigned smooth_cfg = 0); + + /// + /// @brief forward led data to HyperionDaemon + /// + const bool setGlobalInput(const int priority, const std::vector& ledColors, const int timeout_ms = -1, const bool& clearEffect = true); + + /// + /// @brief forward clear to HyperionDaemon + /// + void clearGlobalPriority(const int& _priority, const hyperion::Components& component); + private slots: /// /// Slot which is called when a client tries to create a new connection @@ -75,15 +89,10 @@ private slots: void processTheDatagram(const QByteArray * datagram, const QHostAddress * sender); private: - /// Hyperion instance - Hyperion * _hyperion; /// The UDP server object QUdpSocket * _server; - /// List with open connections - QSet _openConnections; - /// hyperion priority int _priority; @@ -94,7 +103,7 @@ private: Logger * _log; /// Bonjour Service Register - BonjourServiceRegister* _bonjourService = nullptr; + BonjourServiceRegister* _serviceRegister = nullptr; /// state of connection bool _isActive; @@ -103,7 +112,4 @@ private: QHostAddress _listenAddress; uint16_t _listenPort; QAbstractSocket::BindFlag _bondage; - - /// Check Network Origin - NetOrigin* _netOrigin; }; diff --git a/include/utils/Components.h b/include/utils/Components.h index 3e917d9a..1840f9a3 100644 --- a/include/utils/Components.h +++ b/include/utils/Components.h @@ -22,7 +22,8 @@ enum Components COMP_IMAGE, COMP_EFFECT, COMP_PROTOSERVER, - COMP_LEDDEVICE + COMP_LEDDEVICE, + COMP_FLATBUFSERVER }; inline const char* componentToString(Components c) @@ -42,6 +43,7 @@ inline const char* componentToString(Components c) case COMP_IMAGE: return "Image"; case COMP_PROTOSERVER: return "Proto Server"; case COMP_LEDDEVICE: return "LED device"; + case COMP_FLATBUFSERVER: return "Image Receiver"; default: return ""; } } @@ -63,6 +65,7 @@ inline const char* componentToIdString(Components c) case COMP_IMAGE: return "IMAGE"; case COMP_PROTOSERVER: return "PROTOSERVER"; case COMP_LEDDEVICE: return "LEDDEVICE"; + case COMP_FLATBUFSERVER: return "FLATBUFSERVER"; default: return ""; } } @@ -83,7 +86,7 @@ inline Components stringToComponent(QString component) if (component == "IMAGE") return COMP_IMAGE; if (component == "PROTOSERVER") return COMP_PROTOSERVER; if (component == "LEDDEVICE") return COMP_LEDDEVICE; - + if (component == "FLATBUFSERVER") return COMP_FLATBUFSERVER; return COMP_INVALID; } diff --git a/include/utils/NetUtils.h b/include/utils/NetUtils.h new file mode 100644 index 00000000..1678947a --- /dev/null +++ b/include/utils/NetUtils.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +namespace NetUtils { + /// + /// @brief Check if the port is available for listening + /// @param[in/out] port The port to test, will be incremented if port is in use + /// @param log The logger of the caller to print + /// @return True on success else false + /// + static const bool portAvailable(quint16& port, Logger* log) + { + const quint16 prevPort = port; + QTcpServer server; + bool corrected = false; + while (!server.listen(QHostAddress::Any, port)) + { + corrected = true; + Warning(log,"Port '%d' is already in use, will increment", port); + port ++; + } + server.close(); + if(corrected) + { + Warning(log, "The requested Port '%d' was already in use, will use Port '%d' instead", prevPort, port); + return false; + } + return true; + } + +} diff --git a/include/utils/Stats.h b/include/utils/Stats.h index d2c2556c..7263f50b 100644 --- a/include/utils/Stats.h +++ b/include/utils/Stats.h @@ -16,6 +16,14 @@ class Stats : public QObject public: Stats(); + static Stats* getInstance() { return instance; }; + static Stats* instance; + + void handleDataUpdate(const QJsonObject& config); + +private: + friend class HyperionDaemon; + Stats(const QJsonObject& config); ~Stats(); private: diff --git a/include/utils/settings.h b/include/utils/settings.h index 7af1a262..9191b012 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -29,6 +29,7 @@ enum type { WEBSERVER, INSTCAPTURE, NETWORK, + FLATBUFSERVER, INVALID }; @@ -62,6 +63,7 @@ inline QString typeToString(const type& type) case WEBSERVER: return "webConfig"; case INSTCAPTURE: return "instCapture"; case NETWORK: return "network"; + case FLATBUFSERVER: return "flatbufServer"; default: return "invalid"; } } @@ -94,6 +96,7 @@ inline type stringToType(const QString& type) 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 return INVALID; } }; diff --git a/include/webserver/WebServer.h b/include/webserver/WebServer.h index 7d0aaf39..d719f38c 100644 --- a/include/webserver/WebServer.h +++ b/include/webserver/WebServer.h @@ -5,13 +5,13 @@ #include #include -// hyperion / utils -#include +// utils include #include // settings #include +class BonjourServiceRegister; class StaticFileServing; class QtHttpServer; @@ -42,7 +42,6 @@ public slots: private: Logger* _log; - Hyperion* _hyperion; QString _baseUrl; quint16 _port; StaticFileServing* _staticFileServing; @@ -50,6 +49,8 @@ private: const QString WEBSERVER_DEFAULT_PATH = ":/webconfig"; const quint16 WEBSERVER_DEFAULT_PORT = 8090; + + BonjourServiceRegister * _serviceRegister = nullptr; }; #endif // WEBSERVER_H diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index d2d85b23..9173e1cd 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -8,6 +8,7 @@ add_subdirectory(commandline) add_subdirectory(blackborder) add_subdirectory(jsonserver) add_subdirectory(protoserver) +add_subdirectory(flatbufserver) add_subdirectory(bonjour) add_subdirectory(boblightserver) add_subdirectory(udplistener) diff --git a/libsrc/api/CMakeLists.txt b/libsrc/api/CMakeLists.txt new file mode 100644 index 00000000..64180a9c --- /dev/null +++ b/libsrc/api/CMakeLists.txt @@ -0,0 +1,21 @@ +# Define the current source locations + +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/api) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/api) + +FILE ( GLOB_RECURSE Api_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" ) + +set(Api_RESOURCES ${CURRENT_SOURCE_DIR}/JSONRPC_schemas.qrc ) + +add_library(hyperion-api + ${Api_SOURCES} + ${Api_RESOURCES} +) + +target_link_libraries(hyperion-api + hyperion + hyperion-utils + Qt5::Core + Qt5::Gui + Qt5::Network +) diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp new file mode 100644 index 00000000..29a9319f --- /dev/null +++ b/libsrc/api/JsonAPI.cpp @@ -0,0 +1,1069 @@ +// project includes +#include + +// stl includes +#include +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// hyperion includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// bonjour wrapper +#include + +// ledmapping int <> string transform methods +#include + +// api includes +#include + +using namespace hyperion; + +JsonAPI::JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener) + : QObject(parent) + , _jsonCB(new JsonCB(this)) + , _noListener(noListener) + , _peerAddress(peerAddress) + , _log(log) + , _hyperion(Hyperion::getInstance()) + , _streaming_logging_activated(false) + , _image_stream_timeout(0) +{ + // the JsonCB creates json messages you can subscribe to e.g. data change events; forward them to the parent client + connect(_jsonCB, &JsonCB::newCallback, this, &JsonAPI::callbackMessage); + // notify hyperion about a jsonMessageForward + connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage); + + // led color stream update timer + connect(&_timer_ledcolors, SIGNAL(timeout()), this, SLOT(streamLedcolorsUpdate())); + _image_stream_mutex.unlock(); +} + +void JsonAPI::handleMessage(const QString& messageString, const QString& httpAuthHeader) +{ + const QString ident = "JsonRpc@"+_peerAddress; + Q_INIT_RESOURCE(JSONRPC_schemas); + QJsonObject message; + // parse the message + if(!JsonUtils::parse(ident, messageString, message, _log)) + { + sendErrorReply("Errors during message parsing, please consult the Hyperion Log."); + return; + } + + // check basic message + if(!JsonUtils::validate(ident, message, ":schema", _log)) + { + sendErrorReply("Errors during message validation, please consult the Hyperion Log."); + return; + } + + // check specific message + const QString command = message["command"].toString(); + if(!JsonUtils::validate(ident, message, QString(":schema-%1").arg(command), _log)) + { + sendErrorReply("Errors during specific message validation, please consult the Hyperion Log"); + return; + } + + int tan = message["tan"].toInt(); + + // switch over all possible commands and handle them + if (command == "color") handleColorCommand (message, command, tan); + else if (command == "image") handleImageCommand (message, command, tan); + else if (command == "effect") handleEffectCommand (message, command, tan); + else if (command == "create-effect") handleCreateEffectCommand (message, command, tan); + else if (command == "delete-effect") handleDeleteEffectCommand (message, command, tan); + else if (command == "sysinfo") handleSysInfoCommand (message, command, tan); + else if (command == "serverinfo") handleServerInfoCommand (message, command, tan); + else if (command == "clear") handleClearCommand (message, command, tan); + else if (command == "adjustment") handleAdjustmentCommand (message, command, tan); + else if (command == "sourceselect") handleSourceSelectCommand (message, command, tan); + else if (command == "config") handleConfigCommand (message, command, tan); + else if (command == "componentstate") handleComponentStateCommand(message, command, tan); + else if (command == "ledcolors") handleLedColorsCommand (message, command, tan); + else if (command == "logging") handleLoggingCommand (message, command, tan); + else if (command == "processing") handleProcessingCommand (message, command, tan); + else if (command == "videomode") handleVideoModeCommand (message, command, tan); + else handleNotImplemented (); +} + +void JsonAPI::handleColorCommand(const QJsonObject& message, const QString& command, const int tan) +{ + emit forwardJsonMessage(message); + + // extract parameters + int priority = message["priority"].toInt(); + int duration = message["duration"].toInt(-1); + QString origin = message["origin"].toString() + "@"+_peerAddress; + + std::vector colorData(_hyperion->getLedCount()); + const QJsonArray & jsonColor = message["color"].toArray(); + unsigned int i = 0; + for (; i < unsigned(jsonColor.size()/3) && i < _hyperion->getLedCount(); ++i) + { + colorData[i].red = uint8_t(jsonColor.at(3u*i).toInt()); + colorData[i].green = uint8_t(jsonColor.at(3u*i+1u).toInt()); + colorData[i].blue = uint8_t(jsonColor.at(3u*i+2u).toInt()); + } + + // copy full blocks of led colors + unsigned size = i; + while (i + size < _hyperion->getLedCount()) + { + memcpy(&(colorData[i]), colorData.data(), size * sizeof(ColorRgb)); + i += size; + } + + // copy remaining block of led colors + if (i < _hyperion->getLedCount()) + { + memcpy(&(colorData[i]), colorData.data(), (_hyperion->getLedCount()-i) * sizeof(ColorRgb)); + } + + // register and set color + _hyperion->registerInput(priority, hyperion::COMP_COLOR, origin); + _hyperion->setInput(priority, colorData, duration); + + // send reply + sendSuccessReply(command, tan); +} + +void JsonAPI::handleImageCommand(const QJsonObject& message, const QString& command, const int tan) +{ + emit forwardJsonMessage(message); + + // extract parameters + int priority = message["priority"].toInt(); + int duration = message["duration"].toInt(-1); + int width = message["imagewidth"].toInt(); + int height = message["imageheight"].toInt(); + QByteArray data = QByteArray::fromBase64(QByteArray(message["imagedata"].toString().toUtf8())); + + // check consistency of the size of the received data + if (data.size() != width*height*3) + { + sendErrorReply("Size of image data does not match with the width and height", command, tan); + return; + } + + // create ImageRgb + Image image(width, height); + memcpy(image.memptr(), data.data(), data.size()); + + _hyperion->registerInput(priority, hyperion::COMP_IMAGE, "JsonRpc@"+_peerAddress); + _hyperion->setInputImage(priority, image, duration); + + // send reply + sendSuccessReply(command, tan); +} + +void JsonAPI::handleEffectCommand(const QJsonObject& message, const QString& command, const int tan) +{ + emit forwardJsonMessage(message); + + // extract parameters + int priority = message["priority"].toInt(); + int duration = message["duration"].toInt(-1); + QString pythonScript = message["pythonScript"].toString(); + QString origin = message["origin"].toString() + "@"+_peerAddress; + const QJsonObject & effect = message["effect"].toObject(); + const QString & effectName = effect["name"].toString(); + + // set output + if (effect.contains("args")) + { + _hyperion->setEffect(effectName, effect["args"].toObject(), priority, duration, pythonScript, origin); + } + else + { + _hyperion->setEffect(effectName, priority, duration, origin); + } + + // send reply + sendSuccessReply(command, tan); +} + +void JsonAPI::handleCreateEffectCommand(const QJsonObject& message, const QString &command, const int tan) +{ + if (!message["args"].toObject().isEmpty()) + { + QString scriptName; + (message["script"].toString().mid(0, 1) == ":" ) + ? scriptName = ":/effects//" + message["script"].toString().mid(1) + : scriptName = message["script"].toString(); + + std::list effectsSchemas = _hyperion->getEffectSchemas(); + std::list::iterator it = std::find_if(effectsSchemas.begin(), effectsSchemas.end(), find_schema(scriptName)); + + if (it != effectsSchemas.end()) + { + if(!JsonUtils::validate("JsonRpc@"+_peerAddress, message["args"].toObject(), it->schemaFile, _log)) + { + sendErrorReply("Error during arg validation against schema, please consult the Hyperion Log", command, tan); + return; + } + + QJsonObject effectJson; + QJsonArray effectArray; + effectArray = _hyperion->getQJsonConfig()["effects"].toObject()["paths"].toArray(); + + if (effectArray.size() > 0) + { + if (message["name"].toString().trimmed().isEmpty() || message["name"].toString().trimmed().startsWith(".")) + { + sendErrorReply("Can't save new effect. Effect name is empty or begins with a dot.", command, tan); + return; + } + + effectJson["name"] = message["name"].toString(); + effectJson["script"] = message["script"].toString(); + effectJson["args"] = message["args"].toObject(); + + std::list availableEffects = _hyperion->getEffects(); + std::list::iterator iter = std::find_if(availableEffects.begin(), availableEffects.end(), find_effect(message["name"].toString())); + + QFileInfo newFileName; + if (iter != availableEffects.end()) + { + newFileName.setFile(iter->file); + if (newFileName.absoluteFilePath().mid(0, 1) == ":") + { + sendErrorReply("The effect name '" + message["name"].toString() + "' is assigned to an internal effect. Please rename your effekt.", command, tan); + return; + } + } else + { + QString f = FileUtils::convertPath(effectArray[0].toString() + "/" + message["name"].toString().replace(QString(" "), QString("")) + QString(".json")); + newFileName.setFile(f); + } + + if(!JsonUtils::write(newFileName.absoluteFilePath(), effectJson, _log)) + { + sendErrorReply("Error while saving effect, please check the Hyperion Log", command, tan); + return; + } + + Info(_log, "Reload effect list"); + _hyperion->reloadEffects(); + sendSuccessReply(command, tan); + } else + { + sendErrorReply("Can't save new effect. Effect path empty", command, tan); + return; + } + } else + sendErrorReply("Missing schema file for Python script " + message["script"].toString(), command, tan); + } else + sendErrorReply("Missing or empty Object 'args'", command, tan); +} + +void JsonAPI::handleDeleteEffectCommand(const QJsonObject& message, const QString& command, const int tan) +{ + QString effectName = message["name"].toString(); + std::list effectsDefinition = _hyperion->getEffects(); + std::list::iterator it = std::find_if(effectsDefinition.begin(), effectsDefinition.end(), find_effect(effectName)); + + if (it != effectsDefinition.end()) + { + QFileInfo effectConfigurationFile(it->file); + if (effectConfigurationFile.absoluteFilePath().mid(0, 1) != ":" ) + { + if (effectConfigurationFile.exists()) + { + bool result = QFile::remove(effectConfigurationFile.absoluteFilePath()); + if (result) + { + Info(_log, "Reload effect list"); + _hyperion->reloadEffects(); + sendSuccessReply(command, tan); + } else + sendErrorReply("Can't delete effect configuration file: " + effectConfigurationFile.absoluteFilePath() + ". Please check permissions", command, tan); + } else + sendErrorReply("Can't find effect configuration file: " + effectConfigurationFile.absoluteFilePath(), command, tan); + } else + sendErrorReply("Can't delete internal effect: " + message["name"].toString(), command, tan); + } else + sendErrorReply("Effect " + message["name"].toString() + " not found", command, tan); +} + +void JsonAPI::handleSysInfoCommand(const QJsonObject&, const QString& command, const int tan) +{ + // create result + QJsonObject result; + QJsonObject info; + result["success"] = true; + result["command"] = command; + result["tan"] = tan; + + SysInfo::HyperionSysInfo data = SysInfo::get(); + QJsonObject system; + system["kernelType" ] = data.kernelType; + system["kernelVersion" ] = data.kernelVersion; + system["architecture" ] = data.architecture; + system["wordSize" ] = data.wordSize; + system["productType" ] = data.productType; + system["productVersion"] = data.productVersion; + system["prettyName" ] = data.prettyName; + system["hostName" ] = data.hostName; + system["domainName" ] = data.domainName; + info["system"] = system; + + QJsonObject hyperion; + hyperion["jsonrpc_version" ] = QString(HYPERION_JSON_VERSION); + hyperion["version" ] = QString(HYPERION_VERSION); + hyperion["build" ] = QString(HYPERION_BUILD_ID); + hyperion["time" ] = QString(__DATE__ " " __TIME__); + hyperion["id" ] = _hyperion->getId(); + info["hyperion"] = hyperion; + + // send the result + result["info" ] = info; + emit callbackMessage(result); +} + +void JsonAPI::handleServerInfoCommand(const QJsonObject& message, const QString& command, const int tan) +{ + QJsonObject info; + + // collect priority information + QJsonArray priorities; + uint64_t now = QDateTime::currentMSecsSinceEpoch(); + QList activePriorities = _hyperion->getActivePriorities(); + activePriorities.removeAll(255); + int currentPriority = _hyperion->getCurrentPriority(); + + foreach (int priority, activePriorities) { + const Hyperion::InputInfo & priorityInfo = _hyperion->getPriorityInfo(priority); + QJsonObject item; + item["priority"] = priority; + if (int(priorityInfo.timeoutTime_ms - now) > -1 ) + { + item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); + } + // owner has optional informations to the component + if(!priorityInfo.owner.isEmpty()) + item["owner"] = priorityInfo.owner; + + item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); + item["origin"] = priorityInfo.origin; + item["active"] = (priorityInfo.timeoutTime_ms >= -1); + item["visible"] = (priority == currentPriority); + + if(priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty()) + { + QJsonObject LEDcolor; + + // add RGB Value to Array + QJsonArray RGBValue; + RGBValue.append(priorityInfo.ledColors.begin()->red); + RGBValue.append(priorityInfo.ledColors.begin()->green); + RGBValue.append(priorityInfo.ledColors.begin()->blue); + LEDcolor.insert("RGB", RGBValue); + + uint16_t Hue; + float Saturation, Luminace; + + // add HSL Value to Array + QJsonArray HSLValue; + ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red, + priorityInfo.ledColors.begin()->green, + priorityInfo.ledColors.begin()->blue, + Hue, Saturation, Luminace); + + HSLValue.append(Hue); + HSLValue.append(Saturation); + HSLValue.append(Luminace); + LEDcolor.insert("HSL", HSLValue); + + item["value"] = LEDcolor; + } + // priorities[priorities.size()] = item; + priorities.append(item); + } + + info["priorities"] = priorities; + info["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled(); + + // collect adjustment information + QJsonArray adjustmentArray; + for (const QString& adjustmentId : _hyperion->getAdjustmentIds()) + { + const ColorAdjustment * colorAdjustment = _hyperion->getAdjustment(adjustmentId); + if (colorAdjustment == nullptr) + { + Error(_log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId)); + continue; + } + + QJsonObject adjustment; + adjustment["id"] = adjustmentId; + + QJsonArray whiteAdjust; + whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentR()); + whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentG()); + whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentB()); + adjustment.insert("white", whiteAdjust); + + QJsonArray redAdjust; + redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentR()); + redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentG()); + redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentB()); + adjustment.insert("red", redAdjust); + + QJsonArray greenAdjust; + greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentR()); + greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentG()); + greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentB()); + adjustment.insert("green", greenAdjust); + + QJsonArray blueAdjust; + blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentR()); + blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentG()); + blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentB()); + adjustment.insert("blue", blueAdjust); + + QJsonArray cyanAdjust; + cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentR()); + cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentG()); + cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentB()); + adjustment.insert("cyan", cyanAdjust); + + QJsonArray magentaAdjust; + magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentR()); + magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentG()); + magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentB()); + adjustment.insert("magenta", magentaAdjust); + + QJsonArray yellowAdjust; + yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentR()); + yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentG()); + yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentB()); + adjustment.insert("yellow", yellowAdjust); + + adjustment["backlightThreshold"] = colorAdjustment->_rgbTransform.getBacklightThreshold(); + adjustment["backlightColored"] = colorAdjustment->_rgbTransform.getBacklightColored(); + adjustment["brightness"] = colorAdjustment->_rgbTransform.getBrightness(); + adjustment["brightnessCompensation"] = colorAdjustment->_rgbTransform.getBrightnessCompensation(); + adjustment["gammaRed"] = colorAdjustment->_rgbTransform.getGammaR(); + adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG(); + adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB(); + + adjustmentArray.append(adjustment); + } + + info["adjustment"] = adjustmentArray; + + // collect effect info + QJsonArray effects; + const std::list & effectsDefinitions = _hyperion->getEffects(); + for (const EffectDefinition & effectDefinition : effectsDefinitions) + { + QJsonObject effect; + effect["name"] = effectDefinition.name; + effect["file"] = effectDefinition.file; + effect["script"] = effectDefinition.script; + effect["args"] = effectDefinition.args; + effects.append(effect); + } + + info["effects"] = effects; + + // get available led devices + QJsonObject ledDevices; + ledDevices["active"] = _hyperion->getActiveDevice(); + QJsonArray availableLedDevices; + for (auto dev: LedDevice::getDeviceMap()) + { + availableLedDevices.append(dev.first); + } + + ledDevices["available"] = availableLedDevices; + info["ledDevices"] = ledDevices; + + QJsonObject grabbers; + QJsonArray availableGrabbers; +#if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11) + // get available grabbers + //grabbers["active"] = ????; + for (auto grabber: GrabberWrapper::availableGrabbers()) + { + availableGrabbers.append(grabber); + } +#endif + grabbers["available"] = availableGrabbers; + info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode())); + info["grabbers"] = grabbers; + + // get available components + QJsonArray component; + std::map components = _hyperion->getComponentRegister().getRegister(); + for(auto comp : components) + { + QJsonObject item; + item["name"] = QString::fromStdString(hyperion::componentToIdString(comp.first)); + item["enabled"] = comp.second; + + component.append(item); + } + + info["components"] = component; + info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(_hyperion->getLedMappingType()); + + // Add Hyperion + QJsonObject hyperion; + hyperion["config_modified" ] = _hyperion->configModified(); + hyperion["config_writeable"] = _hyperion->configWriteable(); + hyperion["enabled"] = _hyperion->getComponentRegister().isComponentEnabled(hyperion::COMP_ALL) ? true : false; + + info["hyperion"] = hyperion; + + // add sessions + QJsonArray sessions; + for (auto session: BonjourBrowserWrapper::getInstance()->getAllServices()) + { + if (session.port<0) continue; + QJsonObject item; + item["name"] = session.serviceName; + item["type"] = session.registeredType; + item["domain"] = session.replyDomain; + item["host"] = session.hostName; + item["address"]= session.address; + item["port"] = session.port; + sessions.append(item); + } + info["sessions"] = sessions; + + sendSuccessDataReply(QJsonDocument(info), command, tan); + + // AFTER we send the info, the client might want to subscribe to future updates + if(message.contains("subscribe")) + { + // check if listeners are allowed + if(_noListener) + return; + + QJsonArray subsArr = message["subscribe"].toArray(); + // catch the all keyword and build a list of all cmds + if(subsArr.contains("all")) + { + subsArr = QJsonArray(); + for(const auto & entry : _jsonCB->getCommands()) + { + subsArr.append(entry); + } + } + for(const auto & entry : subsArr) + { + if(entry == "settings-update") + continue; + + if(!_jsonCB->subscribeFor(entry.toString())) + sendErrorReply(QString("Subscription for '%1' not found. Possible values: %2").arg(entry.toString(), _jsonCB->getCommands().join(", ")), command, tan); + } + } +} + +void JsonAPI::handleClearCommand(const QJsonObject& message, const QString& command, const int tan) +{ + emit forwardJsonMessage(message); + + int priority = message["priority"].toInt(); + + if(priority > 0) + _hyperion->clear(priority); + else if(priority < 0) + _hyperion->clearall(); + else + { + sendErrorReply("Priority 0 is not allowed", command, tan); + return; + } + + // send reply + sendSuccessReply(command, tan); +} + +void JsonAPI::handleAdjustmentCommand(const QJsonObject& message, const QString& command, const int tan) +{ + const QJsonObject & adjustment = message["adjustment"].toObject(); + + const QString adjustmentId = adjustment["id"].toString(_hyperion->getAdjustmentIds().first()); + ColorAdjustment * colorAdjustment = _hyperion->getAdjustment(adjustmentId); + if (colorAdjustment == nullptr) + { + Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str()); + return; + } + + if (adjustment.contains("red")) + { + const QJsonArray & values = adjustment["red"].toArray(); + colorAdjustment->_rgbRedAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + + if (adjustment.contains("green")) + { + const QJsonArray & values = adjustment["green"].toArray(); + colorAdjustment->_rgbGreenAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + + if (adjustment.contains("blue")) + { + const QJsonArray & values = adjustment["blue"].toArray(); + colorAdjustment->_rgbBlueAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + if (adjustment.contains("cyan")) + { + const QJsonArray & values = adjustment["cyan"].toArray(); + colorAdjustment->_rgbCyanAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + if (adjustment.contains("magenta")) + { + const QJsonArray & values = adjustment["magenta"].toArray(); + colorAdjustment->_rgbMagentaAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + if (adjustment.contains("yellow")) + { + const QJsonArray & values = adjustment["yellow"].toArray(); + colorAdjustment->_rgbYellowAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + if (adjustment.contains("white")) + { + const QJsonArray & values = adjustment["white"].toArray(); + colorAdjustment->_rgbWhiteAdjustment.setAdjustment(values[0u].toInt(), values[1u].toInt(), values[2u].toInt()); + } + + if (adjustment.contains("gammaRed")) + { + colorAdjustment->_rgbTransform.setGamma(adjustment["gammaRed"].toDouble(), colorAdjustment->_rgbTransform.getGammaG(), colorAdjustment->_rgbTransform.getGammaB()); + } + if (adjustment.contains("gammaGreen")) + { + colorAdjustment->_rgbTransform.setGamma(colorAdjustment->_rgbTransform.getGammaR(), adjustment["gammaGreen"].toDouble(), colorAdjustment->_rgbTransform.getGammaB()); + } + if (adjustment.contains("gammaBlue")) + { + colorAdjustment->_rgbTransform.setGamma(colorAdjustment->_rgbTransform.getGammaR(), colorAdjustment->_rgbTransform.getGammaG(), adjustment["gammaBlue"].toDouble()); + } + + if (adjustment.contains("backlightThreshold")) + { + colorAdjustment->_rgbTransform.setBacklightThreshold(adjustment["backlightThreshold"].toDouble()); + } + if (adjustment.contains("backlightColored")) + { + colorAdjustment->_rgbTransform.setBacklightColored(adjustment["backlightColored"].toBool()); + } + if (adjustment.contains("brightness")) + { + colorAdjustment->_rgbTransform.setBrightness(adjustment["brightness"].toInt()); + } + if (adjustment.contains("brightnessCompensation")) + { + colorAdjustment->_rgbTransform.setBrightnessCompensation(adjustment["brightnessCompensation"].toInt()); + } + + // commit the changes + _hyperion->adjustmentsUpdated(); + + sendSuccessReply(command, tan); +} + +void JsonAPI::handleSourceSelectCommand(const QJsonObject& message, const QString& command, const int tan) +{ + bool success = false; + if (message["auto"].toBool(false)) + { + _hyperion->setSourceAutoSelectEnabled(true); + success = true; + } + else if (message.contains("priority")) + { + success = _hyperion->setCurrentSourcePriority(message["priority"].toInt()); + } + + if (success) + { + sendSuccessReply(command, tan); + } + else + { + sendErrorReply("setting current priority failed", command, tan); + } +} + +void JsonAPI::handleConfigCommand(const QJsonObject& message, const QString& command, const int tan) +{ + QString subcommand = message["subcommand"].toString(""); + QString full_command = command + "-" + subcommand; + + if (subcommand == "getschema") + { + handleSchemaGetCommand(message, full_command, tan); + } + else if (subcommand == "setconfig") + { + handleConfigSetCommand(message, full_command, tan); + } + else if (subcommand == "getconfig") + { + sendSuccessDataReply(QJsonDocument(_hyperion->getQJsonConfig()), full_command, tan); + } + else if (subcommand == "reload") + { + _hyperion->freeObjects(true); + Process::restartHyperion(); + sendErrorReply("failed to restart hyperion", full_command, tan); + } + else + { + sendErrorReply("unknown or missing subcommand", full_command, tan); + } +} + +void JsonAPI::handleConfigSetCommand(const QJsonObject& message, const QString &command, const int tan) +{ + if (message.contains("config")) + { + QJsonObject config = message["config"].toObject(); + if(_hyperion->getComponentRegister().isComponentEnabled(hyperion::COMP_ALL)) + { + if(_hyperion->saveSettings(config, true)) + sendSuccessReply(command,tan); + else + sendErrorReply("Failed to save configuration, more information at the Hyperion log", command, tan); + } + else + sendErrorReply("Saving configuration while Hyperion is disabled isn't possible", command, tan); + } +} + +void JsonAPI::handleSchemaGetCommand(const QJsonObject& message, const QString& command, const int tan) +{ + // create result + QJsonObject schemaJson, alldevices, properties; + + // make sure the resources are loaded (they may be left out after static linking) + Q_INIT_RESOURCE(resource); + + // read the hyperion json schema from the resource + QString schemaFile = ":/hyperion-schema"; + + try + { + schemaJson = QJsonFactory::readSchema(schemaFile); + } + catch(const std::runtime_error& error) + { + throw std::runtime_error(error.what()); + } + + // collect all LED Devices + properties = schemaJson["properties"].toObject(); + alldevices = LedDevice::getLedDeviceSchemas(); + properties.insert("alldevices", alldevices); + + // collect all available effect schemas + QJsonObject pyEffectSchemas, pyEffectSchema; + QJsonArray in, ex; + const std::list & effectsSchemas = _hyperion->getEffectSchemas(); + for (const EffectSchema & effectSchema : effectsSchemas) + { + if (effectSchema.pyFile.mid(0, 1) == ":") + { + QJsonObject internal; + internal.insert("script", effectSchema.pyFile); + internal.insert("schemaLocation", effectSchema.schemaFile); + internal.insert("schemaContent", effectSchema.pySchema); + in.append(internal); + } + else + { + QJsonObject external; + external.insert("script", effectSchema.pyFile); + external.insert("schemaLocation", effectSchema.schemaFile); + external.insert("schemaContent", effectSchema.pySchema); + ex.append(external); + } + } + + if (!in.empty()) + pyEffectSchema.insert("internal", in); + if (!ex.empty()) + pyEffectSchema.insert("external", ex); + + pyEffectSchemas = pyEffectSchema; + properties.insert("effectSchemas", pyEffectSchemas); + + schemaJson.insert("properties", properties); + + // send the result + sendSuccessDataReply(QJsonDocument(schemaJson), command, tan); +} + +void JsonAPI::handleComponentStateCommand(const QJsonObject& message, const QString &command, const int tan) +{ + const QJsonObject & componentState = message["componentstate"].toObject(); + + QString compStr = componentState["component"].toString("invalid"); + bool compState = componentState["state"].toBool(true); + + Components component = stringToComponent(compStr); + + if (compStr == "ALL" ) + { + if(_hyperion->getComponentRegister().setHyperionEnable(compState)) + sendSuccessReply(command, tan); + else + sendErrorReply(QString("Hyperion is already %1").arg(compState ? "enabled" : "disabled"), command, tan ); + + return; + } + else if (component != COMP_INVALID) + { + // send result before apply + sendSuccessReply(command, tan); + _hyperion->setComponentState(component, compState); + return; + } + sendErrorReply("invalid component name", command, tan); +} + +void JsonAPI::handleLedColorsCommand(const QJsonObject& message, const QString &command, const int tan) +{ + // create result + QString subcommand = message["subcommand"].toString(""); + + if (subcommand == "ledstream-start") + { + _streaming_leds_reply["success"] = true; + _streaming_leds_reply["command"] = command+"-ledstream-update"; + _streaming_leds_reply["tan"] = tan; + _timer_ledcolors.start(125); + } + else if (subcommand == "ledstream-stop") + { + _timer_ledcolors.stop(); + } + else if (subcommand == "imagestream-start") + { + _streaming_image_reply["success"] = true; + _streaming_image_reply["command"] = command+"-imagestream-update"; + _streaming_image_reply["tan"] = tan; + connect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage, Qt::UniqueConnection); + } + else if (subcommand == "imagestream-stop") + { + disconnect(_hyperion, &Hyperion::currentImage, this, &JsonAPI::setImage); + } + else + { + sendErrorReply("unknown subcommand \""+subcommand+"\"",command,tan); + return; + } + + sendSuccessReply(command+"-"+subcommand,tan); +} + +void JsonAPI::handleLoggingCommand(const QJsonObject& message, const QString &command, const int tan) +{ + // create result + QString subcommand = message["subcommand"].toString(""); + _streaming_logging_reply["success"] = true; + _streaming_logging_reply["command"] = command; + _streaming_logging_reply["tan"] = tan; + + if (subcommand == "start") + { + if (!_streaming_logging_activated) + { + _streaming_logging_reply["command"] = command+"-update"; + connect(LoggerManager::getInstance(),SIGNAL(newLogMessage(Logger::T_LOG_MESSAGE)), this, SLOT(incommingLogMessage(Logger::T_LOG_MESSAGE))); + Debug(_log, "log streaming activated for client %s",_peerAddress.toStdString().c_str()); // needed to trigger log sending + } + } + else if (subcommand == "stop") + { + if (_streaming_logging_activated) + { + disconnect(LoggerManager::getInstance(), SIGNAL(newLogMessage(Logger::T_LOG_MESSAGE)), this, 0); + _streaming_logging_activated = false; + Debug(_log, "log streaming deactivated for client %s",_peerAddress.toStdString().c_str()); + + } + } + else + { + sendErrorReply("unknown subcommand",command,tan); + return; + } + + sendSuccessReply(command+"-"+subcommand,tan); +} + +void JsonAPI::handleProcessingCommand(const QJsonObject& message, const QString &command, const int tan) +{ + _hyperion->setLedMappingType(ImageProcessor::mappingTypeToInt( message["mappingType"].toString("multicolor_mean")) ); + + sendSuccessReply(command, tan); +} + +void JsonAPI::handleVideoModeCommand(const QJsonObject& message, const QString &command, const int tan) +{ + _hyperion->setVideoMode(parse3DMode(message["videoMode"].toString("2D"))); + + sendSuccessReply(command, tan); +} + + +void JsonAPI::handleNotImplemented() +{ + sendErrorReply("Command not implemented"); +} + +void JsonAPI::sendSuccessReply(const QString &command, const int tan) +{ + // create reply + QJsonObject reply; + reply["success"] = true; + reply["command"] = command; + reply["tan"] = tan; + + // send reply + emit callbackMessage(reply); +} + +void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &command, const int &tan) +{ + QJsonObject reply; + reply["success"] = true; + reply["command"] = command; + reply["tan"] = tan; + if(doc.isArray()) + reply["info"] = doc.array(); + else + reply["info"] = doc.object(); + + emit callbackMessage(reply); +} + +void JsonAPI::sendErrorReply(const QString &error, const QString &command, const int tan) +{ + // create reply + QJsonObject reply; + reply["success"] = false; + reply["error"] = error; + reply["command"] = command; + reply["tan"] = tan; + + // send reply + emit callbackMessage(reply); +} + + +void JsonAPI::streamLedcolorsUpdate() +{ + QJsonObject result; + QJsonArray leds; + + const std::vector & ledColors = _hyperion->getRawLedBuffer(); + for(auto color = ledColors.begin(); color != ledColors.end(); ++color) + { + QJsonObject item; + item["index"] = int(color - ledColors.begin()); + item["red"] = color->red; + item["green"] = color->green; + item["blue"] = color->blue; + leds.append(item); + } + + result["leds"] = leds; + _streaming_leds_reply["result"] = result; + + // send the result + emit callbackMessage(_streaming_leds_reply); +} + +void JsonAPI::setImage(const Image & image) +{ + if ( (_image_stream_timeout+250) < QDateTime::currentMSecsSinceEpoch() && _image_stream_mutex.tryLock(0) ) + { + _image_stream_timeout = QDateTime::currentMSecsSinceEpoch(); + + QImage jpgImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + jpgImage.save(&buffer, "jpg"); + + QJsonObject result; + result["image"] = "data:image/jpg;base64,"+QString(ba.toBase64()); + _streaming_image_reply["result"] = result; + emit callbackMessage(_streaming_image_reply); + + _image_stream_mutex.unlock(); + } +} + +void JsonAPI::incommingLogMessage(Logger::T_LOG_MESSAGE msg) +{ + QJsonObject result, message; + QJsonArray messageArray; + + if (!_streaming_logging_activated) + { + _streaming_logging_activated = true; + QVector* logBuffer = LoggerManager::getInstance()->getLogMessageBuffer(); + for(int i=0; ilength(); i++) + { + message["appName"] = logBuffer->at(i).appName; + message["loggerName"] = logBuffer->at(i).loggerName; + message["function"] = logBuffer->at(i).function; + message["line"] = QString::number(logBuffer->at(i).line); + message["fileName"] = logBuffer->at(i).fileName; + message["message"] = logBuffer->at(i).message; + message["levelString"] = logBuffer->at(i).levelString; + + messageArray.append(message); + } + } + else + { + message["appName"] = msg.appName; + message["loggerName"] = msg.loggerName; + message["function"] = msg.function; + message["line"] = QString::number(msg.line); + message["fileName"] = msg.fileName; + message["message"] = msg.message; + message["levelString"] = msg.levelString; + + messageArray.append(message); + } + + result.insert("messages", messageArray); + _streaming_logging_reply["result"] = result; + + // send the result + emit callbackMessage(_streaming_logging_reply); +} diff --git a/libsrc/api/JsonCB.cpp b/libsrc/api/JsonCB.cpp new file mode 100644 index 00000000..f1f8e109 --- /dev/null +++ b/libsrc/api/JsonCB.cpp @@ -0,0 +1,304 @@ +// proj incl +#include + +// hyperion +#include +// components +#include +// bonjour wrapper +#include +// priorityMuxer +#include +#include +#include + +// Image to led map helper +#include + +using namespace hyperion; + +JsonCB::JsonCB(QObject* parent) + : QObject(parent) + , _hyperion(Hyperion::getInstance()) + , _componentRegister(& _hyperion->getComponentRegister()) + , _bonjour(BonjourBrowserWrapper::getInstance()) + , _prioMuxer(_hyperion->getMuxerInstance()) +{ + _availableCommands << "components-update" << "sessions-update" << "priorities-update" << "imageToLedMapping-update" + << "adjustment-update" << "videomode-update" << "effects-update" << "settings-update"; +} + +bool JsonCB::subscribeFor(const QString& type) +{ + if(!_availableCommands.contains(type)) + return false; + + if(type == "components-update") + { + _subscribedCommands << type; + connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonCB::handleComponentState, Qt::UniqueConnection); + } + + if(type == "sessions-update") + { + _subscribedCommands << type; + connect(_bonjour, &BonjourBrowserWrapper::browserChange, this, &JsonCB::handleBonjourChange, Qt::UniqueConnection); + } + + if(type == "priorities-update") + { + _subscribedCommands << type; + connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection); + connect(_prioMuxer, &PriorityMuxer::autoSelectChanged, this, &JsonCB::handlePriorityUpdate, Qt::UniqueConnection); + } + + if(type == "imageToLedMapping-update") + { + _subscribedCommands << type; + connect(_hyperion, &Hyperion::imageToLedsMappingChanged, this, &JsonCB::handleImageToLedsMappingChange, Qt::UniqueConnection); + } + + if(type == "adjustment-update") + { + _subscribedCommands << type; + connect(_hyperion, &Hyperion::adjustmentChanged, this, &JsonCB::handleAdjustmentChange, Qt::UniqueConnection); + } + + if(type == "videomode-update") + { + _subscribedCommands << type; + connect(_hyperion, &Hyperion::newVideoMode, this, &JsonCB::handleVideoModeChange, Qt::UniqueConnection); + } + + if(type == "effects-update") + { + _subscribedCommands << type; + connect(_hyperion, &Hyperion::effectListUpdated, this, &JsonCB::handleEffectListChange, Qt::UniqueConnection); + } + + if(type == "settings-update") + { + _subscribedCommands << type; + connect(_hyperion, &Hyperion::settingsChanged, this, &JsonCB::handleSettingsChange, Qt::UniqueConnection); + } + + return true; +} + +void JsonCB::doCallback(const QString& cmd, const QVariant& data) +{ + QJsonObject obj; + obj["command"] = cmd; + + if(static_cast(data.type()) == QMetaType::QJsonArray) + obj["data"] = data.toJsonArray(); + else + obj["data"] = data.toJsonObject(); + + emit newCallback(obj); +} + +void JsonCB::handleComponentState(const hyperion::Components comp, const bool state) +{ + QJsonObject data; + data["name"] = componentToIdString(comp); + data["enabled"] = state; + + doCallback("components-update", QVariant(data)); +} + +void JsonCB::handleBonjourChange(const QMap& bRegisters) +{ + QJsonArray data; + for (const auto & session: bRegisters) + { + if (session.port<0) continue; + QJsonObject item; + item["name"] = session.serviceName; + item["type"] = session.registeredType; + item["domain"] = session.replyDomain; + item["host"] = session.hostName; + item["address"]= session.address; + item["port"] = session.port; + data.append(item); + } + + doCallback("sessions-update", QVariant(data)); +} + +void JsonCB::handlePriorityUpdate() +{ + QJsonObject data; + QJsonArray priorities; + uint64_t now = QDateTime::currentMSecsSinceEpoch(); + QList activePriorities = _prioMuxer->getPriorities(); + activePriorities.removeAll(255); + int currentPriority = _prioMuxer->getCurrentPriority(); + + foreach (int priority, activePriorities) { + const Hyperion::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority); + QJsonObject item; + item["priority"] = priority; + if (int(priorityInfo.timeoutTime_ms - now) > -1 ) + { + item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); + } + // owner has optional informations to the component + if(!priorityInfo.owner.isEmpty()) + item["owner"] = priorityInfo.owner; + + item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); + item["origin"] = priorityInfo.origin; + item["active"] = (priorityInfo.timeoutTime_ms >= -1); + item["visible"] = (priority == currentPriority); + + if(priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty()) + { + QJsonObject LEDcolor; + + // add RGB Value to Array + QJsonArray RGBValue; + RGBValue.append(priorityInfo.ledColors.begin()->red); + RGBValue.append(priorityInfo.ledColors.begin()->green); + RGBValue.append(priorityInfo.ledColors.begin()->blue); + LEDcolor.insert("RGB", RGBValue); + + uint16_t Hue; + float Saturation, Luminace; + + // add HSL Value to Array + QJsonArray HSLValue; + ColorSys::rgb2hsl(priorityInfo.ledColors.begin()->red, + priorityInfo.ledColors.begin()->green, + priorityInfo.ledColors.begin()->blue, + Hue, Saturation, Luminace); + + HSLValue.append(Hue); + HSLValue.append(Saturation); + HSLValue.append(Luminace); + LEDcolor.insert("HSL", HSLValue); + + item["value"] = LEDcolor; + } + priorities.append(item); + } + + data["priorities"] = priorities; + data["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled(); + + doCallback("priorities-update", QVariant(data)); +} + +void JsonCB::handleImageToLedsMappingChange(const int& mappingType) +{ + QJsonObject data; + data["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(mappingType); + + doCallback("imageToLedMapping-update", QVariant(data)); +} + +void JsonCB::handleAdjustmentChange() +{ + QJsonArray adjustmentArray; + for (const QString& adjustmentId : _hyperion->getAdjustmentIds()) + { + const ColorAdjustment * colorAdjustment = _hyperion->getAdjustment(adjustmentId); + if (colorAdjustment == nullptr) + { + continue; + } + + QJsonObject adjustment; + adjustment["id"] = adjustmentId; + + QJsonArray whiteAdjust; + whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentR()); + whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentG()); + whiteAdjust.append(colorAdjustment->_rgbWhiteAdjustment.getAdjustmentB()); + adjustment.insert("white", whiteAdjust); + + QJsonArray redAdjust; + redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentR()); + redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentG()); + redAdjust.append(colorAdjustment->_rgbRedAdjustment.getAdjustmentB()); + adjustment.insert("red", redAdjust); + + QJsonArray greenAdjust; + greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentR()); + greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentG()); + greenAdjust.append(colorAdjustment->_rgbGreenAdjustment.getAdjustmentB()); + adjustment.insert("green", greenAdjust); + + QJsonArray blueAdjust; + blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentR()); + blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentG()); + blueAdjust.append(colorAdjustment->_rgbBlueAdjustment.getAdjustmentB()); + adjustment.insert("blue", blueAdjust); + + QJsonArray cyanAdjust; + cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentR()); + cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentG()); + cyanAdjust.append(colorAdjustment->_rgbCyanAdjustment.getAdjustmentB()); + adjustment.insert("cyan", cyanAdjust); + + QJsonArray magentaAdjust; + magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentR()); + magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentG()); + magentaAdjust.append(colorAdjustment->_rgbMagentaAdjustment.getAdjustmentB()); + adjustment.insert("magenta", magentaAdjust); + + QJsonArray yellowAdjust; + yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentR()); + yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentG()); + yellowAdjust.append(colorAdjustment->_rgbYellowAdjustment.getAdjustmentB()); + adjustment.insert("yellow", yellowAdjust); + + adjustment["backlightThreshold"] = colorAdjustment->_rgbTransform.getBacklightThreshold(); + adjustment["backlightColored"] = colorAdjustment->_rgbTransform.getBacklightColored(); + adjustment["brightness"] = colorAdjustment->_rgbTransform.getBrightness(); + adjustment["brightnessCompensation"] = colorAdjustment->_rgbTransform.getBrightnessCompensation(); + adjustment["gammaRed"] = colorAdjustment->_rgbTransform.getGammaR(); + adjustment["gammaGreen"] = colorAdjustment->_rgbTransform.getGammaG(); + adjustment["gammaBlue"] = colorAdjustment->_rgbTransform.getGammaB(); + + adjustmentArray.append(adjustment); + } + + doCallback("adjustment-update", QVariant(adjustmentArray)); +} + +void JsonCB::handleVideoModeChange(const VideoMode& mode) +{ + QJsonObject data; + data["videomode"] = QString(videoMode2String(mode)); + doCallback("videomode-update", QVariant(data)); +} + +void JsonCB::handleEffectListChange() +{ + QJsonArray effectList; + QJsonObject effects; + const std::list & effectsDefinitions = _hyperion->getEffects(); + for (const EffectDefinition & effectDefinition : effectsDefinitions) + { + QJsonObject effect; + effect["name"] = effectDefinition.name; + effect["file"] = effectDefinition.file; + effect["script"] = effectDefinition.script; + effect["args"] = effectDefinition.args; + effectList.append(effect); + }; + effects["effects"] = effectList; + doCallback("effects-update", QVariant(effects)); +} + +void JsonCB::handleSettingsChange(const settings::type& type, const QJsonDocument& data) +{ + QJsonObject dat; + if(data.isObject()) + dat[typeToString(type)] = data.object(); + else + dat[typeToString(type)] = data.array(); + + doCallback("settings-update", QVariant(dat)); +} diff --git a/libsrc/blackborder/BlackBorderProcessor.cpp b/libsrc/blackborder/BlackBorderProcessor.cpp index 608c970a..a566df17 100644 --- a/libsrc/blackborder/BlackBorderProcessor.cpp +++ b/libsrc/blackborder/BlackBorderProcessor.cpp @@ -50,12 +50,16 @@ void BlackBorderProcessor::handleSettingsUpdate(const settings::type& type, cons _maxInconsistentCnt = obj["maxInconsistentCnt"].toInt(10); _blurRemoveCnt = obj["blurRemoveCnt"].toInt(1); _detectionMode = obj["mode"].toString("default"); + const double newThreshold = obj["threshold"].toDouble(5.0)/100.0; - if(_oldThreshold != obj["threshold"].toDouble(5.0/100)) + if(_oldThreshold != newThreshold) { - _oldThreshold = obj["threshold"].toDouble(5.0/100); - if(_detector != nullptr) delete _detector; - _detector = new BlackBorderDetector(obj["threshold"].toDouble(5.0/100)); + _oldThreshold = newThreshold; + + if(_detector != nullptr) + delete _detector; + + _detector = new BlackBorderDetector(newThreshold); } Debug(Logger::getInstance("BLACKBORDER"), "Set mode to: %s", QSTRING_CSTR(_detectionMode)); diff --git a/libsrc/boblightserver/BoblightClientConnection.cpp b/libsrc/boblightserver/BoblightClientConnection.cpp index 9997517b..92828df2 100644 --- a/libsrc/boblightserver/BoblightClientConnection.cpp +++ b/libsrc/boblightserver/BoblightClientConnection.cpp @@ -15,23 +15,22 @@ #include // hyperion util includes -//#include "hyperion/ImageProcessorFactory.h" -//#include "hyperion/ImageProcessor.h" -#include "utils/ColorRgb.h" +#include #include "HyperionConfig.h" +#include // project includes #include "BoblightClientConnection.h" -BoblightClientConnection::BoblightClientConnection(QTcpSocket *socket, const int priority) +BoblightClientConnection::BoblightClientConnection(Hyperion* hyperion, QTcpSocket *socket, const int priority) : QObject() , _locale(QLocale::C) , _socket(socket) - //, _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()) - , _hyperion(Hyperion::getInstance()) + , _imageProcessor(hyperion->getImageProcessor()) + , _hyperion(hyperion) , _receiveBuffer() , _priority(priority) - , _ledColors(Hyperion::getInstance()->getLedCount(), ColorRgb::BLACK) + , _ledColors(hyperion->getLedCount(), ColorRgb::BLACK) , _log(Logger::getInstance("BOBLIGHT")) , _clientAddress(QHostInfo::fromName(socket->peerAddress().toString()).hostName()) { @@ -227,11 +226,11 @@ void BoblightClientConnection::sendLightMessage() int n = snprintf(buffer, sizeof(buffer), "lights %d\n", _hyperion->getLedCount()); sendMessage(QByteArray(buffer, n)); - //double h0, h1, v0, v1; + double h0, h1, v0, v1; for (unsigned i = 0; i < _hyperion->getLedCount(); ++i) { - //_imageProcessor->getScanParameters(i, h0, h1, v0, v1); - //n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100*v0, 100*v1, 100*h0, 100*h1); - //sendMessage(QByteArray(buffer, n)); + _imageProcessor->getScanParameters(i, h0, h1, v0, v1); + n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100*v0, 100*v1, 100*h0, 100*h1); + sendMessage(QByteArray(buffer, n)); } } diff --git a/libsrc/boblightserver/BoblightClientConnection.h b/libsrc/boblightserver/BoblightClientConnection.h index 76eb1280..f4e4d00e 100644 --- a/libsrc/boblightserver/BoblightClientConnection.h +++ b/libsrc/boblightserver/BoblightClientConnection.h @@ -5,9 +5,12 @@ #include #include -// Hyperion includes -#include +// utils includes #include +#include + +class ImageProcessor; +class Hyperion; /// /// The Connection object created by \a BoblightServer when a new connection is establshed @@ -22,7 +25,7 @@ public: /// @param socket The Socket object for this connection /// @param hyperion The Hyperion server /// - BoblightClientConnection(QTcpSocket * socket, const int priority); + BoblightClientConnection(Hyperion* hyperion, QTcpSocket * socket, const int priority); /// /// Destructor @@ -74,6 +77,9 @@ private: /// The TCP-Socket that is connected tot the boblight-client QTcpSocket * _socket; + /// The processor for translating images to led-values + ImageProcessor * _imageProcessor; + /// Link to Hyperion for writing led-values to a priority channel Hyperion * _hyperion; diff --git a/libsrc/boblightserver/BoblightServer.cpp b/libsrc/boblightserver/BoblightServer.cpp index bba2c493..21e0da35 100644 --- a/libsrc/boblightserver/BoblightServer.cpp +++ b/libsrc/boblightserver/BoblightServer.cpp @@ -12,9 +12,9 @@ using namespace hyperion; -BoblightServer::BoblightServer(const QJsonDocument& config) +BoblightServer::BoblightServer(Hyperion* hyperion,const QJsonDocument& config) : QObject() - , _hyperion(Hyperion::getInstance()) + , _hyperion(hyperion) , _server(new QTcpServer(this)) , _openConnections() , _priority(0) @@ -96,7 +96,7 @@ void BoblightServer::newConnection() { Info(_log, "new connection"); _hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(socket->peerAddress().toString())); - BoblightClientConnection * connection = new BoblightClientConnection(socket, _priority); + BoblightClientConnection * connection = new BoblightClientConnection(_hyperion, socket, _priority); _openConnections.insert(connection); // register slot for cleaning up after the connection closed diff --git a/libsrc/bonjour/bonjourserviceregister.cpp b/libsrc/bonjour/bonjourserviceregister.cpp index d3124b6f..d0a4609f 100755 --- a/libsrc/bonjour/bonjourserviceregister.cpp +++ b/libsrc/bonjour/bonjourserviceregister.cpp @@ -34,8 +34,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include - +#include BonjourServiceRegister::BonjourServiceRegister(QObject *parent) : QObject(parent), dnssref(0), bonjourSocket(0) @@ -54,10 +53,11 @@ BonjourServiceRegister::~BonjourServiceRegister() void BonjourServiceRegister::registerService(const QString& service, const int& port) { + _port = port; // zeroconf $configname@$hostname:port - QString prettyName = Hyperion::getInstance()->getQJsonConfig()["general"].toObject()["name"].toString(); + // TODO add name of the main instance registerService( - BonjourRecord(prettyName+"@"+QHostInfo::localHostName()+ ":" + QString::number(port), + BonjourRecord(QHostInfo::localHostName()+ ":" + QString::number(port), service, QString() ), diff --git a/libsrc/bonjour/bonjourserviceresolver.cpp b/libsrc/bonjour/bonjourserviceresolver.cpp index 217ae00f..aa1a932f 100644 --- a/libsrc/bonjour/bonjourserviceresolver.cpp +++ b/libsrc/bonjour/bonjourserviceresolver.cpp @@ -117,6 +117,6 @@ void BonjourServiceResolver::bonjourResolveReply(DNSServiceRef sdRef, DNSService void BonjourServiceResolver::finishConnect(const QHostInfo &hostInfo) { - emit bonjourRecordResolved(hostInfo, bonjourPort); + emit bonjourRecordResolved(hostInfo, bonjourPort); QMetaObject::invokeMethod(this, "cleanupResolve", Qt::QueuedConnection); } diff --git a/libsrc/effectengine/CMakeLists.txt b/libsrc/effectengine/CMakeLists.txt index aac420f7..5fb38e1a 100644 --- a/libsrc/effectengine/CMakeLists.txt +++ b/libsrc/effectengine/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(PythonLibs 3.4 REQUIRED) +find_package(PythonLibs 3.5 REQUIRED) # Include the python directory. Also include the parent (which is for example /usr/include) # which may be required when it is not includes by the (cross-) compiler by default. diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 7e1c52c3..b2d0b845 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -81,7 +81,7 @@ void Effect::run() PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _hyperion->getLedCount())); // add minimumWriteTime variable to the interpreter - PyObject_SetAttrString(module, "latchTime", Py_BuildValue("i", Hyperion::getInstance()->getLatchTime())); + PyObject_SetAttrString(module, "latchTime", Py_BuildValue("i", _hyperion->getLatchTime())); // add a args variable to the interpreter PyObject_SetAttrString(module, "args", EffectModule::json2python(_args)); diff --git a/libsrc/flatbufserver/FlatBufferClient.cpp b/libsrc/flatbufserver/FlatBufferClient.cpp index 335bf829..e1c4b36c 100644 --- a/libsrc/flatbufserver/FlatBufferClient.cpp +++ b/libsrc/flatbufserver/FlatBufferClient.cpp @@ -29,7 +29,6 @@ FlatBufferClient::FlatBufferClient(QTcpSocket* socket, const int &timeout, QObje void FlatBufferClient::readyRead() { - qDebug()<<"readyRead"; _timeoutTimer->start(); _receiveBuffer += _socket->readAll(); @@ -37,7 +36,6 @@ void FlatBufferClient::readyRead() // check if we can read a header while(_receiveBuffer.size() >= 4) { - uint32_t messageSize = ((_receiveBuffer[0]<<24) & 0xFF000000) | ((_receiveBuffer[1]<<16) & 0x00FF0000) | @@ -47,10 +45,11 @@ void FlatBufferClient::readyRead() // check if we can read a complete message if((uint32_t) _receiveBuffer.size() < messageSize + 4) return; - // remove header + msg from buffer - const QByteArray& msg = _receiveBuffer.remove(0, messageSize + 4); + // extract message only and remove header + msg from buffer :: QByteArray::remove() does not return the removed data + const QByteArray msg = _receiveBuffer.right(messageSize); + _receiveBuffer.remove(0, messageSize + 4); - const uint8_t* msgData = reinterpret_cast(msg.mid(3, messageSize).constData()); + const uint8_t* msgData = reinterpret_cast(msg.constData()); flatbuffers::Verifier verifier(msgData, messageSize); if (flatbuf::VerifyHyperionRequestBuffer(verifier)) @@ -59,7 +58,6 @@ void FlatBufferClient::readyRead() handleMessage(message); continue; } - qDebug()<<"Unable to pasrse msg"; sendErrorReply("Unable to parse message"); } //emit newMessage(msgData,messageSize); diff --git a/libsrc/flatbufserver/FlatBufferConnection.cpp b/libsrc/flatbufserver/FlatBufferConnection.cpp index 064b8d56..c758c6d5 100644 --- a/libsrc/flatbufserver/FlatBufferConnection.cpp +++ b/libsrc/flatbufserver/FlatBufferConnection.cpp @@ -7,16 +7,15 @@ // protoserver includes #include -FlatBufferConnection::FlatBufferConnection(const QString & address) : - _socket(), - _skipReply(false), - _prevSocketState(QAbstractSocket::UnconnectedState), - _log(Logger::getInstance("FLATBUFCONNECTION")) - { +FlatBufferConnection::FlatBufferConnection(const QString & address) + : _socket() + , _prevSocketState(QAbstractSocket::UnconnectedState) + , _log(Logger::getInstance("FLATBUFCONNECTION")) +{ QStringList parts = address.split(":"); if (parts.size() != 2) { - throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Wrong address: Unable to parse address (%1)").arg(address).toStdString()); + throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Unable to parse address (%1)").arg(address).toStdString()); } _host = parts[0]; @@ -24,19 +23,17 @@ FlatBufferConnection::FlatBufferConnection(const QString & address) : _port = parts[1].toUShort(&ok); if (!ok) { - throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Wrong port: Unable to parse the port number (%1)").arg(parts[1]).toStdString()); + throw std::runtime_error(QString("FLATBUFCONNECTION ERROR: Unable to parse the port (%1)").arg(parts[1]).toStdString()); } - // try to connect to host + // init connect Info(_log, "Connecting to Hyperion: %s:%d", _host.toStdString().c_str(), _port); connectToHost(); // start the connection timer _timer.setInterval(5000); - _timer.setSingleShot(false); - connect(&_timer,SIGNAL(timeout()), this, SLOT(connectToHost())); - connect(&_socket, SIGNAL(readyRead()), this, SLOT(readData())); + connect(&_timer, &QTimer::timeout, this, &FlatBufferConnection::connectToHost); _timer.start(); } @@ -48,48 +45,43 @@ FlatBufferConnection::~FlatBufferConnection() void FlatBufferConnection::readData() { - qint64 bytesAvail; - while((bytesAvail = _socket.bytesAvailable())) + _receiveBuffer += _socket.readAll(); + + // check if we can read a header + while(_receiveBuffer.size() >= 4) { - // ignore until we get 4 bytes. - if (bytesAvail < 4) { + uint32_t messageSize = + ((_receiveBuffer[0]<<24) & 0xFF000000) | + ((_receiveBuffer[1]<<16) & 0x00FF0000) | + ((_receiveBuffer[2]<< 8) & 0x0000FF00) | + ((_receiveBuffer[3] ) & 0x000000FF); + + // check if we can read a complete message + if((uint32_t) _receiveBuffer.size() < messageSize + 4) return; + + // extract message only and remove header + msg from buffer :: QByteArray::remove() does not return the removed data + const QByteArray msg = _receiveBuffer.right(messageSize); + _receiveBuffer.remove(0, messageSize + 4); + + const uint8_t* msgData = reinterpret_cast(msg.constData()); + flatbuffers::Verifier verifier(msgData, messageSize); + + if (flatbuf::VerifyHyperionReplyBuffer(verifier)) + { + auto message = flatbuf::GetHyperionReply(msgData); + parseReply(message); continue; } - - char sizeBuf[4]; - _socket.read(sizeBuf, sizeof(sizeBuf)); - - uint32_t messageSize = - ((sizeBuf[0]<<24) & 0xFF000000) | - ((sizeBuf[1]<<16) & 0x00FF0000) | - ((sizeBuf[2]<< 8) & 0x0000FF00) | - ((sizeBuf[3] ) & 0x000000FF); - - QByteArray buffer; - while((uint32_t)buffer.size() < messageSize) - { - _socket.waitForReadyRead(); - buffer.append(_socket.read(messageSize - buffer.size())); - } - - const uint8_t* replyData = reinterpret_cast(buffer.constData()); - flatbuffers::Verifier verifier(replyData, messageSize); - - if (!flatbuf::VerifyHyperionReplyBuffer(verifier)) - { - Error(_log, "Error while reading data from host"); - return; - } - - auto reply = flatbuf::GetHyperionReply(replyData); - - parseReply(reply); + Error(_log, "Unable to parse reply"); } } -void FlatBufferConnection::setSkipReply(bool skip) +void FlatBufferConnection::setSkipReply(const bool& skip) { - _skipReply = skip; + if(skip) + disconnect(&_socket, &QTcpSocket::readyRead, 0, 0); + else + connect(&_socket, &QTcpSocket::readyRead, this, &FlatBufferConnection::readData, Qt::UniqueConnection); } void FlatBufferConnection::setColor(const ColorRgb & color, int priority, int duration) @@ -191,24 +183,22 @@ bool FlatBufferConnection::parseReply(const flatbuf::HyperionReply *reply) { case flatbuf::Type_REPLY: { - if (!_skipReply) + if (!reply->success()) { - if (!reply->success()) + if (flatbuffers::IsFieldPresent(reply, flatbuf::HyperionReply::VT_ERROR)) { - if (flatbuffers::IsFieldPresent(reply, flatbuf::HyperionReply::VT_ERROR)) - { - throw std::runtime_error("PROTOCONNECTION ERROR: " + reply->error()->str()); - } - else - { - throw std::runtime_error("PROTOCONNECTION ERROR: No error info"); - } + throw std::runtime_error("PROTOCONNECTION ERROR: " + reply->error()->str()); } else { - success = true; + throw std::runtime_error("PROTOCONNECTION ERROR: No error info"); } } + else + { + success = true; + } + break; } case flatbuf::Type_VIDEO: diff --git a/libsrc/flatbufserver/FlatBufferServer.cpp b/libsrc/flatbufserver/FlatBufferServer.cpp index ffef50f1..95694729 100644 --- a/libsrc/flatbufserver/FlatBufferServer.cpp +++ b/libsrc/flatbufserver/FlatBufferServer.cpp @@ -6,10 +6,6 @@ #include #include - -#include - - FlatBufferServer::FlatBufferServer(const QJsonDocument& config, QObject* parent) : QObject(parent) , _server(new QTcpServer(this)) @@ -28,7 +24,6 @@ FlatBufferServer::~FlatBufferServer() void FlatBufferServer::initServer() { - qDebug()<<"Thread in InitServer is"<thread(); connect(_server, &QTcpServer::newConnection, this, &FlatBufferServer::newConnection); // apply config @@ -37,7 +32,6 @@ void FlatBufferServer::initServer() void FlatBufferServer::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config) { - qDebug()<<"Thread in handleSettingsUpdate is"<thread(); if(type == settings::FLATBUFSERVER) { const QJsonObject& obj = config.object(); @@ -60,7 +54,6 @@ void FlatBufferServer::handleSettingsUpdate(const settings::type& type, const QJ void FlatBufferServer::newConnection() { - qDebug()<<"Thread in newConnection is"<thread(); while(_server->hasPendingConnections()) { if(QTcpSocket* socket = _server->nextPendingConnection()) diff --git a/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp b/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp index c1222ef7..54bcbf52 100644 --- a/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp +++ b/libsrc/grabber/dispmanx/DispmanxFrameGrabber.cpp @@ -30,12 +30,19 @@ DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned int result = vc_dispmanx_display_get_info(_vc_display, &vc_info); // Keep compiler happy in 'release' mode (void)result; - assert(result == 0); - Info(_log, "Display opened with resolution: %dx%d", vc_info.width, vc_info.height); - // Close the displaye + // Close the display vc_dispmanx_display_close(_vc_display); + if(result != 0) + { + Error(_log, "Failed to open display! Probably no permissions to access the capture interface"); + setEnabled(false); + return; + } + else + Info(_log, "Display opened with resolution: %dx%d", vc_info.width, vc_info.height); + // init the resource and capture rectangle setWidthHeight(width, height); } @@ -55,11 +62,12 @@ void DispmanxFrameGrabber::freeResources() vc_dispmanx_resource_delete(_vc_resource); } -void DispmanxFrameGrabber::setWidthHeight(int width, int height) +bool DispmanxFrameGrabber::setWidthHeight(int width, int height) { - if(_width != width || _height != height) + if(Grabber::setWidthHeight(width, height)) { - freeResources(); + if(_vc_resource != 0) + vc_dispmanx_resource_delete(_vc_resource); // Create the resources for capturing image uint32_t vc_nativeImageHandle; _vc_resource = vc_dispmanx_resource_create( @@ -71,7 +79,9 @@ void DispmanxFrameGrabber::setWidthHeight(int width, int height) // Define the capture rectangle with the same size vc_dispmanx_rect_set(&_rectangle, 0, 0, width, height); + return true; } + return false; } void DispmanxFrameGrabber::setFlags(const int vc_flags) diff --git a/libsrc/grabber/dispmanx/DispmanxWrapper.cpp b/libsrc/grabber/dispmanx/DispmanxWrapper.cpp index 8a12ed27..0acf81d8 100644 --- a/libsrc/grabber/dispmanx/DispmanxWrapper.cpp +++ b/libsrc/grabber/dispmanx/DispmanxWrapper.cpp @@ -4,7 +4,7 @@ DispmanxWrapper::DispmanxWrapper(const unsigned grabWidth, const unsigned grabHe : GrabberWrapper("Dispmanx", &_grabber, grabWidth, grabHeight, updateRate_Hz) , _grabber(grabWidth, grabHeight) { - setImageProcessorEnabled(false); + } void DispmanxWrapper::action() diff --git a/libsrc/grabber/osx/OsxFrameGrabber.cpp b/libsrc/grabber/osx/OsxFrameGrabber.cpp index 37b384ef..7306b40a 100755 --- a/libsrc/grabber/osx/OsxFrameGrabber.cpp +++ b/libsrc/grabber/osx/OsxFrameGrabber.cpp @@ -82,7 +82,14 @@ void OsxFrameGrabber::setDisplayIndex(int index) } image = CGDisplayCreateImage(_display); - assert(image != NULL); + if(image == NULL) + { + Error(_log, "Failed to open main display, disable capture interface"); + setEnabled(false); + return; + } + else + setEnabled(true); Info(_log, "Display opened with resolution: %dx%d@%dbit", CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerPixel(image)); diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 64f4d418..b9b1c31e 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -18,29 +18,29 @@ #include #include +#include #include "grabber/V4L2Grabber.h" #define CLEAR(x) memset(&(x), 0, sizeof(x)) V4L2Grabber::V4L2Grabber(const QString & device - , int input , VideoStandard videoStandard , PixelFormat pixelFormat , int pixelDecimation ) : Grabber("V4L2:"+device) - , _deviceName(device) - , _input(input) + , _deviceName() + , _input(-1) , _videoStandard(videoStandard) , _ioMethod(IO_METHOD_MMAP) , _fileDescriptor(-1) , _buffers() , _pixelFormat(pixelFormat) - , _pixelDecimation(pixelDecimation) + , _pixelDecimation(-1) , _lineLength(-1) , _frameByteSize(-1) - , _noSignalCounterThreshold(50) + , _noSignalCounterThreshold(40) , _noSignalThresholdColor(ColorRgb{0,0,0}) , _signalDetectionEnabled(true) , _noSignalDetected(false) @@ -52,12 +52,17 @@ V4L2Grabber::V4L2Grabber(const QString & device , _streamNotifier(nullptr) , _initialized(false) , _deviceAutoDiscoverEnabled(false) - + , _readFrameAdaptTimer(new QTimer(this)) { - //_imageResampler.setHorizontalPixelDecimation(pixelDecimation); - //_imageResampler.setVerticalPixelDecimation(pixelDecimation); + // setup stream notify locker with 10hz + connect(_readFrameAdaptTimer, &QTimer::timeout, this, &V4L2Grabber::unlockReadFrame); + _readFrameAdaptTimer->setInterval(100); + setPixelDecimation(pixelDecimation); getV4Ldevices(); + + // init + setDeviceVideoStandard(device, videoStandard); } V4L2Grabber::~V4L2Grabber() @@ -67,10 +72,12 @@ V4L2Grabber::~V4L2Grabber() void V4L2Grabber::uninit() { - Debug(_log,"uninit grabber: %s", QSTRING_CSTR(_deviceName)); // stop if the grabber was not stopped if (_initialized) { + Debug(_log,"uninit grabber: %s", QSTRING_CSTR(_deviceName)); + + _readFrameAdaptTimer->stop(); stop(); uninit_device(); close_device(); @@ -78,7 +85,6 @@ void V4L2Grabber::uninit() } } - bool V4L2Grabber::init() { if (! _initialized) @@ -133,10 +139,15 @@ bool V4L2Grabber::init() bool opened = false; try { - open_device(); - opened = true; - init_device(_videoStandard, _input); - _initialized = true; + // do not init with unknown device + if(_deviceName != "unknown") + { + open_device(); + opened = true; + init_device(_videoStandard, _input); + _initialized = true; + _readFrameAdaptTimer->start(); + } } catch(std::exception& e) { @@ -529,13 +540,13 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) break; } +// TODO Does never accept own sizes? use always _imageResampler instead +/* + // calc the size based on pixelDecimation fmt.fmt.pix.width = fmt.fmt.pix.width / _pixelDecimation; fmt.fmt.pix.height = fmt.fmt.pix.height / _pixelDecimation; - // set the line length - _lineLength = fmt.fmt.pix.bytesperline; - // set the settings if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) { @@ -550,6 +561,9 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) throw_errno_exception("VIDIOC_G_FMT"); return; } +*/ + // set the line length + _lineLength = fmt.fmt.pix.bytesperline; // store width & height _width = fmt.fmt.pix.width; @@ -701,6 +715,10 @@ void V4L2Grabber::stop_capturing() int V4L2Grabber::read_frame() { + // read_frame() is called with 25Hz, adapt to 10Hz. In the end it's up to the stream notifier if we get calls or not + if(!_readFrame) return -1; + _readFrame = false; + bool rc = false; try @@ -933,18 +951,30 @@ void V4L2Grabber::setPixelDecimation(int pixelDecimation) { if(_pixelDecimation != pixelDecimation) { + _pixelDecimation = pixelDecimation; uninit(); - init(); + // start if init is a success + if(init()) + start(); + _imageResampler.setHorizontalPixelDecimation(pixelDecimation); + _imageResampler.setVerticalPixelDecimation(pixelDecimation); } } -void V4L2Grabber::setInputVideoStandard(int input, VideoStandard videoStandard) +void V4L2Grabber::setDeviceVideoStandard(QString device, VideoStandard videoStandard) { - if(_input != input || _videoStandard != videoStandard) + if(_deviceName != device || _videoStandard != videoStandard) { - _input = input; - _videoStandard = videoStandard; + // extract input of device + QChar input = device.at(device.size() - 1); + _input = input.isNumber() ? input.digitValue() : -1; + uninit(); - init(); + _deviceName = device; + _videoStandard = videoStandard; + + // start if init is a success + if(init()) + start(); } } diff --git a/libsrc/grabber/v4l2/V4L2Wrapper.cpp b/libsrc/grabber/v4l2/V4L2Wrapper.cpp index 809b1e09..f6b161f5 100644 --- a/libsrc/grabber/v4l2/V4L2Wrapper.cpp +++ b/libsrc/grabber/v4l2/V4L2Wrapper.cpp @@ -6,13 +6,11 @@ #include V4L2Wrapper::V4L2Wrapper(const QString &device, - int input, VideoStandard videoStandard, PixelFormat pixelFormat, int pixelDecimation ) : GrabberWrapper("V4L2:"+device, &_grabber, 0, 0, 10) , _grabber(device, - input, videoStandard, pixelFormat, pixelDecimation) @@ -66,7 +64,7 @@ void V4L2Wrapper::readError(const char* err) void V4L2Wrapper::action() { - + // dummy as v4l get notifications from stream } void V4L2Wrapper::setSignalDetectionEnable(bool enable) diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp index f4bc62d0..4a3e3754 100755 --- a/libsrc/grabber/x11/X11Grabber.cpp +++ b/libsrc/grabber/x11/X11Grabber.cpp @@ -110,6 +110,7 @@ bool X11Grabber::Setup() bool result = (updateScreenDimensions(true) >=0); ErrorIf(!result, _log, "X11 Grabber start failed"); + setEnabled(result); return result; } @@ -278,11 +279,6 @@ void X11Grabber::setVideoMode(VideoMode mode) updateScreenDimensions(true); } -void X11Grabber::setWidthHeight(int width, int height) -{ - // empty overwrite -} - void X11Grabber::setPixelDecimation(int pixelDecimation) { if(_pixelDecimation != pixelDecimation) diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 428d7738..501f4501 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -17,6 +17,7 @@ target_link_libraries(hyperion hyperion-utils leddevice bonjour + boblightserver effectengine ${QT_LIBRARIES} ) diff --git a/libsrc/hyperion/CaptureCont.cpp b/libsrc/hyperion/CaptureCont.cpp index cc9a2a1f..f50ea338 100644 --- a/libsrc/hyperion/CaptureCont.cpp +++ b/libsrc/hyperion/CaptureCont.cpp @@ -1,12 +1,14 @@ #include #include +#include CaptureCont::CaptureCont(Hyperion* hyperion) : QObject() , _hyperion(hyperion) , _systemCaptEnabled(false) , _v4lCaptEnabled(false) + , _v4lInactiveTimer(new QTimer(this)) { // settings changes connect(_hyperion, &Hyperion::settingsChanged, this, &CaptureCont::handleSettingsUpdate); @@ -14,6 +16,11 @@ CaptureCont::CaptureCont(Hyperion* hyperion) // comp changes connect(_hyperion, &Hyperion::componentStateChanged, this, &CaptureCont::componentStateChanged); + // inactive timer v4l + connect(_v4lInactiveTimer, &QTimer::timeout, this, &CaptureCont::setV4lInactive); + _v4lInactiveTimer->setSingleShot(true); + _v4lInactiveTimer->setInterval(1000); + // init handleSettingsUpdate(settings::INSTCAPTURE, _hyperion->getSetting(settings::INSTCAPTURE)); } @@ -25,6 +32,7 @@ CaptureCont::~CaptureCont() void CaptureCont::handleV4lImage(const Image & image) { + _v4lInactiveTimer->start(); _hyperion->setInputImage(_v4lCaptPrio, image); } @@ -40,7 +48,7 @@ void CaptureCont::setSystemCaptureEnable(const bool& enable) { if(enable) { - _hyperion->registerInput(_systemCaptPrio, hyperion::COMP_GRABBER, "System", "DoNotKnow"); + _hyperion->registerInput(_systemCaptPrio, hyperion::COMP_GRABBER); connect(_hyperion, &Hyperion::systemImage, this, &CaptureCont::handleSystemImage); } else @@ -59,13 +67,14 @@ void CaptureCont::setV4LCaptureEnable(const bool& enable) { if(enable) { - _hyperion->registerInput(_v4lCaptPrio, hyperion::COMP_V4L, "System", "DoNotKnow"); + _hyperion->registerInput(_v4lCaptPrio, hyperion::COMP_V4L); connect(_hyperion, &Hyperion::v4lImage, this, &CaptureCont::handleV4lImage); } else { disconnect(_hyperion, &Hyperion::v4lImage, this, &CaptureCont::handleV4lImage); _hyperion->clear(_v4lCaptPrio); + _v4lInactiveTimer->stop(); } _v4lCaptEnabled = enable; _hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_V4L, enable); @@ -104,3 +113,8 @@ void CaptureCont::componentStateChanged(const hyperion::Components component, bo setV4LCaptureEnable(enable); } } + +void CaptureCont::setV4lInactive() +{ + _hyperion->setInputInactive(_v4lCaptPrio); +} diff --git a/libsrc/hyperion/ComponentRegister.cpp b/libsrc/hyperion/ComponentRegister.cpp index baa573d7..4a686aeb 100644 --- a/libsrc/hyperion/ComponentRegister.cpp +++ b/libsrc/hyperion/ComponentRegister.cpp @@ -55,9 +55,9 @@ bool ComponentRegister::setHyperionEnable(const bool& state) return false; } -bool ComponentRegister::isComponentEnabled(const hyperion::Components& comp) const +int ComponentRegister::isComponentEnabled(const hyperion::Components& comp) const { - return _componentStates.at(comp); + return (_componentStates.count(comp)) ? _componentStates.at(comp) : -1; } void ComponentRegister::componentStateChanged(const hyperion::Components comp, const bool activated) diff --git a/libsrc/hyperion/Grabber.cpp b/libsrc/hyperion/Grabber.cpp index 9811f79f..e04a5770 100644 --- a/libsrc/hyperion/Grabber.cpp +++ b/libsrc/hyperion/Grabber.cpp @@ -24,12 +24,13 @@ Grabber::~Grabber() void Grabber::setEnabled(bool enable) { + Info(_log,"Capture interface is now %s", enable ? "enabled" : "disabled"); _enabled = enable; } void Grabber::setVideoMode(VideoMode mode) { - Debug(_log,"setvideomode %d", mode); + Debug(_log,"Set videomode to %d", mode); _videoMode = mode; if ( _useImageResampler ) { @@ -68,18 +69,20 @@ void Grabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTo } } -void Grabber::setWidthHeight(int width, int height) +bool Grabber::setWidthHeight(int width, int height) { // eval changes with crop - if (width>0 && height>0) + if ( (width>0 && height>0) && (_width != width || _height != height) ) { if (_cropLeft + _cropRight >= width || _cropTop + _cropBottom >= height) { Error(_log, "Rejecting invalid width/height values as it collides with image cropping: width: %d, height: %d", width, height); - return; + return false; } + Debug(_log, "Set new width: %d, height: %d for capture", width, height); _width = width; _height = height; + return true; } - + return false; } diff --git a/libsrc/hyperion/GrabberWrapper.cpp b/libsrc/hyperion/GrabberWrapper.cpp index 08e77fa7..d41bd35e 100644 --- a/libsrc/hyperion/GrabberWrapper.cpp +++ b/libsrc/hyperion/GrabberWrapper.cpp @@ -3,19 +3,14 @@ #include #include -//forwarder -#include - // qt #include GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned width, unsigned height, const unsigned updateRate_Hz) : _grabberName(grabberName) - , _hyperion(Hyperion::getInstance()) , _timer(new QTimer(this)) , _updateInterval_ms(1000/updateRate_Hz) , _log(Logger::getInstance(grabberName)) - , _forward(true) , _ggrabber(ggrabber) , _image(0,0) { @@ -24,8 +19,6 @@ GrabberWrapper::GrabberWrapper(QString grabberName, Grabber * ggrabber, unsigned _image.resize(width, height); - _forward = _hyperion->getForwarder()->protoForwardingEnabled(); - connect(_timer, &QTimer::timeout, this, &GrabberWrapper::action); } @@ -105,7 +98,7 @@ void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJso else obj = config.object(); - if(type == settings::SYSTEMCAPTURE) + if(type == settings::SYSTEMCAPTURE && !_grabberName.startsWith("V4L")) { // width/height _ggrabber->setWidthHeight(obj["width"].toInt(96), obj["height"].toInt(96)); @@ -138,7 +131,8 @@ void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJso } } - if(type == settings::V4L2) + // v4l instances only! + if(type == settings::V4L2 && _grabberName.startsWith("V4L")) { // pixel decimation for v4l _ggrabber->setPixelDecimation(obj["sizeDecimation"].toInt(8)); @@ -160,8 +154,8 @@ void GrabberWrapper::handleSettingsUpdate(const settings::type& type, const QJso obj["redSignalThreshold"].toDouble(0.0)/100.0, obj["greenSignalThreshold"].toDouble(0.0)/100.0, obj["blueSignalThreshold"].toDouble(0.0)/100.0); - _ggrabber->setInputVideoStandard( - obj["input"].toInt(0), + _ggrabber->setDeviceVideoStandard( + obj["device"].toString("auto"), parseVideoStandard(obj["standard"].toString("no-change"))); } diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 669994a1..12345e72 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -47,6 +47,9 @@ // CaptureControl (Daemon capture) #include +// Boblight +#include + Hyperion* Hyperion::_hyperion = nullptr; Hyperion* Hyperion::initInstance( HyperionDaemon* daemon, const quint8& instance, const QString configFile, const QString rootPath) @@ -122,7 +125,7 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString const QJsonObject color = getSetting(settings::COLOR).object(); // initialize leddevices - const QJsonObject ledDevice = getSetting(settings::DEVICE).object(); + QJsonObject ledDevice = getSetting(settings::DEVICE).object(); ledDevice["currentLedCount"] = int(_hwLedCount); // Inject led count info _device = LedDeviceFactory::construct(ledDevice); @@ -159,6 +162,11 @@ Hyperion::Hyperion(HyperionDaemon* daemon, const quint8& instance, const QString // if there is no startup / background eff and no sending capture interface we probably want to push once BLACK (as PrioMuxer won't emit a prioritiy change) update(); + + // 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); } Hyperion::~Hyperion() @@ -178,6 +186,7 @@ void Hyperion::freeObjects(bool emitCloseSignal) } // delete components on exit of hyperion core + delete _boblightServer; delete _captureCont; delete _effectEngine; //delete _deviceSmooth; @@ -249,7 +258,7 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum else if(type == settings::DEVICE) { _lockUpdate = true; - const QJsonObject dev = config.object(); + QJsonObject dev = config.object(); // handle hwLedCount update _hwLedCount = qMax(unsigned(dev["hardwareLedCount"].toInt(getLedCount())), getLedCount()); @@ -281,7 +290,7 @@ void Hyperion::handleSettingsUpdate(const settings::type& type, const QJsonDocum _deviceSmooth->startTimerDelayed(); _lockUpdate = false; } - // update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer + // update once to push single color sets / adjustments/ ledlayout resizes and update ledBuffer color update(); } @@ -421,6 +430,11 @@ const bool Hyperion::setInputImage(const int priority, const Image& im return false; } +const bool Hyperion::setInputInactive(const quint8& priority) +{ + return _muxer.setInputInactive(priority); +} + void Hyperion::setColor(int priority, const ColorRgb &color, const int timeout_ms, const QString& origin, bool clearEffects) { // clear effect if this call does not come from an effect @@ -602,7 +616,7 @@ void Hyperion::update() // disable the black border detector for effects and ledmapping to 0 if(compChanged) { - _imageProcessor->setBlackbarDetectDisable((_prevCompId == hyperion::COMP_EFFECT || _prevCompId == hyperion::COMP_GRABBER)); + _imageProcessor->setBlackbarDetectDisable((_prevCompId == hyperion::COMP_EFFECT)); _imageProcessor->setHardLedMappingType((_prevCompId == hyperion::COMP_EFFECT) ? 0 : -1); } _imageProcessor->process(image, _ledBuffer); diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index c243de17..f5e8e5c6 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -228,7 +228,7 @@ bool LinearColorSmoothing::selectConfig(unsigned cfg, const bool& force) } _currentConfigId = cfg; //DebugIf( enabled() && !_pause, _log, "set smoothing cfg: %d, interval: %d ms, settlingTime: %d ms, updateDelay: %d frames", _currentConfigId, _updateInterval, _settlingTime, _outputDelay ); - InfoIf( _pause, _log, "set smoothing cfg: %d, pause", _currentConfigId ); + DebugIf( _pause, _log, "set smoothing cfg: %d, pause", _currentConfigId ); return true; } diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index 79ed8ed1..40292939 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -240,6 +240,12 @@ const bool PriorityMuxer::setInputImage(const int priority, const Image image; + return setInputImage(priority, image, -100); +} + const bool PriorityMuxer::clearInput(const uint8_t priority) { if (priority < PriorityMuxer::LOWEST_PRIORITY && _activeInputs.remove(priority)) diff --git a/libsrc/hyperion/SettingsManager.cpp b/libsrc/hyperion/SettingsManager.cpp index 3eeec8d3..6de6929c 100644 --- a/libsrc/hyperion/SettingsManager.cpp +++ b/libsrc/hyperion/SettingsManager.cpp @@ -133,8 +133,6 @@ SettingsManager::~SettingsManager() const QJsonDocument SettingsManager::getSetting(const settings::type& type) { - //return _sTable->getSettingsRecord(settings::typeToString(type)); - QString key = settings::typeToString(type); if(_qconfig[key].isObject()) return QJsonDocument(_qconfig[key].toObject()); @@ -168,6 +166,23 @@ const bool SettingsManager::saveSettings(QJsonObject config, const bool& correct return false; } + // compare old data with new data to emit/save changes accordingly + for(const auto key : config.keys()) + { + QString newData, oldData; + + _qconfig[key].isObject() + ? oldData = QString(QJsonDocument(_qconfig[key].toObject()).toJson(QJsonDocument::Compact)) + : oldData = QString(QJsonDocument(_qconfig[key].toArray()).toJson(QJsonDocument::Compact)); + + config[key].isObject() + ? newData = QString(QJsonDocument(config[key].toObject()).toJson(QJsonDocument::Compact)) + : newData = QString(QJsonDocument(config[key].toArray()).toJson(QJsonDocument::Compact)); + + if(oldData != newData) + emit settingsChanged(settings::stringToType(key), QJsonDocument::fromJson(newData.toLocal8Bit())); + } + // store the current state _qconfig = config; diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index b49d2f44..864a0485 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -55,6 +55,10 @@ { "$ref": "schema-protoServer.json" }, + "flatbufServer": + { + "$ref": "schema-flatbufServer.json" + }, "boblightServer" : { "$ref": "schema-boblightServer.json" diff --git a/libsrc/hyperion/resource.qrc b/libsrc/hyperion/resource.qrc index 717d548a..ad3a1993 100644 --- a/libsrc/hyperion/resource.qrc +++ b/libsrc/hyperion/resource.qrc @@ -16,6 +16,7 @@ schema/schema-forwarder.json schema/schema-jsonServer.json schema/schema-protoServer.json + schema/schema-flatbufServer.json schema/schema-boblightServer.json schema/schema-udpListener.json schema/schema-webConfig.json diff --git a/libsrc/hyperion/schema/schema-device.json b/libsrc/hyperion/schema/schema-device.json index 53556c79..9785f3d2 100644 --- a/libsrc/hyperion/schema/schema-device.json +++ b/libsrc/hyperion/schema/schema-device.json @@ -2,18 +2,21 @@ "type" : "object", "title" : "edt_dev_general_heading_title", "required" : true, - "defaultProperties": ["ledCount","colorOrder","rewriteTime","minimumWriteTime"], + "defaultProperties": ["hardwareLedCount","colorOrder","rewriteTime"], "properties" : { "type" : { - "type" : "string" + "type" : "string", + "propertyOrder" : 1 }, - "ledCount" : + "hardwareLedCount" : { "type" : "integer", - "minimum" : 0, - "title" : "edt_dev_general_ledCount_title", + "title" : "edt_dev_general_hardwareLedCount_title", + "minimum" : 1, + "default" : 1, + "access" : "expert", "propertyOrder" : 2 }, "colorOrder" : diff --git a/libsrc/hyperion/schema/schema-grabberV4L2.json b/libsrc/hyperion/schema/schema-grabberV4L2.json index bd928614..01df9d8d 100644 --- a/libsrc/hyperion/schema/schema-grabberV4L2.json +++ b/libsrc/hyperion/schema/schema-grabberV4L2.json @@ -3,7 +3,7 @@ "required" : true, "title" : "edt_conf_v4l2_heading_title", "minItems": 1, - "maxItems": 2, + "maxItems": 1, "items": { "type" : "object", @@ -16,17 +16,9 @@ "type" : "string", "title" : "edt_conf_v4l2_device_title", "default" : "auto", + "minLength" : 4, "required" : true, - "propertyOrder" : 2 - }, - "input" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_input_title", - "minimum" : 0, - "default" : 0, - "required" : true, - "propertyOrder" : 3 + "propertyOrder" : 1 }, "standard" : { @@ -38,7 +30,7 @@ "enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM", "edt_conf_enum_NO_CHANGE"] }, "required" : true, - "propertyOrder" : 4 + "propertyOrder" : 2 }, "sizeDecimation" : { @@ -48,7 +40,7 @@ "maximum" : 30, "default" : 6, "required" : true, - "propertyOrder" : 8 + "propertyOrder" : 3 }, "cropLeft" : { @@ -58,7 +50,7 @@ "default" : 0, "append" : "edt_append_pixel", "required" : true, - "propertyOrder" : 11 + "propertyOrder" : 4 }, "cropRight" : { @@ -68,7 +60,7 @@ "default" : 0, "append" : "edt_append_pixel", "required" : true, - "propertyOrder" : 12 + "propertyOrder" : 5 }, "cropTop" : { @@ -78,7 +70,7 @@ "default" : 0, "append" : "edt_append_pixel", "required" : true, - "propertyOrder" : 13 + "propertyOrder" : 6 }, "cropBottom" : { @@ -88,7 +80,7 @@ "default" : 0, "append" : "edt_append_pixel", "required" : true, - "propertyOrder" : 14 + "propertyOrder" : 7 }, "signalDetection" : { @@ -96,7 +88,7 @@ "title" : "edt_conf_v4l2_signalDetection_title", "default" : false, "required" : true, - "propertyOrder" : 15 + "propertyOrder" : 8 }, "redSignalThreshold" : { @@ -112,7 +104,7 @@ } }, "required" : true, - "propertyOrder" : 16 + "propertyOrder" : 9 }, "greenSignalThreshold" : { @@ -128,7 +120,7 @@ } }, "required" : true, - "propertyOrder" : 17 + "propertyOrder" : 10 }, "blueSignalThreshold" : { @@ -144,7 +136,7 @@ } }, "required" : true, - "propertyOrder" : 18 + "propertyOrder" : 11 }, "sDVOffsetMin" : { @@ -160,7 +152,7 @@ } }, "required" : true, - "propertyOrder" : 19 + "propertyOrder" : 12 }, "sDVOffsetMax" : { @@ -176,7 +168,7 @@ } }, "required" : true, - "propertyOrder" : 20 + "propertyOrder" : 13 }, "sDHOffsetMin" : { @@ -192,7 +184,7 @@ } }, "required" : true, - "propertyOrder" : 21 + "propertyOrder" : 14 }, "sDHOffsetMax" : { @@ -208,7 +200,7 @@ } }, "required" : true, - "propertyOrder" : 22 + "propertyOrder" : 15 } }, "additionalProperties" : false diff --git a/libsrc/hyperion/schema/schema-instCapture.json b/libsrc/hyperion/schema/schema-instCapture.json index 2e59df10..608717c1 100644 --- a/libsrc/hyperion/schema/schema-instCapture.json +++ b/libsrc/hyperion/schema/schema-instCapture.json @@ -8,7 +8,7 @@ { "type" : "boolean", "required" : true, - "title" : "edt_conf_instC_systemEnable", + "title" : "edt_conf_instC_systemEnable_title", "default" : true, "propertyOrder" : 1 }, @@ -26,7 +26,7 @@ { "type" : "boolean", "required" : true, - "title" : "edt_conf_instC_v4lEnable", + "title" : "edt_conf_instC_v4lEnable_title", "default" : false, "propertyOrder" : 3 }, diff --git a/libsrc/hyperion/schema/schema-webConfig.json b/libsrc/hyperion/schema/schema-webConfig.json index 6440dc76..ea33a383 100644 --- a/libsrc/hyperion/schema/schema-webConfig.json +++ b/libsrc/hyperion/schema/schema-webConfig.json @@ -3,14 +3,6 @@ "title" : "edt_conf_webc_heading_title", "properties" : { - "enable" : - { - "type" : "boolean", - "title" : "edt_conf_general_enable_title", - "default" : true, - "access" : "expert", - "propertyOrder" : 1 - }, "document_root" : { "type" : "string", diff --git a/libsrc/jsonserver/JsonServer.cpp b/libsrc/jsonserver/JsonServer.cpp index 42deda01..ffea30d7 100644 --- a/libsrc/jsonserver/JsonServer.cpp +++ b/libsrc/jsonserver/JsonServer.cpp @@ -5,11 +5,8 @@ #include #include "JsonClientConnection.h" -// hyperion include -#include -#include +// bonjour include #include -#include // qt includes #include @@ -20,27 +17,16 @@ JsonServer::JsonServer(const QJsonDocument& config) : QObject() , _server(new QTcpServer(this)) - , _hyperion(Hyperion::getInstance()) , _openConnections() , _log(Logger::getInstance("JSONSERVER")) - , _componentRegister( & _hyperion->getComponentRegister()) { Debug(_log, "Created instance"); // Set trigger for incoming connections connect(_server, SIGNAL(newConnection()), this, SLOT(newConnection())); - // receive state of forwarder - connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &JsonServer::componentStateChanged); - - // listen for component register changes - connect(_hyperion, &Hyperion::forwardJsonMessage, this, &JsonServer::forwardJsonMessage); - // init handleSettingsUpdate(settings::JSONSERVER, config); - - // set initial state of forwarding - componentStateChanged(hyperion::COMP_FORWARDER, _componentRegister->isComponentEnabled(hyperion::COMP_FORWARDER)); } JsonServer::~JsonServer() @@ -64,7 +50,13 @@ void JsonServer::start() if(_serviceRegister == nullptr) { - _serviceRegister = new BonjourServiceRegister(); + _serviceRegister = new BonjourServiceRegister(this); + _serviceRegister->registerService("_hyperiond-json._tcp", _port); + } + else if( _serviceRegister->getPort() != _port) + { + delete _serviceRegister; + _serviceRegister = new BonjourServiceRegister(this); _serviceRegister->registerService("_hyperiond-json._tcp", _port); } } @@ -123,38 +115,6 @@ void JsonServer::closedConnection(void) connection->deleteLater(); } -void JsonServer::componentStateChanged(const hyperion::Components component, bool enable) -{ - if (component == hyperion::COMP_FORWARDER) - { - if(enable) - { - connect(_hyperion, &Hyperion::forwardJsonMessage, this, &JsonServer::forwardJsonMessage); - } - else - { - disconnect(_hyperion, &Hyperion::forwardJsonMessage, this, &JsonServer::forwardJsonMessage); - } - } -} - -void JsonServer::forwardJsonMessage(const QJsonObject &message) -{ - QTcpSocket client; - QStringList list = _hyperion->getForwarder()->getJsonSlaves(); - - for (const auto& entry : list) - { - QStringList splitted = entry.split(":"); - client.connectToHost(splitted[0], splitted[1].toInt()); - if ( client.waitForConnected(500) ) - { - sendMessage(message,&client); - client.close(); - } - } -} - void JsonServer::sendMessage(const QJsonObject & message, QTcpSocket * socket) { // serialize message diff --git a/libsrc/protoserver/ProtoClientConnection.cpp b/libsrc/protoserver/ProtoClientConnection.cpp index 2a3b6b22..f3c42c33 100644 --- a/libsrc/protoserver/ProtoClientConnection.cpp +++ b/libsrc/protoserver/ProtoClientConnection.cpp @@ -16,6 +16,9 @@ // hyperion util includes #include "utils/ColorRgb.h" +// Hyperion includes +#include + // project includes #include "ProtoClientConnection.h" @@ -198,7 +201,7 @@ void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &messag int priority = message.priority(); // clear priority - _hyperion->clear(priority); + //_hyperion->clear(priority); // send reply sendSuccessReply(); } @@ -206,7 +209,7 @@ void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &messag void ProtoClientConnection::handleClearallCommand() { // clear priority - _hyperion->clearall(); + //_hyperion->clearall(); // send reply sendSuccessReply(); diff --git a/libsrc/protoserver/ProtoClientConnection.h b/libsrc/protoserver/ProtoClientConnection.h index f61cfed1..4f8c62ee 100644 --- a/libsrc/protoserver/ProtoClientConnection.h +++ b/libsrc/protoserver/ProtoClientConnection.h @@ -9,9 +9,6 @@ #include #include -// Hyperion includes -#include - //Utils includes #include @@ -19,6 +16,8 @@ #include "message.pb.h" #include "protoserver/ProtoConnection.h" +class Hyperion; + /// /// The Connection object created by a ProtoServer when a new connection is establshed /// diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp index 4504dda0..56e8ec1a 100644 --- a/libsrc/protoserver/ProtoServer.cpp +++ b/libsrc/protoserver/ProtoServer.cpp @@ -3,10 +3,9 @@ // qt incl #include +#include // project includes -#include -#include #include #include "protoserver/ProtoConnection.h" #include "ProtoClientConnection.h" @@ -15,30 +14,13 @@ ProtoServer::ProtoServer(const QJsonDocument& config) : QObject() - , _hyperion(Hyperion::getInstance()) , _server(new QTcpServer(this)) , _openConnections() , _log(Logger::getInstance("PROTOSERVER")) - , _componentRegister( & _hyperion->getComponentRegister()) { Debug(_log,"Instance created"); connect( _server, SIGNAL(newConnection()), this, SLOT(newConnection())); handleSettingsUpdate(settings::PROTOSERVER, config); - - QStringList slaves = _hyperion->getForwarder()->getProtoSlaves(); - - for (const auto& entry : slaves) - { - ProtoConnection* p = new ProtoConnection(entry.toLocal8Bit().constData()); - p->setSkipReply(true); - _proxy_connections << p; - } - - // listen for component changes - connect(_componentRegister, &ComponentRegister::updatedComponentState, this, &ProtoServer::componentStateChanged); - - // get inital forwarder state - componentStateChanged(hyperion::COMP_FORWARDER, _componentRegister->isComponentEnabled(hyperion::COMP_FORWARDER)); } ProtoServer::~ProtoServer() @@ -46,9 +28,6 @@ ProtoServer::~ProtoServer() foreach (ProtoClientConnection * connection, _openConnections) { delete connection; } - - while (!_proxy_connections.isEmpty()) - delete _proxy_connections.takeFirst(); } void ProtoServer::start() @@ -65,7 +44,13 @@ void ProtoServer::start() if(_serviceRegister == nullptr) { - _serviceRegister = new BonjourServiceRegister(); + _serviceRegister = new BonjourServiceRegister(this); + _serviceRegister->registerService("_hyperiond-proto._tcp", _port); + } + else if( _serviceRegister->getPort() != _port) + { + delete _serviceRegister; + _serviceRegister = new BonjourServiceRegister(this); _serviceRegister->registerService("_hyperiond-proto._tcp", _port); } } @@ -110,37 +95,11 @@ void ProtoServer::newConnection() // register slot for cleaning up after the connection closed connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*))); - connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*))); - - // register forward signal for video mode - connect(this, SIGNAL(videoMode(VideoMode)), connection, SLOT(setVideoMode(VideoMode))); + //connect(connection, SIGNAL(newMessage(const proto::HyperionRequest*)), this, SLOT(newMessage(const proto::HyperionRequest*))); } } } -void ProtoServer::newMessage(const proto::HyperionRequest * message) -{ - for (int i = 0; i < _proxy_connections.size(); ++i) - _proxy_connections.at(i)->sendMessage(*message); -} - -void ProtoServer::sendImageToProtoSlaves(int priority, const Image & image, int duration_ms) -{ - if ( _forwarder_enabled ) - { - for (int i = 0; i < _proxy_connections.size(); ++i) - _proxy_connections.at(i)->setImage(image, priority, duration_ms); - } -} - -void ProtoServer::componentStateChanged(const hyperion::Components component, bool enable) -{ - if (component == hyperion::COMP_FORWARDER) - { - _forwarder_enabled = enable; - } -} - void ProtoServer::closedConnection(ProtoClientConnection *connection) { Debug(_log, "Connection closed"); diff --git a/libsrc/python/CMakeLists.txt b/libsrc/python/CMakeLists.txt index 2f363d88..cbe8f641 100644 --- a/libsrc/python/CMakeLists.txt +++ b/libsrc/python/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(PythonLibs 3.4 REQUIRED) +find_package(PythonLibs 3.5 REQUIRED) # Include the python directory. Also include the parent (which is for example /usr/include) # which may be required when it is not includes by the (cross-) compiler by default. diff --git a/libsrc/udplistener/UDPListener.cpp b/libsrc/udplistener/UDPListener.cpp index 3674587b..4888ea52 100644 --- a/libsrc/udplistener/UDPListener.cpp +++ b/libsrc/udplistener/UDPListener.cpp @@ -1,24 +1,21 @@ // project includes #include -// hyperion includes -#include +// bonjour includes #include -// hyperion util includes -#include "utils/ColorRgb.h" +// hyperion includes #include "HyperionConfig.h" // qt includes #include +#include using namespace hyperion; UDPListener::UDPListener(const QJsonDocument& config) : QObject(), - _hyperion(Hyperion::getInstance()), _server(new QUdpSocket(this)), - _openConnections(), _priority(0), _timeout(0), _log(Logger::getInstance("UDPLISTENER")), @@ -26,10 +23,6 @@ UDPListener::UDPListener(const QJsonDocument& config) : _listenPort(0) { Debug(_log, "Instance created"); - // listen for comp changes - connect(_hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool))); - // Set trigger for incoming connections - connect(_server, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); // init handleSettingsUpdate(settings::UDPLISTENER, config); @@ -40,7 +33,6 @@ UDPListener::~UDPListener() // clear the current channel stop(); delete _server; - _hyperion->clear(_priority); } @@ -67,12 +59,17 @@ void UDPListener::start() WarningIf( ! joinGroupOK, _log, "Multicast failed"); } _isActive = true; - _hyperion->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive); - if(_bonjourService == nullptr) + if(_serviceRegister == nullptr) { - _bonjourService = new BonjourServiceRegister(); - _bonjourService->registerService("_hyperiond-udp._udp", _listenPort); + _serviceRegister = new BonjourServiceRegister(this); + _serviceRegister->registerService("_hyperiond-udp._udp", _listenPort); + } + else if( _serviceRegister->getPort() != _listenPort) + { + delete _serviceRegister; + _serviceRegister = new BonjourServiceRegister(this); + _serviceRegister->registerService("_hyperiond-udp._udp", _listenPort); } } } @@ -85,8 +82,7 @@ void UDPListener::stop() _server->close(); _isActive = false; Info(_log, "Stopped"); - _hyperion->clear(_priority); - _hyperion->getComponentRegister().componentStateChanged(COMP_UDPLISTENER, _isActive); + emit clearGlobalPriority(_priority, hyperion::COMP_UDPLISTENER); } void UDPListener::componentStateChanged(const hyperion::Components component, bool enable) @@ -124,20 +120,19 @@ void UDPListener::readPendingDatagrams() void UDPListener::processTheDatagram(const QByteArray * datagram, const QHostAddress * sender) { int packetLedCount = datagram->size()/3; - int hyperionLedCount = Hyperion::getInstance()->getLedCount(); - DebugIf( (packetLedCount != hyperionLedCount), _log, "packetLedCount (%d) != hyperionLedCount (%d)", packetLedCount, hyperionLedCount); + //DebugIf( (packetLedCount != hyperionLedCount), _log, "packetLedCount (%d) != hyperionLedCount (%d)", packetLedCount, hyperionLedCount); - std::vector _ledColors(Hyperion::getInstance()->getLedCount(), ColorRgb::BLACK); + std::vector _ledColors(packetLedCount, ColorRgb::BLACK); - for (int ledIndex=0; ledIndex < qMin(packetLedCount, hyperionLedCount); ledIndex++) { + for (int ledIndex=0; ledIndex < packetLedCount; ledIndex++) { ColorRgb & rgb = _ledColors[ledIndex]; rgb.red = datagram->at(ledIndex*3+0); rgb.green = datagram->at(ledIndex*3+1); rgb.blue = datagram->at(ledIndex*3+2); } // TODO provide a setInput with origin arg to overwrite senders smarter - _hyperion->registerInput(_priority, hyperion::COMP_UDPLISTENER, QString("UDPListener@%1").arg(sender->toString())); - _hyperion->setInput(_priority, _ledColors, _timeout); + emit registerGlobalInput(_priority, hyperion::COMP_UDPLISTENER, QString("UDPListener@%1").arg(sender->toString())); + emit setGlobalInput(_priority, _ledColors, _timeout); } void UDPListener::handleSettingsUpdate(const settings::type& type, const QJsonDocument& config) diff --git a/libsrc/utils/Stats.cpp b/libsrc/utils/Stats.cpp index 039e8361..e31a5594 100644 --- a/libsrc/utils/Stats.cpp +++ b/libsrc/utils/Stats.cpp @@ -13,11 +13,15 @@ #include #include -Stats::Stats() +Stats* Stats::instance = nullptr; + +Stats::Stats(const QJsonObject& config) : QObject() , _log(Logger::getInstance("STATS")) , _hyperion(Hyperion::getInstance()) { + Stats::instance = this; + // generate hash foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces()) { @@ -38,8 +42,31 @@ Stats::Stats() return; } + // prep data + handleDataUpdate(config); + + // QNetworkRequest Header + _req.setRawHeader("Content-Type", "application/json"); + _req.setRawHeader("Authorization", "Basic SHlwZXJpb25YbDQ5MlZrcXA6ZDQxZDhjZDk4ZjAwYjIw"); + + connect(&_mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(resolveReply(QNetworkReply*))); + + // 7 days interval + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(sendHTTP())); + timer->start(604800000); + + // delay initial check + QTimer::singleShot(60000, this, SLOT(initialExec())); +} + +Stats::~Stats() +{ +} + +void Stats::handleDataUpdate(const QJsonObject& config) +{ // prepare content - QJsonObject config = _hyperion->getQJsonConfig(); SysInfo::HyperionSysInfo data = SysInfo::get(); QJsonObject system; @@ -63,25 +90,6 @@ Stats::Stats() QJsonDocument doc(system); _ba = doc.toJson(); - - // QNetworkRequest Header - _req.setRawHeader("Content-Type", "application/json"); - _req.setRawHeader("Authorization", "Basic SHlwZXJpb25YbDQ5MlZrcXA6ZDQxZDhjZDk4ZjAwYjIw"); - - connect(&_mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(resolveReply(QNetworkReply*))); - - // 7 days interval - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(sendHTTP())); - timer->start(604800000); - - //delay initial check - QTimer::singleShot(60000, this, SLOT(initialExec())); -} - -Stats::~Stats() -{ - } void Stats::initialExec() diff --git a/libsrc/webserver/CgiHandler.cpp b/libsrc/webserver/CgiHandler.cpp index a9ddc107..1824397c 100644 --- a/libsrc/webserver/CgiHandler.cpp +++ b/libsrc/webserver/CgiHandler.cpp @@ -13,11 +13,9 @@ #include #include -CgiHandler::CgiHandler (Hyperion * hyperion, QObject * parent) +CgiHandler::CgiHandler (QObject * parent) : QObject(parent) - , _hyperion(hyperion) , _args(QStringList()) - , _hyperionConfig(_hyperion->getQJsonConfig()) , _baseUrl() , _log(Logger::getInstance("WEBSERVER")) { @@ -57,11 +55,6 @@ void CgiHandler::cmd_cfg_jsonserver() if ( _args.at(0) == "cfg_jsonserver" ) { quint16 jsonPort = 19444; - if (_hyperionConfig.contains("jsonServer")) - { - const QJsonObject jsonConfig = _hyperionConfig["jsonServer"].toObject(); - jsonPort = jsonConfig["port"].toInt(jsonPort); - } // send result as reply _reply->addHeader ("Content-Type", "text/plain" ); diff --git a/libsrc/webserver/CgiHandler.h b/libsrc/webserver/CgiHandler.h index 0be79812..e48d9577 100644 --- a/libsrc/webserver/CgiHandler.h +++ b/libsrc/webserver/CgiHandler.h @@ -5,7 +5,6 @@ #include #include -#include #include #include "QtHttpReply.h" @@ -15,7 +14,7 @@ class CgiHandler : public QObject { Q_OBJECT public: - CgiHandler (Hyperion * hyperion, QObject * parent = NULL); + CgiHandler (QObject * parent = NULL); virtual ~CgiHandler (void); void setBaseUrl(const QString& url); @@ -26,11 +25,9 @@ public: void cmd_runscript (); private: - Hyperion* _hyperion; QtHttpReply * _reply; QtHttpRequest * _request; QStringList _args; - const QJsonObject & _hyperionConfig; QString _baseUrl; Logger * _log; }; diff --git a/libsrc/webserver/QtHttpServer.h b/libsrc/webserver/QtHttpServer.h index a14b191e..50264470 100644 --- a/libsrc/webserver/QtHttpServer.h +++ b/libsrc/webserver/QtHttpServer.h @@ -19,67 +19,69 @@ class QtHttpReply; class QtHttpClientWrapper; class QtHttpServerWrapper : public QTcpServer { - Q_OBJECT + Q_OBJECT public: - explicit QtHttpServerWrapper (QObject * parent = Q_NULLPTR); - virtual ~QtHttpServerWrapper (void); + explicit QtHttpServerWrapper (QObject * parent = Q_NULLPTR); + virtual ~QtHttpServerWrapper (void); - void setUseSecure (const bool ssl = true); + void setUseSecure (const bool ssl = true); protected: - void incomingConnection (qintptr handle) Q_DECL_OVERRIDE; + void incomingConnection (qintptr handle) Q_DECL_OVERRIDE; private: - bool m_useSsl; + bool m_useSsl; }; class QtHttpServer : public QObject { - Q_OBJECT + Q_OBJECT public: - explicit QtHttpServer (QObject * parent = Q_NULLPTR); + explicit QtHttpServer (QObject * parent = Q_NULLPTR); - static const QString & HTTP_VERSION; + static const QString & HTTP_VERSION; - typedef void (QSslSocket::* SslErrorSignal) (const QList &); + typedef void (QSslSocket::* SslErrorSignal) (const QList &); - const QString & getServerName (void) const; + const QString & getServerName (void) const; - quint16 getServerPort (void) const; - QString getErrorString (void) const; + quint16 getServerPort (void) const; + QString getErrorString (void) const; + +// const bool isListening(void) { return m_sockServer->isListening(); }; public slots: - void start (quint16 port = 0); - void stop (void); - void setServerName (const QString & serverName); - void setUseSecure (const bool ssl = true); - void setPrivateKey (const QSslKey & key); - void setCertificates (const QList & certs); + void start (quint16 port = 0); + void stop (void); + void setServerName (const QString & serverName); + void setUseSecure (const bool ssl = true); + void setPrivateKey (const QSslKey & key); + void setCertificates (const QList & certs); signals: - void started (quint16 port); - void stopped (void); - void error (const QString & msg); - void clientConnected (const QString & guid); - void clientDisconnected (const QString & guid); - void requestNeedsReply (QtHttpRequest * request, QtHttpReply * reply); + void started (quint16 port); + void stopped (void); + void error (const QString & msg); + void clientConnected (const QString & guid); + void clientDisconnected (const QString & guid); + void requestNeedsReply (QtHttpRequest * request, QtHttpReply * reply); private slots: - void onClientConnected (void); - void onClientDisconnected (void); - void onClientSslEncrypted (void); - void onClientSslPeerVerifyError (const QSslError & err); - void onClientSslErrors (const QList & errors); - void onClientSslModeChanged (QSslSocket::SslMode mode); + void onClientConnected (void); + void onClientDisconnected (void); + void onClientSslEncrypted (void); + void onClientSslPeerVerifyError (const QSslError & err); + void onClientSslErrors (const QList & errors); + void onClientSslModeChanged (QSslSocket::SslMode mode); private: - bool m_useSsl; - QSslKey m_sslKey; - QList m_sslCerts; - QString m_serverName; - QtHttpServerWrapper * m_sockServer; - QHash m_socksClientsHash; + bool m_useSsl; + QSslKey m_sslKey; + QList m_sslCerts; + QString m_serverName; + QtHttpServerWrapper * m_sockServer; + QHash m_socksClientsHash; }; #endif // QTHTTPSERVER_H diff --git a/libsrc/webserver/StaticFileServing.cpp b/libsrc/webserver/StaticFileServing.cpp index 85004470..bf11cbc5 100644 --- a/libsrc/webserver/StaticFileServing.cpp +++ b/libsrc/webserver/StaticFileServing.cpp @@ -10,11 +10,10 @@ #include #include -StaticFileServing::StaticFileServing (Hyperion *hyperion, QObject * parent) +StaticFileServing::StaticFileServing (QObject * parent) : QObject (parent) - , _hyperion(hyperion) , _baseUrl () - , _cgi(hyperion, this) + , _cgi(this) , _log(Logger::getInstance("WEBSERVER")) { Q_INIT_RESOURCE(WebConfig); diff --git a/libsrc/webserver/StaticFileServing.h b/libsrc/webserver/StaticFileServing.h index 875bad4d..88821cbf 100644 --- a/libsrc/webserver/StaticFileServing.h +++ b/libsrc/webserver/StaticFileServing.h @@ -1,22 +1,23 @@ #ifndef STATICFILESERVING_H #define STATICFILESERVING_H -#include +// locales includes +#include "CgiHandler.h" -//#include "QtHttpServer.h" +// qt includes +#include #include "QtHttpRequest.h" #include "QtHttpReply.h" #include "QtHttpHeader.h" -#include "CgiHandler.h" -#include +//utils includes #include class StaticFileServing : public QObject { Q_OBJECT public: - explicit StaticFileServing (Hyperion *hyperion, QObject * parent = nullptr); + explicit StaticFileServing (QObject * parent = nullptr); virtual ~StaticFileServing (void); void setBaseUrl(const QString& url); @@ -25,7 +26,6 @@ public slots: void onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply); private: - Hyperion * _hyperion; QString _baseUrl; QMimeDatabase * _mimeDb; CgiHandler _cgi; diff --git a/libsrc/webserver/WebJsonRpc.h b/libsrc/webserver/WebJsonRpc.h index d074dd2f..06adb33b 100644 --- a/libsrc/webserver/WebJsonRpc.h +++ b/libsrc/webserver/WebJsonRpc.h @@ -1,7 +1,11 @@ #pragma once +// utils includes #include +// qt includes +#include + class QtHttpServer; class QtHttpRequest; class QtHttpClientWrapper; diff --git a/libsrc/webserver/WebServer.cpp b/libsrc/webserver/WebServer.cpp index 2298d25d..90be5709 100644 --- a/libsrc/webserver/WebServer.cpp +++ b/libsrc/webserver/WebServer.cpp @@ -1,17 +1,21 @@ #include "webserver/WebServer.h" #include "StaticFileServing.h" -#include "QtHttpServer.h" -// bonjour +// qt includes +#include "QtHttpServer.h" +#include +#include + +// bonjour includes #include #include -#include +// utils includes +#include WebServer::WebServer(const QJsonDocument& config, QObject * parent) : QObject(parent) , _log(Logger::getInstance("WEBSERVER")) - , _hyperion(Hyperion::getInstance()) , _server(new QtHttpServer (this)) { _server->setServerName (QStringLiteral ("Hyperion Webserver")); @@ -21,7 +25,7 @@ WebServer::WebServer(const QJsonDocument& config, QObject * parent) connect (_server, &QtHttpServer::error, this, &WebServer::onServerError); // create StaticFileServing - _staticFileServing = new StaticFileServing (_hyperion, this); + _staticFileServing = new StaticFileServing (this); connect(_server, &QtHttpServer::requestNeedsReply, _staticFileServing, &StaticFileServing::onRequestNeedsReply); Debug(_log, "Instance created"); @@ -38,8 +42,17 @@ void WebServer::onServerStarted (quint16 port) { Info(_log, "Started on port %d name '%s'", port ,_server->getServerName().toStdString().c_str()); - BonjourServiceRegister *bonjourRegister_http = new BonjourServiceRegister(); - bonjourRegister_http->registerService("_hyperiond-http._tcp", port); + if(_serviceRegister == nullptr) + { + _serviceRegister = new BonjourServiceRegister(this); + _serviceRegister->registerService("_hyperiond-http._tcp", port); + } + else if( _serviceRegister->getPort() != port) + { + delete _serviceRegister; + _serviceRegister = new BonjourServiceRegister(this); + _serviceRegister->registerService("_hyperiond-http._tcp", port); + } } void WebServer::onServerStopped () { @@ -57,10 +70,8 @@ void WebServer::handleSettingsUpdate(const settings::type& type, const QJsonDocu { const QJsonObject& obj = config.object(); - bool webconfigEnable = obj["enable"].toBool(true); _baseUrl = obj["document_root"].toString(WEBSERVER_DEFAULT_PATH); - if ( (_baseUrl != ":/webconfig") && !_baseUrl.trimmed().isEmpty()) { QFileInfo info(_baseUrl); @@ -81,10 +92,10 @@ void WebServer::handleSettingsUpdate(const settings::type& type, const QJsonDocu _port = obj["port"].toInt(WEBSERVER_DEFAULT_PORT); stop(); } - if ( webconfigEnable ) - { - start(); - } + + // eval if the port is available, will be incremented if not + NetUtils::portAvailable(_port, _log); + start(); } } diff --git a/libsrc/webserver/WebSocketClient.cpp b/libsrc/webserver/WebSocketClient.cpp index 7ee64530..938175a6 100644 --- a/libsrc/webserver/WebSocketClient.cpp +++ b/libsrc/webserver/WebSocketClient.cpp @@ -1,13 +1,18 @@ #include "WebSocketClient.h" -#include "QtHttpRequest.h" -#include "QtHttpHeader.h" +// hyperion includes #include + +// JsonAPI includes #include +// qt includes +#include "QtHttpRequest.h" +#include "QtHttpHeader.h" #include #include #include +#include WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, QObject* parent) : QObject(parent) diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index e343a340..045a3cf4 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -56,10 +56,9 @@ int main(int argc, char** argv) // create the option parser and initialize all parameters Parser parser("V4L capture application for Hyperion"); - Option & argDevice = parser.add