From 081f8046d5370f6042005f6f907ffe8e327424a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunics=20Bal=C3=A1zs?= Date: Wed, 3 Jun 2026 22:51:20 +0200 Subject: [PATCH 1/4] Fix DDOP filename creation. --- src/task_controller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/task_controller.cpp b/src/task_controller.cpp index ce6a2b0..919ac31 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -295,8 +295,8 @@ bool MyTCServer::activate_object_pool(std::shared_ptr p auto labelBytes = deviceObject->get_localization_label(); std::string label(reinterpret_cast(labelBytes.data()), labelBytes.size()); - // trim at first occurrence of null or ETX (0x03) - auto it = std::find_if(label.begin(), label.end(), [](char c) { return c == '\0' || static_cast(c) == 0x03; }); + // trim at first non-printable character (control chars, DEL, etc.) + auto it = std::find_if(label.begin(), label.end(), [](unsigned char c) { return c < 0x20 || c >= 0x7F; }); label.erase(it, label.end()); auto fileName = std::to_string(partnerCF->get_NAME().get_full_name()) + "\\" + label + ".ddop"; From 733f21b820e8705dfe2ddfa7bf1421fa7be52c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gunics?= Date: Mon, 15 Jun 2026 11:31:36 +0200 Subject: [PATCH 2/4] Patch cmake policy for the build to succeed --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0763f95..e3ffc41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.16) project("AOG-TaskController") set(PROJECT_VERSION_MAJOR 1) -set(PROJECT_VERSION_MINOR 3) -set(PROJECT_VERSION_PATCH 3) +set(PROJECT_VERSION_MINOR 5) +set(PROJECT_VERSION_PATCH 0) set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}" ) @@ -61,8 +61,8 @@ FetchContent_MakeAvailable(Boost) FetchContent_Declare( isobus - GIT_REPOSITORY https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git - GIT_TAG 869430f0347188e0f8dc0740d6c6a1cfcce37706 + GIT_REPOSITORY https://github.com/gunicsba/AgIsoStack-plus-plus.git + GIT_TAG d4d36dd6f15af6c6f6218317572025a1ffec6883 DOWNLOAD_EXTRACT_TIMESTAMP TRUE) FetchContent_MakeAvailable(isobus) @@ -73,11 +73,13 @@ FetchContent_Declare( FetchContent_MakeAvailable(json) include(FetchContent) +set(CMAKE_POLICY_VERSION_MINIMUM 3.5) FetchContent_Declare( git_version GIT_REPOSITORY https://github.com/andrew-hardin/cmake-git-version-tracking.git GIT_TAG 904dbda1336ba4b9a1415a68d5f203f576b696bb) FetchContent_MakeAvailable(git_version) +unset(CMAKE_POLICY_VERSION_MINIMUM) find_package(Threads REQUIRED) From 191d83a889311dfa6c6f408d394c3ba3a9209ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gunics?= Date: Mon, 15 Jun 2026 12:11:24 +0200 Subject: [PATCH 3/4] upcoming stack changes --- include/task_controller.hpp | 1 + src/task_controller.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/include/task_controller.hpp b/include/task_controller.hpp index d1ca919..490e3c9 100644 --- a/include/task_controller.hpp +++ b/include/task_controller.hpp @@ -87,6 +87,7 @@ class MyTCServer : public isobus::TaskControllerServer bool get_is_enough_memory_available(std::uint32_t) override; void identify_task_controller(std::uint8_t) override; void on_client_timeout(std::shared_ptr partner) override; + void on_client_version_received(std::shared_ptr clientControlFunction, std::uint8_t version) override; void on_process_data_acknowledge(std::shared_ptr partner, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, std::uint8_t errorCodesFromClient, ProcessDataCommands processDataCommand) override; bool on_value_command(std::shared_ptr partner, std::uint16_t dataDescriptionIndex, diff --git a/src/task_controller.cpp b/src/task_controller.cpp index 919ac31..799534b 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -420,6 +420,12 @@ void MyTCServer::on_client_timeout(std::shared_ptr part clients.erase(partner); } +void MyTCServer::on_client_version_received(std::shared_ptr clientControlFunction, std::uint8_t version) +{ + std::cout << "[" << get_timestamp() << "] [TC Server] Client " << clientControlFunction->get_NAME().get_full_name() + << " reported TC version " << static_cast(version) << std::endl; +} + void MyTCServer::on_process_data_acknowledge(std::shared_ptr partner, std::uint16_t dataDescriptionIndex, std::uint16_t elementNumber, From 6dba77d95afafbbb2a49552b59ff60a7e1e16e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Gunics?= Date: Mon, 15 Jun 2026 19:04:03 +0200 Subject: [PATCH 4/4] add more sanitization --- src/task_controller.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/task_controller.cpp b/src/task_controller.cpp index 799534b..169374a 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -17,6 +17,22 @@ #include #include +// Sanitize a string for use as a filename by replacing invalid characters with underscores +static std::string sanitize_filename(const std::string &input) +{ + std::string result = input; + // Characters invalid on Windows (and / is invalid on POSIX too) + const std::string invalid_chars = "\\/*:?\"<>|"; + for (char &c : result) + { + if (invalid_chars.find(c) != std::string::npos) + { + c = '_'; + } + } + return result; +} + void ClientState::set_number_of_sections(std::uint8_t number) { numberOfSections = number; @@ -299,7 +315,7 @@ bool MyTCServer::activate_object_pool(std::shared_ptr p auto it = std::find_if(label.begin(), label.end(), [](unsigned char c) { return c < 0x20 || c >= 0x7F; }); label.erase(it, label.end()); - auto fileName = std::to_string(partnerCF->get_NAME().get_full_name()) + "\\" + label + ".ddop"; + auto fileName = std::to_string(partnerCF->get_NAME().get_full_name()) + "/" + sanitize_filename(label) + ".ddop"; std::vector binaryPool; if (state.get_pool().generate_binary_object_pool(binaryPool)) { @@ -508,7 +524,8 @@ void MyTCServer::request_measurement_commands() { for (auto &client : clients) { - if (!client.second.are_measurement_commands_sent()) + // Skip clients with 0 sections (e.g. tractors) - sending measurement commands to a tractor ECU can cause unexpected behavior + if (!client.second.are_measurement_commands_sent() && client.second.get_number_of_sections() > 0) { // Find all actual (condensed) work state DDIs and request them to trigger "On Change" and "Time Interval" for (std::uint32_t i = 0; i < client.second.get_pool().size(); i++)