From 35576c6c27c9e0edde37bd41447f4d2478b6569d Mon Sep 17 00:00:00 2001 From: Eric Courtois Date: Sun, 24 Jul 2022 19:23:23 +0200 Subject: [PATCH] uartpb: add a unique id for each Protobuf message definition Using a unique id for each Protobuf message allows libraries/applications defining these messages to register a callback function associated with each id. This way, uartpb is not dependent of applications anymore, which had to declare a Protobuf message gathering all known Protobuf messages. This unique id will be sent first, followed by encoded Protobuf message. The unique id can also be sent alone if the message does not required any content (like a reset or trigger message). This commit also updates all modules and applications affected by this refactoring. Fixes #67 Signed-off-by: Eric Courtois --- .../app_test/PB_GameInputMessage.proto | 15 -- .../app_test/PB_GameOutputMessage.proto | 16 --- applications/app_test/PB_Score.proto | 5 + applications/app_test/app_samples.cpp | 20 ++- applications/app_test/app_uartpb.cpp | 50 ------- applications/app_test/include/app.hpp | 1 - applications/app_test/include/app_samples.hpp | 3 +- applications/app_test/include/app_uartpb.hpp | 14 -- .../app_test/include/uartpb_config.hpp | 59 -------- .../cup2022/PB_GameInputMessage.proto | 15 -- .../cup2022/PB_GameOutputMessage.proto | 16 --- applications/cup2022/app_samples.cpp | 20 ++- applications/cup2022/app_uartpb.cpp | 50 ------- applications/cup2022/include/app.hpp | 1 - applications/cup2022/include/app_samples.hpp | 3 +- applications/cup2022/include/app_uartpb.hpp | 14 -- .../cup2022/include/uartpb_config.hpp | 59 -------- examples/uartpb/PB_ExampleInputMessage.proto | 13 -- examples/uartpb/PB_ExampleOutputMessage.proto | 16 --- examples/uartpb/include/uartpb_config.hpp | 11 -- examples/uartpb/main.cpp | 122 +++++++--------- examples/uartpb/uartpb-client.py | 136 +++++++++++------- lib/wizard/Wizard.cpp | 22 +-- lib/wizard/include/wizard/Wizard.hpp | 3 +- platforms/pegasus/platform.cpp | 32 ++++- platforms/pf_test/platform.cpp | 32 ++++- sys/shell_menu/Menu.cpp | 11 +- .../include/shell_menu/shell_menu.hpp | 7 +- sys/shell_menu/shell_menu.cpp | 8 +- sys/uartpb/UartProtobuf.cpp | 75 +++++++--- sys/uartpb/include/uartpb/UartProtobuf.hpp | 29 +++- sys/uartpb/include/uartpb/uartpb.hpp | 4 + 32 files changed, 338 insertions(+), 544 deletions(-) delete mode 100644 applications/app_test/PB_GameInputMessage.proto delete mode 100644 applications/app_test/PB_GameOutputMessage.proto create mode 100644 applications/app_test/PB_Score.proto delete mode 100644 applications/app_test/app_uartpb.cpp delete mode 100644 applications/app_test/include/app_uartpb.hpp delete mode 100644 applications/app_test/include/uartpb_config.hpp delete mode 100644 applications/cup2022/PB_GameInputMessage.proto delete mode 100644 applications/cup2022/PB_GameOutputMessage.proto delete mode 100644 applications/cup2022/app_uartpb.cpp delete mode 100644 applications/cup2022/include/app_uartpb.hpp delete mode 100644 applications/cup2022/include/uartpb_config.hpp delete mode 100644 examples/uartpb/PB_ExampleInputMessage.proto delete mode 100644 examples/uartpb/PB_ExampleOutputMessage.proto delete mode 100644 examples/uartpb/include/uartpb_config.hpp diff --git a/applications/app_test/PB_GameInputMessage.proto b/applications/app_test/PB_GameInputMessage.proto deleted file mode 100644 index cac4d02aad..0000000000 --- a/applications/app_test/PB_GameInputMessage.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -import "PB_Command.proto"; -import "PB_Wizard.proto"; -import "PB_Samples.proto"; - -message PB_GameInputMessage { - oneof type { - PB_Command command = 1; - bool copilot_connected = 2; - bool copilot_disconnected = 3; - PB_Wizard wizard = 4; - PB_Samples samples = 5; - } -} diff --git a/applications/app_test/PB_GameOutputMessage.proto b/applications/app_test/PB_GameOutputMessage.proto deleted file mode 100644 index 5b5f829b6a..0000000000 --- a/applications/app_test/PB_GameOutputMessage.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -import "PB_Menu.proto"; -import "PB_State.proto"; -import "PB_Wizard.proto"; - -message PB_GameOutputMessage { - oneof type { - bool reset = 1; - PB_Menu menu = 2; - PB_State state = 3; - PB_Wizard wizard = 4; - bool req_samples = 5; - uint32 score = 6; - } -} diff --git a/applications/app_test/PB_Score.proto b/applications/app_test/PB_Score.proto new file mode 100644 index 0000000000..74a21cc607 --- /dev/null +++ b/applications/app_test/PB_Score.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +message PB_Score { + int32 value = 1; +} diff --git a/applications/app_test/app_samples.cpp b/applications/app_test/app_samples.cpp index 0241021739..9d82037cc9 100644 --- a/applications/app_test/app_samples.cpp +++ b/applications/app_test/app_samples.cpp @@ -1,7 +1,6 @@ // Firmware includes #include "platform.hpp" #include "trigonometry.h" -#include "uartpb_config.hpp" #include "app_camp.hpp" #include "app_samples.hpp" @@ -16,9 +15,11 @@ namespace cogip { namespace app { -static PB_OutputMessage _output_message; static DetectedSamples _detected_samples; +constexpr cogip::uartpb::uuid_t sample_request_uuid = 3781855956; +constexpr cogip::uartpb::uuid_t sample_response_uuid = 1538397045; + static std::map _samples; typedef struct { @@ -57,6 +58,8 @@ void app_samples_init() return; } + pf_get_uartpb()->register_message_handler(sample_response_uuid, app_samples_process); + event_queue_init_detached(&_sample_queue); _sample_event.super.list_node.next = nullptr; @@ -170,9 +173,7 @@ const DetectedSamples & app_samples_detect(void) { event_queue_claim(&_sample_queue); cogip::uartpb::UartProtobuf *uartpb = pf_get_uartpb(); - _output_message.clear(); - _output_message.set_req_samples(true); - uartpb->send_message(_output_message); + uartpb->send_message(sample_request_uuid); sample_event_t *event = (sample_event_t *)event_wait(&_sample_queue); auto & samples = event->pb_message.get_samples(); @@ -203,9 +204,14 @@ const DetectedSamples & app_samples_detect(void) return _detected_samples; } -void app_samples_process(const PB_Samples &samples) +void app_samples_process(cogip::uartpb::ReadBuffer *buffer) { - _sample_event.pb_message = samples; + EmbeddedProto::Error error = _sample_event.pb_message.deserialize(*buffer); + if (error != EmbeddedProto::Error::NO_ERRORS) { + std::cout << "Samples: Protobuf deserialization error: " << static_cast(error) << std::endl; + return; + } + event_post(&_sample_queue, (event_t *)&_sample_event); } diff --git a/applications/app_test/app_uartpb.cpp b/applications/app_test/app_uartpb.cpp deleted file mode 100644 index 2a2897dc34..0000000000 --- a/applications/app_test/app_uartpb.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "app.hpp" -#include "platform.hpp" -#include "shell_menu/shell_menu.hpp" - -#include "uartpb_config.hpp" - -#include - -namespace cogip { - -namespace app { - -// Read incoming Protobuf message and call the corresponding message handler -void app_uartpb_message_handler(cogip::uartpb::ReadBuffer &buffer) -{ - PB_InputMessage *message = new PB_InputMessage(); - EmbeddedProto::Error error = message->deserialize(buffer); - if (error != EmbeddedProto::Error::NO_ERRORS) { - COGIP_DEBUG_COUT("Protobuf deserialization error: " << static_cast(error)); - return; - } - if (message->has_command()) { - cogip::shell::handle_pb_command(message->command()); - } - else if (message->has_copilot_connected()) { - pf_set_copilot_connected(true); - COGIP_DEBUG_COUT("Copilot connected"); - if (cogip::shell::current_menu) { - cogip::shell::current_menu->send_pb_message(); - } - } - else if (message->has_copilot_disconnected()) { - pf_set_copilot_connected(false); - COGIP_DEBUG_COUT("Copilot disconnected"); - } - else if (message->has_wizard()) { - pf_get_wizard()->handle_response(message->wizard()); - } - else if (message->has_samples()) { - app_samples_process(message->samples()); - } - else { - COGIP_DEBUG_CERR("Unknown response type: " << static_cast(message->get_which_type())); - } - delete message; -} - -} // namespace app - -} // namespace cogip diff --git a/applications/app_test/include/app.hpp b/applications/app_test/include/app.hpp index 02cea9ad6d..9cdde07f02 100644 --- a/applications/app_test/include/app.hpp +++ b/applications/app_test/include/app.hpp @@ -8,7 +8,6 @@ #include "app_obstacles.hpp" #include "app_samples.hpp" #include "app_shell.hpp" -#include "app_uartpb.hpp" /* * Machine parameters diff --git a/applications/app_test/include/app_samples.hpp b/applications/app_test/include/app_samples.hpp index be5fd96dc2..abd7e0598a 100644 --- a/applications/app_test/include/app_samples.hpp +++ b/applications/app_test/include/app_samples.hpp @@ -1,6 +1,7 @@ #pragma once #include "obstacles/Circle.hpp" +#include "uartpb/ReadBuffer.hpp" #include "PB_Samples.hpp" @@ -117,7 +118,7 @@ void app_samples_init(); const DetectedSamples & app_samples_detect(void); -void app_samples_process(const PB_Samples &samples); +void app_samples_process(cogip::uartpb::ReadBuffer *buffer); }; // namespace app diff --git a/applications/app_test/include/app_uartpb.hpp b/applications/app_test/include/app_uartpb.hpp deleted file mode 100644 index 383522dc05..0000000000 --- a/applications/app_test/include/app_uartpb.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "uartpb/ReadBuffer.hpp" - -namespace cogip { - -namespace app { - -/// Message handler for incoming Protobuf messages. -void app_uartpb_message_handler(cogip::uartpb::ReadBuffer &buffer); - -} // namespace app - -} // namespace cogip diff --git a/applications/app_test/include/uartpb_config.hpp b/applications/app_test/include/uartpb_config.hpp deleted file mode 100644 index 6cde54f7b1..0000000000 --- a/applications/app_test/include/uartpb_config.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "shell_menu/Menu.hpp" -#include "obstacles/List.hpp" -#include "obstacles/Obstacle.hpp" -#include "avoidance.hpp" -#include "wizard/Wizard.hpp" -#include "app_samples.hpp" - -#include "PB_GameInputMessage.hpp" -#include "PB_GameOutputMessage.hpp" - -using PB_InputMessage = PB_GameInputMessage< - COMMAND_NAME_MAX_LENGTH, - COMMAND_DESC_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - APP_SAMPLES_MAX_DETECTED>; - -using PB_OutputMessage = PB_GameOutputMessage< - COMMAND_NAME_MAX_LENGTH, - NB_SHELL_COMMANDS, - COMMAND_NAME_MAX_LENGTH, - COMMAND_DESC_MAX_LENGTH, - AVOIDANCE_GRAPH_MAX_VERTICES, - OBSTACLES_MAX_NUMBER, - OBSTACLE_BOUNDING_BOX_VERTICES, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH>; diff --git a/applications/cup2022/PB_GameInputMessage.proto b/applications/cup2022/PB_GameInputMessage.proto deleted file mode 100644 index cac4d02aad..0000000000 --- a/applications/cup2022/PB_GameInputMessage.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -import "PB_Command.proto"; -import "PB_Wizard.proto"; -import "PB_Samples.proto"; - -message PB_GameInputMessage { - oneof type { - PB_Command command = 1; - bool copilot_connected = 2; - bool copilot_disconnected = 3; - PB_Wizard wizard = 4; - PB_Samples samples = 5; - } -} diff --git a/applications/cup2022/PB_GameOutputMessage.proto b/applications/cup2022/PB_GameOutputMessage.proto deleted file mode 100644 index 5b5f829b6a..0000000000 --- a/applications/cup2022/PB_GameOutputMessage.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -import "PB_Menu.proto"; -import "PB_State.proto"; -import "PB_Wizard.proto"; - -message PB_GameOutputMessage { - oneof type { - bool reset = 1; - PB_Menu menu = 2; - PB_State state = 3; - PB_Wizard wizard = 4; - bool req_samples = 5; - uint32 score = 6; - } -} diff --git a/applications/cup2022/app_samples.cpp b/applications/cup2022/app_samples.cpp index 0241021739..9d82037cc9 100644 --- a/applications/cup2022/app_samples.cpp +++ b/applications/cup2022/app_samples.cpp @@ -1,7 +1,6 @@ // Firmware includes #include "platform.hpp" #include "trigonometry.h" -#include "uartpb_config.hpp" #include "app_camp.hpp" #include "app_samples.hpp" @@ -16,9 +15,11 @@ namespace cogip { namespace app { -static PB_OutputMessage _output_message; static DetectedSamples _detected_samples; +constexpr cogip::uartpb::uuid_t sample_request_uuid = 3781855956; +constexpr cogip::uartpb::uuid_t sample_response_uuid = 1538397045; + static std::map _samples; typedef struct { @@ -57,6 +58,8 @@ void app_samples_init() return; } + pf_get_uartpb()->register_message_handler(sample_response_uuid, app_samples_process); + event_queue_init_detached(&_sample_queue); _sample_event.super.list_node.next = nullptr; @@ -170,9 +173,7 @@ const DetectedSamples & app_samples_detect(void) { event_queue_claim(&_sample_queue); cogip::uartpb::UartProtobuf *uartpb = pf_get_uartpb(); - _output_message.clear(); - _output_message.set_req_samples(true); - uartpb->send_message(_output_message); + uartpb->send_message(sample_request_uuid); sample_event_t *event = (sample_event_t *)event_wait(&_sample_queue); auto & samples = event->pb_message.get_samples(); @@ -203,9 +204,14 @@ const DetectedSamples & app_samples_detect(void) return _detected_samples; } -void app_samples_process(const PB_Samples &samples) +void app_samples_process(cogip::uartpb::ReadBuffer *buffer) { - _sample_event.pb_message = samples; + EmbeddedProto::Error error = _sample_event.pb_message.deserialize(*buffer); + if (error != EmbeddedProto::Error::NO_ERRORS) { + std::cout << "Samples: Protobuf deserialization error: " << static_cast(error) << std::endl; + return; + } + event_post(&_sample_queue, (event_t *)&_sample_event); } diff --git a/applications/cup2022/app_uartpb.cpp b/applications/cup2022/app_uartpb.cpp deleted file mode 100644 index 0fb1d39f99..0000000000 --- a/applications/cup2022/app_uartpb.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "app.hpp" -#include "platform.hpp" -#include "shell_menu/shell_menu.hpp" - -#include "uartpb_config.hpp" - -#include - -namespace cogip { - -namespace app { - -// Read incoming Protobuf message and call the corresponding message handler -void app_uartpb_message_handler(cogip::uartpb::ReadBuffer &buffer) -{ - PB_InputMessage *message = new PB_InputMessage(); - EmbeddedProto::Error error = message->deserialize(buffer); - if (error != EmbeddedProto::Error::NO_ERRORS) { - COGIP_DEBUG_COUT("Protobuf deserialization error: " << static_cast(error)); - return; - } - if (message->has_command()) { - cogip::shell::handle_pb_command(message->command()); - } - else if (message->has_copilot_connected()) { - pf_set_copilot_connected(true); - COGIP_DEBUG_COUT("Copilot connected"); - if (cogip::shell::current_menu) { - cogip::shell::current_menu->send_pb_message(); - } - } - else if (message->has_copilot_disconnected()) { - pf_set_copilot_connected(false); - COGIP_DEBUG_COUT("Copilot disconnected"); - } - else if (message->has_wizard()) { - pf_get_wizard()->handle_response(message->wizard()); - } - else if (message->has_samples()) { - app_samples_process(message->samples()); - } - else { - COGIP_DEBUG_COUT("Unknown response type: " << static_cast(message->get_which_type())); - } - delete message; -} - -} // namespace app - -} // namespace cogip diff --git a/applications/cup2022/include/app.hpp b/applications/cup2022/include/app.hpp index 02cea9ad6d..9cdde07f02 100644 --- a/applications/cup2022/include/app.hpp +++ b/applications/cup2022/include/app.hpp @@ -8,7 +8,6 @@ #include "app_obstacles.hpp" #include "app_samples.hpp" #include "app_shell.hpp" -#include "app_uartpb.hpp" /* * Machine parameters diff --git a/applications/cup2022/include/app_samples.hpp b/applications/cup2022/include/app_samples.hpp index be5fd96dc2..abd7e0598a 100644 --- a/applications/cup2022/include/app_samples.hpp +++ b/applications/cup2022/include/app_samples.hpp @@ -1,6 +1,7 @@ #pragma once #include "obstacles/Circle.hpp" +#include "uartpb/ReadBuffer.hpp" #include "PB_Samples.hpp" @@ -117,7 +118,7 @@ void app_samples_init(); const DetectedSamples & app_samples_detect(void); -void app_samples_process(const PB_Samples &samples); +void app_samples_process(cogip::uartpb::ReadBuffer *buffer); }; // namespace app diff --git a/applications/cup2022/include/app_uartpb.hpp b/applications/cup2022/include/app_uartpb.hpp deleted file mode 100644 index 383522dc05..0000000000 --- a/applications/cup2022/include/app_uartpb.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "uartpb/ReadBuffer.hpp" - -namespace cogip { - -namespace app { - -/// Message handler for incoming Protobuf messages. -void app_uartpb_message_handler(cogip::uartpb::ReadBuffer &buffer); - -} // namespace app - -} // namespace cogip diff --git a/applications/cup2022/include/uartpb_config.hpp b/applications/cup2022/include/uartpb_config.hpp deleted file mode 100644 index 6cde54f7b1..0000000000 --- a/applications/cup2022/include/uartpb_config.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "shell_menu/Menu.hpp" -#include "obstacles/List.hpp" -#include "obstacles/Obstacle.hpp" -#include "avoidance.hpp" -#include "wizard/Wizard.hpp" -#include "app_samples.hpp" - -#include "PB_GameInputMessage.hpp" -#include "PB_GameOutputMessage.hpp" - -using PB_InputMessage = PB_GameInputMessage< - COMMAND_NAME_MAX_LENGTH, - COMMAND_DESC_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - APP_SAMPLES_MAX_DETECTED>; - -using PB_OutputMessage = PB_GameOutputMessage< - COMMAND_NAME_MAX_LENGTH, - NB_SHELL_COMMANDS, - COMMAND_NAME_MAX_LENGTH, - COMMAND_DESC_MAX_LENGTH, - AVOIDANCE_GRAPH_MAX_VERTICES, - OBSTACLES_MAX_NUMBER, - OBSTACLE_BOUNDING_BOX_VERTICES, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_REP_MAX, - WIZARD_NAME_MAX_LENGTH, - WIZARD_NAME_MAX_LENGTH>; diff --git a/examples/uartpb/PB_ExampleInputMessage.proto b/examples/uartpb/PB_ExampleInputMessage.proto deleted file mode 100644 index 75d7f8a956..0000000000 --- a/examples/uartpb/PB_ExampleInputMessage.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -import "PB_RespHello.proto"; -import "PB_RespPing.proto"; -import "PB_RespPong.proto"; - -message PB_ExampleInputMessage { - oneof type { - PB_RespHello resp_hello = 1; - PB_RespPing resp_ping = 2; - PB_RespPong resp_pong = 3; - } -} diff --git a/examples/uartpb/PB_ExampleOutputMessage.proto b/examples/uartpb/PB_ExampleOutputMessage.proto deleted file mode 100644 index 9c3e411b35..0000000000 --- a/examples/uartpb/PB_ExampleOutputMessage.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto3"; - -import "PB_Menu.proto"; -import "PB_ReqHello.proto"; -import "PB_ReqPing.proto"; -import "PB_ReqPong.proto"; - -message PB_ExampleOutputMessage { - oneof type { - PB_Menu menu = 1; - bool reset = 2; - PB_ReqHello req_hello = 3; - PB_ReqPing req_ping = 4; - PB_ReqPong req_pong = 5; - } -} diff --git a/examples/uartpb/include/uartpb_config.hpp b/examples/uartpb/include/uartpb_config.hpp deleted file mode 100644 index 8248eb5f05..0000000000 --- a/examples/uartpb/include/uartpb_config.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "shell_menu/Menu.hpp" -#include "PB_ExampleOutputMessage.hpp" - -using PB_OutputMessage = PB_ExampleOutputMessage< - COMMAND_NAME_MAX_LENGTH, - NB_SHELL_COMMANDS, - COMMAND_NAME_MAX_LENGTH, - COMMAND_DESC_MAX_LENGTH, - 64>; diff --git a/examples/uartpb/main.cpp b/examples/uartpb/main.cpp index 8f920ad1d1..432bcbffcb 100644 --- a/examples/uartpb/main.cpp +++ b/examples/uartpb/main.cpp @@ -6,8 +6,6 @@ #include "thread.h" #include "xtimer.h" -#include "uartpb_config.hpp" - // Projet includes #include "shell_menu/shell_menu.hpp" @@ -20,8 +18,6 @@ #include "PB_RespHello.hpp" #include "PB_RespPing.hpp" #include "PB_RespPong.hpp" -#include "PB_ExampleInputMessage.hpp" -#include "PB_ExampleOutputMessage.hpp" #include "uartpb/UartProtobuf.hpp" #include "uartpb/ReadBuffer.hpp" @@ -33,86 +29,78 @@ static char sender_stack[THREAD_STACKSIZE_MAIN]; bool suspend_sender = false; cogip::uartpb::UartProtobuf *uartpb = nullptr; -static PB_OutputMessage output_message; -static void handle_response_hello(const PB_RespHello &hello) -{ - printf("<<== Hello response with number=%" PRId32 "\n\n", (int32_t)hello.get_number()); -} +constexpr cogip::uartpb::uuid_t reset_uuid = 3351980141; +constexpr cogip::uartpb::uuid_t req_hello_uuid = 3938291130; +constexpr cogip::uartpb::uuid_t req_ping_uuid = 2537089183; +constexpr cogip::uartpb::uuid_t req_pong_uuid = 3650317449; +constexpr cogip::uartpb::uuid_t resp_hello_uuid = 4187249687; +constexpr cogip::uartpb::uuid_t resp_ping_uuid = 4288740491; +constexpr cogip::uartpb::uuid_t resp_pong_uuid = 2687718320; -static void handle_response_ping(const PB_RespPing &ping) +static PB_RespHello resp_hello; +static PB_RespPing resp_ping; +static PB_RespPong resp_pong; + +static PB_ReqHello<64> req_hello; +static PB_ReqPing req_ping; +static PB_ReqPong req_pong; + +static void handle_response_hello(cogip::uartpb::ReadBuffer *buffer) { - printf("<<== Ping response with color=%s\n\n", get_color_name((cogip::cogip_defs::Color)ping.get_color())); + resp_hello.deserialize(*buffer); + std::cout << "<<== Hello response with number=" << resp_hello.get_number() << std::endl; } -static void handle_response_pong(const PB_RespPong &pong) +static void handle_response_ping(cogip::uartpb::ReadBuffer *buffer) { - const cogip::cogip_defs::Pose &pose = pong.get_new_pose(); - printf( - "<<== Pong response with pose={x=%.2lf, y=%.2lf, angle=%.2lf}\n\n", - pose.x(), pose.y(), pose.O() - ); + resp_ping.deserialize(*buffer); + std::cout << "<<== Ping response with color="<< get_color_name((cogip::cogip_defs::Color)resp_ping.get_color()) << std::endl; } -void message_handler(cogip::uartpb::ReadBuffer &buffer) +static void handle_response_pong(cogip::uartpb::ReadBuffer *buffer) { - PB_ExampleInputMessage *message = new PB_ExampleInputMessage(); - message->deserialize(buffer); - - if (message->has_resp_hello()) { - handle_response_hello(message->resp_hello()); - } - else if (message->has_resp_ping()) { - handle_response_ping(message->resp_ping()); - } - else if (message->has_resp_pong()) { - handle_response_pong(message->resp_pong()); - } - else { - printf("Unknown response type: %" PRIu32 "\n", static_cast(message->get_which_type())); - } - - delete message; + resp_pong.deserialize(*buffer); + const cogip::cogip_defs::Pose &pose = resp_pong.get_new_pose(); + std::cout + << "<<== Pong response with pose={x=" << pose.x() + << ", y=" << pose.y() << ", angle=" << pose.O() << "}" + << std::endl; } static void send_hello() { - PB_ReqHello<64> hello; - hello.set_number(std::rand()); - hello.mutable_message() = "hellohello"; - printf( - "==>> Hello request with number=%" PRIi32 " and message='%s'\n", - (int32_t)hello.get_number(), (const char *)hello.get_message().get_const() - ); - - output_message.set_req_hello(hello); - uartpb->send_message(output_message); + req_hello.set_number(std::rand()); + req_hello.mutable_message() = "hellohello"; + std::cout + << "==>> Hello request with number=" << (int32_t)req_hello.get_number() + << " and message='" << req_hello.get_message().get_const() << "'" + << std::endl; + + uartpb->send_message(req_hello_uuid, &req_hello); } static void send_ping() { - PB_ReqPing ping; - ping.set_color((PB_Color)cogip::cogip_defs::Color::RED); + req_ping.set_color((PB_Color)cogip::cogip_defs::Color::RED); - printf("==>> Ping request with color=%s\n", get_color_name((cogip::cogip_defs::Color)ping.get_color())); + std::cout << "==>> Ping request with color=" << get_color_name((cogip::cogip_defs::Color)req_ping.get_color()) << std::endl; - output_message.set_req_ping(ping); - uartpb->send_message(output_message); + uartpb->send_message(req_ping_uuid, &req_ping); } static void send_pong() { - PB_ReqPong pong; cogip::cogip_defs::Pose pose = {15, 30, 90}; - pose.pb_copy(pong.mutable_pose()); + pose.pb_copy(req_pong.mutable_pose()); - printf( - "==>> Pong request with pose={x=%.2lf, y=%.2lf, angle=%.2lf}\n", - (double)pong.get_pose().get_x(), (double)pong.get_pose().get_y(), (double)pong.get_pose().get_O() - ); + std::cout + << "==>> Pong request with pose={x=" << req_pong.get_pose().get_x() + << ", y=" << req_pong.get_pose().get_y() + << ", angle=" << req_pong.get_pose().get_O() << "}" + << std::endl; - output_message.set_req_pong(pong); - uartpb->send_message(output_message); + uartpb->send_message(req_pong_uuid, &req_pong); } static void *message_sender(void *arg) @@ -176,14 +164,14 @@ static int cmd_sub(int argc, char **argv) (void)argc; (void)argv; - printf("Sub-menu command\n"); + std::cout << "Sub-menu command" << std::endl; return 0; } int main(void) { - printf("\n== UART/EmbeddedProto Example ==\n"); + std::cout << std::endl << "== UART/EmbeddedProto Example ==" << std::endl; cogip::shell::root_menu.push_back( new cogip::shell::Command("hello", "Send hello", cmd_hello)); @@ -197,14 +185,15 @@ int main(void) "Sub-menu", "sub", &cogip::shell::root_menu); menu->push_back(new cogip::shell::Command("cmd", "Sub-menu command", cmd_sub)); - uartpb = new cogip::uartpb::UartProtobuf( - message_handler, - UART_DEV(1) - ); + uartpb = new cogip::uartpb::UartProtobuf(UART_DEV(1)); + + uartpb->register_message_handler(resp_hello_uuid, handle_response_hello); + uartpb->register_message_handler(resp_ping_uuid, handle_response_ping); + uartpb->register_message_handler(resp_pong_uuid, handle_response_pong); bool res = uartpb->connect(); if (! res) { - printf("UART initialization status: %d\n", res); + std::cout << "UART initialization status: " << res << std::endl; exit(1); } @@ -214,8 +203,7 @@ int main(void) sender_stack, sizeof(sender_stack), SENDER_PRIO, THREAD_CREATE_SLEEPING, message_sender, NULL, "sender"); - output_message.set_reset(true); - uartpb->send_message(output_message); + uartpb->send_message(reset_uuid); // Start shell cogip::shell::register_uartpb(uartpb); diff --git a/examples/uartpb/uartpb-client.py b/examples/uartpb/uartpb-client.py index 7ea4c36301..947575ec30 100755 --- a/examples/uartpb/uartpb-client.py +++ b/examples/uartpb/uartpb-client.py @@ -2,6 +2,7 @@ import base64 import binascii from pathlib import Path +from typing import Union from google import protobuf from google.protobuf import json_format @@ -12,77 +13,107 @@ from PB_ReqHello_pb2 import PB_ReqHello from PB_ReqPing_pb2 import PB_ReqPing from PB_ReqPong_pb2 import PB_ReqPong +from PB_RespHello_pb2 import PB_RespHello +from PB_RespPing_pb2 import PB_RespPing +from PB_RespPong_pb2 import PB_RespPong from PB_Menu_pb2 import PB_Menu -from PB_ExampleInputMessage_pb2 import PB_ExampleInputMessage -from PB_ExampleOutputMessage_pb2 import PB_ExampleOutputMessage serial_port = Serial() - -def send_response(response: PB_ExampleInputMessage) -> None: - response_serialized = response.SerializeToString() - response_base64 = base64.encodebytes(response_serialized) - serial_port.write(response_base64) - serial_port.write(b"\n") +menu_uuid = 2168120333 +reset_uuid = 3351980141 +req_hello_uuid = 3938291130 +req_ping_uuid = 2537089183 +req_pong_uuid = 3650317449 +resp_hello_uuid = 4187249687 +resp_ping_uuid = 4288740491 +resp_pong_uuid = 2687718320 + + +def send_response( + uuid: int, + response: Union[None, PB_RespHello, PB_RespPing, PB_RespPong] = None) -> None: + serial_port.write(uuid.to_bytes(4, "little")) + if response: + response_serialized = response.SerializeToString() + response_base64 = base64.encodebytes(response_serialized) + serial_port.write(response_base64) + serial_port.write(b"\0") + + +def pb_exception_handler(func): + def inner_function(*args, **kwargs): + try: + func(*args, **kwargs) + except protobuf.message.DecodeError as exc: + print(exc) + return inner_function -def handle_message_reset(reset: bool) -> None: - print("Reset received:", reset) +def handle_message_reset() -> None: + print("Reset received") -def handle_message_menu(menu: PB_Menu) -> None: +@pb_exception_handler +def handle_message_menu(message: bytes) -> None: + menu = PB_Menu() + menu.ParseFromString(message) print("Received new menu:") print(json_format.MessageToDict(menu)) -def handle_message_hello(hello: PB_ReqHello) -> None: +@pb_exception_handler +def handle_message_hello(message: bytes) -> None: + hello = PB_ReqHello() + hello.ParseFromString(message) print(f"Received Hello request with number={hello.number} and message='{hello.message}'") - response = PB_ExampleInputMessage() - response.resp_hello.number = -hello.number - send_response(response) + response = PB_RespHello() + response.number = -hello.number + send_response(resp_hello_uuid, response) -def handle_message_ping(ping: PB_ReqPing) -> None: +@pb_exception_handler +def handle_message_ping(message: bytes) -> None: + ping = PB_ReqPing() + ping.ParseFromString(message) print(f"Received Ping request with color={PB_Color.Name(ping.color)}") - response = PB_ExampleInputMessage() - response.resp_ping.color = PB_Color.BLUE - send_response(response) + response = PB_RespPing() + response.color = PB_Color.BLUE + send_response(resp_ping_uuid, response) -def handle_message_pong(pong: PB_ReqPong) -> None: +@pb_exception_handler +def handle_message_pong(message: bytes) -> None: + pong = PB_ReqPong() + pong.ParseFromString(message) print(f"Received Pong request with pose={{x={pong.pose.x}, y={pong.pose.y}, angle={pong.pose.O}}}") - response = PB_ExampleInputMessage() - response.resp_pong.new_pose.x = -pong.pose.x - response.resp_pong.new_pose.y = -pong.pose.y - response.resp_pong.new_pose.O = -pong.pose.O - send_response(response) + response = PB_RespPong() + response.new_pose.x = -pong.pose.x + response.new_pose.y = -pong.pose.y + response.new_pose.O = -pong.pose.O + send_response(resp_pong_uuid, response) request_handlers = { - "reset": handle_message_reset, - "menu": handle_message_menu, - "req_hello": handle_message_hello, - "req_ping": handle_message_ping, - "req_pong": handle_message_pong + reset_uuid: handle_message_reset, + menu_uuid: handle_message_menu, + req_hello_uuid: handle_message_hello, + req_ping_uuid: handle_message_ping, + req_pong_uuid: handle_message_pong } -def decode_message(encoded_message: bytes) -> None: - try: - message = PB_ExampleOutputMessage() - message.ParseFromString(encoded_message) - except protobuf.message.DecodeError as exc: - print(exc) - return -1 - - message_type = message.WhichOneof("type") +def handle_message(uuid: int, pb_message: bytes = b"") -> None: + request_handler = request_handlers.get(uuid) + if not request_handler: + print(f"No handler found for message uuid '{uuid}'") + return - request_handler = request_handlers.get(message_type) - if request_handler: - request_handler(getattr(message, message_type)) + if not pb_message: + request_handler() else: - print(f"No handler found for message type '{message_type}'") + request_handler(pb_message) def main( @@ -97,17 +128,24 @@ def main( serial_port.open() while(True): - # Read next base64 message - base64_message = serial_port.readline() + # Read next message + message = serial_port.readline() + message = message.rstrip(b"\n") + + # Get message uuid on first bytes + uuid = int.from_bytes(message[:4], "little") + + if len(message) == 4: + handle_message(uuid) + continue - # Base64 decoding try: - pb_message = base64.decodebytes(base64_message) + pb_message = base64.decodebytes(message[4:]) except binascii.Error: - print("Failed to decode base64 message.") + print(f"Failed to decode base64 message (uuid={uuid}).") continue - decode_message(pb_message) + handle_message(uuid, pb_message) if __name__ == "__main__": diff --git a/lib/wizard/Wizard.cpp b/lib/wizard/Wizard.cpp index 6effc07a1a..c259b7bcc1 100644 --- a/lib/wizard/Wizard.cpp +++ b/lib/wizard/Wizard.cpp @@ -1,23 +1,28 @@ // Project includes #include "wizard/Wizard.hpp" -#include "uartpb_config.hpp" + +#include namespace cogip { namespace wizard { -static PB_OutputMessage output_message; -static bool queue_claimed = false; +constexpr cogip::uartpb::uuid_t wizard_uuid = 1525532810; Wizard::Wizard(cogip::uartpb::UartProtobuf *uartpb) : uartpb_(uartpb) { event_queue_init_detached(&queue_); event_.super.list_node.next = nullptr; + uartpb->register_message_handler(wizard_uuid, std::bind(&Wizard::handle_response, this, std::placeholders::_1)); } -void Wizard::handle_response(const PB_Message &pb_message) +void Wizard::handle_response(cogip::uartpb::ReadBuffer *buffer) { - event_.pb_message = pb_message; + EmbeddedProto::Error error = event_.pb_message.deserialize(*buffer); + if (error != EmbeddedProto::Error::NO_ERRORS) { + std::cout << "Wizard: Protobuf deserialization error: " << static_cast(error) << std::endl; + return; + } event_post(&queue_, (event_t *)&event_); } @@ -27,13 +32,12 @@ const Wizard::PB_Message &Wizard::request(const PB_Message &request) return request; } - if (! queue_claimed) { + if (! queue_claimed_) { event_queue_claim(&queue_); - queue_claimed = true; + queue_claimed_ = true; } - output_message.set_wizard(request); - uartpb_->send_message(output_message); + uartpb_->send_message(wizard_uuid, &request); wizard_event_t *event = (wizard_event_t *)event_wait(&queue_); return event->pb_message; diff --git a/lib/wizard/include/wizard/Wizard.hpp b/lib/wizard/include/wizard/Wizard.hpp index 799f066b95..6ef262d487 100644 --- a/lib/wizard/include/wizard/Wizard.hpp +++ b/lib/wizard/include/wizard/Wizard.hpp @@ -74,7 +74,7 @@ class Wizard { /// Message handler for responses. void handle_response( - const PB_Message &pb_message ///< [in] received message + cogip::uartpb::ReadBuffer *buffer ///< [in] buffer containing the received message ); /// Send a request and wait for the response. @@ -93,6 +93,7 @@ class Wizard { cogip::uartpb::UartProtobuf *uartpb_; ///< uartpb pointer used to send/receive messages wizard_event_t event_; ///< event containing the response event_queue_t queue_; ///< queue receiving response events + bool queue_claimed_ = false; ///< whether event queue has been claimed on the thread or not }; } // namespace wizard diff --git a/platforms/pegasus/platform.cpp b/platforms/pegasus/platform.cpp index bdbb144fe0..c74a30ae49 100644 --- a/platforms/pegasus/platform.cpp +++ b/platforms/pegasus/platform.cpp @@ -22,7 +22,6 @@ #include "lidar_obstacles.hpp" #include "trace_utils.hpp" -#include "uartpb_config.hpp" #include "PB_Command.hpp" #include "PB_State.hpp" @@ -80,7 +79,12 @@ PB_Statesend_message(output_message); + uartpb->send_message(state_uuid, &pb_state); } void pf_init_quadpid_params(ctrl_quadpid_parameters_t ctrl_quadpid_params) @@ -257,11 +260,25 @@ cogip::wizard::Wizard *pf_get_wizard() return wizard; } +void handle_copilot_connected([[maybe_unused]] cogip::uartpb::ReadBuffer *buffer) +{ + pf_set_copilot_connected(true); + std::cout << "Copilot connected" << std::endl; + if (cogip::shell::current_menu) { + cogip::shell::current_menu->send_pb_message(); + } +} + +void handle_copilot_disconnected([[maybe_unused]] cogip::uartpb::ReadBuffer *buffer) +{ + pf_set_copilot_connected(false); + std::cout << "Copilot disconnected" << std::endl; +} + void pf_init(void) { /* Initialize UARTPB */ uartpb = new cogip::uartpb::UartProtobuf( - cogip::app::app_uartpb_message_handler, UART_DEV(1) ); @@ -272,9 +289,10 @@ void pf_init(void) } else { cogip::shell::register_uartpb(uartpb); + uartpb->register_message_handler(copilot_connected_uuid, handle_copilot_connected); + uartpb->register_message_handler(copilot_disconnected_uuid, handle_copilot_disconnected); uartpb->start_reader(); - output_message.set_reset(true); - uartpb->send_message(output_message); + uartpb->send_message(reset_uuid); } #ifdef MODULE_SHELL_PLATFORMS diff --git a/platforms/pf_test/platform.cpp b/platforms/pf_test/platform.cpp index 1103cc9c0d..018eb370b2 100644 --- a/platforms/pf_test/platform.cpp +++ b/platforms/pf_test/platform.cpp @@ -22,7 +22,6 @@ #include "lidar_obstacles.hpp" #include "trace_utils.hpp" -#include "uartpb_config.hpp" #include "PB_Command.hpp" #include "PB_State.hpp" @@ -80,7 +79,12 @@ PB_Statesend_message(output_message); + uartpb->send_message(state_uuid, &pb_state); } void pf_init_quadpid_params(ctrl_quadpid_parameters_t ctrl_quadpid_params) @@ -250,11 +253,25 @@ cogip::wizard::Wizard *pf_get_wizard() return wizard; } +void handle_copilot_connected([[maybe_unused]] cogip::uartpb::ReadBuffer *buffer) +{ + pf_set_copilot_connected(true); + std::cout << "Copilot connected" << std::endl; + if (cogip::shell::current_menu) { + cogip::shell::current_menu->send_pb_message(); + } +} + +void handle_copilot_disconnected([[maybe_unused]] cogip::uartpb::ReadBuffer *buffer) +{ + pf_set_copilot_connected(false); + std::cout << "Copilot disconnected" << std::endl; +} + void pf_init(void) { /* Initialize UARTPB */ uartpb = new cogip::uartpb::UartProtobuf( - cogip::app::app_uartpb_message_handler, UART_DEV(1) ); @@ -265,9 +282,10 @@ void pf_init(void) } else { cogip::shell::register_uartpb(uartpb); + uartpb->register_message_handler(copilot_connected_uuid, handle_copilot_connected); + uartpb->register_message_handler(copilot_disconnected_uuid, handle_copilot_disconnected); uartpb->start_reader(); - output_message.set_reset(true); - uartpb->send_message(output_message); + uartpb->send_message(reset_uuid); } #ifdef MODULE_SHELL_PLATFORMS diff --git a/sys/shell_menu/Menu.cpp b/sys/shell_menu/Menu.cpp index ef85c66625..7f64fdc940 100644 --- a/sys/shell_menu/Menu.cpp +++ b/sys/shell_menu/Menu.cpp @@ -4,10 +4,6 @@ // System includes #include -#ifdef MODULE_UARTPB -# include "uartpb_config.hpp" -#endif - #include "PB_Menu.hpp" namespace cogip { @@ -83,7 +79,7 @@ void Menu::enter(void) i++; } - COGIP_DEBUG_COUT("Enter shell menu: " << current_menu->name().c_str()); + COGIP_DEBUG_COUT("Enter shell menu: " << current_menu->name() << std::endl); #ifdef MODULE_UARTPB send_pb_message(); @@ -111,14 +107,11 @@ void Menu::update_pb_message(void) } #ifdef MODULE_UARTPB -static PB_OutputMessage output_message; - void Menu::send_pb_message(void) { if (uart_protobuf) { update_pb_message(); - output_message.set_menu(pb_message_); - uart_protobuf->send_message(output_message); + uart_protobuf->send_message(menu_uuid, &pb_message_); } } #endif diff --git a/sys/shell_menu/include/shell_menu/shell_menu.hpp b/sys/shell_menu/include/shell_menu/shell_menu.hpp index ba0c7a9d27..67b8856f21 100644 --- a/sys/shell_menu/include/shell_menu/shell_menu.hpp +++ b/sys/shell_menu/include/shell_menu/shell_menu.hpp @@ -24,6 +24,11 @@ namespace cogip { namespace shell { +#ifdef MODULE_UARTPB +inline constexpr uartpb::uuid_t command_uuid = 2168120333; ///< uuid for uartpb +inline constexpr uartpb::uuid_t menu_uuid = 1485239280; ///< uuid for uartpb +#endif + extern Menu root_menu; ///< root menu extern Menu *current_menu; ///< pointer to the current menu extern std::list global_commands; ///< global commands, available in all menus @@ -50,7 +55,7 @@ void register_uartpb( ); /// Handle and execute a command coming from a Protobuf message -void handle_pb_command(const Command::PB_Message &pb_command); +void handle_pb_command(cogip::uartpb::ReadBuffer *buffer); #endif } // namespace shell diff --git a/sys/shell_menu/shell_menu.cpp b/sys/shell_menu/shell_menu.cpp index 2ba1c1460d..d61a5755a4 100644 --- a/sys/shell_menu/shell_menu.cpp +++ b/sys/shell_menu/shell_menu.cpp @@ -17,6 +17,7 @@ shell_command_t current_commands[NB_SHELL_COMMANDS]; #ifdef MODULE_UARTPB cogip::uartpb::UartProtobuf *uart_protobuf = nullptr; +static Command::PB_Message pb_command; #endif // Callbacks for default global commands @@ -97,9 +98,11 @@ void rename_command(const std::string &old_name, const std::string &new_name) } #ifdef MODULE_UARTPB + void register_uartpb(cogip::uartpb::UartProtobuf *uartpb_ptr) { uart_protobuf = uartpb_ptr; + uartpb_ptr->register_message_handler(command_uuid, handle_pb_command); } /// Execute a shell command callback using arguments from Protobuf message. @@ -142,10 +145,11 @@ static void run_pb_command_(Command *command, const Command::PB_Message &pb_comm command->handler()(argc, argv); } - // Handle a Protobuf command message -void handle_pb_command(const Command::PB_Message &pb_command) +void handle_pb_command(cogip::uartpb::ReadBuffer *buffer) { + pb_command.deserialize(*buffer); + if (cogip::shell::current_menu == nullptr) { COGIP_DEBUG_CERR( "Warning: received PB command before current_menu is initialized: " diff --git a/sys/uartpb/UartProtobuf.cpp b/sys/uartpb/UartProtobuf.cpp index 703d63c382..d22ca0bcb8 100644 --- a/sys/uartpb/UartProtobuf.cpp +++ b/sys/uartpb/UartProtobuf.cpp @@ -1,5 +1,8 @@ #include "uartpb/UartProtobuf.hpp" +// System includes +#include + // RIOT includes #include "riot/chrono.hpp" #include "riot/thread.hpp" @@ -17,11 +20,9 @@ static ReadBuffer read_buffer_; ///< buffer used to decode static WriteBuffer write_buffer_; ///< buffer used to encode a message UartProtobuf::UartProtobuf( - message_handler_t message_handler, uart_t uart_dev, uint32_t uart_speed) : uart_dev_(uart_dev), uart_speed_(uart_speed) { - message_handler_ = message_handler; ringbuffer_init(&rx_buf_, rx_mem_, UART_BUFFER_SIZE); } @@ -53,42 +54,71 @@ void UartProtobuf::uart_rx_cb(uint8_t data) void UartProtobuf::message_reader() { + uuid_t uuid; msg_t msg; msg_t msg_queue[8]; msg_init_queue(msg_queue, 8); while (1) { + // Wait for a new message msg_receive(&msg); + + // Read and check message length uint32_t message_length = (uint32_t)msg.content.value; assert(message_length <= UARTPB_INPUT_MESSAGE_LENGTH_MAX); - read_buffer_.clear(); - ringbuffer_get(&rx_buf_, (char *)read_buffer_.get_base64_data(), message_length); - size_t res = read_buffer_.base64_decode(); - if (res > 0 && message_handler_) { - message_handler_(read_buffer_); + + // Read uuid + ringbuffer_get(&rx_buf_, (char *)&uuid, sizeof(uuid_t)); + message_length -= sizeof(uuid_t); + + // Check a handler corresponding to the uuid is registered + if (message_handlers_.count(uuid) != 1) { + std::cout << "Unknown message uuid: " << uuid << std::endl; + continue; } + + // Read Protobuf message if any + if (message_length > 0) { + read_buffer_.clear(); + ringbuffer_get(&rx_buf_, (char *)read_buffer_.get_base64_data(), message_length); + size_t res = read_buffer_.base64_decode(); + if (res <= 0) { + std::cout << "Failed to base64 decode Protobuf message (res = " << res << ")" << std::endl; + continue; + } + } + + message_handlers_[uuid](message_length > 0 ? &read_buffer_ : nullptr); } } - -bool UartProtobuf::send_message(const EmbeddedProto::MessageInterface &message) +bool UartProtobuf::send_message(uuid_t uuid, const EmbeddedProto::MessageInterface *message) { bool success = true; + size_t base64_size = 0; + char separator = '\n'; mutex_.lock(); - write_buffer_.clear(); - auto serialization_status = message.serialize(write_buffer_); - if(EmbeddedProto::Error::NO_ERRORS != serialization_status) { - puts("Failed to serialize Protobuf message."); - success = false; - } - else { - size_t base64_size = write_buffer_.base64_encode(); - if (base64_size == 0) { - puts("Failed to base64 encode Protobuf serialized message."); + + if (message) { + write_buffer_.clear(); + auto serialization_status = message->serialize(write_buffer_); + if(EmbeddedProto::Error::NO_ERRORS != serialization_status) { + puts("Failed to serialize Protobuf message."); success = false; } else { - uart_write(uart_dev_, write_buffer_.get_base64_data(), base64_size+1); + base64_size = write_buffer_.base64_encode(); + if (base64_size == 0) { + puts("Failed to base64 encode Protobuf serialized message."); + success = false; + } + } + } + if (success) { + uart_write(uart_dev_, (uint8_t *)&uuid, sizeof(uuid_t)); + if (message) { + uart_write(uart_dev_, write_buffer_.get_base64_data(), base64_size); } + uart_write(uart_dev_, (uint8_t *)&separator, 1); } mutex_.unlock(); @@ -96,6 +126,11 @@ bool UartProtobuf::send_message(const EmbeddedProto::MessageInterface &message) return success; } +void UartProtobuf::register_message_handler(uuid_t uuid, message_handler_t handler) +{ + message_handlers_[uuid] = handler; +} + } // namespace uartpb } // namespace cogip diff --git a/sys/uartpb/include/uartpb/UartProtobuf.hpp b/sys/uartpb/include/uartpb/UartProtobuf.hpp index 9fda20905f..f98aa5f85e 100644 --- a/sys/uartpb/include/uartpb/UartProtobuf.hpp +++ b/sys/uartpb/include/uartpb/UartProtobuf.hpp @@ -7,7 +7,9 @@ /// @brief Exchange Protobuf messages over UART module. /// @details This module provides a class that controls an UART to send and /// receive Protobuf messages using EmbbededProto. -/// A callback function must also be provided to handle decoded messages. +/// Each library/application using uartpb must define a unique id (32-bit integer +/// of type cogip::uartpb::uuid_t) and registrer a callback function to send +/// or receive their own messages. /// See `examples/uartb`. /// @{ /// @file @@ -20,6 +22,7 @@ #include "periph/uart.h" #include "ringbuffer.h" #include "thread.h" +#include "etl/map.h" #include "uartpb/uartpb.hpp" #include "uartpb/ReadBuffer.hpp" @@ -36,6 +39,12 @@ namespace cogip { namespace uartpb { +/// Custom type for uuids +using uuid_t = uint32_t; + +/// Prototype for incoming Protobuf message handlers +using message_handler_t = std::function; + /// Function called when data is received on serial port. /// This wrapper is used to call the uart_rx_cb() function from UartProtobuf class /// from a C context, passing the UartProtobuf instance pointer as first parameter. @@ -54,11 +63,9 @@ void *message_reader_wrapper( /// Generic UART Protobuf communication class. class UartProtobuf { public: - using message_handler_t = void (*)(cogip::uartpb::ReadBuffer &); /// Class constructor. UartProtobuf( - message_handler_t message_handler, ///< [in] callback to process the message after decoding uart_t uart_dev, ///< [in] UART device uint32_t uart_speed=230400U ///< [in] UART baud rate ); @@ -80,7 +87,17 @@ class UartProtobuf { /// Send message on serial port. /// @return true if message was encoded and sent, false otherwise - bool send_message(const EmbeddedProto::MessageInterface &message); + bool send_message( + uuid_t uuid, ///< [in] message uuid + const EmbeddedProto::MessageInterface *message = nullptr + ///< [in] message to send + ); + + /// Associate a message handle to a specific uuid + void register_message_handler( + uuid_t uuid, ///< [in] message uuid + message_handler_t handler ///< [in] message handler + ); private: uart_t uart_dev_; ///< UART device @@ -88,9 +105,9 @@ class UartProtobuf { kernel_pid_t reader_pid_; ///< reader thread PID ringbuffer_t rx_buf_; ///< ring buffer for UART incoming bytes uint32_t msg_length_ = 0; ///< message length - void (*message_handler_)(cogip::uartpb::ReadBuffer &); - ///< callback to process the message after decoding riot::mutex mutex_; ///< mutex protecting serial port access + etl::map message_handlers_; + ///< callbacks to process the message after decoding }; } // namespace uartpb diff --git a/sys/uartpb/include/uartpb/uartpb.hpp b/sys/uartpb/include/uartpb/uartpb.hpp index 5da0b59b38..8961c6d462 100644 --- a/sys/uartpb/include/uartpb/uartpb.hpp +++ b/sys/uartpb/include/uartpb/uartpb.hpp @@ -28,4 +28,8 @@ #define UARTPB_OUTPUT_MESSAGE_LENGTH_MAX (4*1024) ///< max outgoing message length #endif +#ifndef UARTPB_MAX_HANDLERS +#define UARTPB_MAX_HANDLERS 16 ///< max numbers of registered message handlers +#endif + /// @}