From 26421dd47fb4a21b63a20740714ccf78c6ef2130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunics=20Bal=C3=A1zs?= Date: Mon, 16 Mar 2026 22:38:57 +0100 Subject: [PATCH 1/6] This commit aims to fix the following: Add an extra parameter in the settings.json file under %APPDATA%/AOG-Taskcontroller to disable tecuFunction. (John Deere tractor didn't appreciate having a second TECU on the bus) and likely all the tractors with proper ISOBUS will be the same. Our TC discovered the tractor as ISOBUS implement and resulted in a section collision... --- CMakeLists.txt | 4 ++-- include/settings.hpp | 16 ++++++++++++++++ readme.md | 2 +- src/app.cpp | 20 +++++++++++++++++--- src/settings.cpp | 33 +++++++++++++++++++++++++++++++++ src/task_controller.cpp | 17 +++++++++++++++++ 6 files changed, 86 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c36781c..bffc7a3 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 2) -set(PROJECT_VERSION_PATCH 0) +set(PROJECT_VERSION_MINOR 3) +set(PROJECT_VERSION_PATCH 1) set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}" ) diff --git a/include/settings.hpp b/include/settings.hpp index 878adc0..97adac0 100644 --- a/include/settings.hpp +++ b/include/settings.hpp @@ -48,6 +48,20 @@ class Settings */ bool set_subnet(std::array subnet, bool save = true); + /** + * @brief Check if Tractor ECU is enabled + * @return True if TECU is enabled, false otherwise + */ + bool is_tecu_enabled() const; + + /** + * @brief Set the Tractor ECU enabled state + * @param enabled Whether to enable the Tractor ECU + * @param save Whether or not to save the settings to file + * @return True if the setting was set successfully, false otherwise + */ + bool set_tecu_enabled(bool enabled, bool save = true); + /** * @brief Get the absolute path to the settings file * @param filename The filename to get the path for @@ -57,5 +71,7 @@ class Settings private: constexpr static std::array DEFAULT_SUBNET = { 192, 168, 5 }; + constexpr static bool DEFAULT_TECU_ENABLED = true; std::array configuredSubnet = DEFAULT_SUBNET; + bool tecuEnabled = DEFAULT_TECU_ENABLED; }; diff --git a/readme.md b/readme.md index 190a36f..ce5dfef 100644 --- a/readme.md +++ b/readme.md @@ -25,7 +25,7 @@ Then, you can run the following commands: ```bash mkdir build -cmake -S . -B build -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -Wno-dev +cmake -S . -B build -DBUILD_EXAMPLES=OFF -DCMAKE_POLICY_VERSION_MINIMUM="3.16" -DBUILD_TESTING=OFF -Wno-dev cmake --build build --config Release --target package ``` diff --git a/src/app.cpp b/src/app.cpp index e11ba02..f5cce0d 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -84,8 +84,8 @@ bool Application::initialize() // Create TECU control function // TODO: Should we wait between this and TC? // TODO: If there's already a TECU on the bus we should not create ours - if (tcCF) - { // Only create TECU if TC was created + if (tcCF && settings->is_tecu_enabled()) + { // Only create TECU if TC was created and ECU is enabled std::cout << "[Init] Creating Tractor ECU control function..." << std::endl; tecuCF = isobus::CANNetworkManager::CANNetwork.create_internal_control_function(tecuNAME, 0, isobus::preferred_addresses::IndustryGroup2::TractorECU); std::cout << "[Init] Tractor ECU control function created, waiting 1.5 seconds..." << std::endl; @@ -125,7 +125,14 @@ bool Application::initialize() } else { - std::cout << "[Warning] TECU Control Function not available, Speed/NMEA interfaces not created" << std::endl; + if (!settings->is_tecu_enabled()) + { + std::cout << "[Info] Tractor ECU disabled in settings, skipping ECU initialization." << std::endl; + } + else + { + std::cout << "[Warning] TECU Control Function not available, Speed/NMEA interfaces not created" << std::endl; + } } std::cout << "Task controller server started." << std::endl; @@ -248,6 +255,13 @@ bool Application::update() for (auto &client : tcServer->get_clients()) { auto &state = client.second; + + // Skip clients with no sections (e.g., tractors or non-implement devices) + if (state.get_number_of_sections() == 0) + { + continue; + } + std::vector data = { state.is_section_control_enabled(), state.get_number_of_sections() }; std::uint8_t sectionIndex = 0; diff --git a/src/settings.cpp b/src/settings.cpp index c6c5cf9..d8a7474 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -44,6 +44,23 @@ bool Settings::load() configuredSubnet = DEFAULT_SUBNET; // Key not found, use default } + if (data.contains("tecuEnabled")) + { + try + { + tecuEnabled = data["tecuEnabled"].get(); + } + catch (const nlohmann::json::exception &e) + { + std::cout << "Error parsing 'tecuEnabled': " << e.what() << std::endl; + tecuEnabled = DEFAULT_TECU_ENABLED; // Fallback to default + } + } + else + { + tecuEnabled = DEFAULT_TECU_ENABLED; // Key not found, use default + } + return true; } @@ -51,6 +68,7 @@ bool Settings::save() const { json data; data["subnet"] = configuredSubnet; + data["tecuEnabled"] = tecuEnabled; std::ofstream file(get_filename_path("settings.json")); if (!file.is_open()) @@ -82,6 +100,21 @@ bool Settings::set_subnet(std::array subnet, bool save) return true; } +bool Settings::is_tecu_enabled() const +{ + return tecuEnabled; +} + +bool Settings::set_tecu_enabled(bool enabled, bool save) +{ + tecuEnabled = enabled; + if (save) + { + return this->save(); + } + return true; +} + std::string Settings::get_filename_path(std::string fileName) { char path[MAX_PATH]; diff --git a/src/task_controller.cpp b/src/task_controller.cpp index 3d7e61f..0022867 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -238,6 +238,7 @@ MyTCServer::MyTCServer(std::shared_ptr internal bool MyTCServer::activate_object_pool(std::shared_ptr partnerCF, ObjectPoolActivationError &, ObjectPoolErrorCodes &, std::uint16_t &, std::uint16_t &) { + std::cout << "[TC Server] Client " << partnerCF->get_NAME().get_full_name() << " requesting object pool activation" << std::endl; // Safety check to make sure partnerCF has uploaded a DDOP if (uploadedPools.find(partnerCF) == uploadedPools.end()) { @@ -339,6 +340,8 @@ bool MyTCServer::activate_object_pool(std::shared_ptr p } clients[partnerCF] = state; + std::cout << "[TC Server] Client " << partnerCF->get_NAME().get_full_name() << " registered successfully with " + << static_cast(state.get_number_of_sections()) << " sections." << std::endl; return true; } @@ -385,6 +388,7 @@ void MyTCServer::identify_task_controller(std::uint8_t) void MyTCServer::on_client_timeout(std::shared_ptr partner) { // Cleanup the client state + std::cout << "[TC Server] Client " << partner->get_NAME().get_full_name() << " has timed out!" << std::endl; clients.erase(partner); } @@ -452,6 +456,7 @@ bool MyTCServer::on_value_command(std::shared_ptr partn bool MyTCServer::store_device_descriptor_object_pool(std::shared_ptr partnerCF, const std::vector &binaryPool, bool appendToPool) { + std::cout << "[TC Server] Client " << partnerCF->get_NAME().get_full_name() << " requesting object pool transfer of " << binaryPool.size() << " bytes" << std::endl; if (uploadedPools.find(partnerCF) == uploadedPools.end()) { uploadedPools[partnerCF] = std::queue>(); @@ -582,6 +587,12 @@ void MyTCServer::update_section_states(std::vector §ionStates) return; } + // Skip clients that don't support section control (e.g., tractors or other non-implement devices) + if (!state.has_element_number_for_ddi(isobus::DataDescriptionIndex::SectionControlState)) + { + continue; + } + bool requiresUpdate = false; for (std::uint8_t i = 0; i < state.get_number_of_sections(); i++) { @@ -614,6 +625,12 @@ void MyTCServer::update_section_control_enabled(bool enabled) { for (auto &client : clients) { + // Skip clients that don't support section control (e.g., tractors or other non-implement devices) + if (!client.second.has_element_number_for_ddi(isobus::DataDescriptionIndex::SectionControlState)) + { + continue; + } + if (client.second.is_section_control_enabled() != enabled) { client.second.set_section_control_enabled(enabled); From 90ddc57d3bf07961e61e753c634748d69e2db9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunics=20Bal=C3=A1zs?= Date: Mon, 16 Mar 2026 23:12:41 +0100 Subject: [PATCH 2/6] Fix formatting --- readme.md | 13 +++++++++++++ src/task_controller.cpp | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index ce5dfef..22f2ce8 100644 --- a/readme.md +++ b/readme.md @@ -30,3 +30,16 @@ cmake --build build --config Release --target package ``` The installer will be generated in the `build` directory. + +Before committing it's better to run these commands: (requires the LLVM project to be installed) +git ls-files | Select-String '\.(c|cc|cpp|cxx|h|hh|hpp|hxx|proto)$' | ForEach-Object { + clang-format -i $_.ToString() +} + +(Install cmake-format with: +python -m pip install --upgrade pip +pip install cmake-format pyyaml +) +Get-ChildItem -Recurse -Filter CMakeLists.txt | ForEach-Object { + python -m cmakelang.format -i $_.FullName +} diff --git a/src/task_controller.cpp b/src/task_controller.cpp index 0022867..8b34395 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -340,7 +340,7 @@ bool MyTCServer::activate_object_pool(std::shared_ptr p } clients[partnerCF] = state; - std::cout << "[TC Server] Client " << partnerCF->get_NAME().get_full_name() << " registered successfully with " + std::cout << "[TC Server] Client " << partnerCF->get_NAME().get_full_name() << " registered successfully with " << static_cast(state.get_number_of_sections()) << " sections." << std::endl; return true; } From 6d773d3995929b8272c201e8c195a07c4b131d97 Mon Sep 17 00:00:00 2001 From: gunicsba Date: Mon, 16 Mar 2026 23:37:27 +0100 Subject: [PATCH 3/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- readme.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/readme.md b/readme.md index 22f2ce8..cfd9d7d 100644 --- a/readme.md +++ b/readme.md @@ -31,6 +31,27 @@ cmake --build build --config Release --target package The installer will be generated in the `build` directory. +## Configuration + +AOG-TaskController reads its configuration from a `settings.json` file located in: + +- **Windows:** `%APPDATA%\AOG-Taskcontroller\settings.json` + +One of the available options is the `tecuEnabled` flag: + +- **Key:** `tecuEnabled` +- **Default:** `true` +- **Description:** Enables communication with the Tractor ECU (TECU) over ISOBUS/CAN. +- **When to disable:** Set to `false` if your tractor or simulator does not provide TECU messages, if you do not need TECU-related data, or if you are troubleshooting TECU-related issues on the CAN bus. + +Example `settings.json` snippet: + +```json +{ + "tecuEnabled": true +} +``` + Before committing it's better to run these commands: (requires the LLVM project to be installed) git ls-files | Select-String '\.(c|cc|cpp|cxx|h|hh|hpp|hxx|proto)$' | ForEach-Object { clang-format -i $_.ToString() From 503fcd316abf8ada4c463b9de5affdd3aa3bf7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunics=20Bal=C3=A1zs?= Date: Tue, 17 Mar 2026 00:02:40 +0100 Subject: [PATCH 4/6] Update readme.md and implement suggestions of copilot --- readme.md | 10 ++++++++-- src/task_controller.cpp | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 22f2ce8..4cd2c93 100644 --- a/readme.md +++ b/readme.md @@ -32,14 +32,20 @@ cmake --build build --config Release --target package The installer will be generated in the `build` directory. Before committing it's better to run these commands: (requires the LLVM project to be installed) + ```powershell git ls-files | Select-String '\.(c|cc|cpp|cxx|h|hh|hpp|hxx|proto)$' | ForEach-Object { clang-format -i $_.ToString() } +``` -(Install cmake-format with: +Install cmake-format with: +```powershell python -m pip install --upgrade pip pip install cmake-format pyyaml -) +``` +Then execute with: +```powershell Get-ChildItem -Recurse -Filter CMakeLists.txt | ForEach-Object { python -m cmakelang.format -i $_.FullName } +``` diff --git a/src/task_controller.cpp b/src/task_controller.cpp index 8b34395..8e99ad1 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -584,7 +584,7 @@ void MyTCServer::update_section_states(std::vector §ionStates) if (!state.is_section_control_enabled()) { // According to standard, the section setpoint states should only be sent when in auto mode - return; + continue; } // Skip clients that don't support section control (e.g., tractors or other non-implement devices) From fe11139a22f74780ae58338e0a37440efd5b09ae Mon Sep 17 00:00:00 2001 From: gunicsba Date: Tue, 17 Mar 2026 00:13:15 +0100 Subject: [PATCH 5/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- 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 8e99ad1..7ecc306 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -587,8 +587,8 @@ void MyTCServer::update_section_states(std::vector §ionStates) continue; } - // Skip clients that don't support section control (e.g., tractors or other non-implement devices) - if (!state.has_element_number_for_ddi(isobus::DataDescriptionIndex::SectionControlState)) + // Skip clients that don't have any sections configured (e.g., tractors or other non-implement devices) + if (state.get_number_of_sections() == 0) { continue; } From 5e0d65d23153e863175682295a74b69d5fc2c4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunics=20Bal=C3=A1zs?= Date: Tue, 17 Mar 2026 00:18:21 +0100 Subject: [PATCH 6/6] Fix based on copilot review --- src/task_controller.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/task_controller.cpp b/src/task_controller.cpp index 7ecc306..8b260bc 100644 --- a/src/task_controller.cpp +++ b/src/task_controller.cpp @@ -625,15 +625,16 @@ void MyTCServer::update_section_control_enabled(bool enabled) { for (auto &client : clients) { - // Skip clients that don't support section control (e.g., tractors or other non-implement devices) - if (!client.second.has_element_number_for_ddi(isobus::DataDescriptionIndex::SectionControlState)) + // Always update the local flag + if (client.second.is_section_control_enabled() != enabled) { - continue; + client.second.set_section_control_enabled(enabled); } - if (client.second.is_section_control_enabled() != enabled) + // Only send ISOBUS command to clients that support SectionControlState DDI and have sections + if (client.second.has_element_number_for_ddi(isobus::DataDescriptionIndex::SectionControlState) && + client.second.get_number_of_sections() > 0) { - client.second.set_section_control_enabled(enabled); send_section_control_state(client.first, enabled); } }