From 207501a89400e9f1ee8a49539bc4cf0e556532da Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 26 May 2025 12:03:49 +0200 Subject: [PATCH] add operation controll messages to protocol, fix xomments in op and ctrl package --- rims_app/proto/ctrl.proto | 45 ++++---------- rims_app/proto/log.proto | 6 +- rims_app/proto/operation.proto | 90 ++++++++++++++++++++++----- rims_app/src/operation.cpp | 6 -- rims_app/src/operation.hpp | 5 +- rims_app/src/operation_pb_helpers.hpp | 48 ++++++++++++-- 6 files changed, 139 insertions(+), 61 deletions(-) diff --git a/rims_app/proto/ctrl.proto b/rims_app/proto/ctrl.proto index 0053ed62ab7..b17561da439 100644 --- a/rims_app/proto/ctrl.proto +++ b/rims_app/proto/ctrl.proto @@ -1,19 +1,19 @@ syntax = "proto3"; -import "nanopb.proto"; +//import "nanopb.proto"; package ctrl; -// channel configuration + +// Control Layer + +// Messages in this package control the behavior of physical power output. +// Each output is hardwired to a Zero-Cross Detector (ZCD) internally and can be controlled either by: +// * Grouping AC cycles (e.g., power level 23 means 23% of cycles are ON) – suitable for heating control. +// * Phase control (e.g., power level 23 means 23% of a single AC cycle is ON) – required for dimming capability. enum Error { NoError = 0; - -// WrongMode = 1; -// UnsupportedMode = 2; - WrongChannel = 3; - PowerLevelFailedToSet = 4; - UnknownError = 32; } @@ -25,7 +25,7 @@ enum PowerControlAlgorithm { // GroupModulation adjusts power by delivering full AC sine wave cycles in groups. // It delivers N full cycles ON followed by M cycles OFF (zero voltage). // Example: For 50% power, it might deliver 5 cycles on, then 5 cycles off. - // To modyfy number of grouped ac cycles, use GroupModulationConfigRequest + // To modify the number of grouped AC cycles, use GroupModulationConfigRequest. GroupModulation = 1; // PhaseModulation adjusts power by controlling the phase angle of each AC cycle. @@ -55,7 +55,7 @@ message PowerLevelRequest uint32 channel_id = 1; // Optional power level percentage (0.0–100.0). - // If omitted, the current level will be queried (read operation). + // If omitted, the current level will be returned (read operation). optional float level = 2; } @@ -78,8 +78,7 @@ message GroupModulationConfigRequest // The channel ID to which the request is directed. uint32 channel_id = 1; - // The maximum number of ON cycles for Group Modulation. If - // absent, it indicates a read request. + // The maximum number of ON cycles for Group Modulation. optional uint32 cycles_max = 2; } @@ -146,28 +145,10 @@ message PhaseModulationControlResponse optional Error error = 254; } -// message ActiveChannelsRequest -//{ -// // gets number of active channels -// } +/// TODO softstart for phase modulation -// message ActiveChannelResponse -//{ -// // info about ZCD, linked channels? -// } +// Only these messages are sent through the interface. -message LinkChannelRequest -{ - repeated uint32 channel_id = 1 [ (nanopb).max_count = 16 ]; -} - -message LinkChannelResponse -{ - uint32 channel_id = 1; - optional Error error = 254; -} - -// only those messages are send through interface message IngressMessage { uint32 request_id = 255; diff --git a/rims_app/proto/log.proto b/rims_app/proto/log.proto index a5760c4d9e3..cf4d4a6d9fa 100644 --- a/rims_app/proto/log.proto +++ b/rims_app/proto/log.proto @@ -3,6 +3,9 @@ syntax = "proto3"; import "nanopb.proto"; package log; +// TODO add error +// TODO change the message layout so that it has the same structure as other messages + enum LogLevel { DEBUG = 0; INFO = 1; @@ -30,7 +33,7 @@ message LogEntry string sourceFile = 2 [ (nanopb).max_size = 32 ]; // Source file where log was generated uint32 lineNumber = 3; // Line number in the source file string logLine = 4 [ (nanopb).max_size = 120 ]; // The formatted log message - fixed64 systick = 5; // optional systick + fixed64 systick = 5; // systick } // only those messages are send through wire at temperature endpoint @@ -49,6 +52,7 @@ message EgressMessage oneof data { LogLevelResponse logLevelResponse = 1; + // broadcast LogEntry logEntry = 16; } diff --git a/rims_app/proto/operation.proto b/rims_app/proto/operation.proto index d6c2f6b3d9a..2e0ec4fbe75 100644 --- a/rims_app/proto/operation.proto +++ b/rims_app/proto/operation.proto @@ -3,8 +3,17 @@ syntax = "proto3"; import "nanopb.proto"; package op; +// Operational / Automation Layer + +// The 'op' package contains messages used to automate physical output behavior. +// Unlike the 'ctrl' package, 'op' includes an automatic feedback loop to maintain the desired +// behavior over time. + enum Error { NoError = 0; + + UsedPowerChannelNotActive = 1; + UnknownError = 32; } @@ -17,28 +26,68 @@ enum Mode { ConstantTemperature = 3; // TemperatureSlope controls the rate of temperature change over time. - // Example: Heating at a rate of 1°C per minute by adjusting power output dynamically, holds - // temperature after that. + // Example: Heating at a rate of 1°C per minute by adjusting power output dynamically, + // then holding the temperature once the target slope is reached. // Useful for processes requiring a controlled temperature ramp. TemperatureSlope = 4; - // ConstantPower applies a fixed power output using the selected power control algorithm. - // No feedback from temperature sensors is used to adjust the output dynamically. - ManualPower = 5; - - // Maintains the temperature within a specified range (min to max). - // Turns heating on/off to stay inside the window. - // Useful for simple thermostatic control. - // TemperatureWindow = 9; - // Special mode for automatic tuning of PID parameters based on system response. // The system applies test stimuli and calculates optimal regulation parameters. // AutoTune = 7; } +message ActiveChannelsRequest +{ +} + +message ActiveChannelsResponse +{ + // List of currently active logical operation channels. + repeated uint32 channels = 1 [ (nanopb).max_count = 8 ]; + optional Error error = 254; +} + +message InitializeChannelRequest +{ + // Control channels used in this operation stream. Each stream can manage multiple outputs. + // Each control channel must be pre-configured before it can be assigned here. + // The newly initialized operation channel will start in 'Disabled' mode by default. + repeated uint32 ctrl_channels = 1 [ (nanopb).max_count = 16 ]; +} + +message InitializeChannelResponse +{ + // Logical channel ID assigned to this operation stream. + uint32 channel = 1; + + // Control channels associated with this logical operation channel. + repeated uint32 ctrl_channel_ids = 2 [ (nanopb).max_count = 16 ]; + + optional Error error = 254; +} + +message DeinitializeChannelRequest +{ + // The logical operation channel to be removed. + uint32 channel_id = 1; +} + +message DeinitializeChannelResponse +{ + // The ID of the operation channel that was deinitialized. + uint32 channel_id = 1; + + // The list of control channels that were released (for confirmation/logging). + repeated uint32 ctrl_channel_ids = 2 [ (nanopb).max_count = 16 ]; + + // Optional error information. + optional Error error = 254; +} + message ModeRequest { - uint32 channel_id = 1; + // The ID of the operation channel + uint32 channel_id = 1; // one of optional Mode mode = 2; } @@ -49,13 +98,22 @@ message ModeResponse optional Error error = 254; } +// GENERAL OPERATION FLOW: +// 1. Initialize a logical operation channel by assigning one or more control channels. +// 2. Set the desired runtime mode (e.g., ConstantTemperature, TemperatureSlope). +// 3. The system will manage outputs automatically based on feedback and configuration. + // only those messages are send through interface message IngressMessage { uint32 request_id = 255; oneof data { - ModeRequest mode_request = 2; + ActiveChannelsRequest active_channels_request = 1; + InitializeChannelRequest initialize_channel_request = 2; + DeinitializeChannelRequest deinitialize_channel_request = 3; + + ModeRequest mode_request = 4; } }; @@ -64,6 +122,10 @@ message EgressMessage optional uint32 request_id = 255; // not set for broadcast oneof data { - ModeResponse mode_response = 2; + ActiveChannelsResponse active_channels_response = 1; + InitializeChannelResponse initialize_channel_response = 2; + DeinitializeChannelResponse deinitialize_channel_response = 3; + + ModeResponse mode_response = 4; } }; diff --git a/rims_app/src/operation.cpp b/rims_app/src/operation.cpp index 1416808469a..63e24d2768d 100644 --- a/rims_app/src/operation.cpp +++ b/rims_app/src/operation.cpp @@ -95,9 +95,6 @@ void OperationOrchestrator::setMode(uint8_t ch, op_Mode mode) { case op_Mode_Disabled: _mode.at(ch).emplace(); break; - case op_Mode_ManualPower: - _mode.at(ch).emplace(); - break; case op_Mode_ConstantTemperature: _mode.at(ch).emplace(_pc, _pid); break; @@ -111,9 +108,6 @@ void OperationOrchestrator::setMode(uint8_t ch, op_Mode mode) { op_Mode OperationOrchestrator::mode(uint8_t ch) const noexcept { /// TODO check channel - if (std::holds_alternative(_mode.at(ch))) { - return op_Mode_ManualPower; - } if (std::holds_alternative(_mode.at(ch))) { return op_Mode_ConstantTemperature; } diff --git a/rims_app/src/operation.hpp b/rims_app/src/operation.hpp index 3d9c8fc0702..95a2a74fa01 100644 --- a/rims_app/src/operation.hpp +++ b/rims_app/src/operation.hpp @@ -78,8 +78,6 @@ class ConstantTemperatureMode : public ModeStrategy { class TemperatureSlopeMode : public ModeStrategy {}; -class ManualPowerMode : public ModeStrategy {}; - /* * global operation of the device, it listens to temperature changes, and listens to user requests */ @@ -87,8 +85,7 @@ class OperationOrchestrator { using Mode = std::variant< // DisabledMode, ConstantTemperatureMode, - TemperatureSlopeMode, - ManualPowerMode>; + TemperatureSlopeMode>; public: OperationOrchestrator(); diff --git a/rims_app/src/operation_pb_helpers.hpp b/rims_app/src/operation_pb_helpers.hpp index 14945588f9b..11abd63663c 100644 --- a/rims_app/src/operation_pb_helpers.hpp +++ b/rims_app/src/operation_pb_helpers.hpp @@ -9,18 +9,58 @@ namespace rims { template <> constexpr int tag() { return op_IngressMessage_mode_request_tag; } +template <> constexpr int tag() { + return op_IngressMessage_initialize_channel_request_tag; +} +template <> constexpr int tag() { + return op_IngressMessage_active_channels_request_tag; +} +template <> constexpr int tag() { + return op_IngressMessage_deinitialize_channel_request_tag; +} template <> constexpr int tag() { return op_EgressMessage_mode_response_tag; } +template <> constexpr int tag() { + return op_EgressMessage_initialize_channel_response_tag; +} +template <> constexpr int tag() { + return op_EgressMessage_active_channels_response_tag; +} +template <> constexpr int tag() { + return op_EgressMessage_deinitialize_channel_response_tag; +} -// Ingress and egress message categorization for GPIO -template constexpr bool is_op_ingress_msg_v = is_any_of_v; -template constexpr bool is_op_egress_msg_v = is_any_of_v; +// Ingress and egress message categorization for operation package +template +constexpr bool is_op_ingress_msg_v = is_any_of_v< + MsgT, // + op_ModeRequest, + op_InitializeChannelRequest, + op_ActiveChannelsRequest, + op_DeinitializeChannelRequest>; -// Response selector specialization +template +constexpr bool is_op_egress_msg_v = is_any_of_v< + MsgT, // + op_ModeResponse, + op_InitializeChannelResponse, + op_ActiveChannelsResponse, + op_DeinitializeChannelResponse>; + +// ResponseSelector specializations template <> struct ResponseSelector { using response_t = op_ModeResponse; }; +template <> struct ResponseSelector { + using response_t = op_InitializeChannelResponse; +}; +template <> struct ResponseSelector { + using response_t = op_ActiveChannelsResponse; +}; +template <> struct ResponseSelector { + using response_t = op_DeinitializeChannelResponse; +}; } // namespace rims