From 006f49d033666a4453c258e45a992b390c964c13 Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sat, 2 Apr 2016 16:47:10 +1100 Subject: [PATCH 01/12] Removed -HUP so the default -TERM signal is sent instead. - hyperiond only listens for TERM and INT. HUP is often used to get an exe to reread its config Changed pgrep to add '-x' so it wont partial match on the exe name. - I have multiple instances with multiple hyperiond-instance1 names - this ensures the service script only kills the right process --- bin/service/hyperion.init.sh | 5 ++--- bin/service/hyperion.systemd.sh | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/bin/service/hyperion.init.sh b/bin/service/hyperion.init.sh index 3cd0723d..ca3f8d14 100644 --- a/bin/service/hyperion.init.sh +++ b/bin/service/hyperion.init.sh @@ -22,7 +22,7 @@ PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME case "$1" in start) - if [ $(pgrep -l $NAME |wc -l) = 1 ] + if [ $(pgrep -xl $NAME |wc -l) = 1 ] then printf "%-50s\n" "Already running..." exit 1 @@ -59,8 +59,7 @@ stop) PID=`cat $PIDFILE` cd $DAEMON_PATH if [ -f $PIDFILE ]; then - hyperion-remote -c black - kill -HUP $PID + kill $PID printf "%s\n" "Ok" rm -f $PIDFILE else diff --git a/bin/service/hyperion.systemd.sh b/bin/service/hyperion.systemd.sh index 005c0c14..f87eb8da 100644 --- a/bin/service/hyperion.systemd.sh +++ b/bin/service/hyperion.systemd.sh @@ -7,9 +7,9 @@ User=root Group=root UMask=007 ExecStart=/opt/hyperion/bin/hyperiond /etc/hyperion.config.json -ExecReload=/bin/kill -HUP $MAINPID +ExecReload=/bin/kill $MAINPID Restart=on-failure TimeoutStopSec=10 [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target From dc12495ba04bc119de7ac67ddd3f880b51612b9d Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sat, 2 Apr 2016 21:53:22 +1100 Subject: [PATCH 02/12] reversing errant change to hyperion.systemd.sh --- bin/service/hyperion.systemd.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/service/hyperion.systemd.sh b/bin/service/hyperion.systemd.sh index f87eb8da..c3c66255 100644 --- a/bin/service/hyperion.systemd.sh +++ b/bin/service/hyperion.systemd.sh @@ -7,7 +7,7 @@ User=root Group=root UMask=007 ExecStart=/opt/hyperion/bin/hyperiond /etc/hyperion.config.json -ExecReload=/bin/kill $MAINPID +ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure TimeoutStopSec=10 From e9ac1da6c7444afd906a60b5ae5f1636a69e8a58 Mon Sep 17 00:00:00 2001 From: penfold42 Date: Tue, 17 May 2016 19:54:07 +1000 Subject: [PATCH 03/12] cleaned up a couple of compiler warnings --- libsrc/leddevice/LedDevicePiBlaster.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/leddevice/LedDevicePiBlaster.cpp b/libsrc/leddevice/LedDevicePiBlaster.cpp index dc09fe15..ced94ed4 100644 --- a/libsrc/leddevice/LedDevicePiBlaster.cpp +++ b/libsrc/leddevice/LedDevicePiBlaster.cpp @@ -27,7 +27,7 @@ LedDevicePiBlaster::LedDevicePiBlaster(const std::string & deviceName, const Jso // { "gpio" : 4, "ledindex" : 0, "ledcolor" : "r" }, #define TABLE_SZ sizeof(_gpio_to_led)/sizeof(_gpio_to_led[0]) - for (int i=0; i < TABLE_SZ; i++ ) + for (unsigned i=0; i < TABLE_SZ; i++ ) { _gpio_to_led[i] = -1; _gpio_to_color[i] = 'z'; @@ -41,7 +41,7 @@ LedDevicePiBlaster::LedDevicePiBlaster(const std::string & deviceName, const Jso const std::string ledcolor = gpioMap.get("ledcolor","z").asString(); // printf ("got gpio %d ledindex %d color %c\n", gpio,ledindex, ledcolor[0]); // ignore missing/invalid settings - if ( (gpio >= 0) && (gpio < TABLE_SZ) && (ledindex >= 0) ){ + if ( (gpio >= 0) && (gpio < signed(TABLE_SZ)) && (ledindex >= 0) ){ _gpio_to_led[gpio] = ledindex; _gpio_to_color[gpio] = ledcolor[0]; // 1st char of string } else { From 46de806daae3d201a3c4e76cbabf658eff72575a Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sun, 22 May 2016 12:38:22 +1000 Subject: [PATCH 04/12] moved bitpair_to_byte initialiser to (hopefully) work with older GCC --- libsrc/leddevice/LedDeviceWs2812SPI.cpp | 9 ++++++++- libsrc/leddevice/LedDeviceWs2812SPI.h | 8 +------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWs2812SPI.cpp b/libsrc/leddevice/LedDeviceWs2812SPI.cpp index 7b915595..377d85c1 100644 --- a/libsrc/leddevice/LedDeviceWs2812SPI.cpp +++ b/libsrc/leddevice/LedDeviceWs2812SPI.cpp @@ -13,7 +13,14 @@ LedDeviceWs2812SPI::LedDeviceWs2812SPI(const std::string& outputDevice, const unsigned baudrate) : LedSpiDevice(outputDevice, baudrate, 0), - mLedCount(0) + mLedCount(0), + bitpair_to_byte { + 0b10001000, + 0b10001100, + 0b11001000, + 0b11001100, + } + { // empty } diff --git a/libsrc/leddevice/LedDeviceWs2812SPI.h b/libsrc/leddevice/LedDeviceWs2812SPI.h index e82d9134..408e2b9f 100644 --- a/libsrc/leddevice/LedDeviceWs2812SPI.h +++ b/libsrc/leddevice/LedDeviceWs2812SPI.h @@ -42,11 +42,5 @@ class LedDeviceWs2812SPI : public LedSpiDevice size_t mLedCount; std::vector _spiBuffer; - uint8_t bitpair_to_byte[4] = { - 0b10001000, - 0b10001100, - 0b11001000, - 0b11001100, - }; - + uint8_t bitpair_to_byte[4]; }; From ba84e8533aa0104fb49e92a17fa84afe2301a3cf Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sun, 22 May 2016 12:59:05 +1000 Subject: [PATCH 05/12] compiler warning in udp driver removed some tabs in ws2812b.cpp --- libsrc/leddevice/LedDeviceUdp.cpp | 2 +- libsrc/leddevice/LedDeviceWS2812b.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libsrc/leddevice/LedDeviceUdp.cpp b/libsrc/leddevice/LedDeviceUdp.cpp index 1fc67647..48c2a8d7 100644 --- a/libsrc/leddevice/LedDeviceUdp.cpp +++ b/libsrc/leddevice/LedDeviceUdp.cpp @@ -17,7 +17,7 @@ struct addrinfo hints, *servinfo, *p; //char udpbuffer[1024]; int sockfd; int ledprotocol; -int leds_per_pkt; +unsigned leds_per_pkt; int update_number; int fragment_number; diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 67a10947..c012639b 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -666,15 +666,15 @@ void LedDeviceWS2812b::initHardware() // Allocate memory for the DMA control block & data to be sent // --------------------------------------------------------------- virtbase = (uint8_t *) mmap( - NULL, // Address - NUM_PAGES * PAGE_SIZE, // Length - PROT_READ | PROT_WRITE, // Protection - MAP_SHARED | // Shared - MAP_ANONYMOUS | // Not file-based, init contents to 0 - MAP_NORESERVE | // Don't reserve swap space - MAP_LOCKED, // Lock in RAM (don't swap) - -1, // File descriptor - 0); // Offset + NULL, // Address + NUM_PAGES * PAGE_SIZE, // Length + PROT_READ | PROT_WRITE, // Protection + MAP_SHARED | // Shared + MAP_ANONYMOUS | // Not file-based, init contents to 0 + MAP_NORESERVE | // Don't reserve swap space + MAP_LOCKED, // Lock in RAM (don't swap) + -1, // File descriptor + 0); // Offset if (virtbase == MAP_FAILED) { From d6767036756af1ff23e55808656ebf90f43fafad Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sun, 22 May 2016 14:40:39 +1000 Subject: [PATCH 06/12] formatting - spaces to tabs --- libsrc/leddevice/LedDeviceWs2812SPI.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWs2812SPI.cpp b/libsrc/leddevice/LedDeviceWs2812SPI.cpp index 377d85c1..b9dc8a3d 100644 --- a/libsrc/leddevice/LedDeviceWs2812SPI.cpp +++ b/libsrc/leddevice/LedDeviceWs2812SPI.cpp @@ -14,11 +14,11 @@ LedDeviceWs2812SPI::LedDeviceWs2812SPI(const std::string& outputDevice, const unsigned baudrate) : LedSpiDevice(outputDevice, baudrate, 0), mLedCount(0), - bitpair_to_byte { - 0b10001000, - 0b10001100, - 0b11001000, - 0b11001100, + bitpair_to_byte { + 0b10001000, + 0b10001100, + 0b11001000, + 0b11001100, } { From 9ac9450c18c7d7dba99f50f1286c9172120ea2f1 Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sun, 22 May 2016 23:13:30 +1000 Subject: [PATCH 07/12] moved rpi_281x to tag sk6812-v1.0 --- dependencies/external/rpi_ws281x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/external/rpi_ws281x b/dependencies/external/rpi_ws281x index 0165896a..dfcf7408 160000 --- a/dependencies/external/rpi_ws281x +++ b/dependencies/external/rpi_ws281x @@ -1 +1 @@ -Subproject commit 0165896aa04b08a777fb6732a1af6fa29e4a93c5 +Subproject commit dfcf740848898b432fe3a3170417de60f81521ee From 95d75e141de75fb3df7288581a6b8b829a1f300a Mon Sep 17 00:00:00 2001 From: penfold42 Date: Mon, 23 May 2016 20:39:11 +1000 Subject: [PATCH 08/12] moving to my fork of rpi_281x --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index c23ec3d2..462e3f07 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/tvdzwan/protobuf.git [submodule "dependencies/external/rpi_ws281x"] path = dependencies/external/rpi_ws281x - url = https://github.com/jgarff/rpi_ws281x + url = https://github.com/penfold42/rpi_ws281x From 9518ba258175b7e8e347cc929247662ded18a56a Mon Sep 17 00:00:00 2001 From: penfold42 Date: Wed, 1 Jun 2016 09:53:31 +1000 Subject: [PATCH 09/12] removed dos line endings --- libsrc/bonjour/CMakeLists.txt | 104 +++++++++++++++++----------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/libsrc/bonjour/CMakeLists.txt b/libsrc/bonjour/CMakeLists.txt index 131b3d93..c23f51d9 100644 --- a/libsrc/bonjour/CMakeLists.txt +++ b/libsrc/bonjour/CMakeLists.txt @@ -1,52 +1,52 @@ - -# Define the current source locations -set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/bonjour) -set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/bonjour) - -# Group the headers that go through the MOC compiler -set(Bonjour_QT_HEADERS - ${CURRENT_HEADER_DIR}/bonjourrecord.h - ${CURRENT_HEADER_DIR}/bonjourserviceregister.h -) - -set(Bonjour_HEADERS -) - -set(Bonjour_SOURCES - ${CURRENT_SOURCE_DIR}/bonjourserviceregister.cpp -) - -set(Bonjour_RESOURCES -) - -if(ENABLE_QT5) -qt5_wrap_cpp(Bonjour_HEADERS_MOC ${Bonjour_QT_HEADERS}) -qt5_add_resources(Bonjour_RESOURCES_RCC ${Bonjour_RESOURCES} OPTIONS "-no-compress") -else(ENABLE_QT5) -qt4_wrap_cpp(Bonjour_HEADERS_MOC ${Bonjour_QT_HEADERS}) -qt4_add_resources(Bonjour_RESOURCES_RCC ${Bonjour_RESOURCES} OPTIONS "-no-compress") -endif(ENABLE_QT5) - -add_library(bonjour - ${Bonjour_HEADERS} - ${Bonjour_QT_HEADERS} - ${Bonjour_SOURCES} - ${Bonjour_RESOURCES} - ${Bonjour_HEADERS_MOC} - ${Bonjour_RESOURCES_RCC} -) - -if(ENABLE_QT5) -qt5_use_modules(bonjour Widgets Network) -endif(ENABLE_QT5) - -target_link_libraries(bonjour - libdns_sd.a - libavahi-client.a - libavahi-common.a - libavahi-core.a - libavahi-qt4.a - libdbus-1.a - hyperion - hyperion-utils - ${QT_LIBRARIES}) + +# Define the current source locations +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/bonjour) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/bonjour) + +# Group the headers that go through the MOC compiler +set(Bonjour_QT_HEADERS + ${CURRENT_HEADER_DIR}/bonjourrecord.h + ${CURRENT_HEADER_DIR}/bonjourserviceregister.h +) + +set(Bonjour_HEADERS +) + +set(Bonjour_SOURCES + ${CURRENT_SOURCE_DIR}/bonjourserviceregister.cpp +) + +set(Bonjour_RESOURCES +) + +if(ENABLE_QT5) +qt5_wrap_cpp(Bonjour_HEADERS_MOC ${Bonjour_QT_HEADERS}) +qt5_add_resources(Bonjour_RESOURCES_RCC ${Bonjour_RESOURCES} OPTIONS "-no-compress") +else(ENABLE_QT5) +qt4_wrap_cpp(Bonjour_HEADERS_MOC ${Bonjour_QT_HEADERS}) +qt4_add_resources(Bonjour_RESOURCES_RCC ${Bonjour_RESOURCES} OPTIONS "-no-compress") +endif(ENABLE_QT5) + +add_library(bonjour + ${Bonjour_HEADERS} + ${Bonjour_QT_HEADERS} + ${Bonjour_SOURCES} + ${Bonjour_RESOURCES} + ${Bonjour_HEADERS_MOC} + ${Bonjour_RESOURCES_RCC} +) + +if(ENABLE_QT5) +qt5_use_modules(bonjour Widgets Network) +endif(ENABLE_QT5) + +target_link_libraries(bonjour + libdns_sd.a + libavahi-client.a + libavahi-common.a + libavahi-core.a + libavahi-qt4.a + libdbus-1.a + hyperion + hyperion-utils + ${QT_LIBRARIES}) From c82b9ca576aa8458b7b18e240b737eba561333b6 Mon Sep 17 00:00:00 2001 From: penfold42 Date: Fri, 3 Jun 2016 23:38:28 +1000 Subject: [PATCH 10/12] Found some more "dos" line ending files --- dependencies/CMakeLists.txt | 220 +-- .../include/tinkerforge/ip_connection.h | 1260 ++++++++--------- libsrc/leddevice/LedDevicePhilipsHue.cpp | 684 ++++----- test/TestSpi.cpp | 322 ++--- 4 files changed, 1243 insertions(+), 1243 deletions(-) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 43569460..0ba08dd5 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -1,110 +1,110 @@ -add_subdirectory(build/getoptPlusPlus) -add_subdirectory(build/hidapi) -add_subdirectory(build/jsoncpp) -add_subdirectory(build/serial) -add_subdirectory(build/tinkerforge) - -if(ENABLE_WS281XPWM) - add_library(ws281x - external/rpi_ws281x/mailbox.c external/rpi_ws281x/ws2811.c - external/rpi_ws281x/pwm.c external/rpi_ws281x/dma.c - external/rpi_ws281x/rpihw.c) -endif(ENABLE_WS281XPWM) - -if(ENABLE_PROTOBUF) - set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared protobuf library") - add_subdirectory(external/protobuf) - - if(CMAKE_CROSSCOMPILING) - # when crosscompiling import the protoc executable targets from a file generated by a native build - option(IMPORT_PROTOC "Protoc export file (protoc_export.cmake) from a native build" "IMPORT_PROTOC-FILE_NOT_FOUND") - include(${IMPORT_PROTOC}) - else() - # export the protoc compiler so it can be used when cross compiling - export(TARGETS protoc_compiler FILE "${CMAKE_BINARY_DIR}/protoc_export.cmake") - endif() - - # define the include for the protobuf library at the parent scope - set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src") - set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE) - - # define the protoc executable at the parent scope - get_property(PROTOBUF_PROTOC_EXECUTABLE TARGET protoc_compiler PROPERTY LOCATION) - set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE} PARENT_SCOPE) - message(STATUS "Using protobuf compiler: " ${PROTOBUF_PROTOC_EXECUTABLE}) - - #============================================================================= - # Copyright 2009 Kitware, Inc. - # Copyright 2009-2011 Philip Lowman - # Copyright 2008 Esben Mose Hansen, Ange Optimization ApS - # - # Distributed under the OSI-approved BSD License (the "License"); - # see accompanying file Copyright.txt for details. - # - # This software is distributed WITHOUT ANY WARRANTY; without even the - # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - # See the License for more information. - #============================================================================= - # (To distribute this file outside of CMake, substitute the full - # License text for the above reference.) - function(PROTOBUF_GENERATE_CPP SRCS HDRS) - if(NOT ARGN) - message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files") - return() - endif() - - if(PROTOBUF_GENERATE_CPP_APPEND_PATH) - # Create an include path for each file specified - foreach(FIL ${ARGN}) - get_filename_component(ABS_FIL ${FIL} ABSOLUTE) - get_filename_component(ABS_PATH ${ABS_FIL} PATH) - list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) - if(${_contains_already} EQUAL -1) - list(APPEND _protobuf_include_path -I ${ABS_PATH}) - endif() - endforeach() - else() - set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) - endif() - - if(DEFINED PROTOBUF_IMPORT_DIRS) - foreach(DIR ${PROTOBUF_IMPORT_DIRS}) - get_filename_component(ABS_PATH ${DIR} ABSOLUTE) - list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) - if(${_contains_already} EQUAL -1) - list(APPEND _protobuf_include_path -I ${ABS_PATH}) - endif() - endforeach() - endif() - - if(CMAKE_CROSSCOMPILING) - set(PROTOC_DEPENDENCY ${PROTOBUF_PROTOC_EXECUTABLE}) - else() - set(PROTOC_DEPENDENCY protoc_compiler) - endif() - - set(${SRCS}) - set(${HDRS}) - foreach(FIL ${ARGN}) - get_filename_component(ABS_FIL ${FIL} ABSOLUTE) - get_filename_component(FIL_WE ${FIL} NAME_WE) - - list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc") - list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h") - - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc" - "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h" - COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} - ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL} - DEPENDS ${ABS_FIL} ${PROTOC_DEPENDENCY} - COMMENT "Running C++ protocol buffer compiler on ${FIL}" - VERBATIM - ) - endforeach() - - set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE) - set(${SRCS} ${${SRCS}} PARENT_SCOPE) - set(${HDRS} ${${HDRS}} PARENT_SCOPE) - endfunction() -endif() +add_subdirectory(build/getoptPlusPlus) +add_subdirectory(build/hidapi) +add_subdirectory(build/jsoncpp) +add_subdirectory(build/serial) +add_subdirectory(build/tinkerforge) + +if(ENABLE_WS281XPWM) + add_library(ws281x + external/rpi_ws281x/mailbox.c external/rpi_ws281x/ws2811.c + external/rpi_ws281x/pwm.c external/rpi_ws281x/dma.c + external/rpi_ws281x/rpihw.c) +endif(ENABLE_WS281XPWM) + +if(ENABLE_PROTOBUF) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared protobuf library") + add_subdirectory(external/protobuf) + + if(CMAKE_CROSSCOMPILING) + # when crosscompiling import the protoc executable targets from a file generated by a native build + option(IMPORT_PROTOC "Protoc export file (protoc_export.cmake) from a native build" "IMPORT_PROTOC-FILE_NOT_FOUND") + include(${IMPORT_PROTOC}) + else() + # export the protoc compiler so it can be used when cross compiling + export(TARGETS protoc_compiler FILE "${CMAKE_BINARY_DIR}/protoc_export.cmake") + endif() + + # define the include for the protobuf library at the parent scope + set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src") + set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIRS} PARENT_SCOPE) + + # define the protoc executable at the parent scope + get_property(PROTOBUF_PROTOC_EXECUTABLE TARGET protoc_compiler PROPERTY LOCATION) + set(PROTOBUF_PROTOC_EXECUTABLE ${PROTOBUF_PROTOC_EXECUTABLE} PARENT_SCOPE) + message(STATUS "Using protobuf compiler: " ${PROTOBUF_PROTOC_EXECUTABLE}) + + #============================================================================= + # Copyright 2009 Kitware, Inc. + # Copyright 2009-2011 Philip Lowman + # Copyright 2008 Esben Mose Hansen, Ange Optimization ApS + # + # Distributed under the OSI-approved BSD License (the "License"); + # see accompanying file Copyright.txt for details. + # + # This software is distributed WITHOUT ANY WARRANTY; without even the + # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + # See the License for more information. + #============================================================================= + # (To distribute this file outside of CMake, substitute the full + # License text for the above reference.) + function(PROTOBUF_GENERATE_CPP SRCS HDRS) + if(NOT ARGN) + message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files") + return() + endif() + + if(PROTOBUF_GENERATE_CPP_APPEND_PATH) + # Create an include path for each file specified + foreach(FIL ${ARGN}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(ABS_PATH ${ABS_FIL} PATH) + list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) + if(${_contains_already} EQUAL -1) + list(APPEND _protobuf_include_path -I ${ABS_PATH}) + endif() + endforeach() + else() + set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + if(DEFINED PROTOBUF_IMPORT_DIRS) + foreach(DIR ${PROTOBUF_IMPORT_DIRS}) + get_filename_component(ABS_PATH ${DIR} ABSOLUTE) + list(FIND _protobuf_include_path ${ABS_PATH} _contains_already) + if(${_contains_already} EQUAL -1) + list(APPEND _protobuf_include_path -I ${ABS_PATH}) + endif() + endforeach() + endif() + + if(CMAKE_CROSSCOMPILING) + set(PROTOC_DEPENDENCY ${PROTOBUF_PROTOC_EXECUTABLE}) + else() + set(PROTOC_DEPENDENCY protoc_compiler) + endif() + + set(${SRCS}) + set(${HDRS}) + foreach(FIL ${ARGN}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(FIL_WE ${FIL} NAME_WE) + + list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc") + list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h") + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.cc" + "${CMAKE_CURRENT_BINARY_DIR}/${FIL_WE}.pb.h" + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR} ${_protobuf_include_path} ${ABS_FIL} + DEPENDS ${ABS_FIL} ${PROTOC_DEPENDENCY} + COMMENT "Running C++ protocol buffer compiler on ${FIL}" + VERBATIM + ) + endforeach() + + set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE) + set(${SRCS} ${${SRCS}} PARENT_SCOPE) + set(${HDRS} ${${HDRS}} PARENT_SCOPE) + endfunction() +endif() diff --git a/dependencies/include/tinkerforge/ip_connection.h b/dependencies/include/tinkerforge/ip_connection.h index 5369bf76..bd86940b 100644 --- a/dependencies/include/tinkerforge/ip_connection.h +++ b/dependencies/include/tinkerforge/ip_connection.h @@ -1,630 +1,630 @@ -/* - * Copyright (C) 2012-2013 Matthias Bolte - * Copyright (C) 2011 Olaf Lüke - * - * Redistribution and use in source and binary forms of this file, - * with or without modification, are permitted. - */ - -#ifndef IP_CONNECTION_H -#define IP_CONNECTION_H - -/** - * \defgroup IPConnection IP Connection - */ - -#ifndef __STDC_LIMIT_MACROS - #define __STDC_LIMIT_MACROS -#endif -#include -#include -#include - -#if !defined __cplusplus && defined __GNUC__ - #include -#endif - -#ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include -#else - #include - #include -#endif - -enum { - E_OK = 0, - E_TIMEOUT = -1, - E_NO_STREAM_SOCKET = -2, - E_HOSTNAME_INVALID = -3, - E_NO_CONNECT = -4, - E_NO_THREAD = -5, - E_NOT_ADDED = -6, // unused since v2.0 - E_ALREADY_CONNECTED = -7, - E_NOT_CONNECTED = -8, - E_INVALID_PARAMETER = -9, // error response from device - E_NOT_SUPPORTED = -10, // error response from device - E_UNKNOWN_ERROR_CODE = -11 // error response from device -}; - -#ifdef IPCON_EXPOSE_INTERNALS - -typedef struct _Socket Socket; - -typedef struct { -#ifdef _WIN32 - CRITICAL_SECTION handle; -#else - pthread_mutex_t handle; -#endif -} Mutex; - -void mutex_create(Mutex *mutex); - -void mutex_destroy(Mutex *mutex); - -void mutex_lock(Mutex *mutex); - -void mutex_unlock(Mutex *mutex); - -typedef struct { -#ifdef _WIN32 - HANDLE handle; -#else - pthread_cond_t condition; - pthread_mutex_t mutex; - bool flag; -#endif -} Event; - -typedef struct { -#ifdef _WIN32 - HANDLE handle; -#else - sem_t object; - sem_t *pointer; -#endif -} Semaphore; - -typedef void (*ThreadFunction)(void *opaque); - -typedef struct { -#ifdef _WIN32 - HANDLE handle; - DWORD id; -#else - pthread_t handle; -#endif - ThreadFunction function; - void *opaque; -} Thread; - -typedef struct { - Mutex mutex; - int used; - int allocated; - uint32_t *keys; - void **values; -} Table; - -typedef struct _QueueItem { - struct _QueueItem *next; - int kind; - void *data; - int length; -} QueueItem; - -typedef struct { - Mutex mutex; - Semaphore semaphore; - QueueItem *head; - QueueItem *tail; -} Queue; - -#if defined _MSC_VER || defined __BORLANDC__ - #pragma pack(push) - #pragma pack(1) - #define ATTRIBUTE_PACKED -#elif defined __GNUC__ - #ifdef _WIN32 - // workaround struct packing bug in GCC 4.7 on Windows - // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 - #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) - #else - #define ATTRIBUTE_PACKED __attribute__((packed)) - #endif -#else - #error unknown compiler, do not know how to enable struct packing -#endif - -typedef struct { - uint32_t uid; - uint8_t length; - uint8_t function_id; - uint8_t sequence_number_and_options; - uint8_t error_code_and_future_use; -} ATTRIBUTE_PACKED PacketHeader; - -typedef struct { - PacketHeader header; - uint8_t payload[64]; - uint8_t optional_data[8]; -} ATTRIBUTE_PACKED Packet; - -#if defined _MSC_VER || defined __BORLANDC__ - #pragma pack(pop) -#endif -#undef ATTRIBUTE_PACKED - -#endif // IPCON_EXPOSE_INTERNALS - -typedef struct _IPConnection IPConnection; -typedef struct _IPConnectionPrivate IPConnectionPrivate; -typedef struct _Device Device; -typedef struct _DevicePrivate DevicePrivate; - -#ifdef IPCON_EXPOSE_INTERNALS - -typedef struct _CallbackContext CallbackContext; - -#endif - -typedef void (*EnumerateCallbackFunction)(const char *uid, - const char *connected_uid, - char position, - uint8_t hardware_version[3], - uint8_t firmware_version[3], - uint16_t device_identifier, - uint8_t enumeration_type, - void *user_data); -typedef void (*ConnectedCallbackFunction)(uint8_t connect_reason, - void *user_data); -typedef void (*DisconnectedCallbackFunction)(uint8_t disconnect_reason, - void *user_data); - -#ifdef IPCON_EXPOSE_INTERNALS - -typedef void (*CallbackWrapperFunction)(DevicePrivate *device_p, Packet *packet); - -#endif - -/** - * \internal - */ -struct _Device { - DevicePrivate *p; -}; - -#ifdef IPCON_EXPOSE_INTERNALS - -#define DEVICE_NUM_FUNCTION_IDS 256 - -/** - * \internal - */ -struct _DevicePrivate { - uint32_t uid; - - IPConnectionPrivate *ipcon_p; - - uint8_t api_version[3]; - - Mutex request_mutex; - - uint8_t expected_response_function_id; // protected by request_mutex - uint8_t expected_response_sequence_number; // protected by request_mutex - Mutex response_mutex; - Packet response_packet; // protected by response_mutex - Event response_event; - int response_expected[DEVICE_NUM_FUNCTION_IDS]; - - void *registered_callbacks[DEVICE_NUM_FUNCTION_IDS]; - void *registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS]; - CallbackWrapperFunction callback_wrappers[DEVICE_NUM_FUNCTION_IDS]; -}; - -/** - * \internal - */ -enum { - DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID = 0, - DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE, // getter - DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE, // callback - DEVICE_RESPONSE_EXPECTED_TRUE, // setter - DEVICE_RESPONSE_EXPECTED_FALSE // setter, default -}; - -/** - * \internal - */ -void device_create(Device *device, const char *uid, - IPConnectionPrivate *ipcon_p, uint8_t api_version_major, - uint8_t api_version_minor, uint8_t api_version_release); - -/** - * \internal - */ -void device_destroy(Device *device); - -/** - * \internal - */ -int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, - bool *ret_response_expected); - -/** - * \internal - */ -int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id, - bool response_expected); - -/** - * \internal - */ -int device_set_response_expected_all(DevicePrivate *device_p, bool response_expected); - -/** - * \internal - */ -void device_register_callback(DevicePrivate *device_p, uint8_t id, void *callback, - void *user_data); - -/** - * \internal - */ -int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]); - -/** - * \internal - */ -int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response); - -#endif // IPCON_EXPOSE_INTERNALS - -/** - * \ingroup IPConnection - * - * Possible IDs for ipcon_register_callback. - */ -enum { - IPCON_CALLBACK_ENUMERATE = 253, - IPCON_CALLBACK_CONNECTED = 0, - IPCON_CALLBACK_DISCONNECTED = 1 -}; - -/** - * \ingroup IPConnection - * - * Possible values for enumeration_type parameter of EnumerateCallback. - */ -enum { - IPCON_ENUMERATION_TYPE_AVAILABLE = 0, - IPCON_ENUMERATION_TYPE_CONNECTED = 1, - IPCON_ENUMERATION_TYPE_DISCONNECTED = 2 -}; - -/** - * \ingroup IPConnection - * - * Possible values for connect_reason parameter of ConnectedCallback. - */ -enum { - IPCON_CONNECT_REASON_REQUEST = 0, - IPCON_CONNECT_REASON_AUTO_RECONNECT = 1 -}; - -/** - * \ingroup IPConnection - * - * Possible values for disconnect_reason parameter of DisconnectedCallback. - */ -enum { - IPCON_DISCONNECT_REASON_REQUEST = 0, - IPCON_DISCONNECT_REASON_ERROR = 1, - IPCON_DISCONNECT_REASON_SHUTDOWN = 2 -}; - -/** - * \ingroup IPConnection - * - * Possible return values of ipcon_get_connection_state. - */ -enum { - IPCON_CONNECTION_STATE_DISCONNECTED = 0, - IPCON_CONNECTION_STATE_CONNECTED = 1, - IPCON_CONNECTION_STATE_PENDING = 2 // auto-reconnect in progress -}; - -/** - * \internal - */ -struct _IPConnection { - IPConnectionPrivate *p; -}; - -#ifdef IPCON_EXPOSE_INTERNALS - -#define IPCON_NUM_CALLBACK_IDS 256 - -/** - * \internal - */ -struct _IPConnectionPrivate { -#ifdef _WIN32 - bool wsa_startup_done; // protected by socket_mutex -#endif - - char *host; - uint16_t port; - - uint32_t timeout; // in msec - - bool auto_reconnect; - bool auto_reconnect_allowed; - bool auto_reconnect_pending; - - Mutex sequence_number_mutex; - uint8_t next_sequence_number; // protected by sequence_number_mutex - - Table devices; - - void *registered_callbacks[IPCON_NUM_CALLBACK_IDS]; - void *registered_callback_user_data[IPCON_NUM_CALLBACK_IDS]; - - Mutex socket_mutex; - Socket *socket; // protected by socket_mutex - uint64_t socket_id; // protected by socket_mutex - - bool receive_flag; - Thread receive_thread; // protected by socket_mutex - - CallbackContext *callback; - - bool disconnect_probe_flag; - Thread disconnect_probe_thread; // protected by socket_mutex - Event disconnect_probe_event; - - Semaphore wait; -}; - -#endif // IPCON_EXPOSE_INTERNALS - -/** - * \ingroup IPConnection - * - * Creates an IP Connection object that can be used to enumerate the available - * devices. It is also required for the constructor of Bricks and Bricklets. - */ -void ipcon_create(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Destroys the IP Connection object. Calls ipcon_disconnect internally. - * The connection to the Brick Daemon gets closed and the threads of the - * IP Connection are terminated. - */ -void ipcon_destroy(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Creates a TCP/IP connection to the given \c host and c\ port. The host and - * port can point to a Brick Daemon or to a WIFI/Ethernet Extension. - * - * Devices can only be controlled when the connection was established - * successfully. - * - * Blocks until the connection is established and returns an error code if - * there is no Brick Daemon or WIFI/Ethernet Extension listening at the given - * host and port. - */ -int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port); - -/** - * \ingroup IPConnection - * - * Disconnects the TCP/IP connection from the Brick Daemon or the WIFI/Ethernet - * Extension. - */ -int ipcon_disconnect(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Can return the following states: - * - * - IPCON_CONNECTION_STATE_DISCONNECTED: No connection is established. - * - IPCON_CONNECTION_STATE_CONNECTED: A connection to the Brick Daemon or - * the WIFI/Ethernet Extension is established. - * - IPCON_CONNECTION_STATE_PENDING: IP Connection is currently trying to - * connect. - */ -int ipcon_get_connection_state(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Enables or disables auto-reconnect. If auto-reconnect is enabled, - * the IP Connection will try to reconnect to the previously given - * host and port, if the connection is lost. - * - * Default value is *true*. - */ -void ipcon_set_auto_reconnect(IPConnection *ipcon, bool auto_reconnect); - -/** - * \ingroup IPConnection - * - * Returns *true* if auto-reconnect is enabled, *false* otherwise. - */ -bool ipcon_get_auto_reconnect(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Sets the timeout in milliseconds for getters and for setters for which the - * response expected flag is activated. - * - * Default timeout is 2500. - */ -void ipcon_set_timeout(IPConnection *ipcon, uint32_t timeout); - -/** - * \ingroup IPConnection - * - * Returns the timeout as set by ipcon_set_timeout. - */ -uint32_t ipcon_get_timeout(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Broadcasts an enumerate request. All devices will respond with an enumerate - * callback. - */ -int ipcon_enumerate(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Stops the current thread until ipcon_unwait is called. - * - * This is useful if you rely solely on callbacks for events, if you want - * to wait for a specific callback or if the IP Connection was created in - * a thread. - * - * ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release" - * of a semaphore. - */ -void ipcon_wait(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Unwaits the thread previously stopped by ipcon_wait. - * - * ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release" - * of a semaphore. - */ -void ipcon_unwait(IPConnection *ipcon); - -/** - * \ingroup IPConnection - * - * Registers a callback for a given ID. - */ -void ipcon_register_callback(IPConnection *ipcon, uint8_t id, - void *callback, void *user_data); - -#ifdef IPCON_EXPOSE_INTERNALS - -/** - * \internal - */ -int packet_header_create(PacketHeader *header, uint8_t length, - uint8_t function_id, IPConnectionPrivate *ipcon_p, - DevicePrivate *device_p); - -/** - * \internal - */ -uint8_t packet_header_get_sequence_number(PacketHeader *header); - -/** - * \internal - */ -void packet_header_set_sequence_number(PacketHeader *header, - uint8_t sequence_number); - -/** - * \internal - */ -uint8_t packet_header_get_response_expected(PacketHeader *header); - -/** - * \internal - */ -void packet_header_set_response_expected(PacketHeader *header, - uint8_t response_expected); - -/** - * \internal - */ -uint8_t packet_header_get_error_code(PacketHeader *header); - -/** - * \internal - */ -int16_t leconvert_int16_to(int16_t native); - -/** - * \internal - */ -uint16_t leconvert_uint16_to(uint16_t native); - -/** - * \internal - */ -int32_t leconvert_int32_to(int32_t native); - -/** - * \internal - */ -uint32_t leconvert_uint32_to(uint32_t native); - -/** - * \internal - */ -int64_t leconvert_int64_to(int64_t native); - -/** - * \internal - */ -uint64_t leconvert_uint64_to(uint64_t native); - -/** - * \internal - */ -float leconvert_float_to(float native); - -/** - * \internal - */ -int16_t leconvert_int16_from(int16_t little); - -/** - * \internal - */ -uint16_t leconvert_uint16_from(uint16_t little); - -/** - * \internal - */ -int32_t leconvert_int32_from(int32_t little); - -/** - * \internal - */ -uint32_t leconvert_uint32_from(uint32_t little); - -/** - * \internal - */ -int64_t leconvert_int64_from(int64_t little); - -/** - * \internal - */ -uint64_t leconvert_uint64_from(uint64_t little); - -/** - * \internal - */ -float leconvert_float_from(float little); - -#endif // IPCON_EXPOSE_INTERNALS - -#endif +/* + * Copyright (C) 2012-2013 Matthias Bolte + * Copyright (C) 2011 Olaf Lüke + * + * Redistribution and use in source and binary forms of this file, + * with or without modification, are permitted. + */ + +#ifndef IP_CONNECTION_H +#define IP_CONNECTION_H + +/** + * \defgroup IPConnection IP Connection + */ + +#ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS +#endif +#include +#include +#include + +#if !defined __cplusplus && defined __GNUC__ + #include +#endif + +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include +#else + #include + #include +#endif + +enum { + E_OK = 0, + E_TIMEOUT = -1, + E_NO_STREAM_SOCKET = -2, + E_HOSTNAME_INVALID = -3, + E_NO_CONNECT = -4, + E_NO_THREAD = -5, + E_NOT_ADDED = -6, // unused since v2.0 + E_ALREADY_CONNECTED = -7, + E_NOT_CONNECTED = -8, + E_INVALID_PARAMETER = -9, // error response from device + E_NOT_SUPPORTED = -10, // error response from device + E_UNKNOWN_ERROR_CODE = -11 // error response from device +}; + +#ifdef IPCON_EXPOSE_INTERNALS + +typedef struct _Socket Socket; + +typedef struct { +#ifdef _WIN32 + CRITICAL_SECTION handle; +#else + pthread_mutex_t handle; +#endif +} Mutex; + +void mutex_create(Mutex *mutex); + +void mutex_destroy(Mutex *mutex); + +void mutex_lock(Mutex *mutex); + +void mutex_unlock(Mutex *mutex); + +typedef struct { +#ifdef _WIN32 + HANDLE handle; +#else + pthread_cond_t condition; + pthread_mutex_t mutex; + bool flag; +#endif +} Event; + +typedef struct { +#ifdef _WIN32 + HANDLE handle; +#else + sem_t object; + sem_t *pointer; +#endif +} Semaphore; + +typedef void (*ThreadFunction)(void *opaque); + +typedef struct { +#ifdef _WIN32 + HANDLE handle; + DWORD id; +#else + pthread_t handle; +#endif + ThreadFunction function; + void *opaque; +} Thread; + +typedef struct { + Mutex mutex; + int used; + int allocated; + uint32_t *keys; + void **values; +} Table; + +typedef struct _QueueItem { + struct _QueueItem *next; + int kind; + void *data; + int length; +} QueueItem; + +typedef struct { + Mutex mutex; + Semaphore semaphore; + QueueItem *head; + QueueItem *tail; +} Queue; + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(push) + #pragma pack(1) + #define ATTRIBUTE_PACKED +#elif defined __GNUC__ + #ifdef _WIN32 + // workaround struct packing bug in GCC 4.7 on Windows + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 + #define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed)) + #else + #define ATTRIBUTE_PACKED __attribute__((packed)) + #endif +#else + #error unknown compiler, do not know how to enable struct packing +#endif + +typedef struct { + uint32_t uid; + uint8_t length; + uint8_t function_id; + uint8_t sequence_number_and_options; + uint8_t error_code_and_future_use; +} ATTRIBUTE_PACKED PacketHeader; + +typedef struct { + PacketHeader header; + uint8_t payload[64]; + uint8_t optional_data[8]; +} ATTRIBUTE_PACKED Packet; + +#if defined _MSC_VER || defined __BORLANDC__ + #pragma pack(pop) +#endif +#undef ATTRIBUTE_PACKED + +#endif // IPCON_EXPOSE_INTERNALS + +typedef struct _IPConnection IPConnection; +typedef struct _IPConnectionPrivate IPConnectionPrivate; +typedef struct _Device Device; +typedef struct _DevicePrivate DevicePrivate; + +#ifdef IPCON_EXPOSE_INTERNALS + +typedef struct _CallbackContext CallbackContext; + +#endif + +typedef void (*EnumerateCallbackFunction)(const char *uid, + const char *connected_uid, + char position, + uint8_t hardware_version[3], + uint8_t firmware_version[3], + uint16_t device_identifier, + uint8_t enumeration_type, + void *user_data); +typedef void (*ConnectedCallbackFunction)(uint8_t connect_reason, + void *user_data); +typedef void (*DisconnectedCallbackFunction)(uint8_t disconnect_reason, + void *user_data); + +#ifdef IPCON_EXPOSE_INTERNALS + +typedef void (*CallbackWrapperFunction)(DevicePrivate *device_p, Packet *packet); + +#endif + +/** + * \internal + */ +struct _Device { + DevicePrivate *p; +}; + +#ifdef IPCON_EXPOSE_INTERNALS + +#define DEVICE_NUM_FUNCTION_IDS 256 + +/** + * \internal + */ +struct _DevicePrivate { + uint32_t uid; + + IPConnectionPrivate *ipcon_p; + + uint8_t api_version[3]; + + Mutex request_mutex; + + uint8_t expected_response_function_id; // protected by request_mutex + uint8_t expected_response_sequence_number; // protected by request_mutex + Mutex response_mutex; + Packet response_packet; // protected by response_mutex + Event response_event; + int response_expected[DEVICE_NUM_FUNCTION_IDS]; + + void *registered_callbacks[DEVICE_NUM_FUNCTION_IDS]; + void *registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS]; + CallbackWrapperFunction callback_wrappers[DEVICE_NUM_FUNCTION_IDS]; +}; + +/** + * \internal + */ +enum { + DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID = 0, + DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE, // getter + DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE, // callback + DEVICE_RESPONSE_EXPECTED_TRUE, // setter + DEVICE_RESPONSE_EXPECTED_FALSE // setter, default +}; + +/** + * \internal + */ +void device_create(Device *device, const char *uid, + IPConnectionPrivate *ipcon_p, uint8_t api_version_major, + uint8_t api_version_minor, uint8_t api_version_release); + +/** + * \internal + */ +void device_destroy(Device *device); + +/** + * \internal + */ +int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id, + bool *ret_response_expected); + +/** + * \internal + */ +int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id, + bool response_expected); + +/** + * \internal + */ +int device_set_response_expected_all(DevicePrivate *device_p, bool response_expected); + +/** + * \internal + */ +void device_register_callback(DevicePrivate *device_p, uint8_t id, void *callback, + void *user_data); + +/** + * \internal + */ +int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]); + +/** + * \internal + */ +int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response); + +#endif // IPCON_EXPOSE_INTERNALS + +/** + * \ingroup IPConnection + * + * Possible IDs for ipcon_register_callback. + */ +enum { + IPCON_CALLBACK_ENUMERATE = 253, + IPCON_CALLBACK_CONNECTED = 0, + IPCON_CALLBACK_DISCONNECTED = 1 +}; + +/** + * \ingroup IPConnection + * + * Possible values for enumeration_type parameter of EnumerateCallback. + */ +enum { + IPCON_ENUMERATION_TYPE_AVAILABLE = 0, + IPCON_ENUMERATION_TYPE_CONNECTED = 1, + IPCON_ENUMERATION_TYPE_DISCONNECTED = 2 +}; + +/** + * \ingroup IPConnection + * + * Possible values for connect_reason parameter of ConnectedCallback. + */ +enum { + IPCON_CONNECT_REASON_REQUEST = 0, + IPCON_CONNECT_REASON_AUTO_RECONNECT = 1 +}; + +/** + * \ingroup IPConnection + * + * Possible values for disconnect_reason parameter of DisconnectedCallback. + */ +enum { + IPCON_DISCONNECT_REASON_REQUEST = 0, + IPCON_DISCONNECT_REASON_ERROR = 1, + IPCON_DISCONNECT_REASON_SHUTDOWN = 2 +}; + +/** + * \ingroup IPConnection + * + * Possible return values of ipcon_get_connection_state. + */ +enum { + IPCON_CONNECTION_STATE_DISCONNECTED = 0, + IPCON_CONNECTION_STATE_CONNECTED = 1, + IPCON_CONNECTION_STATE_PENDING = 2 // auto-reconnect in progress +}; + +/** + * \internal + */ +struct _IPConnection { + IPConnectionPrivate *p; +}; + +#ifdef IPCON_EXPOSE_INTERNALS + +#define IPCON_NUM_CALLBACK_IDS 256 + +/** + * \internal + */ +struct _IPConnectionPrivate { +#ifdef _WIN32 + bool wsa_startup_done; // protected by socket_mutex +#endif + + char *host; + uint16_t port; + + uint32_t timeout; // in msec + + bool auto_reconnect; + bool auto_reconnect_allowed; + bool auto_reconnect_pending; + + Mutex sequence_number_mutex; + uint8_t next_sequence_number; // protected by sequence_number_mutex + + Table devices; + + void *registered_callbacks[IPCON_NUM_CALLBACK_IDS]; + void *registered_callback_user_data[IPCON_NUM_CALLBACK_IDS]; + + Mutex socket_mutex; + Socket *socket; // protected by socket_mutex + uint64_t socket_id; // protected by socket_mutex + + bool receive_flag; + Thread receive_thread; // protected by socket_mutex + + CallbackContext *callback; + + bool disconnect_probe_flag; + Thread disconnect_probe_thread; // protected by socket_mutex + Event disconnect_probe_event; + + Semaphore wait; +}; + +#endif // IPCON_EXPOSE_INTERNALS + +/** + * \ingroup IPConnection + * + * Creates an IP Connection object that can be used to enumerate the available + * devices. It is also required for the constructor of Bricks and Bricklets. + */ +void ipcon_create(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Destroys the IP Connection object. Calls ipcon_disconnect internally. + * The connection to the Brick Daemon gets closed and the threads of the + * IP Connection are terminated. + */ +void ipcon_destroy(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Creates a TCP/IP connection to the given \c host and c\ port. The host and + * port can point to a Brick Daemon or to a WIFI/Ethernet Extension. + * + * Devices can only be controlled when the connection was established + * successfully. + * + * Blocks until the connection is established and returns an error code if + * there is no Brick Daemon or WIFI/Ethernet Extension listening at the given + * host and port. + */ +int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port); + +/** + * \ingroup IPConnection + * + * Disconnects the TCP/IP connection from the Brick Daemon or the WIFI/Ethernet + * Extension. + */ +int ipcon_disconnect(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Can return the following states: + * + * - IPCON_CONNECTION_STATE_DISCONNECTED: No connection is established. + * - IPCON_CONNECTION_STATE_CONNECTED: A connection to the Brick Daemon or + * the WIFI/Ethernet Extension is established. + * - IPCON_CONNECTION_STATE_PENDING: IP Connection is currently trying to + * connect. + */ +int ipcon_get_connection_state(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Enables or disables auto-reconnect. If auto-reconnect is enabled, + * the IP Connection will try to reconnect to the previously given + * host and port, if the connection is lost. + * + * Default value is *true*. + */ +void ipcon_set_auto_reconnect(IPConnection *ipcon, bool auto_reconnect); + +/** + * \ingroup IPConnection + * + * Returns *true* if auto-reconnect is enabled, *false* otherwise. + */ +bool ipcon_get_auto_reconnect(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Sets the timeout in milliseconds for getters and for setters for which the + * response expected flag is activated. + * + * Default timeout is 2500. + */ +void ipcon_set_timeout(IPConnection *ipcon, uint32_t timeout); + +/** + * \ingroup IPConnection + * + * Returns the timeout as set by ipcon_set_timeout. + */ +uint32_t ipcon_get_timeout(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Broadcasts an enumerate request. All devices will respond with an enumerate + * callback. + */ +int ipcon_enumerate(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Stops the current thread until ipcon_unwait is called. + * + * This is useful if you rely solely on callbacks for events, if you want + * to wait for a specific callback or if the IP Connection was created in + * a thread. + * + * ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release" + * of a semaphore. + */ +void ipcon_wait(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Unwaits the thread previously stopped by ipcon_wait. + * + * ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release" + * of a semaphore. + */ +void ipcon_unwait(IPConnection *ipcon); + +/** + * \ingroup IPConnection + * + * Registers a callback for a given ID. + */ +void ipcon_register_callback(IPConnection *ipcon, uint8_t id, + void *callback, void *user_data); + +#ifdef IPCON_EXPOSE_INTERNALS + +/** + * \internal + */ +int packet_header_create(PacketHeader *header, uint8_t length, + uint8_t function_id, IPConnectionPrivate *ipcon_p, + DevicePrivate *device_p); + +/** + * \internal + */ +uint8_t packet_header_get_sequence_number(PacketHeader *header); + +/** + * \internal + */ +void packet_header_set_sequence_number(PacketHeader *header, + uint8_t sequence_number); + +/** + * \internal + */ +uint8_t packet_header_get_response_expected(PacketHeader *header); + +/** + * \internal + */ +void packet_header_set_response_expected(PacketHeader *header, + uint8_t response_expected); + +/** + * \internal + */ +uint8_t packet_header_get_error_code(PacketHeader *header); + +/** + * \internal + */ +int16_t leconvert_int16_to(int16_t native); + +/** + * \internal + */ +uint16_t leconvert_uint16_to(uint16_t native); + +/** + * \internal + */ +int32_t leconvert_int32_to(int32_t native); + +/** + * \internal + */ +uint32_t leconvert_uint32_to(uint32_t native); + +/** + * \internal + */ +int64_t leconvert_int64_to(int64_t native); + +/** + * \internal + */ +uint64_t leconvert_uint64_to(uint64_t native); + +/** + * \internal + */ +float leconvert_float_to(float native); + +/** + * \internal + */ +int16_t leconvert_int16_from(int16_t little); + +/** + * \internal + */ +uint16_t leconvert_uint16_from(uint16_t little); + +/** + * \internal + */ +int32_t leconvert_int32_from(int32_t little); + +/** + * \internal + */ +uint32_t leconvert_uint32_from(uint32_t little); + +/** + * \internal + */ +int64_t leconvert_int64_from(int64_t little); + +/** + * \internal + */ +uint64_t leconvert_uint64_from(uint64_t little); + +/** + * \internal + */ +float leconvert_float_from(float little); + +#endif // IPCON_EXPOSE_INTERNALS + +#endif diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index df7c831a..fe9e359d 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -1,342 +1,342 @@ -// Local-Hyperion includes -#include "LedDevicePhilipsHue.h" - -// jsoncpp includes -#include - -// qt includes -#include -#include -#include - -#include -#include - -bool operator ==(CiColor p1, CiColor p2) { - return (p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri); -} - -bool operator !=(CiColor p1, CiColor p2) { - return !(p1 == p2); -} - -PhilipsHueLight::PhilipsHueLight(unsigned int id, QString originalState, QString modelId) : - id(id), originalState(originalState) { - // Hue system model ids (http://www.developers.meethue.com/documentation/supported-lights). - // Light strips, color iris, ... - const std::set GAMUT_A_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC010", "LLC011", "LLC012", - "LLC013", "LLC014", "LST001" }; - // Hue bulbs, spots, ... - const std::set GAMUT_B_MODEL_IDS = { "LCT001", "LCT002", "LCT003", "LCT007", "LLM001" }; - // Hue Lightstrip plus, go ... - const std::set GAMUT_C_MODEL_IDS = { "LLC020", "LST002" }; - // Find id in the sets and set the appropiate color space. - if (GAMUT_A_MODEL_IDS.find(modelId) != GAMUT_A_MODEL_IDS.end()) { - colorSpace.red = {0.703f, 0.296f}; - colorSpace.green = {0.2151f, 0.7106f}; - colorSpace.blue = {0.138f, 0.08f}; - } else if (GAMUT_B_MODEL_IDS.find(modelId) != GAMUT_B_MODEL_IDS.end()) { - colorSpace.red = {0.675f, 0.322f}; - colorSpace.green = {0.4091f, 0.518f}; - colorSpace.blue = {0.167f, 0.04f}; - } else if (GAMUT_C_MODEL_IDS.find(modelId) != GAMUT_B_MODEL_IDS.end()) { - colorSpace.red = {0.675f, 0.322f}; - colorSpace.green = {0.2151f, 0.7106f}; - colorSpace.blue = {0.167f, 0.04f}; - } else { - colorSpace.red = {1.0f, 0.0f}; - colorSpace.green = {0.0f, 1.0f}; - colorSpace.blue = {0.0f, 0.0f}; - } - // Initialize black color. - black = rgbToCiColor(0.0f, 0.0f, 0.0f); - // Initialize color with black - color = {black.x, black.y, black.bri}; -} - -float PhilipsHueLight::crossProduct(CiColor p1, CiColor p2) { - return p1.x * p2.y - p1.y * p2.x; -} - -bool PhilipsHueLight::isPointInLampsReach(CiColor p) { - CiColor v1 = { colorSpace.green.x - colorSpace.red.x, colorSpace.green.y - colorSpace.red.y }; - CiColor v2 = { colorSpace.blue.x - colorSpace.red.x, colorSpace.blue.y - colorSpace.red.y }; - CiColor q = { p.x - colorSpace.red.x, p.y - colorSpace.red.y }; - float s = crossProduct(q, v2) / crossProduct(v1, v2); - float t = crossProduct(v1, q) / crossProduct(v1, v2); - if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) { - return true; - } - return false; -} - -CiColor PhilipsHueLight::getClosestPointToPoint(CiColor a, CiColor b, CiColor p) { - CiColor AP = { p.x - a.x, p.y - a.y }; - CiColor AB = { b.x - a.x, b.y - a.y }; - float ab2 = AB.x * AB.x + AB.y * AB.y; - float ap_ab = AP.x * AB.x + AP.y * AB.y; - float t = ap_ab / ab2; - if (t < 0.0f) { - t = 0.0f; - } else if (t > 1.0f) { - t = 1.0f; - } - return {a.x + AB.x * t, a.y + AB.y * t}; -} - -float PhilipsHueLight::getDistanceBetweenTwoPoints(CiColor p1, CiColor p2) { - // Horizontal difference. - float dx = p1.x - p2.x; - // Vertical difference. - float dy = p1.y - p2.y; - // Absolute value. - return sqrt(dx * dx + dy * dy); -} - -CiColor PhilipsHueLight::rgbToCiColor(float red, float green, float blue) { - // Apply gamma correction. - float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); - float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); - float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); - // Convert to XYZ space. - float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; - float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; - float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; - // Convert to x,y space. - float cx = X / (X + Y + Z); - float cy = Y / (X + Y + Z); - if (std::isnan(cx)) { - cx = 0.0f; - } - if (std::isnan(cy)) { - cy = 0.0f; - } - // Brightness is simply Y in the XYZ space. - CiColor xy = { cx, cy, Y }; - // Check if the given XY value is within the color reach of our lamps. - if (!isPointInLampsReach(xy)) { - // It seems the color is out of reach let's find the closes color we can produce with our lamp and send this XY value out. - CiColor pAB = getClosestPointToPoint(colorSpace.red, colorSpace.green, xy); - CiColor pAC = getClosestPointToPoint(colorSpace.blue, colorSpace.red, xy); - CiColor pBC = getClosestPointToPoint(colorSpace.green, colorSpace.blue, xy); - // Get the distances per point and see which point is closer to our Point. - float dAB = getDistanceBetweenTwoPoints(xy, pAB); - float dAC = getDistanceBetweenTwoPoints(xy, pAC); - float dBC = getDistanceBetweenTwoPoints(xy, pBC); - float lowest = dAB; - CiColor closestPoint = pAB; - if (dAC < lowest) { - lowest = dAC; - closestPoint = pAC; - } - if (dBC < lowest) { - lowest = dBC; - closestPoint = pBC; - } - // Change the xy value to a value which is within the reach of the lamp. - xy.x = closestPoint.x; - xy.y = closestPoint.y; - } - return xy; -} - -LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, const std::string& username, bool switchOffOnBlack, - int transitiontime, std::vector lightIds) : - host(output.c_str()), username(username.c_str()), switchOffOnBlack(switchOffOnBlack), transitiontime( - transitiontime), lightIds(lightIds) { - manager = new QNetworkAccessManager(); - timer.setInterval(3000); - timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates())); -} - -LedDevicePhilipsHue::~LedDevicePhilipsHue() { - delete manager; -} - -int LedDevicePhilipsHue::write(const std::vector & ledValues) { - // Save light states if not done before. - if (!areStatesSaved()) { - saveStates((unsigned int) ledValues.size()); - switchOn((unsigned int) ledValues.size()); - } - // If there are less states saved than colors given, then maybe something went wrong before. - if (lights.size() != ledValues.size()) { - restoreStates(); - return 0; - } - // Iterate through colors and set light states. - unsigned int idx = 0; - for (const ColorRgb& color : ledValues) { - // Get lamp. - PhilipsHueLight& lamp = lights.at(idx); - // Scale colors from [0, 255] to [0, 1] and convert to xy space. - CiColor xy = lamp.rgbToCiColor(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f); - // Write color if color has been changed. - if (xy != lamp.color) { - // From a color to black. - if (switchOffOnBlack && lamp.color != lamp.black && xy == lamp.black) { - put(getStateRoute(lamp.id), QString("{\"on\": false}")); - } - // From black to a color - else if (switchOffOnBlack && lamp.color == lamp.black && xy != lamp.black) { - // Send adjust color and brightness command in JSON format. - // We have to set the transition time each time. - // Send also command to switch the lamp on. - put(getStateRoute(lamp.id), - QString("{\"on\": true, \"xy\": [%1, %2], \"bri\": %3, \"transitiontime\": %4}").arg(xy.x).arg( - xy.y).arg(qRound(xy.bri * 255.0f)).arg(transitiontime)); - } - // Normal color change. - else { - // Send adjust color and brightness command in JSON format. - // We have to set the transition time each time. - put(getStateRoute(lamp.id), - QString("{\"xy\": [%1, %2], \"bri\": %3, \"transitiontime\": %4}").arg(xy.x).arg(xy.y).arg( - qRound(xy.bri * 255.0f)).arg(transitiontime)); - } - } - // Remember last color. - lamp.color = xy; - // Next light id. - idx++; - } - timer.start(); - return 0; -} - -int LedDevicePhilipsHue::switchOff() { - timer.stop(); - // If light states have been saved before, ... - if (areStatesSaved()) { - // ... restore them. - restoreStates(); - } - return 0; -} - -void LedDevicePhilipsHue::put(QString route, QString content) { - QString url = getUrl(route); - // Perfrom request - QNetworkRequest request(url); - QNetworkReply* reply = manager->put(request, content.toLatin1()); - // Connect finished signal to quit slot of the loop. - QEventLoop loop; - loop.connect(reply, SIGNAL(finished()), SLOT(quit())); - // Go into the loop until the request is finished. - loop.exec(); - // Free space. - reply->deleteLater(); -} - -QByteArray LedDevicePhilipsHue::get(QString route) { - QString url = getUrl(route); - // Perfrom request - QNetworkRequest request(url); - QNetworkReply* reply = manager->get(request); - // Connect requestFinished signal to quit slot of the loop. - QEventLoop loop; - loop.connect(reply, SIGNAL(finished()), SLOT(quit())); - // Go into the loop until the request is finished. - loop.exec(); - // Read all data of the response. - QByteArray response = reply->readAll(); - // Free space. - reply->deleteLater(); - // Return response - return response; -} - -QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) { - return QString("lights/%1/state").arg(lightId); -} - -QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { - return QString("lights/%1").arg(lightId); -} - -QString LedDevicePhilipsHue::getUrl(QString route) { - return QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route); -} - -void LedDevicePhilipsHue::saveStates(unsigned int nLights) { - // Clear saved lamps. - lights.clear(); - // Use json parser to parse reponse. - Json::Reader reader; - Json::FastWriter writer; - // Read light ids if none have been supplied by the user. - if (lightIds.size() != nLights) { - lightIds.clear(); - // - QByteArray response = get("lights"); - Json::Value json; - if (!reader.parse(QString(response).toStdString(), json)) { - throw std::runtime_error(("No lights found at " + getUrl("lights")).toStdString()); - } - // Loop over all children. - for (Json::ValueIterator it = json.begin(); it != json.end() && lightIds.size() < nLights; it++) { - int lightId = atoi(it.key().asCString()); - lightIds.push_back(lightId); - std::cout << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): found light with id " << lightId - << "." << std::endl; - } - // Check if we found enough lights. - if (lightIds.size() != nLights) { - throw std::runtime_error(("Not enough lights found at " + getUrl("lights")).toStdString()); - } - } - // Iterate lights. - for (unsigned int i = 0; i < nLights; i++) { - // Read the response. - QByteArray response = get(getRoute(lightIds.at(i))); - // Parse JSON. - Json::Value json; - if (!reader.parse(QString(response).toStdString(), json)) { - // Error occured, break loop. - std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got invalid response from light " - << getUrl(getRoute(lightIds.at(i))).toStdString() << "." << std::endl; - break; - } - // Get state object values which are subject to change. - Json::Value state(Json::objectValue); - if (!json.isMember("state")) { - std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got no state for light from " - << getUrl(getRoute(lightIds.at(i))).toStdString() << std::endl; - break; - } - if (!json["state"].isMember("on")) { - std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got no valid state from light " - << getUrl(getRoute(lightIds.at(i))).toStdString() << std::endl; - break; - } - state["on"] = json["state"]["on"]; - if (json["state"]["on"] == true) { - state["xy"] = json["state"]["xy"]; - state["bri"] = json["state"]["bri"]; - } - // Determine the model id. - QString modelId = QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", ""); - QString originalState = QString(writer.write(state).c_str()).trimmed(); - // Save state object. - lights.push_back(PhilipsHueLight(lightIds.at(i), originalState, modelId)); - } -} - -void LedDevicePhilipsHue::switchOn(unsigned int nLights) { - for (PhilipsHueLight light : lights) { - put(getStateRoute(light.id), "{\"on\": true}"); - } -} - -void LedDevicePhilipsHue::restoreStates() { - for (PhilipsHueLight light : lights) { - put(getStateRoute(light.id), light.originalState); - } - // Clear saved light states. - lights.clear(); -} - -bool LedDevicePhilipsHue::areStatesSaved() { - return !lights.empty(); -} +// Local-Hyperion includes +#include "LedDevicePhilipsHue.h" + +// jsoncpp includes +#include + +// qt includes +#include +#include +#include + +#include +#include + +bool operator ==(CiColor p1, CiColor p2) { + return (p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri); +} + +bool operator !=(CiColor p1, CiColor p2) { + return !(p1 == p2); +} + +PhilipsHueLight::PhilipsHueLight(unsigned int id, QString originalState, QString modelId) : + id(id), originalState(originalState) { + // Hue system model ids (http://www.developers.meethue.com/documentation/supported-lights). + // Light strips, color iris, ... + const std::set GAMUT_A_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC010", "LLC011", "LLC012", + "LLC013", "LLC014", "LST001" }; + // Hue bulbs, spots, ... + const std::set GAMUT_B_MODEL_IDS = { "LCT001", "LCT002", "LCT003", "LCT007", "LLM001" }; + // Hue Lightstrip plus, go ... + const std::set GAMUT_C_MODEL_IDS = { "LLC020", "LST002" }; + // Find id in the sets and set the appropiate color space. + if (GAMUT_A_MODEL_IDS.find(modelId) != GAMUT_A_MODEL_IDS.end()) { + colorSpace.red = {0.703f, 0.296f}; + colorSpace.green = {0.2151f, 0.7106f}; + colorSpace.blue = {0.138f, 0.08f}; + } else if (GAMUT_B_MODEL_IDS.find(modelId) != GAMUT_B_MODEL_IDS.end()) { + colorSpace.red = {0.675f, 0.322f}; + colorSpace.green = {0.4091f, 0.518f}; + colorSpace.blue = {0.167f, 0.04f}; + } else if (GAMUT_C_MODEL_IDS.find(modelId) != GAMUT_B_MODEL_IDS.end()) { + colorSpace.red = {0.675f, 0.322f}; + colorSpace.green = {0.2151f, 0.7106f}; + colorSpace.blue = {0.167f, 0.04f}; + } else { + colorSpace.red = {1.0f, 0.0f}; + colorSpace.green = {0.0f, 1.0f}; + colorSpace.blue = {0.0f, 0.0f}; + } + // Initialize black color. + black = rgbToCiColor(0.0f, 0.0f, 0.0f); + // Initialize color with black + color = {black.x, black.y, black.bri}; +} + +float PhilipsHueLight::crossProduct(CiColor p1, CiColor p2) { + return p1.x * p2.y - p1.y * p2.x; +} + +bool PhilipsHueLight::isPointInLampsReach(CiColor p) { + CiColor v1 = { colorSpace.green.x - colorSpace.red.x, colorSpace.green.y - colorSpace.red.y }; + CiColor v2 = { colorSpace.blue.x - colorSpace.red.x, colorSpace.blue.y - colorSpace.red.y }; + CiColor q = { p.x - colorSpace.red.x, p.y - colorSpace.red.y }; + float s = crossProduct(q, v2) / crossProduct(v1, v2); + float t = crossProduct(v1, q) / crossProduct(v1, v2); + if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) { + return true; + } + return false; +} + +CiColor PhilipsHueLight::getClosestPointToPoint(CiColor a, CiColor b, CiColor p) { + CiColor AP = { p.x - a.x, p.y - a.y }; + CiColor AB = { b.x - a.x, b.y - a.y }; + float ab2 = AB.x * AB.x + AB.y * AB.y; + float ap_ab = AP.x * AB.x + AP.y * AB.y; + float t = ap_ab / ab2; + if (t < 0.0f) { + t = 0.0f; + } else if (t > 1.0f) { + t = 1.0f; + } + return {a.x + AB.x * t, a.y + AB.y * t}; +} + +float PhilipsHueLight::getDistanceBetweenTwoPoints(CiColor p1, CiColor p2) { + // Horizontal difference. + float dx = p1.x - p2.x; + // Vertical difference. + float dy = p1.y - p2.y; + // Absolute value. + return sqrt(dx * dx + dy * dy); +} + +CiColor PhilipsHueLight::rgbToCiColor(float red, float green, float blue) { + // Apply gamma correction. + float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + // Convert to XYZ space. + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; + // Convert to x,y space. + float cx = X / (X + Y + Z); + float cy = Y / (X + Y + Z); + if (std::isnan(cx)) { + cx = 0.0f; + } + if (std::isnan(cy)) { + cy = 0.0f; + } + // Brightness is simply Y in the XYZ space. + CiColor xy = { cx, cy, Y }; + // Check if the given XY value is within the color reach of our lamps. + if (!isPointInLampsReach(xy)) { + // It seems the color is out of reach let's find the closes color we can produce with our lamp and send this XY value out. + CiColor pAB = getClosestPointToPoint(colorSpace.red, colorSpace.green, xy); + CiColor pAC = getClosestPointToPoint(colorSpace.blue, colorSpace.red, xy); + CiColor pBC = getClosestPointToPoint(colorSpace.green, colorSpace.blue, xy); + // Get the distances per point and see which point is closer to our Point. + float dAB = getDistanceBetweenTwoPoints(xy, pAB); + float dAC = getDistanceBetweenTwoPoints(xy, pAC); + float dBC = getDistanceBetweenTwoPoints(xy, pBC); + float lowest = dAB; + CiColor closestPoint = pAB; + if (dAC < lowest) { + lowest = dAC; + closestPoint = pAC; + } + if (dBC < lowest) { + lowest = dBC; + closestPoint = pBC; + } + // Change the xy value to a value which is within the reach of the lamp. + xy.x = closestPoint.x; + xy.y = closestPoint.y; + } + return xy; +} + +LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, const std::string& username, bool switchOffOnBlack, + int transitiontime, std::vector lightIds) : + host(output.c_str()), username(username.c_str()), switchOffOnBlack(switchOffOnBlack), transitiontime( + transitiontime), lightIds(lightIds) { + manager = new QNetworkAccessManager(); + timer.setInterval(3000); + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates())); +} + +LedDevicePhilipsHue::~LedDevicePhilipsHue() { + delete manager; +} + +int LedDevicePhilipsHue::write(const std::vector & ledValues) { + // Save light states if not done before. + if (!areStatesSaved()) { + saveStates((unsigned int) ledValues.size()); + switchOn((unsigned int) ledValues.size()); + } + // If there are less states saved than colors given, then maybe something went wrong before. + if (lights.size() != ledValues.size()) { + restoreStates(); + return 0; + } + // Iterate through colors and set light states. + unsigned int idx = 0; + for (const ColorRgb& color : ledValues) { + // Get lamp. + PhilipsHueLight& lamp = lights.at(idx); + // Scale colors from [0, 255] to [0, 1] and convert to xy space. + CiColor xy = lamp.rgbToCiColor(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f); + // Write color if color has been changed. + if (xy != lamp.color) { + // From a color to black. + if (switchOffOnBlack && lamp.color != lamp.black && xy == lamp.black) { + put(getStateRoute(lamp.id), QString("{\"on\": false}")); + } + // From black to a color + else if (switchOffOnBlack && lamp.color == lamp.black && xy != lamp.black) { + // Send adjust color and brightness command in JSON format. + // We have to set the transition time each time. + // Send also command to switch the lamp on. + put(getStateRoute(lamp.id), + QString("{\"on\": true, \"xy\": [%1, %2], \"bri\": %3, \"transitiontime\": %4}").arg(xy.x).arg( + xy.y).arg(qRound(xy.bri * 255.0f)).arg(transitiontime)); + } + // Normal color change. + else { + // Send adjust color and brightness command in JSON format. + // We have to set the transition time each time. + put(getStateRoute(lamp.id), + QString("{\"xy\": [%1, %2], \"bri\": %3, \"transitiontime\": %4}").arg(xy.x).arg(xy.y).arg( + qRound(xy.bri * 255.0f)).arg(transitiontime)); + } + } + // Remember last color. + lamp.color = xy; + // Next light id. + idx++; + } + timer.start(); + return 0; +} + +int LedDevicePhilipsHue::switchOff() { + timer.stop(); + // If light states have been saved before, ... + if (areStatesSaved()) { + // ... restore them. + restoreStates(); + } + return 0; +} + +void LedDevicePhilipsHue::put(QString route, QString content) { + QString url = getUrl(route); + // Perfrom request + QNetworkRequest request(url); + QNetworkReply* reply = manager->put(request, content.toLatin1()); + // Connect finished signal to quit slot of the loop. + QEventLoop loop; + loop.connect(reply, SIGNAL(finished()), SLOT(quit())); + // Go into the loop until the request is finished. + loop.exec(); + // Free space. + reply->deleteLater(); +} + +QByteArray LedDevicePhilipsHue::get(QString route) { + QString url = getUrl(route); + // Perfrom request + QNetworkRequest request(url); + QNetworkReply* reply = manager->get(request); + // Connect requestFinished signal to quit slot of the loop. + QEventLoop loop; + loop.connect(reply, SIGNAL(finished()), SLOT(quit())); + // Go into the loop until the request is finished. + loop.exec(); + // Read all data of the response. + QByteArray response = reply->readAll(); + // Free space. + reply->deleteLater(); + // Return response + return response; +} + +QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) { + return QString("lights/%1/state").arg(lightId); +} + +QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { + return QString("lights/%1").arg(lightId); +} + +QString LedDevicePhilipsHue::getUrl(QString route) { + return QString("http://%1/api/%2/%3").arg(host).arg(username).arg(route); +} + +void LedDevicePhilipsHue::saveStates(unsigned int nLights) { + // Clear saved lamps. + lights.clear(); + // Use json parser to parse reponse. + Json::Reader reader; + Json::FastWriter writer; + // Read light ids if none have been supplied by the user. + if (lightIds.size() != nLights) { + lightIds.clear(); + // + QByteArray response = get("lights"); + Json::Value json; + if (!reader.parse(QString(response).toStdString(), json)) { + throw std::runtime_error(("No lights found at " + getUrl("lights")).toStdString()); + } + // Loop over all children. + for (Json::ValueIterator it = json.begin(); it != json.end() && lightIds.size() < nLights; it++) { + int lightId = atoi(it.key().asCString()); + lightIds.push_back(lightId); + std::cout << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): found light with id " << lightId + << "." << std::endl; + } + // Check if we found enough lights. + if (lightIds.size() != nLights) { + throw std::runtime_error(("Not enough lights found at " + getUrl("lights")).toStdString()); + } + } + // Iterate lights. + for (unsigned int i = 0; i < nLights; i++) { + // Read the response. + QByteArray response = get(getRoute(lightIds.at(i))); + // Parse JSON. + Json::Value json; + if (!reader.parse(QString(response).toStdString(), json)) { + // Error occured, break loop. + std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got invalid response from light " + << getUrl(getRoute(lightIds.at(i))).toStdString() << "." << std::endl; + break; + } + // Get state object values which are subject to change. + Json::Value state(Json::objectValue); + if (!json.isMember("state")) { + std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got no state for light from " + << getUrl(getRoute(lightIds.at(i))).toStdString() << std::endl; + break; + } + if (!json["state"].isMember("on")) { + std::cerr << "LedDevicePhilipsHue::saveStates(nLights=" << nLights << "): got no valid state from light " + << getUrl(getRoute(lightIds.at(i))).toStdString() << std::endl; + break; + } + state["on"] = json["state"]["on"]; + if (json["state"]["on"] == true) { + state["xy"] = json["state"]["xy"]; + state["bri"] = json["state"]["bri"]; + } + // Determine the model id. + QString modelId = QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", ""); + QString originalState = QString(writer.write(state).c_str()).trimmed(); + // Save state object. + lights.push_back(PhilipsHueLight(lightIds.at(i), originalState, modelId)); + } +} + +void LedDevicePhilipsHue::switchOn(unsigned int nLights) { + for (PhilipsHueLight light : lights) { + put(getStateRoute(light.id), "{\"on\": true}"); + } +} + +void LedDevicePhilipsHue::restoreStates() { + for (PhilipsHueLight light : lights) { + put(getStateRoute(light.id), light.originalState); + } + // Clear saved light states. + lights.clear(); +} + +bool LedDevicePhilipsHue::areStatesSaved() { + return !lights.empty(); +} diff --git a/test/TestSpi.cpp b/test/TestSpi.cpp index 1a3d5a92..4bddd727 100644 --- a/test/TestSpi.cpp +++ b/test/TestSpi.cpp @@ -1,161 +1,161 @@ - -// STL includes -#include -#include -#include -#include -#include -#include - -// Local includes -#include - -#include "../libsrc/leddevice/LedDeviceWs2801.h" - -void setColor(char* colorStr) -{ - ColorRgb color = ColorRgb::BLACK; - std::cout << "Switching all leds to: "; - if (strncmp("red", colorStr, 3) == 0) - { - std::cout << "red"; - color = ColorRgb::RED; - } - else if (strncmp("green", colorStr, 5) == 0) - { - std::cout << "green"; - color = ColorRgb::GREEN; - } - else if (strncmp("blue", colorStr, 5) == 0) - { - std::cout << "blue"; - color = ColorRgb::BLUE; - } - else if (strncmp("cyan", colorStr, 5) == 0) - { - std::cout << "cyan"; - } - else if (strncmp("gray", colorStr, 4) == 0) - { - std::cout << "gray"; - } - else if (strncmp("white", colorStr, 5) == 0) - { - std::cout << "white"; - color = ColorRgb::WHITE; - } - else if (strncmp("black", colorStr, 5) == 0) - { - std::cout << "black"; - color = ColorRgb::BLACK; - } - std::cout << std::endl; - - unsigned ledCnt = 50; - std::vector buff(ledCnt, color); - - LedDeviceWs2801 ledDevice("/dev/spidev0.0", 40000); - ledDevice.open(); - ledDevice.write(buff); -} - -bool _running = true; -void doCircle() -{ - ColorRgb color_1 = ColorRgb::RED; - ColorRgb color_2 = ColorRgb::YELLOW; - - unsigned ledCnt = 50; - std::vector data(ledCnt, ColorRgb::BLACK); - - LedDeviceWs2801 ledDevice("/dev/spidev0.0", 40000); - ledDevice.open(); - - timespec loopTime; - loopTime.tv_sec = 0; - loopTime.tv_nsec = 100000000; // 100 ms - - int curLed_1 = 0; - int nextLed_1 = 1; - - int curLed_2 = 49; - int nextLed_2 = 48; - - - while (_running) - { - data[curLed_1] = ColorRgb::BLACK; - data[curLed_2] = ColorRgb::BLACK; - - // Move the current and the next pointer - curLed_1 = nextLed_1; - curLed_2 = nextLed_2; - ++nextLed_1; - --nextLed_2; - if (nextLed_1 == int(ledCnt)) - { - nextLed_1 = 0; - } - if (nextLed_2 < 0) - { - nextLed_2 = 49; - } - - data[curLed_1] = color_1; - - data[curLed_2] = color_2; - - ledDevice.write(data); - - nanosleep(&loopTime, NULL); - } - - // Switch the current leds off - data[curLed_1] = ColorRgb::BLACK; - data[curLed_2] = ColorRgb::BLACK; - - ledDevice.write(data); -} - -#include - -void signal_handler(int signum) -{ - _running = false; -} - -int main(int argc, char** argv) -{ - if (sizeof(ColorRgb) != 3) - { - std::cout << "sizeof(ColorRgb) = " << sizeof(ColorRgb) << std::endl; - return -1; - } - - // Install signal handlers to stop loops - signal(SIGTERM, &signal_handler); - signal(SIGINT, &signal_handler); - - if (argc < 2) - { - std::cerr << "Missing argument" << std::endl; - return -1; - } - - if (strncmp("fixed", argv[1], 5) == 0) - { - setColor(argv[2]); - return 0; - } - else if (strncmp("circle", argv[1], 6) == 0) - { - doCircle(); - } - else - { - std::cerr << "Unknown option: " << argv[1] << std::endl; - } - - return 0; -} - + +// STL includes +#include +#include +#include +#include +#include +#include + +// Local includes +#include + +#include "../libsrc/leddevice/LedDeviceWs2801.h" + +void setColor(char* colorStr) +{ + ColorRgb color = ColorRgb::BLACK; + std::cout << "Switching all leds to: "; + if (strncmp("red", colorStr, 3) == 0) + { + std::cout << "red"; + color = ColorRgb::RED; + } + else if (strncmp("green", colorStr, 5) == 0) + { + std::cout << "green"; + color = ColorRgb::GREEN; + } + else if (strncmp("blue", colorStr, 5) == 0) + { + std::cout << "blue"; + color = ColorRgb::BLUE; + } + else if (strncmp("cyan", colorStr, 5) == 0) + { + std::cout << "cyan"; + } + else if (strncmp("gray", colorStr, 4) == 0) + { + std::cout << "gray"; + } + else if (strncmp("white", colorStr, 5) == 0) + { + std::cout << "white"; + color = ColorRgb::WHITE; + } + else if (strncmp("black", colorStr, 5) == 0) + { + std::cout << "black"; + color = ColorRgb::BLACK; + } + std::cout << std::endl; + + unsigned ledCnt = 50; + std::vector buff(ledCnt, color); + + LedDeviceWs2801 ledDevice("/dev/spidev0.0", 40000); + ledDevice.open(); + ledDevice.write(buff); +} + +bool _running = true; +void doCircle() +{ + ColorRgb color_1 = ColorRgb::RED; + ColorRgb color_2 = ColorRgb::YELLOW; + + unsigned ledCnt = 50; + std::vector data(ledCnt, ColorRgb::BLACK); + + LedDeviceWs2801 ledDevice("/dev/spidev0.0", 40000); + ledDevice.open(); + + timespec loopTime; + loopTime.tv_sec = 0; + loopTime.tv_nsec = 100000000; // 100 ms + + int curLed_1 = 0; + int nextLed_1 = 1; + + int curLed_2 = 49; + int nextLed_2 = 48; + + + while (_running) + { + data[curLed_1] = ColorRgb::BLACK; + data[curLed_2] = ColorRgb::BLACK; + + // Move the current and the next pointer + curLed_1 = nextLed_1; + curLed_2 = nextLed_2; + ++nextLed_1; + --nextLed_2; + if (nextLed_1 == int(ledCnt)) + { + nextLed_1 = 0; + } + if (nextLed_2 < 0) + { + nextLed_2 = 49; + } + + data[curLed_1] = color_1; + + data[curLed_2] = color_2; + + ledDevice.write(data); + + nanosleep(&loopTime, NULL); + } + + // Switch the current leds off + data[curLed_1] = ColorRgb::BLACK; + data[curLed_2] = ColorRgb::BLACK; + + ledDevice.write(data); +} + +#include + +void signal_handler(int signum) +{ + _running = false; +} + +int main(int argc, char** argv) +{ + if (sizeof(ColorRgb) != 3) + { + std::cout << "sizeof(ColorRgb) = " << sizeof(ColorRgb) << std::endl; + return -1; + } + + // Install signal handlers to stop loops + signal(SIGTERM, &signal_handler); + signal(SIGINT, &signal_handler); + + if (argc < 2) + { + std::cerr << "Missing argument" << std::endl; + return -1; + } + + if (strncmp("fixed", argv[1], 5) == 0) + { + setColor(argv[2]); + return 0; + } + else if (strncmp("circle", argv[1], 6) == 0) + { + doCircle(); + } + else + { + std::cerr << "Unknown option: " << argv[1] << std::endl; + } + + return 0; +} + From 8791f1da56b5acf9231fcb3cc2241a6147fa1e06 Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sat, 4 Jun 2016 15:35:31 +1000 Subject: [PATCH 11/12] Added multicast support to the udp listener "effect" --- effects/udp.json | 17 +++++++++-------- effects/udp.py | 24 ++++++++++++++++++++---- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/effects/udp.json b/effects/udp.json index 29df0971..0e3d6a49 100644 --- a/effects/udp.json +++ b/effects/udp.json @@ -1,8 +1,9 @@ -{ - "name" : "UDP listener", - "script" : "udp.py", - "args" : - { - "udpPort" : 2391 - } -} +{ + "name" : "UDP listener", + "script" : "udp.py", + "args" : + { + "ListenPort" : 2801, + "ListenIP" : "239.255.28.01" + } +} diff --git a/effects/udp.py b/effects/udp.py index f8317a20..6f39b1d1 100644 --- a/effects/udp.py +++ b/effects/udp.py @@ -3,17 +3,33 @@ import colorsys import socket import errno +import struct # Get the parameters -udpPort = int(hyperion.args.get('udpPort', 2812)) +ListenPort = int(hyperion.args.get('ListenPort', 2801)) +ListenIP = hyperion.args.get('ListenIP', "") +octets = ListenIP.split('.'); -UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) +UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM, socket.IPPROTO_UDP) UDPSock.setblocking(False) -listen_addr = ("",udpPort) -print "udp.py: bind socket port:",udpPort +listen_addr = (ListenIP,ListenPort) UDPSock.bind(listen_addr) +if ListenIP == "": + print "udp.py: Listening on *.*.*.*:"+str(ListenPort) +else: + print "udp.py: Listening on "+ListenIP+":"+str(ListenPort) + +if len(octets) == 4 and int(octets[0]) >= 224 and int(octets[0]) < 240: + print "ListenIP is a multicast address\n" + # Multicast handling + try: + mreq = struct.pack("4sl", socket.inet_aton(ListenIP), socket.INADDR_ANY) + UDPSock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + except socket.error: + print "ERROR enabling multicast\n" + hyperion.setColor(hyperion.ledCount * bytearray((int(0), int(0), int(0))) ) # Start the write data loop From ec9f6f610dd0dd97508de9144831a6730ab2d72f Mon Sep 17 00:00:00 2001 From: penfold42 Date: Sat, 4 Jun 2016 15:37:17 +1000 Subject: [PATCH 12/12] the default udp.json will listen to unicast on port 2391 (as it used to) the new udp-mcast.json will listen on multicast 239.255.28.01:2801 --- effects/udp-mcast.json | 9 +++++++++ effects/udp.json | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 effects/udp-mcast.json diff --git a/effects/udp-mcast.json b/effects/udp-mcast.json new file mode 100644 index 00000000..eb492ffe --- /dev/null +++ b/effects/udp-mcast.json @@ -0,0 +1,9 @@ +{ + "name" : "UDP multicast listener", + "script" : "udp.py", + "args" : + { + "ListenPort" : 2801, + "ListenIP" : "239.255.28.01" + } +} diff --git a/effects/udp.json b/effects/udp.json index 0e3d6a49..0fc6e6e6 100644 --- a/effects/udp.json +++ b/effects/udp.json @@ -3,7 +3,6 @@ "script" : "udp.py", "args" : { - "ListenPort" : 2801, - "ListenIP" : "239.255.28.01" + "ListenPort" : 2391 } }