From 5979cd2d866b5d243e9a6294cc0512ad9d7c8627 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 3 May 2025 05:41:13 +0000 Subject: [PATCH 01/12] Add lwjson --- ext/CMakeLists.txt | 1 + ext/lwjson/CMakeLists.txt | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 ext/lwjson/CMakeLists.txt diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index c168e06..4248e80 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -1,6 +1,7 @@ if (XBOT_BUILD_LIB_SERVICE) add_subdirectory(ulog/src) add_subdirectory(cpputest) + add_subdirectory(lwjson) endif () if (XBOT_BUILD_LIB_SERVICE_INTERFACE) diff --git a/ext/lwjson/CMakeLists.txt b/ext/lwjson/CMakeLists.txt new file mode 100644 index 0000000..cd4e033 --- /dev/null +++ b/ext/lwjson/CMakeLists.txt @@ -0,0 +1,6 @@ +include(FetchContent) +FetchContent_Declare( + lwjson + URL https://github.com/MaJerle/lwjson/archive/97dfff90b12772d1ec5f4bf4b80cdfe187693423.tar.gz +) +FetchContent_MakeAvailable(lwjson) From 9fce9c3819d7c9666ceb4557035ff5dc97df699b Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Fri, 9 May 2025 22:37:33 +0000 Subject: [PATCH 02/12] Refactor AddService(Interface).cmake --- codegen/cmake/AddService.cmake | 25 ++++++++++++++----------- codegen/cmake/AddServiceInterface.cmake | 25 ++++++++++++++----------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/codegen/cmake/AddService.cmake b/codegen/cmake/AddService.cmake index 8d17b53..1022c57 100644 --- a/codegen/cmake/AddService.cmake +++ b/codegen/cmake/AddService.cmake @@ -1,24 +1,27 @@ function(add_service SERVICE_NAME JSON_FILE) - add_library(${SERVICE_NAME} OBJECT EXCLUDE_FROM_ALL - ) + add_library(${SERVICE_NAME} OBJECT EXCLUDE_FROM_ALL) target_add_service(${SERVICE_NAME} ${SERVICE_NAME} ${JSON_FILE}) endfunction() function(target_add_service TARGET_NAME SERVICE_NAME JSON_FILE) # generate the output directory, otherwise python will complain when multiple instances are run file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated/include) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_NAME}Base.hpp ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_NAME}Base.cpp - COMMAND ${Python3_EXECUTABLE} -m cogapp -d -I ${XBOT_CODEGEN_PATH}/xbot_codegen -D service_file=${JSON_FILE} -o ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_NAME}Base.hpp ${XBOT_CODEGEN_PATH}/templates/ServiceTemplate.hpp - COMMAND ${Python3_EXECUTABLE} -m cogapp -d -I ${XBOT_CODEGEN_PATH}/xbot_codegen -D service_file=${JSON_FILE} -o ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_NAME}Base.cpp ${XBOT_CODEGEN_PATH}/templates/ServiceTemplate.cpp - DEPENDS ${XBOT_CODEGEN_PATH}/templates/ServiceTemplate.hpp ${XBOT_CODEGEN_PATH}/templates/ServiceTemplate.cpp ${JSON_FILE} - COMMENT "Generating code for service ${SERVICE_NAME}." - ) - target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_NAME}Base.cpp - ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_NAME}Base.hpp + set(COG_BASE_ARGS -m cogapp -d -I ${XBOT_CODEGEN_PATH}/xbot_codegen -Dservice_file=${JSON_FILE}) + set(HEADER_TEMPLATE ${XBOT_CODEGEN_PATH}/templates/ServiceTemplate.hpp) + set(SOURCE_TEMPLATE ${XBOT_CODEGEN_PATH}/templates/ServiceTemplate.cpp) + set(HEADER_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_NAME}Base.hpp) + set(SOURCE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_NAME}Base.cpp) + + add_custom_command( + OUTPUT ${HEADER_OUTPUT} ${SOURCE_OUTPUT} + COMMAND ${Python3_EXECUTABLE} ${COG_BASE_ARGS} -o ${HEADER_OUTPUT} ${HEADER_TEMPLATE} + COMMAND ${Python3_EXECUTABLE} ${COG_BASE_ARGS} -o ${SOURCE_OUTPUT} ${SOURCE_TEMPLATE} + DEPENDS ${HEADER_TEMPLATE} ${SOURCE_TEMPLATE} ${XBOT_CODEGEN_PATH}/xbot_codegen/xbot_codegen.py ${JSON_FILE} + COMMENT "Generating code for service ${SERVICE_NAME}." ) + target_sources(${TARGET_NAME} PRIVATE ${SOURCE_OUTPUT} ${HEADER_OUTPUT}) target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/generated/include) target_link_libraries(${TARGET_NAME} PUBLIC xbot-service) endfunction() diff --git a/codegen/cmake/AddServiceInterface.cmake b/codegen/cmake/AddServiceInterface.cmake index 6a0b641..495a38b 100644 --- a/codegen/cmake/AddServiceInterface.cmake +++ b/codegen/cmake/AddServiceInterface.cmake @@ -1,24 +1,27 @@ function(add_service_interface SERVICE_INTERFACE_NAME JSON_FILE) - add_library(${SERVICE_INTERFACE_NAME} OBJECT EXCLUDE_FROM_ALL - ) + add_library(${SERVICE_INTERFACE_NAME} OBJECT EXCLUDE_FROM_ALL) target_add_service_interface(${SERVICE_INTERFACE_NAME} ${SERVICE_INTERFACE_NAME} ${JSON_FILE}) endfunction() function(target_add_service_interface TARGET_NAME SERVICE_INTERFACE_NAME JSON_FILE) # generate the output directory, otherwise python will complain when multiple instances are run file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated/include) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_INTERFACE_NAME}Base.hpp ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_INTERFACE_NAME}Base.cpp - COMMAND ${Python3_EXECUTABLE} -m cogapp -d -I ${XBOT_CODEGEN_PATH}/xbot_codegen -D service_file=${JSON_FILE} -o ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_INTERFACE_NAME}Base.hpp ${XBOT_CODEGEN_PATH}/templates/ServiceInterfaceTemplate.hpp - COMMAND ${Python3_EXECUTABLE} -m cogapp -d -I ${XBOT_CODEGEN_PATH}/xbot_codegen -D service_file=${JSON_FILE} -o ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_INTERFACE_NAME}Base.cpp ${XBOT_CODEGEN_PATH}/templates/ServiceInterfaceTemplate.cpp - DEPENDS ${XBOT_CODEGEN_PATH}/templates/ServiceInterfaceTemplate.hpp ${XBOT_CODEGEN_PATH}/templates/ServiceInterfaceTemplate.cpp ${JSON_FILE} - COMMENT "Generating code for service interface ${SERVICE_INTERFACE_NAME}." - ) - target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_INTERFACE_NAME}Base.cpp - ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_INTERFACE_NAME}Base.hpp + set(COG_BASE_ARGS -m cogapp -d -I ${XBOT_CODEGEN_PATH}/xbot_codegen -Dservice_file=${JSON_FILE}) + set(HEADER_TEMPLATE ${XBOT_CODEGEN_PATH}/templates/ServiceInterfaceTemplate.hpp) + set(SOURCE_TEMPLATE ${XBOT_CODEGEN_PATH}/templates/ServiceInterfaceTemplate.cpp) + set(HEADER_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_INTERFACE_NAME}Base.hpp) + set(SOURCE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_INTERFACE_NAME}Base.cpp) + + add_custom_command( + OUTPUT ${HEADER_OUTPUT} ${SOURCE_OUTPUT} + COMMAND ${Python3_EXECUTABLE} ${COG_BASE_ARGS} -o ${HEADER_OUTPUT} ${HEADER_TEMPLATE} + COMMAND ${Python3_EXECUTABLE} ${COG_BASE_ARGS} -o ${SOURCE_OUTPUT} ${SOURCE_TEMPLATE} + DEPENDS ${HEADER_TEMPLATE} ${SOURCE_TEMPLATE} ${XBOT_CODEGEN_PATH}/xbot_codegen/xbot_codegen.py ${JSON_FILE} + COMMENT "Generating code for service interface ${SERVICE_INTERFACE_NAME}." ) + target_sources(${TARGET_NAME} PRIVATE ${SOURCE_OUTPUT} ${HEADER_OUTPUT}) target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/generated/include) target_link_libraries(${TARGET_NAME} PUBLIC xbot-service-interface) endfunction() From b2b143f9bc4b5768f0e3aa688d5d6d117893b897 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 10 May 2025 19:59:40 +0000 Subject: [PATCH 03/12] Add ability to inject a ServiceExt class --- codegen/cmake/AddService.cmake | 4 ++++ codegen/templates/ServiceTemplate.hpp | 16 ++++++++++------ libxbot-service/include/xbot-service/Service.hpp | 2 ++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/codegen/cmake/AddService.cmake b/codegen/cmake/AddService.cmake index 1022c57..d84f9b3 100644 --- a/codegen/cmake/AddService.cmake +++ b/codegen/cmake/AddService.cmake @@ -13,6 +13,10 @@ function(target_add_service TARGET_NAME SERVICE_NAME JSON_FILE) set(HEADER_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/include/${SERVICE_NAME}Base.hpp) set(SOURCE_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/${SERVICE_NAME}Base.cpp) + if (DEFINED XBOT_SERVICE_EXT) + list(APPEND COG_BASE_ARGS -Dservice_ext="${XBOT_SERVICE_EXT}") + endif () + add_custom_command( OUTPUT ${HEADER_OUTPUT} ${SOURCE_OUTPUT} COMMAND ${Python3_EXECUTABLE} ${COG_BASE_ARGS} -o ${HEADER_OUTPUT} ${HEADER_TEMPLATE} diff --git a/codegen/templates/ServiceTemplate.hpp b/codegen/templates/ServiceTemplate.hpp index 18e47c9..35648e8 100644 --- a/codegen/templates/ServiceTemplate.hpp +++ b/codegen/templates/ServiceTemplate.hpp @@ -16,7 +16,11 @@ cog.outl(f"#define {service['class_name'].upper()}_HPP") #define SERVICETEMPLATEBASE_HPP //[[[end]]] +/*[[[cog +cog.outl(f"#include <{vars().get('service_ext', 'xbot-service/Service.hpp')}>") +]]]*/ #include +//[[[end]]] /*[[[cog xbot_codegen.generateEnums(service) @@ -37,7 +41,8 @@ namespace ExampleBitmaskEnum { //[[[end]]] /*[[[cog -cog.outl(f"class {service['class_name']} : public xbot::service::Service {{") +service_class = 'ServiceExt' if 'service_ext' in vars() else 'Service' +cog.outl(f"class {service['class_name']} : public xbot::service::{service_class} {{") ]]]*/ class ServiceTemplateBase : public xbot::service::Service { //[[[end]]] @@ -45,21 +50,20 @@ class ServiceTemplateBase : public xbot::service::Service { /*[[[cog cog.outl("#ifdef XBOT_ENABLE_STATIC_STACK") cog.outl(f"explicit {service['class_name']}(uint16_t service_id, void* stack, size_t stack_size)") + cog.outl(f" : {service_class}(service_id, stack, stack_size) {{") cog.outl("#else") cog.outl(f"explicit {service['class_name']}(uint16_t service_id)") + cog.outl(f" : {service_class}(service_id, nullptr, 0) {{") cog.outl("#endif") ]]]*/ #ifdef XBOT_ENABLE_STATIC_STACK explicit ServiceTemplateBase(uint16_t service_id, void* stack, size_t stack_size) - #else - explicit ServiceTemplateBase(uint16_t service_id) - #endif - //[[[end]]] - #ifdef XBOT_ENABLE_STATIC_STACK : Service(service_id, stack, stack_size) { #else + explicit ServiceTemplateBase(uint16_t service_id) : Service(service_id, nullptr, 0) { #endif + //[[[end]]] } /*[[[cog diff --git a/libxbot-service/include/xbot-service/Service.hpp b/libxbot-service/include/xbot-service/Service.hpp index bf59617..9f1a9fc 100644 --- a/libxbot-service/include/xbot-service/Service.hpp +++ b/libxbot-service/include/xbot-service/Service.hpp @@ -17,6 +17,8 @@ namespace xbot::service { class Service : public ServiceIo { + friend class ServiceExt; + public: explicit Service(uint16_t service_id, void *processing_thread_stack, size_t processing_thread_stack_size); From 627e9f524e8de3f991c50cf09d62877e1ed0dd75 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 10 May 2025 20:10:55 +0000 Subject: [PATCH 04/12] Add extension function inside service loop --- libxbot-service/include/xbot-service/Service.hpp | 4 ++++ libxbot-service/src/Service.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/libxbot-service/include/xbot-service/Service.hpp b/libxbot-service/include/xbot-service/Service.hpp index 9f1a9fc..e65c208 100644 --- a/libxbot-service/include/xbot-service/Service.hpp +++ b/libxbot-service/include/xbot-service/Service.hpp @@ -137,6 +137,10 @@ class Service : public ServiceIo { void heartbeat(); void runProcessing(); + virtual void OnLoop(uint32_t now_micros, uint32_t last_tick_micros) { + (void)now_micros; + (void)last_tick_micros; + }; void HandleClaimMessage(datatypes::XbotHeader *header, const void *payload, size_t payload_len); void HandleDataMessage(datatypes::XbotHeader *header, const void *payload, size_t payload_len); diff --git a/libxbot-service/src/Service.cpp b/libxbot-service/src/Service.cpp index 5255ff2..219f1c3 100644 --- a/libxbot-service/src/Service.cpp +++ b/libxbot-service/src/Service.cpp @@ -217,6 +217,7 @@ void xbot::service::Service::runProcessing() { // Run schedules. uint32_t now_micros = system::getTimeMicros(); + OnLoop(now_micros, last_tick_micros); uint32_t block_time = scheduler_.Tick(now_micros - last_tick_micros); if (block_time > 1'000'000) { block_time = 1'000'000; From 6d21b06fdbae9df19c4d990ae83405bcaa6e70f3 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Mon, 12 May 2025 00:14:27 +0000 Subject: [PATCH 05/12] Add ServiceSchedule --- examples/services/EchoService/EchoService.hpp | 3 +-- libxbot-service/include/xbot-service/Service.hpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/services/EchoService/EchoService.hpp b/examples/services/EchoService/EchoService.hpp index e0072d2..4cd289f 100644 --- a/examples/services/EchoService/EchoService.hpp +++ b/examples/services/EchoService/EchoService.hpp @@ -16,8 +16,7 @@ class EchoService : public EchoServiceBase { private: void tick(); - ManagedSchedule tick_schedule_{scheduler_, IsRunning(), 1'000'000, - XBOT_FUNCTION_FOR_METHOD(EchoService, &EchoService::tick, this)}; + ServiceSchedule tick_schedule_{*this, 1'000'000, XBOT_FUNCTION_FOR_METHOD(EchoService, &EchoService::tick, this)}; uint32_t echo_count = 0; diff --git a/libxbot-service/include/xbot-service/Service.hpp b/libxbot-service/include/xbot-service/Service.hpp index e65c208..d82a0ce 100644 --- a/libxbot-service/include/xbot-service/Service.hpp +++ b/libxbot-service/include/xbot-service/Service.hpp @@ -16,8 +16,10 @@ #include "xbot/datatypes/XbotHeader.hpp" namespace xbot::service { + class Service : public ServiceIo { friend class ServiceExt; + friend class ServiceSchedule; public: explicit Service(uint16_t service_id, void *processing_thread_stack, size_t processing_thread_stack_size); @@ -169,6 +171,14 @@ class Service : public ServiceIo { virtual bool setRegister(uint16_t target_id, const void *payload, size_t length) = 0; }; + +class ServiceSchedule : public ManagedSchedule { + public: + explicit ServiceSchedule(Service &service, uint32_t interval, Callback callback) + : ManagedSchedule(service.scheduler_, service.IsRunning(), interval, callback) { + } +}; + } // namespace xbot::service #endif // SERVICE_HPP From 707b97c6ae2e061af722252aeb1c3d20af13a2d9 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Wed, 14 May 2025 00:10:59 +0000 Subject: [PATCH 06/12] Don't discard remote logs with less than 2 characters --- .../src/RemoteLoggingReceiverImpl.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/libxbot-service-interface/src/RemoteLoggingReceiverImpl.cpp b/libxbot-service-interface/src/RemoteLoggingReceiverImpl.cpp index 392c26f..9fc45fc 100644 --- a/libxbot-service-interface/src/RemoteLoggingReceiverImpl.cpp +++ b/libxbot-service-interface/src/RemoteLoggingReceiverImpl.cpp @@ -52,16 +52,14 @@ void RemoteLoggingReceiverImpl::Run() { // Validate reported length if (packet.size() == header->payload_size + sizeof(datatypes::XbotHeader)) { - if (header->payload_size > 1) { - std::string_view view{reinterpret_cast(packet.data() + sizeof(datatypes::XbotHeader)), - header->payload_size}; - switch (header->arg1) { - case 1: spdlog::trace(">>> {}", view); break; - case 2: spdlog::debug(">>> {}", view); break; - case 3: spdlog::info(">>> {}", view); break; - case 4: spdlog::warn(">>> {}", view); break; - default: spdlog::error(">>> {}", view); break; - } + std::string_view view{reinterpret_cast(packet.data() + sizeof(datatypes::XbotHeader)), + header->payload_size}; + switch (header->arg1) { + case 1: spdlog::trace(">>> {}", view); break; + case 2: spdlog::debug(">>> {}", view); break; + case 3: spdlog::info(">>> {}", view); break; + case 4: spdlog::warn(">>> {}", view); break; + default: spdlog::error(">>> {}", view); break; } } } From 87a32771fcce992a7219b0c21b5748fb6786c5e1 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 24 May 2025 20:10:26 +0000 Subject: [PATCH 07/12] Add heatshrink dependency --- ext/CMakeLists.txt | 2 ++ ext/heatshrink/CMakeLists.txt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 ext/heatshrink/CMakeLists.txt diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 4248e80..1f82c3b 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -9,3 +9,5 @@ if (XBOT_BUILD_LIB_SERVICE_INTERFACE) add_subdirectory(spdlog) add_subdirectory(crow) endif () + +add_subdirectory(heatshrink) diff --git a/ext/heatshrink/CMakeLists.txt b/ext/heatshrink/CMakeLists.txt new file mode 100644 index 0000000..7d168fe --- /dev/null +++ b/ext/heatshrink/CMakeLists.txt @@ -0,0 +1,16 @@ +include(FetchContent) + +# Use "develop" branch +FetchContent_Declare(heatshrink URL https://github.com/atomicobject/heatshrink/archive/e4084caa5c570c5e733de6b12ee1c9000c52d1b4.tar.gz) +FetchContent_MakeAvailable(heatshrink) + +add_library(heatshrink STATIC + ${heatshrink_SOURCE_DIR}/src/heatshrink_decoder.c + ${heatshrink_SOURCE_DIR}/src/heatshrink_encoder.c +) + +target_include_directories(heatshrink + PUBLIC + ${heatshrink_SOURCE_DIR}/include + ${heatshrink_SOURCE_DIR}/src +) From d3bde95771ca297e89a0b0bc7bcc47fb1c388b56 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 24 May 2025 22:25:55 +0200 Subject: [PATCH 08/12] Helper for heatshrink encoder --- libxbot-service-interface/CMakeLists.txt | 3 +- .../HeatshrinkEncode.hpp | 4 +++ .../src/HeatshrinkEncode.cpp | 30 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 libxbot-service-interface/include/xbot-service-interface/HeatshrinkEncode.hpp create mode 100644 libxbot-service-interface/src/HeatshrinkEncode.cpp diff --git a/libxbot-service-interface/CMakeLists.txt b/libxbot-service-interface/CMakeLists.txt index bcd8c53..e04ca2c 100644 --- a/libxbot-service-interface/CMakeLists.txt +++ b/libxbot-service-interface/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(xbot-service-interface src/ServiceIOImpl.hpp src/XbotServiceInterface.cpp src/RemoteLoggingReceiverImpl.cpp + src/HeatshrinkEncode.cpp ) target_include_directories(xbot-service-interface PUBLIC @@ -22,7 +23,7 @@ target_include_directories(xbot-service-interface PUBLIC ../include ) target_link_libraries(xbot-service-interface PUBLIC - nlohmann_json::nlohmann_json spdlog::spdlog Crow::Crow + nlohmann_json::nlohmann_json spdlog::spdlog Crow::Crow heatshrink ) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include diff --git a/libxbot-service-interface/include/xbot-service-interface/HeatshrinkEncode.hpp b/libxbot-service-interface/include/xbot-service-interface/HeatshrinkEncode.hpp new file mode 100644 index 0000000..559d133 --- /dev/null +++ b/libxbot-service-interface/include/xbot-service-interface/HeatshrinkEncode.hpp @@ -0,0 +1,4 @@ +#include +#include + +std::vector HeatshrinkEncode(uint8_t *data, const size_t size); diff --git a/libxbot-service-interface/src/HeatshrinkEncode.cpp b/libxbot-service-interface/src/HeatshrinkEncode.cpp new file mode 100644 index 0000000..83dd3d5 --- /dev/null +++ b/libxbot-service-interface/src/HeatshrinkEncode.cpp @@ -0,0 +1,30 @@ +#include + +extern "C" { +#include +} + +std::vector HeatshrinkEncode(uint8_t *data, const size_t size) { + heatshrink_encoder *encoder = heatshrink_encoder_alloc(8, 4); + std::vector out; + uint8_t buf[128]; + size_t processed = 0, processed_now, polled_now; + HSE_poll_res poll_res; + + while (processed < size) { + heatshrink_encoder_sink(encoder, &data[processed], size - processed, &processed_now); + processed += processed_now; + + do { + poll_res = heatshrink_encoder_poll(encoder, buf, sizeof(buf), &polled_now); + out.insert(out.end(), buf, buf + polled_now); + } while (poll_res == HSER_POLL_MORE); + } + + while (heatshrink_encoder_finish(encoder) == HSER_FINISH_MORE) { + heatshrink_encoder_poll(encoder, buf, sizeof(buf), &polled_now); + out.insert(out.end(), buf, buf + polled_now); + } + + return out; +} From 981fa0a26f6804827e86ff4a73bf23e585e916f2 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 24 May 2025 20:56:16 +0000 Subject: [PATCH 09/12] Add DataSource as compression abstraction --- libxbot-service/CMakeLists.txt | 4 +- .../include/xbot-service/DataSource.hpp | 76 +++++++++++++++++++ libxbot-service/src/DataSource.cpp | 31 ++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 libxbot-service/include/xbot-service/DataSource.hpp create mode 100644 libxbot-service/src/DataSource.cpp diff --git a/libxbot-service/CMakeLists.txt b/libxbot-service/CMakeLists.txt index 971dfb7..6f0abbc 100644 --- a/libxbot-service/CMakeLists.txt +++ b/libxbot-service/CMakeLists.txt @@ -15,8 +15,10 @@ add_library(xbot-service STATIC src/Schedule.cpp src/Scheduler.cpp src/ServiceIo.cpp + src/DataSource.cpp ) target_compile_options(xbot-service PRIVATE -Wall -Wextra -Werror -Wno-volatile) +target_compile_definitions(heatshrink PUBLIC HEATSHRINK_DYNAMIC_ALLOC=0) if (NOT DEFINED XBOT_CUSTOM_PORT_PATH) SET(XBOT_CUSTOM_PORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/portable/linux) @@ -31,7 +33,7 @@ target_sources(xbot-service PRIVATE ${PORT_SOURCES}) target_include_directories(xbot-service PUBLIC include ../include) -target_link_libraries(xbot-service PUBLIC ulog ${XBOT_CUSTOM_PORT_LIBS}) +target_link_libraries(xbot-service PUBLIC ulog ${XBOT_CUSTOM_PORT_LIBS} heatshrink) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include diff --git a/libxbot-service/include/xbot-service/DataSource.hpp b/libxbot-service/include/xbot-service/DataSource.hpp new file mode 100644 index 0000000..9324c07 --- /dev/null +++ b/libxbot-service/include/xbot-service/DataSource.hpp @@ -0,0 +1,76 @@ +#include +#include + +extern "C" { +#include +} + +namespace xbot::service { + +class DataSource { + public: + DataSource(const uint8_t* data, const size_t size) : data_(data), size_(size) { + } + + virtual bool HasNext() = 0; + virtual uint8_t Next() = 0; + virtual void Rewind() = 0; + virtual size_t Position() { + return pos_; + }; + + protected: + const uint8_t* data_; + const size_t size_; + size_t pos_ = 0; +}; + +class RawDataSource : public DataSource { + public: + using DataSource::DataSource; + + bool HasNext() override { + return pos_ < size_; + } + + uint8_t Next() override { + return data_[pos_++]; + } + + void Rewind() override { + pos_ = 0; + } +}; + +class HeatshrinkDataSource : public DataSource { + public: + using DataSource::DataSource; + + bool HasNext() override { + return buf_pos_ < buf_size_ || Decode(); + } + + uint8_t Next() override { + pos_++; + return buf_[buf_pos_++]; + } + + void Rewind() override { + heatshrink_decoder_reset(&decoder_); + pos_ = 0; + compressed_pos_ = 0; + buf_pos_ = 0; + buf_size_ = 0; + } + + private: + heatshrink_decoder decoder_; + size_t compressed_pos_; + uint8_t buf_[32]; + size_t buf_pos_; + size_t buf_size_; + + bool Decode(); +}; + +} // namespace xbot::service diff --git a/libxbot-service/src/DataSource.cpp b/libxbot-service/src/DataSource.cpp new file mode 100644 index 0000000..b0c1617 --- /dev/null +++ b/libxbot-service/src/DataSource.cpp @@ -0,0 +1,31 @@ +#include + +namespace xbot::service { + +bool HeatshrinkDataSource::Decode() { + size_t processed_now, polled_now; + HSD_poll_res poll_res; + buf_pos_ = 0; + buf_size_ = 0; + while (compressed_pos_ < size_) { + heatshrink_decoder_sink(&decoder_, const_cast(&data_[compressed_pos_]), size_ - compressed_pos_, + &processed_now); + compressed_pos_ += processed_now; + + do { + poll_res = heatshrink_decoder_poll(&decoder_, &buf_[buf_size_], sizeof(buf_) - buf_size_, &polled_now); + buf_size_ += polled_now; + if (buf_size_ == sizeof(buf_)) return true; + } while (poll_res == HSDR_POLL_MORE); + } + + while (heatshrink_decoder_finish(&decoder_) == HSDR_FINISH_MORE) { + heatshrink_decoder_poll(&decoder_, &buf_[buf_size_], sizeof(buf_) - buf_size_, &polled_now); + buf_size_ += polled_now; + if (buf_size_ == sizeof(buf_)) return true; + } + + return buf_size_ > 0; +} + +} // namespace xbot::service From d42951cae08e129ab2162cd13f072268235f0dfc Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 24 May 2025 22:58:49 +0200 Subject: [PATCH 10/12] Patch lwjson to compile with older CMake --- ext/lwjson/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/lwjson/CMakeLists.txt b/ext/lwjson/CMakeLists.txt index cd4e033..b20e785 100644 --- a/ext/lwjson/CMakeLists.txt +++ b/ext/lwjson/CMakeLists.txt @@ -2,5 +2,7 @@ include(FetchContent) FetchContent_Declare( lwjson URL https://github.com/MaJerle/lwjson/archive/97dfff90b12772d1ec5f4bf4b80cdfe187693423.tar.gz + PATCH_COMMAND sed -i "s/^cmake_minimum_required.*$/cmake_minimum_required(VERSION 3.16)/" CMakeLists.txt lwjson/CMakeLists.txt + ) FetchContent_MakeAvailable(lwjson) From bd70f29b5614e6701763ac788455c8cf829c4a05 Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Sat, 24 May 2025 23:58:45 +0200 Subject: [PATCH 11/12] Use static alloc heatshrink encoder They don't mix well if compiled both into one static library. --- ext/heatshrink/CMakeLists.txt | 6 ++++++ libxbot-service-interface/src/HeatshrinkEncode.cpp | 11 ++++++----- libxbot-service/CMakeLists.txt | 1 - 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ext/heatshrink/CMakeLists.txt b/ext/heatshrink/CMakeLists.txt index 7d168fe..e1db8d0 100644 --- a/ext/heatshrink/CMakeLists.txt +++ b/ext/heatshrink/CMakeLists.txt @@ -9,6 +9,12 @@ add_library(heatshrink STATIC ${heatshrink_SOURCE_DIR}/src/heatshrink_encoder.c ) +target_compile_definitions(heatshrink PUBLIC + HEATSHRINK_DYNAMIC_ALLOC=0 + HEATSHRINK_STATIC_WINDOW_BITS=9 + HEATSHRINK_STATIC_LOOKAHEAD_BITS=5 +) + target_include_directories(heatshrink PUBLIC ${heatshrink_SOURCE_DIR}/include diff --git a/libxbot-service-interface/src/HeatshrinkEncode.cpp b/libxbot-service-interface/src/HeatshrinkEncode.cpp index 83dd3d5..56b56da 100644 --- a/libxbot-service-interface/src/HeatshrinkEncode.cpp +++ b/libxbot-service-interface/src/HeatshrinkEncode.cpp @@ -5,24 +5,25 @@ extern "C" { } std::vector HeatshrinkEncode(uint8_t *data, const size_t size) { - heatshrink_encoder *encoder = heatshrink_encoder_alloc(8, 4); + heatshrink_encoder encoder; std::vector out; uint8_t buf[128]; size_t processed = 0, processed_now, polled_now; HSE_poll_res poll_res; + heatshrink_encoder_reset(&encoder); while (processed < size) { - heatshrink_encoder_sink(encoder, &data[processed], size - processed, &processed_now); + heatshrink_encoder_sink(&encoder, &data[processed], size - processed, &processed_now); processed += processed_now; do { - poll_res = heatshrink_encoder_poll(encoder, buf, sizeof(buf), &polled_now); + poll_res = heatshrink_encoder_poll(&encoder, buf, sizeof(buf), &polled_now); out.insert(out.end(), buf, buf + polled_now); } while (poll_res == HSER_POLL_MORE); } - while (heatshrink_encoder_finish(encoder) == HSER_FINISH_MORE) { - heatshrink_encoder_poll(encoder, buf, sizeof(buf), &polled_now); + while (heatshrink_encoder_finish(&encoder) == HSER_FINISH_MORE) { + heatshrink_encoder_poll(&encoder, buf, sizeof(buf), &polled_now); out.insert(out.end(), buf, buf + polled_now); } diff --git a/libxbot-service/CMakeLists.txt b/libxbot-service/CMakeLists.txt index 6f0abbc..ed204c4 100644 --- a/libxbot-service/CMakeLists.txt +++ b/libxbot-service/CMakeLists.txt @@ -18,7 +18,6 @@ add_library(xbot-service STATIC src/DataSource.cpp ) target_compile_options(xbot-service PRIVATE -Wall -Wextra -Werror -Wno-volatile) -target_compile_definitions(heatshrink PUBLIC HEATSHRINK_DYNAMIC_ALLOC=0) if (NOT DEFINED XBOT_CUSTOM_PORT_PATH) SET(XBOT_CUSTOM_PORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/portable/linux) From bc2e3b4570612318a1e21b21de9842a9ad9f8eec Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Thu, 5 Jun 2025 22:40:15 +0000 Subject: [PATCH 12/12] Allow OnLoop() to return the maximum blocking time --- libxbot-service/include/xbot-service/Service.hpp | 3 ++- libxbot-service/src/Service.cpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libxbot-service/include/xbot-service/Service.hpp b/libxbot-service/include/xbot-service/Service.hpp index d82a0ce..a8ffbc0 100644 --- a/libxbot-service/include/xbot-service/Service.hpp +++ b/libxbot-service/include/xbot-service/Service.hpp @@ -139,9 +139,10 @@ class Service : public ServiceIo { void heartbeat(); void runProcessing(); - virtual void OnLoop(uint32_t now_micros, uint32_t last_tick_micros) { + virtual uint32_t OnLoop(uint32_t now_micros, uint32_t last_tick_micros) { (void)now_micros; (void)last_tick_micros; + return UINT32_MAX; }; void HandleClaimMessage(datatypes::XbotHeader *header, const void *payload, size_t payload_len); diff --git a/libxbot-service/src/Service.cpp b/libxbot-service/src/Service.cpp index 219f1c3..f64538c 100644 --- a/libxbot-service/src/Service.cpp +++ b/libxbot-service/src/Service.cpp @@ -217,8 +217,9 @@ void xbot::service::Service::runProcessing() { // Run schedules. uint32_t now_micros = system::getTimeMicros(); - OnLoop(now_micros, last_tick_micros); - uint32_t block_time = scheduler_.Tick(now_micros - last_tick_micros); + uint32_t on_loop_block_time = OnLoop(now_micros, last_tick_micros); + uint32_t scheduler_block_time = scheduler_.Tick(now_micros - last_tick_micros); + uint32_t block_time = on_loop_block_time < scheduler_block_time ? on_loop_block_time : scheduler_block_time; if (block_time > 1'000'000) { block_time = 1'000'000; }