From 63bdc46a93a26aff9f1293ff3c6e207553f87d36 Mon Sep 17 00:00:00 2001 From: Daan Steenbergen Date: Tue, 23 Jan 2024 23:19:00 +0100 Subject: [PATCH] [VT]: add a numeric value tracker and updater for working sets of a VT client (#405) * feat(vt-client): add numeric value state tracker and updater for working sets * feat(vt-client): add callback to validate numeric value change in tracker * fix(vt-client): prevent crashes in multiple places Also changed place of switch-case break points to align with rest of repo * refactor(vt-client): remove shadowing of field "client" --- .../version3_object_pool/main.cpp | 32 ++-- isobus/CMakeLists.txt | 4 + isobus/include/isobus/isobus/can_message.hpp | 6 + .../isobus/isobus_virtual_terminal_client.hpp | 156 +++++++++--------- ..._virtual_terminal_client_state_tracker.hpp | 98 +++++++++++ ..._virtual_terminal_client_update_helper.hpp | 63 +++++++ isobus/src/can_message.cpp | 5 + ..._virtual_terminal_client_state_tracker.cpp | 126 ++++++++++++++ ..._virtual_terminal_client_update_helper.cpp | 92 +++++++++++ 9 files changed, 490 insertions(+), 92 deletions(-) create mode 100644 isobus/include/isobus/isobus/isobus_virtual_terminal_client_state_tracker.hpp create mode 100644 isobus/include/isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp create mode 100644 isobus/src/isobus_virtual_terminal_client_state_tracker.cpp create mode 100644 isobus/src/isobus_virtual_terminal_client_update_helper.cpp diff --git a/examples/virtual_terminal/version3_object_pool/main.cpp b/examples/virtual_terminal/version3_object_pool/main.cpp index 051799d7..33dfe829 100644 --- a/examples/virtual_terminal/version3_object_pool/main.cpp +++ b/examples/virtual_terminal/version3_object_pool/main.cpp @@ -1,10 +1,10 @@ #include "isobus/hardware_integration/available_can_drivers.hpp" #include "isobus/hardware_integration/can_hardware_interface.hpp" -#include "isobus/isobus/can_general_parameter_group_numbers.hpp" #include "isobus/isobus/can_network_manager.hpp" #include "isobus/isobus/can_partnered_control_function.hpp" #include "isobus/isobus/can_stack_logger.hpp" #include "isobus/isobus/isobus_virtual_terminal_client.hpp" +#include "isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp" #include "isobus/utility/iop_file_interface.hpp" #include "console_logger.cpp" @@ -17,7 +17,8 @@ #include //! It is discouraged to use global variables, but it is done here for simplicity. -static std::shared_ptr TestVirtualTerminalClient = nullptr; +static std::shared_ptr virtualTerminalClient = nullptr; +static std::shared_ptr virtualTerminalUpdateHelper = nullptr; static std::atomic_bool running = { true }; void signal_handler(int) @@ -28,35 +29,34 @@ void signal_handler(int) // This callback will provide us with event driven notifications of button presses from the stack void handleVTKeyEvents(const isobus::VirtualTerminalClient::VTKeyEvent &event) { - static std::uint32_t exampleNumberOutput = 214748364; // In the object pool the output number has an offset of -214748364 so we use this to represent 0. - switch (event.keyEvent) { case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: + case isobus::VirtualTerminalClient::KeyActivationCode::ButtonStillHeld: { switch (event.objectID) { case Plus_Button: { - TestVirtualTerminalClient->send_change_numeric_value(ButtonExampleNumber_VarNum, ++exampleNumberOutput); + virtualTerminalUpdateHelper->increase_numeric_value(ButtonExampleNumber_VarNum); } break; case Minus_Button: { - TestVirtualTerminalClient->send_change_numeric_value(ButtonExampleNumber_VarNum, --exampleNumberOutput); + virtualTerminalUpdateHelper->decrease_numeric_value(ButtonExampleNumber_VarNum); } break; case alarm_SoftKey: { - TestVirtualTerminalClient->send_change_active_mask(example_WorkingSet, example_AlarmMask); + virtualTerminalClient->send_change_active_mask(example_WorkingSet, example_AlarmMask); } break; case acknowledgeAlarm_SoftKey: { - TestVirtualTerminalClient->send_change_active_mask(example_WorkingSet, mainRunscreen_DataMask); + virtualTerminalClient->send_change_active_mask(example_WorkingSet, mainRunscreen_DataMask); } break; @@ -138,11 +138,15 @@ int main() auto TestInternalECU = isobus::InternalControlFunction::create(TestDeviceNAME, 0x1C, 0); auto TestPartnerVT = isobus::PartneredControlFunction::create(0, vtNameFilters); - TestVirtualTerminalClient = std::make_shared(TestPartnerVT, TestInternalECU); - TestVirtualTerminalClient->set_object_pool(0, testPool.data(), testPool.size(), objectPoolHash); - auto softKeyListener = TestVirtualTerminalClient->add_vt_soft_key_event_listener(handleVTKeyEvents); - auto buttonListener = TestVirtualTerminalClient->add_vt_button_event_listener(handleVTKeyEvents); - TestVirtualTerminalClient->initialize(true); + virtualTerminalClient = std::make_shared(TestPartnerVT, TestInternalECU); + virtualTerminalClient->set_object_pool(0, testPool.data(), testPool.size(), objectPoolHash); + auto softKeyListener = virtualTerminalClient->add_vt_soft_key_event_listener(handleVTKeyEvents); + auto buttonListener = virtualTerminalClient->add_vt_button_event_listener(handleVTKeyEvents); + virtualTerminalClient->initialize(true); + + virtualTerminalUpdateHelper = std::make_shared(virtualTerminalClient); + virtualTerminalUpdateHelper->add_tracked_numeric_value(ButtonExampleNumber_VarNum, 214748364); // In the object pool the output number has an offset of -214748364 so we use this to represent 0. + virtualTerminalUpdateHelper->initialize(); while (running) { @@ -150,7 +154,7 @@ int main() std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } - TestVirtualTerminalClient->terminate(); + virtualTerminalClient->terminate(); isobus::CANHardwareInterface::stop(); return 0; } diff --git a/isobus/CMakeLists.txt b/isobus/CMakeLists.txt index 886178ab..cafaab5a 100644 --- a/isobus/CMakeLists.txt +++ b/isobus/CMakeLists.txt @@ -40,6 +40,8 @@ set(ISOBUS_SRC "isobus_speed_distance_messages.cpp" "isobus_maintain_power_interface.cpp" "isobus_virtual_terminal_objects.cpp" + "isobus_virtual_terminal_client_state_tracker.cpp" + "isobus_virtual_terminal_client_update_helper.cpp" "nmea2000_message_definitions.cpp" "nmea2000_message_interface.cpp" "can_message_data.cpp") @@ -84,6 +86,8 @@ set(ISOBUS_INCLUDE "isobus_speed_distance_messages.hpp" "isobus_maintain_power_interface.hpp" "isobus_virtual_terminal_objects.hpp" + "isobus_virtual_terminal_client_state_tracker.hpp" + "isobus_virtual_terminal_client_update_helper.hpp" "nmea2000_message_definitions.hpp" "nmea2000_message_interface.hpp" "can_message_data.hpp") diff --git a/isobus/include/isobus/isobus/can_message.hpp b/isobus/include/isobus/isobus/can_message.hpp index cc83ebc6..fa783a58 100644 --- a/isobus/include/isobus/isobus/can_message.hpp +++ b/isobus/include/isobus/isobus/can_message.hpp @@ -12,6 +12,7 @@ #define CAN_MESSAGE_HPP #include "isobus/isobus/can_control_function.hpp" +#include "isobus/isobus/can_general_parameter_group_numbers.hpp" #include "isobus/isobus/can_identifier.hpp" #include "isobus/utility/data_span.hpp" @@ -103,6 +104,11 @@ namespace isobus /// @returns The identifier of the message CANIdentifier get_identifier() const; + /// @brief Compares the identifier of the message to the parameter group number (PGN) supplied + /// @param[in] parameterGroupNumber The parameter group number to compare to + /// @returns True if the message identifier matches the parameter group number, false otherwise + bool is_parameter_group_number(CANLibParameterGroupNumber parameterGroupNumber) const; + /// @brief Returns the CAN channel index associated with the message /// @returns The CAN channel index associated with the message std::uint8_t get_can_port_index() const; diff --git a/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp b/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp index 37caaeaf..a808aff1 100644 --- a/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp +++ b/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp @@ -43,6 +43,84 @@ namespace isobus class VirtualTerminalClient { public: + /// @brief Enumerates the multiplexor byte values for VT commands + enum class Function : std::uint8_t + { + SoftKeyActivationMessage = 0x00, + ButtonActivationMessage = 0x01, + PointingEventMessage = 0x02, + VTSelectInputObjectMessage = 0x03, + VTESCMessage = 0x04, + VTChangeNumericValueMessage = 0x05, + VTChangeActiveMaskMessage = 0x06, + VTChangeSoftKeyMaskMessage = 0x07, + VTChangeStringValueMessage = 0x08, + VTOnUserLayoutHideShowMessage = 0x09, + VTControlAudioSignalTerminationMessage = 0x0A, + ObjectPoolTransferMessage = 0x11, + EndOfObjectPoolMessage = 0x12, + AuxiliaryAssignmentTypeOneCommand = 0x20, + AuxiliaryInputTypeOneStatus = 0x21, + PreferredAssignmentCommand = 0x22, + AuxiliaryInputTypeTwoMaintenanceMessage = 0x23, + AuxiliaryAssignmentTypeTwoCommand = 0x24, + AuxiliaryInputStatusTypeTwoEnableCommand = 0x25, + AuxiliaryInputTypeTwoStatusMessage = 0x26, + AuxiliaryCapabilitiesRequest = 0x27, + SelectActiveWorkingSet = 0x90, + ESCCommand = 0x92, + HideShowObjectCommand = 0xA0, + EnableDisableObjectCommand = 0xA1, + SelectInputObjectCommand = 0xA2, + ControlAudioSignalCommand = 0xA3, + SetAudioVolumeCommand = 0xA4, + ChangeChildLocationCommand = 0xA5, + ChangeSizeCommand = 0xA6, + ChangeBackgroundColourCommand = 0xA7, + ChangeNumericValueCommand = 0xA8, + ChangeEndPointCommand = 0xA9, + ChangeFontAttributesCommand = 0xAA, + ChangeLineAttributesCommand = 0xAB, + ChangeFillAttributesCommand = 0xAC, + ChangeActiveMaskCommand = 0xAD, + ChangeSoftKeyMaskCommand = 0xAE, + ChangeAttributeCommand = 0xAF, + ChangePriorityCommand = 0xB0, + ChangeListItemCommand = 0xB1, + DeleteObjectPoolCommand = 0xB2, + ChangeStringValueCommand = 0xB3, + ChangeChildPositionCommand = 0xB4, + ChangeObjectLabelCommand = 0xB5, + ChangePolygonPointCommand = 0xB6, + ChangePolygonScaleCommand = 0xB7, + GraphicsContextCommand = 0xB8, + GetAttributeValueMessage = 0xB9, + SelectColourMapCommand = 0xBA, + IdentifyVTMessage = 0xBB, + ExecuteExtendedMacroCommand = 0xBC, + LockUnlockMaskCommand = 0xBD, + ExecuteMacroCommand = 0xBE, + GetMemoryMessage = 0xC0, + GetSupportedWidecharsMessage = 0xC1, + GetNumberOfSoftKeysMessage = 0xC2, + GetTextFontDataMessage = 0xC3, + GetWindowMaskDataMessage = 0xC4, + GetSupportedObjectsMessage = 0xC5, + GetHardwareMessage = 0xC7, + StoreVersionCommand = 0xD0, + LoadVersionCommand = 0xD1, + DeleteVersionCommand = 0xD2, + ExtendedGetVersionsMessage = 0xD3, + ExtendedStoreVersionCommand = 0xD4, + ExtendedLoadVersionCommand = 0xD5, + ExtendedDeleteVersionCommand = 0xD6, + GetVersionsMessage = 0xDF, + GetVersionsResponse = 0xE0, + UnsupportedVTFunctionMessage = 0xFD, + VTStatusMessage = 0xFE, + WorkingSetMaintenanceMessage = 0xFF + }; + /// @brief Enumerates the states that can be sent with a hide/show object command enum class HideShowObjectCommand : std::uint8_t { @@ -1197,84 +1275,6 @@ namespace isobus LanguageCommandInterface languageCommandInterface; protected: - /// @brief Enumerates the multiplexor byte values for VT commands - enum class Function : std::uint8_t - { - SoftKeyActivationMessage = 0x00, - ButtonActivationMessage = 0x01, - PointingEventMessage = 0x02, - VTSelectInputObjectMessage = 0x03, - VTESCMessage = 0x04, - VTChangeNumericValueMessage = 0x05, - VTChangeActiveMaskMessage = 0x06, - VTChangeSoftKeyMaskMessage = 0x07, - VTChangeStringValueMessage = 0x08, - VTOnUserLayoutHideShowMessage = 0x09, - VTControlAudioSignalTerminationMessage = 0x0A, - ObjectPoolTransferMessage = 0x11, - EndOfObjectPoolMessage = 0x12, - AuxiliaryAssignmentTypeOneCommand = 0x20, - AuxiliaryInputTypeOneStatus = 0x21, - PreferredAssignmentCommand = 0x22, - AuxiliaryInputTypeTwoMaintenanceMessage = 0x23, - AuxiliaryAssignmentTypeTwoCommand = 0x24, - AuxiliaryInputStatusTypeTwoEnableCommand = 0x25, - AuxiliaryInputTypeTwoStatusMessage = 0x26, - AuxiliaryCapabilitiesRequest = 0x27, - SelectActiveWorkingSet = 0x90, - ESCCommand = 0x92, - HideShowObjectCommand = 0xA0, - EnableDisableObjectCommand = 0xA1, - SelectInputObjectCommand = 0xA2, - ControlAudioSignalCommand = 0xA3, - SetAudioVolumeCommand = 0xA4, - ChangeChildLocationCommand = 0xA5, - ChangeSizeCommand = 0xA6, - ChangeBackgroundColourCommand = 0xA7, - ChangeNumericValueCommand = 0xA8, - ChangeEndPointCommand = 0xA9, - ChangeFontAttributesCommand = 0xAA, - ChangeLineAttributesCommand = 0xAB, - ChangeFillAttributesCommand = 0xAC, - ChangeActiveMaskCommand = 0xAD, - ChangeSoftKeyMaskCommand = 0xAE, - ChangeAttributeCommand = 0xAF, - ChangePriorityCommand = 0xB0, - ChangeListItemCommand = 0xB1, - DeleteObjectPoolCommand = 0xB2, - ChangeStringValueCommand = 0xB3, - ChangeChildPositionCommand = 0xB4, - ChangeObjectLabelCommand = 0xB5, - ChangePolygonPointCommand = 0xB6, - ChangePolygonScaleCommand = 0xB7, - GraphicsContextCommand = 0xB8, - GetAttributeValueMessage = 0xB9, - SelectColourMapCommand = 0xBA, - IdentifyVTMessage = 0xBB, - ExecuteExtendedMacroCommand = 0xBC, - LockUnlockMaskCommand = 0xBD, - ExecuteMacroCommand = 0xBE, - GetMemoryMessage = 0xC0, - GetSupportedWidecharsMessage = 0xC1, - GetNumberOfSoftKeysMessage = 0xC2, - GetTextFontDataMessage = 0xC3, - GetWindowMaskDataMessage = 0xC4, - GetSupportedObjectsMessage = 0xC5, - GetHardwareMessage = 0xC7, - StoreVersionCommand = 0xD0, - LoadVersionCommand = 0xD1, - DeleteVersionCommand = 0xD2, - ExtendedGetVersionsMessage = 0xD3, - ExtendedStoreVersionCommand = 0xD4, - ExtendedLoadVersionCommand = 0xD5, - ExtendedDeleteVersionCommand = 0xD6, - GetVersionsMessage = 0xDF, - GetVersionsResponse = 0xE0, - UnsupportedVTFunctionMessage = 0xFD, - VTStatusMessage = 0xFE, - WorkingSetMaintenanceMessage = 0xFF - }; - /// @brief Enumerates the command types for graphics context objects enum class GraphicsContextSubCommandID : std::uint8_t { diff --git a/isobus/include/isobus/isobus/isobus_virtual_terminal_client_state_tracker.hpp b/isobus/include/isobus/isobus/isobus_virtual_terminal_client_state_tracker.hpp new file mode 100644 index 00000000..9d42dab7 --- /dev/null +++ b/isobus/include/isobus/isobus/isobus_virtual_terminal_client_state_tracker.hpp @@ -0,0 +1,98 @@ +//================================================================================================ +/// @file isobus_virtual_terminal_client_state_tracker.hpp +/// +/// @brief A helper class to track the state of an active working set. +/// @author Daan Steenbergen +/// +/// @copyright 2023 The Open-Agriculture Developers +//================================================================================================ + +#ifndef ISOBUS_VIRTUAL_TERMINAL_CLIENT_STATE_TRACKER_HPP +#define ISOBUS_VIRTUAL_TERMINAL_CLIENT_STATE_TRACKER_HPP + +#include "isobus/isobus/can_control_function.hpp" +#include "isobus/isobus/can_message.hpp" + +#include +#include + +namespace isobus +{ + /// @brief A helper class to update and track the state of an active working set. + /// @details The state is from the client's perspective. It might not be the same + /// as the state of the server, but tries to be as close as possible. + class VirtualTerminalClientStateTracker + { + public: + /// @brief The constructor to track the state of an active working set provided by a client. + /// @param[in] client The control function of the client. May be external. + explicit VirtualTerminalClientStateTracker(std::shared_ptr client); + + /// @brief The destructor. + ~VirtualTerminalClientStateTracker(); + + /// @brief Initializes the state tracker. + void initialize(); + + /// @brief Terminate the state tracker. + void terminate(); + + //! TODO: void initialize_with_defaults(ObjectPool &objectPool); + + /// @brief Adds a numeric value to track. + /// @param[in] objectId The object id of the numeric value to track. + /// @param[in] initialValue The initial value of the numeric value to track. + void add_tracked_numeric_value(std::uint16_t objectId, std::uint32_t initialValue = 0); + + /// @brief Removes a numeric value from tracking. + /// @param[in] objectId The object id of the numeric value to remove from tracking. + void remove_tracked_numeric_value(std::uint16_t objectId); + + /// @brief Gets the current numeric value of a tracked object. + /// @param[in] objectId The object id of the numeric value to get. + /// @return The current numeric value of the tracked object. + std::uint32_t get_numeric_value(std::uint16_t objectId) const; + + protected: + std::shared_ptr client; ///< The control function of the virtual terminal client to track. + + //! TODO: std::map shownStates; ///< Holds the 'hide/show' state of tracked objects. + //! TODO: std::map enabledStates; ///< Holds the 'enable/disable' state of tracked objects. + //! TODO: std::map selectedStates; ///< Holds the 'selected for input' state of tracked objects. + //! TODO: add current audio signal state + //! TODO: std::uint8_t audioVolumeState; ///< Holds the current audio volume. + //! TODO: std::map> positionStates; ///< Holds the 'position (x,y)' state of tracked objects. + //! TODO: std::map> sizeStates; ///< Holds the 'size (width,height)' state of tracked objects. + //! TODO: std::map backgroundColourStates; ///< Holds the 'background colour' state of tracked objects. + std::map numericValueStates; ///< Holds the 'numeric value' state of tracked objects. + //! TODO: std::map stringValueStates; ///< Holds the 'string value' state of tracked objects. + //! TODO: std::map endPointStates; ///< Holds the 'end point' state of tracked objects. + //! TODO: add font attribute state + //! TODO: add line attribute state + //! TODO: add fill attribute state + std::uint16_t currentActiveMask; ///< Holds the currently active mask. + //! TODO: std::uint16_t currentWorkingSet; ///< Holds the working set of the current active mask. + //! TODO: std::map softKeyMasks; ///< Holds the data/alarms masks with their associated soft keys masks for tracked objects. + //! TODO: std::map> attributeStates; ///< Holds the 'attribute' state of tracked objects. + //! TODO: std::map alarmMaskPrioritiesStates; ///< Holds the 'alarm mask priority' state of tracked objects. + //! TODO: std::map> listItemStates; ///< Holds the 'list item' state of tracked objects. + //! TODO: add lock/unlock mask state + //! TODO: add object label state + //! TODO: add polygon point state + //! TODO: add polygon scale state + //! TODO: add graphics context state + //! TODO: std::uint16_t currentColourMap; ///< Holds the current colour map/palette object. + + private: + /// @brief Processes a received message. + /// @param[in] message The received message. + /// @param[in] parentPointer The pointer to the parent object, which should be the VirtualTerminalClientStateTracker. + static void process_rx_message(const CANMessage &message, void *parentPointer); + + /// @brief Processes a received message. + /// @param[in] message The received message. + void process_rx_message(const CANMessage &message); + }; +} // namespace isobus + +#endif // ISOBUS_VIRTUAL_TERMINAL_CLIENT_STATE_TRACKER_HPP diff --git a/isobus/include/isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp b/isobus/include/isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp new file mode 100644 index 00000000..63c67fe6 --- /dev/null +++ b/isobus/include/isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp @@ -0,0 +1,63 @@ +//================================================================================================ +/// @file isobus_virtual_terminal_client_update_helper.hpp +/// +/// @brief A helper class to update and track the state of an active working set. +/// @author Daan Steenbergen +/// +/// @copyright 2023 The Open-Agriculture Developers +//================================================================================================ + +#ifndef ISOBUS_VIRTUAL_TERMINAL_CLIENT_UPDATE_HELPER_HPP +#define ISOBUS_VIRTUAL_TERMINAL_CLIENT_UPDATE_HELPER_HPP + +#include "isobus/isobus/isobus_virtual_terminal_client.hpp" +#include "isobus/isobus/isobus_virtual_terminal_client_state_tracker.hpp" + +namespace isobus +{ + /// @brief A helper class to update and track the state of an active working set. + class VirtualTerminalClientUpdateHelper : public VirtualTerminalClientStateTracker + { + public: + /// @brief The constructor of class to help update the state of an active working set. + /// @param[in] client The virtual terminal client that provides the active working set. + explicit VirtualTerminalClientUpdateHelper(std::shared_ptr client); + + /// @brief Sets the numeric value of a tracked object. + /// @param[in] objectId The object id of the numeric value to set. + /// @param[in] value The value to set the numeric value to. + /// @return True if the value was set successfully, false otherwise. + bool set_numeric_value(std::uint16_t objectId, std::uint32_t value); + + /// @brief Increases the numeric value of a tracked object. + /// @param[in] objectId The object id of the numeric value to increase. + /// @param[in] step The step size to increase the numeric value with. + /// @return True if the value was increased successfully, false otherwise. + bool increase_numeric_value(std::uint16_t objectId, std::uint32_t step = 1); + + /// @brief Decreases the numeric value of a tracked object. + /// @param[in] objectId The object id of the numeric value to decrease. + /// @param[in] step The step size to decrease the numeric value with. + /// @return True if the value was decreased successfully, false otherwise. + bool decrease_numeric_value(std::uint16_t objectId, std::uint32_t step = 1); + + /// @brief Register a callback function to validate a numeric value change of a tracked object. + /// If the callback function returns true, the numeric value change will be acknowledged. + /// Otherwise, if the callback function returns false, the numeric value change will + /// be rejected by sending the current value back to the VT. + /// @param[in] callback The callback function to register, or nullptr to unregister. + void set_callback_validate_numeric_value(const std::function &callback); + + private: + /// @brief Processes a numeric value change event + /// @param[in] event The numeric value change event to process. + void process_numeric_value_change_event(const VirtualTerminalClient::VTChangeNumericValueEvent &event); + + std::shared_ptr vtClient; ///< Holds the vt client. + + std::function callbackValidateNumericValue; ///< Holds the callback function to validate a numeric value change. + std::shared_ptr numericValueChangeEventHandle; ///< Holds the handle to the numeric value change event listener + }; +}; // namespace isobus + +#endif // ISOBUS_VIRTUAL_TERMINAL_CLIENT_UPDATE_HELPER_HPP diff --git a/isobus/src/can_message.cpp b/isobus/src/can_message.cpp index 238826c6..c0b61da1 100644 --- a/isobus/src/can_message.cpp +++ b/isobus/src/can_message.cpp @@ -74,6 +74,11 @@ namespace isobus return identifier; } + bool CANMessage::is_parameter_group_number(CANLibParameterGroupNumber parameterGroupNumber) const + { + return identifier.get_parameter_group_number() == static_cast(parameterGroupNumber); + } + std::uint8_t CANMessage::get_can_port_index() const { return CANPortIndex; diff --git a/isobus/src/isobus_virtual_terminal_client_state_tracker.cpp b/isobus/src/isobus_virtual_terminal_client_state_tracker.cpp new file mode 100644 index 00000000..cd01dca2 --- /dev/null +++ b/isobus/src/isobus_virtual_terminal_client_state_tracker.cpp @@ -0,0 +1,126 @@ +//================================================================================================ +/// @file isobus_virtual_terminal_client_state_tracker.cpp +/// +/// @brief A helper class to track the state of an active working set. +/// @author Daan Steenbergen +/// +/// @copyright 2023 The Open-Agriculture Developers +//================================================================================================ +#include "isobus/isobus/isobus_virtual_terminal_client_state_tracker.hpp" + +#include "isobus/isobus/can_general_parameter_group_numbers.hpp" +#include "isobus/isobus/can_network_manager.hpp" +#include "isobus/isobus/can_stack_logger.hpp" +#include "isobus/isobus/isobus_virtual_terminal_client.hpp" + +#include + +namespace isobus +{ + VirtualTerminalClientStateTracker::VirtualTerminalClientStateTracker(std::shared_ptr client) : + client(client) + { + } + + VirtualTerminalClientStateTracker::~VirtualTerminalClientStateTracker() + { + terminate(); + } + + void VirtualTerminalClientStateTracker::initialize() + { + CANNetworkManager::CANNetwork.add_any_control_function_parameter_group_number_callback(static_cast(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_message, this); + } + + void VirtualTerminalClientStateTracker::terminate() + { + CANNetworkManager::CANNetwork.remove_any_control_function_parameter_group_number_callback(static_cast(CANLibParameterGroupNumber::VirtualTerminalToECU), process_rx_message, this); + } + + void VirtualTerminalClientStateTracker::add_tracked_numeric_value(std::uint16_t objectId, std::uint32_t initialValue) + { + if (numericValueStates.find(objectId) != numericValueStates.end()) + { + CANStackLogger::warn("[VTStateHelper] add_tracked_numeric_value: objectId %lu already tracked", objectId); + return; + } + + numericValueStates[objectId] = initialValue; + } + + void VirtualTerminalClientStateTracker::remove_tracked_numeric_value(std::uint16_t objectId) + { + if (numericValueStates.find(objectId) == numericValueStates.end()) + { + CANStackLogger::warn("[VTStateHelper] remove_tracked_numeric_value: objectId %lu was not tracked", objectId); + return; + } + + numericValueStates.erase(objectId); + } + + std::uint32_t VirtualTerminalClientStateTracker::get_numeric_value(std::uint16_t objectId) const + { + if (numericValueStates.find(objectId) == numericValueStates.end()) + { + CANStackLogger::warn("[VTStateHelper] get_numeric_value: objectId %lu not tracked", objectId); + return 0; + } + + return numericValueStates.at(objectId); + } + + void VirtualTerminalClientStateTracker::process_rx_message(const CANMessage &message, void *parentPointer) + { + auto *parent = static_cast(parentPointer); + parent->process_rx_message(message); + } + + void VirtualTerminalClientStateTracker::process_rx_message(const CANMessage &message) + { + if (message.has_valid_source_control_function() && + message.is_destination(client) && + message.is_parameter_group_number(CANLibParameterGroupNumber::VirtualTerminalToECU) && + (message.get_data_length() >= 1)) + { + std::uint8_t function = message.get_uint8_at(0); + switch (function) + { + case static_cast(VirtualTerminalClient::Function::ChangeNumericValueCommand): + { + if (CAN_DATA_LENGTH == message.get_data_length()) + { + auto errorCode = message.get_uint8_at(3); + if (errorCode == 0) + { + std::uint16_t objectId = message.get_uint16_at(1); + if (numericValueStates.find(objectId) != numericValueStates.end()) + { + std::uint32_t value = message.get_uint32_at(4); + numericValueStates[objectId] = value; + } + } + } + } + break; + + case static_cast(VirtualTerminalClient::Function::VTChangeNumericValueMessage): + { + if (CAN_DATA_LENGTH == message.get_data_length()) + { + std::uint16_t objectId = message.get_uint16_at(1); + if (numericValueStates.find(objectId) != numericValueStates.end()) + { + std::uint32_t value = message.get_uint32_at(4); + numericValueStates[objectId] = value; + } + } + } + break; + + default: + break; + } + } + } +}; // namespace isobus diff --git a/isobus/src/isobus_virtual_terminal_client_update_helper.cpp b/isobus/src/isobus_virtual_terminal_client_update_helper.cpp new file mode 100644 index 00000000..652a76a9 --- /dev/null +++ b/isobus/src/isobus_virtual_terminal_client_update_helper.cpp @@ -0,0 +1,92 @@ +//================================================================================================ +/// @file isobus_virtual_terminal_client_update_helper.cpp +/// +/// @brief A helper class to update and track the state of an active working set. +/// @author Daan Steenbergen +/// +/// @copyright 2023 The Open-Agriculture Developers +//================================================================================================ + +#include "isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp" + +#include "isobus/isobus/can_stack_logger.hpp" + +namespace isobus +{ + VirtualTerminalClientUpdateHelper::VirtualTerminalClientUpdateHelper(std::shared_ptr client) : + VirtualTerminalClientStateTracker(nullptr == client ? nullptr : client->get_internal_control_function()), + vtClient(client) + { + if (nullptr == client) + { + CANStackLogger::error("[VTStateHelper] constructor: client is nullptr"); + return; + } + numericValueChangeEventHandle = client->add_vt_change_numeric_value_event_listener( + std::bind(&VirtualTerminalClientUpdateHelper::process_numeric_value_change_event, this, std::placeholders::_1)); + } + + bool VirtualTerminalClientUpdateHelper::set_numeric_value(std::uint16_t object_id, std::uint32_t value) + { + if (nullptr == client) + { + CANStackLogger::error("[VTStateHelper] set_numeric_value: client is nullptr"); + return false; + } + if (numericValueStates.find(object_id) == numericValueStates.end()) + { + CANStackLogger::warn("[VTStateHelper] set_numeric_value: objectId %lu not tracked", object_id); + return false; + } + if (numericValueStates.at(object_id) == value) + { + return false; + } + + bool success = vtClient->send_change_numeric_value(object_id, value); + if (success) + { + numericValueStates[object_id] = value; + } + return success; + } + + bool VirtualTerminalClientUpdateHelper::increase_numeric_value(std::uint16_t object_id, std::uint32_t step) + { + return set_numeric_value(object_id, get_numeric_value(object_id) + step); + } + + bool VirtualTerminalClientUpdateHelper::decrease_numeric_value(std::uint16_t object_id, std::uint32_t step) + { + return set_numeric_value(object_id, get_numeric_value(object_id) - step); + } + + void VirtualTerminalClientUpdateHelper::set_callback_validate_numeric_value(const std::function &callback) + { + callbackValidateNumericValue = callback; + } + + void VirtualTerminalClientUpdateHelper::process_numeric_value_change_event(const VirtualTerminalClient::VTChangeNumericValueEvent &event) + { + if (numericValueStates.find(event.objectID) == numericValueStates.end()) + { + // Only proccess numeric value changes for tracked objects. + return; + } + + if (numericValueStates.at(event.objectID) == event.value) + { + // Do not process the event if the value has not changed. + return; + } + + std::uint32_t targetValue = event.value; // Default to the value received in the event. + if ((callbackValidateNumericValue != nullptr) && callbackValidateNumericValue(event.objectID, event.value)) + { + // If the callback function returns false, reject the change by sending the previous value. + targetValue = numericValueStates.at(event.objectID); + } + vtClient->send_change_numeric_value(event.objectID, targetValue); + } + +} // namespace isobus