From 9d138b0045e19d416aa36588a870c1b50f26a427 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Tue, 5 Aug 2025 08:10:55 +0200 Subject: [PATCH] skeleton --- CMakeLists.txt | 45 ++++++++- proto/ACPower.proto | 88 ----------------- proto/ACPowerInternal.proto | 49 ---------- proto/energy.proto | 40 -------- proto/heating.proto | 98 ------------------- proto/message.proto | 30 ------ services/CMakeLists.txt | 1 + .../floorheat_hub}/CMakeLists.txt | 9 +- .../floorheat_hub}/heater.cpp | 0 .../floorheat_hub}/heater.hpp | 0 .../floorheat_hub}/main.cpp | 7 +- .../floorheat_hub}/mqtt_client.cpp | 20 ++-- .../floorheat_hub}/mqtt_client.hpp | 0 .../floorheat_hub}/relay.cpp | 0 .../floorheat_hub}/relay.hpp | 0 .../floorheat_hub}/timer.cpp | 0 .../floorheat_hub}/timer.hpp | 0 proto/logging.proto => tests/CMakeLists.txt | 0 18 files changed, 60 insertions(+), 327 deletions(-) delete mode 100644 proto/ACPower.proto delete mode 100644 proto/ACPowerInternal.proto delete mode 100644 proto/energy.proto delete mode 100644 proto/heating.proto delete mode 100644 proto/message.proto create mode 100644 services/CMakeLists.txt rename {floorheat_hub => services/floorheat_hub}/CMakeLists.txt (68%) rename {floorheat_hub => services/floorheat_hub}/heater.cpp (100%) rename {floorheat_hub => services/floorheat_hub}/heater.hpp (100%) rename {floorheat_hub => services/floorheat_hub}/main.cpp (95%) rename {floorheat_hub => services/floorheat_hub}/mqtt_client.cpp (84%) rename {floorheat_hub => services/floorheat_hub}/mqtt_client.hpp (100%) rename {floorheat_hub => services/floorheat_hub}/relay.cpp (100%) rename {floorheat_hub => services/floorheat_hub}/relay.hpp (100%) rename {floorheat_hub => services/floorheat_hub}/timer.cpp (100%) rename {floorheat_hub => services/floorheat_hub}/timer.hpp (100%) rename proto/logging.proto => tests/CMakeLists.txt (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9531e2..762d6d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,50 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) set(BOOST_ROOT /usr/local) find_package(Boost REQUIRED COMPONENTS system json) - include(FetchContent) +# Create a Boost-style interface target for a header-only library +function(create_boost_header_only_target target_name) + string(TOLOWER ${target_name} target_key) + set(interface_target ${target_key}_interface) + set(header_subdir ${target_key}) + + if(NOT TARGET Boost::${target_name}) + message(STATUS "Creating Boost::${target_name} as header-only interface target") + + add_library(${interface_target} INTERFACE) + + # Add include dir if not explicitly set + if(DEFINED Boost_INCLUDE_DIRS) + target_include_directories(${interface_target} INTERFACE ${Boost_INCLUDE_DIRS}) + else() + message(WARNING "Boost_INCLUDE_DIRS not defined when creating Boost::${target_name}") + endif() + + # Optional preprocessor tweaks per library + if(${target_name} STREQUAL "asio") + target_compile_definitions(${interface_target} INTERFACE BOOST_ASIO_NO_DEPRECATED) + endif() + + # Create alias for consistency with imported targets + add_library(Boost::${target_name} ALIAS ${interface_target}) + else() + message(STATUS "Boost::${target_name} already exists") + endif() +endfunction() + +create_boost_header_only_target(asio) +create_boost_header_only_target(assert) +create_boost_header_only_target(core) +create_boost_header_only_target(endian) +create_boost_header_only_target(fusion) +create_boost_header_only_target(optional) +create_boost_header_only_target(random) +create_boost_header_only_target(range) +create_boost_header_only_target(smart_ptr) +create_boost_header_only_target(spirit) +create_boost_header_only_target(type_traits) + # spdlog FetchContent_Declare( spdlog @@ -45,4 +86,4 @@ set(MQTT_BUILD_EXAMPLES OFF) set(async-mqtt5_INCLUDES_WITH_SYSTEM OFF) FetchContent_MakeAvailable(asyncmqtt5) -add_subdirectory(floorheat_hub) +add_subdirectory(services) diff --git a/proto/ACPower.proto b/proto/ACPower.proto deleted file mode 100644 index 01ef34a..0000000 --- a/proto/ACPower.proto +++ /dev/null @@ -1,88 +0,0 @@ -syntax = "proto3"; - -import "ACPowerInternal.proto"; - -package acpowercontrol; - -message Error{ - ///todo change to enum - string details = 1; -} - -message ResetWatchdodRequest { - uint32 channel = 1; -} - -message ResetWatchdodResponse { - uint32 channel = 1; - optional Error error = 2; -} - -// Main control message that can include one of the above actions -message SetChannelRequest { - uint32 channel = 1; - oneof power { - acpowercontrol.internal.ACPowerSwitchBinary switch = 2; - acpowercontrol.internal.ACPowerToggle toggle = 3; - acpowercontrol.internal.ACPowerModulation modulation = 4; - acpowercontrol.internal.ACPowerSwipe swipe = 5; - } -} - -message SetChannelResponse { - uint32 channel = 1; - optional Error error = 2; -} - -message GetChannelRequest { - uint32 channel = 1; -} - -message GetChannelResponse{ - uint32 channel = 1; - oneof resp{ - uint32 current_power = 2; - Error error = 3; - } -} - -message SetChannelConfigRequest { - uint32 channel = 1; - acpowercontrol.internal.ChannelConfig config = 2; -} - -message SetChannelConfigResponse { - uint32 channel = 1; - optional Error error = 2; -} - -message GetChannelConfigRequest { - uint32 channel = 1; -} - -message GetChannelConfigResponse { - uint32 channel = 1; - oneof resp { - acpowercontrol.internal.ChannelConfig config = 2; - Error error = 3; - } -} - -message ACPower{ - oneof data{ - ResetWatchdodRequest reset_wtchdod_request= 1; - ResetWatchdodResponse reset_wtchdod_response= 2; - - SetChannelRequest set_channel_request = 3; - SetChannelResponse set_channel_response = 4; - - GetChannelRequest get_channel_request = 5; - GetChannelResponse get_channel_response = 6; - - SetChannelConfigRequest set_channel_config_request = 7; - SetChannelConfigResponse set_channel_config_response= 8; - - GetChannelConfigRequest get_channel_config_request = 9; - GetChannelConfigResponse get_channel_config_response = 10; - } -} diff --git a/proto/ACPowerInternal.proto b/proto/ACPowerInternal.proto deleted file mode 100644 index e0c118e..0000000 --- a/proto/ACPowerInternal.proto +++ /dev/null @@ -1,49 +0,0 @@ -syntax = "proto3"; - -package acpowercontrol.internal; - -// channel configuration -message ChannelConfig{ - uint32 faze_nr = 1; - uint32 default_watchdog_s = 2; -} - -// Message to represent basic on/off control -message ACPowerSwitchBinary { - bool on = 1; -} - -// Message to represent power toggle -message ACPowerToggle { -} - -// Message to represent AC phase control (e.g., 30% of sine wave) -message ACPowerModulation { - // Phase percentage in i/10 of percent (value 123 rtanslates to 12.3%, typically 100.0) - uint32 phase_percentage = 1; - - // number of sinusoids taken to control (2 is a full sinus cycle, default 1) - optional uint32 phaseGroup = 3; // if not present, assumed 1 -} - -// Message to represent a soft start -message ACPowerSwipe { - enum Function{ - Linear = 0; - Quadratic = 1; // Power increases exponentially over time, starting slow and speeding up. - Logarithmic = 2; // Power increases logarithmically, starting fast and slowing down as it approaches the end. - EaseInOut = 3; // This function starts and ends smoothly with a rapid change in the middle. - } - // Start percentage in i/10 of percent (value 123 rtanslates to 12.3%, typically 100.0) - optional uint32 start_percentage = 1; - - // End percentage in i/10 of percent (value 123 rtanslates to 12.3%, typically 100.0) - uint32 end_percentage = 3; - - // Duration in milliseconds - uint32 duration_ms = 5; - - // function used - Function function=6; -} - diff --git a/proto/energy.proto b/proto/energy.proto deleted file mode 100644 index 3aeb767..0000000 --- a/proto/energy.proto +++ /dev/null @@ -1,40 +0,0 @@ -syntax = "proto3"; - -message FixedPoint { - int32 integer_part = 1; - uint32 fractional_part = 2; -} - -message x { - uint32 ch = 1; - bool state = 2; -} - - message FloatingPoint{ - float val = 2; -} -message SingleFazeData { - float Voltage = 1; - float Current = 2; - float ActivePower = 3; - float ApparentPower = 4; - float PowerFactor = 5; - float ReActivePower = 6; -} - -message EnergymeterSensorData { - int32 sensor_id = 1; - SingleFazeData L1 = 2; - SingleFazeData L2 = 3; - SingleFazeData L3 = 4; - float TotalActivePower = 5; - float TotalPowerFactor = 6; - float TotalApparentePower = 7; - float TotalReActivePower = 8; - float GridFrequency = 9; -} - -message can_frame { - uint32 left = 1; - bytes data = 2; -} diff --git a/proto/heating.proto b/proto/heating.proto deleted file mode 100644 index 362880e..0000000 --- a/proto/heating.proto +++ /dev/null @@ -1,98 +0,0 @@ -syntax = "proto3"; - - -// internal -// channel configuration -message ChannelConfig{ - uint32 faze = 1; - uint32 default_watchdog_s = 2; - -} - -// Message to represent basic on/off control -message ACPowerBinary { - bool on = 1; -} - -// Message to represent power toggle -message ACPowerToggle {} - -// Message to represent AC phase control (e.g., 30% of sine wave) -message ACPowerProcentage { - // Phase percentage from 0 to 100 - uint32 phase_percentage = 1; - uint32 phase_percentageFraction = 2; - - // number of sinusoids taken to control (2 is a full sinus cycle, default 1) - optional uint32 phaseGroup = 3; // if not present, assumed 1 -} - -// Message to represent a soft start -message ACPowerSwipe { - enum Function{ - Linear = 0; - Quadratic = 1; // Power increases exponentially over time, starting slow and speeding up. - Logarithmic = 2; // Power increases logarithmically, starting fast and slowing down as it approaches the end. - EaseInOut = 3; // This function starts and ends smoothly with a rapid change in the middle. - } - // Start percentage (typically 0) - optional uint32 start_percentage = 1; - // Start percentage fraction (as 1/100 part of percentage) (typically 0) - uint32 start_percentage_fraction = 2; - - // End percentage (typically 100) - uint32 end_percentage = 3; - // End percentage fraction (as 1/100 part of percentage) (typically 0) - uint32 end_percentage_fraction = 4; - - // Duration in milliseconds - uint32 duration_ms = 5; - - // function used - Function function=6; -} - -message ACPowerControl{ - oneof control_type { - ACPowerBinary binary = 5; - ACPowerProcentage procentage = 6; - ACPowerSwipe swipe = 7; - ACPowerToggle toggle = 8; // revert previous action - } -} - -message ACPowerResetWatchdod{ - repeated uint32 channel = 1; -} - -// Main control message that can include one of the above actions -message SetACPowerChannelControl { - uint32 channel = 1; - - ACPowerControl power = 2; - - // seconds after which output will be turned off (in case of lack of control messages) 0 -> uses default - uint32 watchdog_s = 3; -} - -message GetAcPowerChannelControl { - uint32 channel = 1; -} - -message SetACPowerChannelConfig { - uint32 channel = 1; - ChannelConfig config = 2; -} - -message GetACPowerChannelConfig { - uint32 channel = 1; -} - -enum HeatingMessagesId{ - ACPowerResetWatchdodID = 0; - - SetACPowerChannelControlID = 1; - GetAcPowerChannelControlID = 2; - SetACPowerChannelConfigID = 3; - GetACPowerChannelConfigID = 4; -} diff --git a/proto/message.proto b/proto/message.proto deleted file mode 100644 index 49bb405..0000000 --- a/proto/message.proto +++ /dev/null @@ -1,30 +0,0 @@ -syntax = "proto3"; - -// single part of message -message Frame{ - // frames left, 0 for End Of Stream - uint32 parts = 1; // parts left - - // encapsulated message - bytes data = 2; - - // type of message being encapsulated,only one of parts need to contain this information - uint32 type = 3; -} - -message PrintfArgument { - oneof arg_type { - int32 int_val = 1; - float float_val = 2; - double double_val = 3; - string string_val = 4; - bool bool_val = 5; - bytes bytes_val = 6; - } -} - -message PrintfMessage { - string format_string = 1; - uint32 severity = 2; - repeated PrintfArgument arguments = 3; -} diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt new file mode 100644 index 0000000..85d90b5 --- /dev/null +++ b/services/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(floorheat_hub) diff --git a/floorheat_hub/CMakeLists.txt b/services/floorheat_hub/CMakeLists.txt similarity index 68% rename from floorheat_hub/CMakeLists.txt rename to services/floorheat_hub/CMakeLists.txt index c8df479..a6a3924 100644 --- a/floorheat_hub/CMakeLists.txt +++ b/services/floorheat_hub/CMakeLists.txt @@ -1,10 +1,3 @@ -# protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS -# ../proto/message.proto -# ../proto/heating.proto -# ../proto/ACPower.proto -# ../proto/ACPowerInternal.proto -# ) - add_executable(ranczo-io_floorheating main.cpp heater.cpp @@ -22,7 +15,7 @@ target_include_directories(ranczo-io_floorheating target_link_libraries(ranczo-io_floorheating PUBLIC # ${Protobuf_LIBRARIES} - Async::MQTT5 + Boost::mqtt5 Boost::system Boost::json spdlog::spdlog diff --git a/floorheat_hub/heater.cpp b/services/floorheat_hub/heater.cpp similarity index 100% rename from floorheat_hub/heater.cpp rename to services/floorheat_hub/heater.cpp diff --git a/floorheat_hub/heater.hpp b/services/floorheat_hub/heater.hpp similarity index 100% rename from floorheat_hub/heater.hpp rename to services/floorheat_hub/heater.hpp diff --git a/floorheat_hub/main.cpp b/services/floorheat_hub/main.cpp similarity index 95% rename from floorheat_hub/main.cpp rename to services/floorheat_hub/main.cpp index 52d3788..60986e4 100644 --- a/floorheat_hub/main.cpp +++ b/services/floorheat_hub/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -131,7 +132,7 @@ boost::asio::awaitable spawn(ranczo::AsyncMqttClient & c1){ int main() { spdlog::set_level(spdlog::level::trace); std::vector< std::unique_ptr< ranczo::IHeater > > _heaters; - boost::asio::io_service io_service; + boost::asio::io_context io_service; /// Strand powoduje że zadania do niego przypisane zostają wykonane sekwencyjnie, /// get_executor pobrany z io_service nie daje takiej możliwości i wtedy można wykonywać zadania równloegle @@ -153,7 +154,9 @@ int main() { // xes.emplace_back(std::allocate_shared< X >(alloc, io_executor, 2, "home/utilityRoom/floor/temperature")); // xes.emplace_back(std::allocate_shared< X >(alloc, io_executor, 3, "home/wardrobe/floor/temperature")); - boost::asio::io_service::work work(io_service); + boost::asio::executor_work_guard work_guard = + boost::asio::make_work_guard(io_service); + io_service.run(); return 0; diff --git a/floorheat_hub/mqtt_client.cpp b/services/floorheat_hub/mqtt_client.cpp similarity index 84% rename from floorheat_hub/mqtt_client.cpp rename to services/floorheat_hub/mqtt_client.cpp index e9e6a7c..f33fb0e 100644 --- a/floorheat_hub/mqtt_client.cpp +++ b/services/floorheat_hub/mqtt_client.cpp @@ -6,12 +6,12 @@ #include #include -#include +#include #include namespace ranczo { constexpr auto use_nothrow_awaitable = boost::asio::as_tuple(boost::asio::use_awaitable); -using client_type = async_mqtt5::mqtt_client< boost::asio::ip::tcp::socket >; +using client_type = boost::mqtt5::mqtt_client< boost::asio::ip::tcp::socket >; struct AsyncMqttClient::AsyncMqttClientImpl { const boost::asio::any_io_executor & _executor; @@ -34,17 +34,17 @@ struct AsyncMqttClient::AsyncMqttClientImpl { boost::asio::awaitable< bool > subscribe(std::string_view topic) { // Configure the request to subscribe to a Topic. - async_mqtt5::subscribe_topic sub_topic = async_mqtt5::subscribe_topic{topic.data(), - async_mqtt5::subscribe_options{ - async_mqtt5::qos_e::exactly_once, // All messages will arrive at QoS 2. - async_mqtt5::no_local_e::no, // Forward message from Clients with same ID. - async_mqtt5::retain_as_published_e::retain, // Keep the original RETAIN flag. - async_mqtt5::retain_handling_e::send // Send retained messages when the subscription is established. + boost::mqtt5::subscribe_topic sub_topic = boost::mqtt5::subscribe_topic{topic.data(), + boost::mqtt5::subscribe_options{ + boost::mqtt5::qos_e::exactly_once, // All messages will arrive at QoS 2. + boost::mqtt5::no_local_e::no, // Forward message from Clients with same ID. + boost::mqtt5::retain_as_published_e::retain, // Keep the original RETAIN flag. + boost::mqtt5::retain_handling_e::send // Send retained messages when the subscription is established. }}; // Subscribe to a single Topic. auto && [ec, sub_codes, sub_props] = - co_await _mqtt_client.async_subscribe(sub_topic, async_mqtt5::subscribe_props{}, use_nothrow_awaitable); + co_await _mqtt_client.async_subscribe(sub_topic, boost::mqtt5::subscribe_props{}, use_nothrow_awaitable); // Note: you can subscribe to multiple Topics in one mqtt_client::async_subscribe call. // An error can occur as a result of: @@ -65,7 +65,7 @@ struct AsyncMqttClient::AsyncMqttClientImpl { // Receive an Appplication Message from the subscribed Topic(s). auto && [ec, topic, payload, publish_props] = co_await _mqtt_client.async_receive(use_nothrow_awaitable); - if(ec == async_mqtt5::client::error::session_expired) { + if(ec == boost::mqtt5::client::error::session_expired) { /// TODO connect first, then subscribe to all topics // The Client has reconnected, and the prior session has expired. // As a result, any previous subscriptions have been lost and must be reinstated. diff --git a/floorheat_hub/mqtt_client.hpp b/services/floorheat_hub/mqtt_client.hpp similarity index 100% rename from floorheat_hub/mqtt_client.hpp rename to services/floorheat_hub/mqtt_client.hpp diff --git a/floorheat_hub/relay.cpp b/services/floorheat_hub/relay.cpp similarity index 100% rename from floorheat_hub/relay.cpp rename to services/floorheat_hub/relay.cpp diff --git a/floorheat_hub/relay.hpp b/services/floorheat_hub/relay.hpp similarity index 100% rename from floorheat_hub/relay.hpp rename to services/floorheat_hub/relay.hpp diff --git a/floorheat_hub/timer.cpp b/services/floorheat_hub/timer.cpp similarity index 100% rename from floorheat_hub/timer.cpp rename to services/floorheat_hub/timer.cpp diff --git a/floorheat_hub/timer.hpp b/services/floorheat_hub/timer.hpp similarity index 100% rename from floorheat_hub/timer.hpp rename to services/floorheat_hub/timer.hpp diff --git a/proto/logging.proto b/tests/CMakeLists.txt similarity index 100% rename from proto/logging.proto rename to tests/CMakeLists.txt