Skip to content
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ FetchContent_MakeAvailable(Boost)

FetchContent_Declare(
isobus
GIT_REPOSITORY https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git
GIT_TAG 0c5e3d2f264270f0a750cc3fdedaaa5861d71a59
GIT_REPOSITORY https://github.com/gunicsba/AgIsoStack-plus-plus.git
GIT_TAG 355a964b0359ce854be96e02bbe9568f036507fd
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
FetchContent_MakeAvailable(isobus)

Expand Down
41 changes: 39 additions & 2 deletions include/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <memory>

#include "isobus/hardware_integration/can_hardware_plugin.hpp"
#include "isobus/isobus/can_message.hpp"
#include "isobus/isobus/isobus_functionalities.hpp"
#include "isobus/isobus/isobus_speed_distance_messages.hpp"
#include "isobus/isobus/nmea2000_message_interface.hpp"
Expand All @@ -21,6 +22,29 @@
#include "task_controller.hpp"
#include "udp_connections.hpp"

#include <fstream>
#include <map>
#include <mutex>
#include <string>

/// @brief Tracks the connection state of a potential TC client seen on the bus
struct ClientConnectionInfo
{
std::uint64_t nameFull = 0;
std::uint8_t address = 0xFF;
std::string typeString;
bool workingSetMasterReceived = false;
bool requestVersionReceived = false;
bool versionResponseSent = false;
bool requestVersionSent = false;
bool clientTaskReceived = false;
bool registeredAsClient = false;
std::uint32_t lastWorkingSetMasterMs = 0;
std::uint32_t lastRequestVersionMs = 0;
std::uint32_t lastClientTaskMs = 0;
std::uint32_t firstSeenMs = 0;
};

class Application
{
public:
Expand All @@ -31,7 +55,12 @@ class Application
void stop();

private:
void send_task_controller_status_message();
void dump_connection_table();
void update_connection_tracker();

static void log_can_working_set_master(const isobus::CANMessage &message, void *parent);
static void log_can_process_data(const isobus::CANMessage &message, void *parent);
static void log_all_can_messages(const isobus::CANMessage &message, void *parent);

std::shared_ptr<Settings> settings = std::make_shared<Settings>();
boost::asio::io_context ioContext = boost::asio::io_context();
Expand All @@ -44,8 +73,16 @@ class Application
std::unique_ptr<isobus::SpeedMessagesInterface> speedMessagesInterface;
std::unique_ptr<isobus::NMEA2000MessageInterface> nmea2000MessageInterface;
std::unique_ptr<isobus::ControlFunctionFunctionalities> tecuFunctionalities;
std::unique_ptr<isobus::ControlFunctionFunctionalities> tcFunctionalities;
std::uint8_t nmea2000SequenceIdentifier = 0;
std::uint32_t lastJ1939SpeedTransmit = 0;
std::uint32_t lastTCStatusTransmit = 0;
std::int32_t lastSpeedValue = 0;

// Connection tracking for diagnostics
std::map<std::uint64_t, ClientConnectionInfo> connectionTracker;
std::uint32_t lastConnectionTableDumpMs = 0;
std::uint32_t tcInitializedTimestampMs = 0;

// CAN message log file
std::ofstream canLogFile;
};
8 changes: 8 additions & 0 deletions include/logging_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <chrono>
#include <ctime>
#include <iomanip>
#include <mutex>
#include <sstream>
#include <string>

Expand All @@ -27,3 +28,10 @@ inline std::string get_timestamp()
<< std::setfill('0') << std::setw(3) << ms.count();
return oss.str();
}

// Global mutex to protect all std::cout writes from concurrent access across threads
inline std::mutex &getLoggingMutex()
{
static std::mutex loggingMutex;
return loggingMutex;
}
48 changes: 48 additions & 0 deletions include/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,66 @@ class Settings
*/
bool set_aog_heartbeat_enabled(bool enabled, bool save = true);

/**
* @brief Get the configured TC ISO 11783-10 version
* @return The configured version (0-4, default 4)
*/
std::uint8_t get_tc_version() const;

/**
* @brief Set the TC ISO 11783-10 version
* @param version The version to set (0=DIS, 1=FDIS.1, 2=FirstEdition, 3=SecondEditionDraft, 4=SecondPublishedEdition)
* @param save Whether or not to save the settings to file
* @return True if the version was set successfully, false otherwise
*/
bool set_tc_version(std::uint8_t version, bool save = true);

/**
* @brief Get the absolute path to the settings file
* @param filename The filename to get the path for
* @return The absolute path to the settings file
*/
static std::string get_filename_path(std::string);

/**
* @brief Get the configured language code (ISO 639-1)
* @return The language code (default "en")
*/
std::string get_language_code() const;

/**
* @brief Set the language code
* @param code The ISO 639-1 language code
* @param save Whether or not to save the settings to file
* @return True if the setting was set successfully, false otherwise
*/
bool set_language_code(std::string code, bool save = true);

/**
* @brief Get the configured country code (ISO 3166-1 alpha-2)
* @return The country code (default "US")
*/
std::string get_country_code() const;

/**
* @brief Set the country code
* @param code The ISO 3166-1 alpha-2 country code
* @param save Whether or not to save the settings to file
* @return True if the setting was set successfully, false otherwise
*/
bool set_country_code(std::string code, bool save = true);

private:
constexpr static std::array<std::uint8_t, 3> DEFAULT_SUBNET = { 192, 168, 5 };
constexpr static bool DEFAULT_TECU_ENABLED = true;
constexpr static bool DEFAULT_AOG_HEARTBEAT_ENABLED = true;
constexpr static std::uint8_t DEFAULT_TC_VERSION = 3; // SecondEditionDraft (V3 default for maximum implement compatibility)
static const std::string DEFAULT_LANGUAGE_CODE;
static const std::string DEFAULT_COUNTRY_CODE;
std::array<std::uint8_t, 3> configuredSubnet = DEFAULT_SUBNET;
bool tecuEnabled = DEFAULT_TECU_ENABLED;
bool aogHeartbeatEnabled = DEFAULT_AOG_HEARTBEAT_ENABLED;
std::uint8_t tcVersion = DEFAULT_TC_VERSION;
std::string languageCode = DEFAULT_LANGUAGE_CODE;
std::string countryCode = DEFAULT_COUNTRY_CODE;
};
5 changes: 3 additions & 2 deletions include/task_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ class ClientState
class MyTCServer : public isobus::TaskControllerServer
{
public:
MyTCServer(std::shared_ptr<isobus::InternalControlFunction> internalControlFunction);
MyTCServer(std::shared_ptr<isobus::InternalControlFunction> internalControlFunction,
isobus::TaskControllerServer::TaskControllerVersion version = isobus::TaskControllerServer::TaskControllerVersion::SecondPublishedEdition);
bool activate_object_pool(std::shared_ptr<isobus::ControlFunction> partnerCF, ObjectPoolActivationError &, ObjectPoolErrorCodes &, std::uint16_t &, std::uint16_t &) override;
bool change_designator(std::shared_ptr<isobus::ControlFunction>, std::uint16_t, const std::vector<std::uint8_t> &) override;
bool deactivate_object_pool(std::shared_ptr<isobus::ControlFunction> partnerCF) override;
Expand All @@ -93,7 +94,7 @@ class MyTCServer : public isobus::TaskControllerServer
std::int32_t processDataValue,
std::uint8_t &errorCodes) override;
bool store_device_descriptor_object_pool(std::shared_ptr<isobus::ControlFunction> partnerCF, const std::vector<std::uint8_t> &binaryPool, bool appendToPool) override;
std::map<std::shared_ptr<isobus::ControlFunction>, ClientState> &get_clients();
std::map<std::shared_ptr<isobus::ControlFunction>, ClientState> &get_clients(); ///< Returns a reference to the clients map
void request_measurement_commands();
void update_section_states(std::vector<bool> &sectionStates);
void update_section_control_enabled(bool enabled);
Expand Down
Loading
Loading