diff --git a/CMakeLists.txt b/CMakeLists.txt index a0945696..8dba2b4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,8 @@ if(BUILD_TESTING) test/vt_client_tests.cpp test/language_command_interface_tests.cpp test/tc_client_tests.cpp - test/ddop_tests.cpp) + test/ddop_tests.cpp + test/event_dispatcher_tests.cpp) add_executable(unit_tests ${TEST_SRC}) target_link_libraries( diff --git a/examples/virtual_terminal/aux_functions/main.cpp b/examples/virtual_terminal/aux_functions/main.cpp index 5029d995..b0c21757 100644 --- a/examples/virtual_terminal/aux_functions/main.cpp +++ b/examples/virtual_terminal/aux_functions/main.cpp @@ -3,6 +3,7 @@ #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/utility/iop_file_interface.hpp" @@ -34,9 +35,9 @@ void raw_can_glue(isobus::HardwareInterfaceCANFrame &rawFrame, void *parentPoint } // This callback will provide us with event driven notifications of auxiliary input from the stack -void handle_aux_input(isobus::VirtualTerminalClient::AssignedAuxiliaryFunction function, std::uint16_t value1, std::uint16_t value2, isobus::VirtualTerminalClient *) +void handle_aux_function_input(const isobus::VirtualTerminalClient::AuxiliaryFunctionEvent &event) { - std::cout << "Auxiliary function event received: (" << function.functionObjectID << ", " << function.inputObjectID << ", " << static_cast(function.functionType) << "), value1: " << value1 << ", value2: " << value2 << std::endl; + std::cout << "Auxiliary function event received: (" << event.function.functionObjectID << ", " << event.function.inputObjectID << ", " << static_cast(event.function.functionType) << "), value1: " << event.value1 << ", value2: " << event.value2 << std::endl; } int main() @@ -107,12 +108,12 @@ int main() const isobus::NAMEFilter filterVirtualTerminal(isobus::NAME::NAMEParameters::FunctionCode, static_cast(isobus::NAME::Function::VirtualTerminal)); const std::vector vtNameFilters = { filterVirtualTerminal }; - std::shared_ptr TestInternalECU = std::make_shared(TestDeviceNAME, 0x1D, 0); - std::shared_ptr TestPartnerVT = std::make_shared(0, vtNameFilters); + auto TestInternalECU = std::make_shared(TestDeviceNAME, 0x1D, 0); + auto TestPartnerVT = std::make_shared(0, vtNameFilters); TestVirtualTerminalClient = std::make_shared(TestPartnerVT, TestInternalECU); TestVirtualTerminalClient->set_object_pool(0, isobus::VirtualTerminalClient::VTVersion::Version3, testPool.data(), testPool.size(), objectPoolHash); - TestVirtualTerminalClient->register_auxiliary_function_event_callback(handle_aux_input); + auto auxFunctionListener = TestVirtualTerminalClient->add_auxiliary_function_event_listener(handle_aux_function_input); TestVirtualTerminalClient->initialize(true); while (running) diff --git a/examples/virtual_terminal/aux_inputs/main.cpp b/examples/virtual_terminal/aux_inputs/main.cpp index 51f114df..c6c1b207 100644 --- a/examples/virtual_terminal/aux_inputs/main.cpp +++ b/examples/virtual_terminal/aux_inputs/main.cpp @@ -99,12 +99,6 @@ void raw_can_glue(isobus::HardwareInterfaceCANFrame &rawFrame, void *parentPoint isobus::CANNetworkManager::CANNetwork.can_lib_process_rx_message(rawFrame, parentPointer); } -// This callback will provide us with event driven notifications of auxiliary input from the stack -void handle_aux_input(isobus::VirtualTerminalClient::AssignedAuxiliaryFunction function, std::uint16_t value1, std::uint16_t value2, isobus::VirtualTerminalClient *) -{ - std::cout << "Auxiliary function event received: (" << function.functionObjectID << ", " << function.inputObjectID << ", " << static_cast(function.functionType) << "), value1: " << value1 << ", value2: " << value2 << std::endl; -} - int main() { std::signal(SIGINT, signal_handler); diff --git a/examples/virtual_terminal/version3_object_pool/main.cpp b/examples/virtual_terminal/version3_object_pool/main.cpp index 4aa84138..dac9da54 100644 --- a/examples/virtual_terminal/version3_object_pool/main.cpp +++ b/examples/virtual_terminal/version3_object_pool/main.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -35,15 +36,15 @@ void raw_can_glue(isobus::HardwareInterfaceCANFrame &rawFrame, void *parentPoint } // This callback will provide us with event driven notifications of button presses from the stack -void handleVTButton(isobus::VirtualTerminalClient::KeyActivationCode keyEvent, std::uint8_t, std::uint16_t objectID, std::uint16_t, isobus::VirtualTerminalClient *) +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 (keyEvent) + switch (event.keyEvent) { case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: { - switch (objectID) + switch (event.objectID) { case Plus_Button: { @@ -152,8 +153,8 @@ int main() TestVirtualTerminalClient = std::make_shared(TestPartnerVT, TestInternalECU); TestVirtualTerminalClient->set_object_pool(0, isobus::VirtualTerminalClient::VTVersion::Version3, testPool.data(), testPool.size(), objectPoolHash); - TestVirtualTerminalClient->register_vt_button_event_callback(handleVTButton); - TestVirtualTerminalClient->register_vt_soft_key_event_callback(handleVTButton); + auto softKeyListener = TestVirtualTerminalClient->add_vt_soft_key_event_listener(handleVTKeyEvents); + auto buttonListener = TestVirtualTerminalClient->add_vt_button_event_listener(handleVTKeyEvents); TestVirtualTerminalClient->initialize(true); while (running) diff --git a/hardware_integration/src/innomaker_usb2can_windows_plugin.cpp b/hardware_integration/src/innomaker_usb2can_windows_plugin.cpp index 255fa8ec..8af269b2 100644 --- a/hardware_integration/src/innomaker_usb2can_windows_plugin.cpp +++ b/hardware_integration/src/innomaker_usb2can_windows_plugin.cpp @@ -13,6 +13,7 @@ #include "isobus/hardware_integration/innomaker_usb2can_windows_plugin.hpp" #include "isobus/isobus/can_stack_logger.hpp" +#include "isobus/utility/to_string.hpp" #include diff --git a/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp b/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp index d2aa2239..92dd029a 100644 --- a/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp +++ b/isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp @@ -13,8 +13,10 @@ #include "isobus/isobus/can_partnered_control_function.hpp" #include "isobus/isobus/isobus_language_command_interface.hpp" #include "isobus/isobus/isobus_virtual_terminal_objects.hpp" +#include "isobus/utility/event_dispatcher.hpp" #include "isobus/utility/processing_flags.hpp" +#include #include #include #include @@ -290,146 +292,170 @@ namespace isobus /// @brief Terminates the client and joins the worker thread if applicable void terminate(); - // Basic Interaction - /// @brief A typedef for a generic key event for convenience - typedef void (*VTKeyEventCallback)(KeyActivationCode keyEvent, std::uint8_t keyNumber, std::uint16_t objectID, std::uint16_t parentObjectID, VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic pointing event, for convenience - typedef void (*VTPointingEventCallback)(KeyActivationCode keyEvent, - std::uint16_t xPosition, - std::uint16_t yPosition, - std::uint16_t parentMaskObjectID, - VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT input object selection callback for convenience - typedef void (*VTSelectInputObjectCallback)(std::uint16_t objectID, bool objectSelected, bool objectOpenForInput, VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT ESC message callback for convenience - typedef void (*VTESCMessageCallback)(std::uint16_t objectID, ESCMessageErrorCode errorCode, VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT change numeric value callback for convenience - typedef void (*VTChangeNumericValueCallback)(std::uint16_t objectID, std::uint32_t value, VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT change active mask callback for convenience - typedef void (*VTChangeActiveMaskCallback)(std::uint16_t maskObjectID, - std::uint16_t errorObjectID, - std::uint16_t parentObjectID, - bool missingObjects, - bool maskOrChildHasErrors, - bool anyOtherEror, - bool poolDeleted, - VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT change soft key mask callback for convenience - typedef void (*VTChangeSoftKeyMaskCallback)(std::uint16_t dataOrAlarmMaskObjectID, - std::uint16_t softKeyMaskObjectID, - bool missingObjects, - bool maskOrChildHasErrors, - bool anyOtherEror, - bool poolDeleted, - VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT change string value callback for convenience - typedef void (*VTChangeStringValueCallback)(std::uint16_t objectID, std::string value, VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT on user-layout hide/show callback for convenience - typedef void (*VTUserLayoutHideShowCallback)(std::uint16_t objectID, bool isHidden, VirtualTerminalClient *parentPointer); - /// @brief A typedef for a generic VT control audio signal termination callback for convenience - typedef void (*VTAudioSignalTerminationCallback)(bool isTerminated, VirtualTerminalClient *parentPointer); - /// @brief A typedef for an auxilary function event for convenience - typedef void (*AuxiliaryFunctionCallback)(AssignedAuxiliaryFunction function, std::uint16_t value1, std::uint16_t value2, VirtualTerminalClient *parentPointer); - - // Callbacks for events that happen on the VT - /// @brief Allows you to register for a callback when a softkey is pressed or released - /// @param[in] value The callback to register - void register_vt_soft_key_event_callback(VTKeyEventCallback value); - - /// @brief Allows you to remove a callback for when a softkey is pressed or released - /// @param[in] value The callback to remove - void remove_vt_soft_key_event_callback(VTKeyEventCallback value); - - /// @brief Allows you to register for a callback when a button is pressed or released - /// @param[in] value The callback to register - void register_vt_button_event_callback(VTKeyEventCallback value); - - /// @brief Allows you to remove a callback when a button is pressed or released - /// @param[in] value The callback to remove - void remove_vt_button_event_callback(VTKeyEventCallback value); - - /// @brief Allows you to register for a callback when a pointing event is "pressed or released" - /// @param[in] value The callback to register - void register_vt_pointing_event_callback(VTPointingEventCallback value); - - /// @brief Allows you to remove a callback when a pointing event is "pressed or released" - /// @param[in] value The callback to remove - void remove_vt_pointing_event_callback(VTPointingEventCallback value); - - /// @brief Allows you to register for a callback when an input object event is triggered - /// @param[in] value The callback to register - void register_vt_select_input_object_event_callback(VTSelectInputObjectCallback value); - - /// @brief Allows you to remove an input object event callback - /// @param[in] value The callback to remove - void remove_vt_selection_input_object_event_callback(VTSelectInputObjectCallback value); - - /// @brief Allows you to register for a callback when an ESC message is received, e.g. an open object input is closed - /// @param[in] value The callback to register - void register_vt_esc_message_event_callback(VTESCMessageCallback value); - - /// @brief Allows you to remove an ESC message callback - /// @param[in] value The callback to remove - void remove_vt_esc_message_event_callback(VTESCMessageCallback value); - - /// @brief Allows you to register for a callback when a numeric value is changed in an input object - /// @param[in] value The callback to register - void register_vt_change_numeric_value_event_callback(VTChangeNumericValueCallback value); - - /// @brief Allows you to remove a numeric value change callback - /// @param[in] value The callback to remove - void remove_vt_change_numeric_value_event_callback(VTChangeNumericValueCallback value); - - /// @brief Allows you to register for a callback when the active mask is changed - /// @details The VT sends this whenever there are missing object references or errors in the mask. - /// @param[in] value The callback to register - void register_vt_change_active_mask_event_callback(VTChangeActiveMaskCallback value); + /// @brief A struct for storing information of a VT key input event + struct VTKeyEvent + { + KeyActivationCode keyEvent; ///< The key event + std::uint8_t keyNumber; ///< The key number + std::uint16_t objectID; ///< The object ID + std::uint16_t parentObjectID; ///< The parent object ID + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to remove a callback when the active mask is changed - /// @param[in] value The callback to remove - void remove_vt_change_active_mask_event_callback(VTChangeActiveMaskCallback value); + /// @brief A struct for storing information of a VT pointing event + struct VTPointingEvent + { + KeyActivationCode keyEvent; ///< The key event + std::uint16_t xPosition; ///< The x position + std::uint16_t yPosition; ///< The y position + std::uint16_t parentObjectID; ///< The parent object ID + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to register for a callback when the soft key mask is changed - /// @details The VT sends this whenever there are missing object references or errors in the mask. - /// @param[in] value The callback to register - void register_vt_change_soft_key_mask_event_callback(VTChangeSoftKeyMaskCallback value); + /// @brief A struct for storing information of a VT input object selection event + struct VTSelectInputObjectEvent + { + std::uint16_t objectID; ///< The object ID + bool objectSelected; ///< Whether the object is selected + bool objectOpenForInput; ///< Whether the object is open for input + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to remove a callback when the soft key mask is changed - /// @param[in] value The callback to remove - void remove_vt_change_soft_key_mask_event_callback(VTChangeSoftKeyMaskCallback value); + /// @brief A struct for storing information of a VT ESC message event + struct VTESCMessageEvent + { + std::uint16_t objectID; ///< The object ID + ESCMessageErrorCode errorCode; ///< The error code + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to register for a callback when a string value is changed - /// @details The object could be either the input string object or the referenced string variable object. - /// @param[in] value The callback to register - void register_vt_change_string_value_event_callback(VTChangeStringValueCallback value); + /// @brief A struct for storing information of a VT change numeric value event + struct VTChangeNumericValueEvent + { + std::uint16_t objectID; ///< The object ID + std::uint32_t value; ///< The value + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to remove a callback when a string value is changed - /// @param[in] value The callback to remove - void remove_vt_change_string_value_event_callback(VTChangeStringValueCallback value); + /// @brief A struct for storing information of a VT change active mask event + struct VTChangeActiveMaskEvent + { + std::uint16_t maskObjectID; ///< The mask object ID + std::uint16_t errorObjectID; ///< The error object ID + std::uint16_t parentObjectID; ///< The parent object ID + bool missingObjects; ///< Whether there are missing objects + bool maskOrChildHasErrors; ///< Whether the mask or child has errors + bool anyOtherError; ///< Whether there are any other errors + bool poolDeleted; ///< Whether the pool has been deleted + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to register for a callback when a user-layout object is hidden or shown - /// @param[in] value The callback to register - void register_vt_user_layout_hide_show_event_callback(VTUserLayoutHideShowCallback value); + /// @brief A struct for storing information of a VT change soft key mask event + struct VTChangeSoftKeyMaskEvent + { + std::uint16_t dataOrAlarmMaskObjectID; ///< The data or alarm mask object ID + std::uint16_t softKeyMaskObjectID; ///< The soft key mask object ID + bool missingObjects; ///< Whether there are missing objects + bool maskOrChildHasErrors; ///< Whether the mask or child has errors + bool anyOtherError; ///< Whether there are any other errors + bool poolDeleted; ///< Whether the pool has been deleted + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to remove a callback when a user-layout object is hidden or shown - /// @param[in] value The callback to remove - void remove_vt_user_layout_hide_show_callback(VTUserLayoutHideShowCallback value); + /// @brief A struct for storing information of a VT change string value event + struct VTChangeStringValueEvent + { + std::uint16_t objectID; ///< The object ID + std::string value; ///< The value + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to register for a callback when an audio signal is terminated - /// @param[in] value The callback to register - void register_vt_control_audio_signal_termination_event_callback(VTAudioSignalTerminationCallback value); + /// @brief A struct for storing information of a VT on user-layout hide/show event + struct VTUserLayoutHideShowEvent + { + std::uint16_t objectID; ///< The object ID + bool isHidden; ///< Whether the object is hidden + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to remove a callback when an audio signal is terminated - /// @param[in] value The callback to remove - void remove_vt_control_audio_signal_termination_event_callback(VTAudioSignalTerminationCallback value); + /// @brief A struct for storing information of a VT control audio signal termination event + struct VTAudioSignalTerminationEvent + { + bool isTerminated; ///< Whether the audio signal is terminated + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to register for a callback for when a change in auxiliary input is received - /// @param[in] value The AuxiliaryFunctionCallback to register - void register_auxiliary_function_event_callback(AuxiliaryFunctionCallback value); + /// @brief A struct for storing information of an auxilary function event + struct AuxiliaryFunctionEvent + { + AssignedAuxiliaryFunction function; ///< The function + std::uint16_t value1; ///< The first value + std::uint16_t value2; ///< The second value + VirtualTerminalClient *parentPointer; ///< A pointer to the parent VT client + }; - /// @brief Allows you to remove a callback when for a change in auxiliary input is received - /// @param[in] value The AuxiliaryFunctionCallback to remove - void remove_auxiliary_function_event_callback(AuxiliaryFunctionCallback value); + /// @brief Add a listener for when a soft key is pressed or released + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_soft_key_event_listener(std::function callback); + + /// @brief Add a listener for when a button is pressed or released + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_button_event_listener(std::function callback); + + /// @brief Add a listener for when a pointing event is "pressed or released" + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_pointing_event_listener(std::function callback); + + /// @brief Add a listener for when an input object event is triggered + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_select_input_object_event_listener(std::function callback); + + /// @brief Add a listener for when an ESC message is received, e.g. an open object input is closed + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_esc_message_event_listener(std::function callback); + + /// @brief Add a listener for when a numeric value is changed in an input object + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_change_numeric_value_event_listener(std::function callback); + + /// @brief Add a listener for when the active mask is changed + /// @details The VT sends this whenever there are missing object references or errors in the mask. + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_change_active_mask_event_listener(std::function callback); + + /// @brief Add a listener for when the soft key mask is changed + /// @details The VT sends this whenever there are missing object references or errors in the mask. + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_change_soft_key_mask_event_listener(std::function callback); + + /// @brief Add a listener for when a string value is changed + /// @details The object could be either the input string object or the referenced string variable object. + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_change_string_value_event_listener(std::function callback); + + /// @brief Add a listener for when a user-layout object is hidden or shown + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_user_layout_hide_show_event_listener(std::function callback); + + /// @brief Add a listener for when an audio signal is terminated + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_vt_control_audio_signal_termination_event_listener(std::function callback); + + /// @brief Add a listener for for when a change in auxiliary input for a function is received + /// @param[in] callback The callback to be invoked + /// @returns A shared pointer to the callback, which must be kept alive for as long as the callback is needed + std::shared_ptr add_auxiliary_function_event_listener(std::function callback); /// @brief Set the model identification code of our auxiliary input device. /// @details The model identification code is used to allow other devices identify @@ -1419,114 +1445,6 @@ namespace isobus /// @param[in] value The new state for the state machine void set_state(StateMachineState value); - /// @brief Calls all registered callbacks for button events - /// @param[in] keyEvent The button event - /// @param[in] keyNumber They key number - /// @param[in] objectID The object ID of the button - /// @param[in] parentObjectID The object ID of the parent object - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_button_event_callback(KeyActivationCode keyEvent, std::uint8_t keyNumber, std::uint16_t objectID, std::uint16_t parentObjectID, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for softkey events - /// @param[in] keyEvent The softkey event - /// @param[in] keyNumber They key number - /// @param[in] objectID The object ID of the softkey - /// @param[in] parentObjectID The object ID of the parent object - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_softkey_event_callback(KeyActivationCode keyEvent, std::uint8_t keyNumber, std::uint16_t objectID, std::uint16_t parentObjectID, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for pointing events - /// @param[in] keyEvent The event's signal - /// @param[in] xPosition The pointing event X position - /// @param[in] yPosition The pointing event Y position - /// @param[in] parentMaskObjectID Object ID of the parent mask - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_pointing_event_callback(KeyActivationCode keyEvent, - std::uint16_t xPosition, - std::uint16_t yPosition, - std::uint16_t parentMaskObjectID, - VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for pointing events - /// @param[in] objectID The object ID of the event's source object - /// @param[in] objectSelected Denotes if the object is selected - /// @param[in] objectOpenForInput Denotes if the input object is open for input - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_select_input_object_callback(std::uint16_t objectID, bool objectSelected, bool objectOpenForInput, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for esc message events - /// @param[in] objectID The object ID where input was aborted - /// @param[in] errorCode The error code - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_esc_message_callback(std::uint16_t objectID, ESCMessageErrorCode errorCode, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for change numeric value events - /// @param[in] objectID The object ID of the numeric object - /// @param[in] value The new value of the numeric object - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_change_numeric_value_callback(std::uint16_t objectID, std::uint32_t value, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for change active mask events - /// @param[in] maskObjectID The object ID of the mask object - /// @param[in] errorObjectID The object ID of the error object - /// @param[in] parentObjectID The object ID of the parent object - /// @param[in] missingObjects Denotes if the mask object is missing objects - /// @param[in] maskOrChildHasErrors Denotes if the mask object or a child has errors - /// @param[in] anyOtherEror Denotes if any other error exists - /// @param[in] poolDeleted Denotes if the pool is deleted - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_change_active_mask_callback(std::uint16_t maskObjectID, - std::uint16_t errorObjectID, - std::uint16_t parentObjectID, - bool missingObjects, - bool maskOrChildHasErrors, - bool anyOtherEror, - bool poolDeleted, - VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for change soft key mask events - /// @param[in] dataOrAlarmMaskObjectID The object ID of the data or alarm mask object - /// @param[in] softKeyMaskObjectID The object ID of the soft key mask object - /// @param[in] missingObjects Denotes if the mask object is missing objects - /// @param[in] maskOrChildHasErrors Denotes if the mask object or a child has errors - /// @param[in] anyOtherEror Denotes if any other error exists - /// @param[in] poolDeleted Denotes if the pool is deleted - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_change_soft_key_mask_callback(std::uint16_t dataOrAlarmMaskObjectID, - std::uint16_t softKeyMaskObjectID, - bool missingObjects, - bool maskOrChildHasErrors, - bool anyOtherEror, - bool poolDeleted, - VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for change string value events - /// @param[in] objectID The object ID of the string object - /// @param[in] value The new value of the string object - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_change_string_value_callback(std::uint16_t objectID, std::string value, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for user layout hide/show events - /// @param[in] objectID The object ID of the user layout object - /// @param[in] isHidden Denotes if the user layout is hidden - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_user_layout_hide_show_callback(std::uint16_t objectID, bool isHidden, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for control audio signal termination events - /// @param[in] isTerminated Denotes if the audio signal is terminated - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_audio_signal_termination_callback(bool isTerminated, VirtualTerminalClient *parentPointer); - - /// @brief Calls all registered callbacks for auxiliary input - /// @param[in] function The function of the auxiliary input object - /// @param[in] value1 The new value1 of the auxiliary input object - /// @param[in] value2 The new value2 of the auxiliary input object - /// @param[in] parentPointer A context variable that is passed back through the callback - void process_auxiliary_input_callback(AssignedAuxiliaryFunction function, - std::uint32_t value1, - std::uint32_t value2, - VirtualTerminalClient *parentPointer); - /// @brief Processes the internal Tx flags /// @param[in] flag The flag to process /// @param[in] parent A context variable to find the relevant VT client class @@ -1663,18 +1581,18 @@ namespace isobus bool shouldTerminate; ///< Used to determine if the client should exit and join the worker thread // Activation event callbacks - std::vector buttonEventCallbacks; ///< A list of all button event callbacks - std::vector softKeyEventCallbacks; ///< A list of all soft key event callbacks - std::vector pointingEventCallbacks; ///< A list of all pointing event callbacks - std::vector selectInputObjectCallbacks; ///< A list of all select input object callbacks - std::vector escMessageCallbacks; ///< A list of all ESC event callbacks - std::vector changeNumericValueCallbacks; ///< A list of all change numeric value callbacks - std::vector changeActiveMaskCallbacks; ///< A list of all change active mask callbacks - std::vector changeSoftKeyMaskCallbacks; ///< A list of all change soft key mask callbacks - std::vector changeStringValueCallbacks; ///< A list of all change string value callbacks - std::vector userLayoutHideShowCallbacks; ///< A list of all user layout hide/show callbacks - std::vector audioSignalTerminationCallbacks; ///< A list of all control audio signal termination callbacks - std::vector auxiliaryFunctionCallbacks; ///< A list of all auxiliary function callbacks + EventDispatcher softKeyEventDispatcher; ///< A list of all soft key event callbacks + EventDispatcher buttonEventDispatcher; ///< A list of all button event callbacks + EventDispatcher pointingEventDispatcher; ///< A list of all pointing event callbacks + EventDispatcher selectInputObjectEventDispatcher; ///< A list of all select input object callbacks + EventDispatcher escMessageEventDispatcher; ///< A list of all ESC event callbacks + EventDispatcher changeNumericValueEventDispatcher; ///< A list of all change numeric value callbacks + EventDispatcher changeActiveMaskEventDispatcher; ///< A list of all change active mask callbacks + EventDispatcher changeSoftKeyMaskEventDispatcher; ///< A list of all change soft key mask callbacks + EventDispatcher changeStringValueEventDispatcher; ///< A list of all change string value callbacks + EventDispatcher userLayoutHideShowEventDispatcher; ///< A list of all user layout hide/show callbacks + EventDispatcher audioSignalTerminationEventDispatcher; ///< A list of all control audio signal termination callbacks + EventDispatcher auxiliaryFunctionEventDispatcher; ///< A list of all auxiliary function callbacks // Object Pool info DataChunkCallback objectPoolDataCallback; ///< The callback to use to get pool data diff --git a/isobus/src/isobus_virtual_terminal_client.cpp b/isobus/src/isobus_virtual_terminal_client.cpp index 8ed72568..ca75aeca 100644 --- a/isobus/src/isobus_virtual_terminal_client.cpp +++ b/isobus/src/isobus_virtual_terminal_client.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -130,192 +131,64 @@ namespace isobus } } - void VirtualTerminalClient::register_vt_soft_key_event_callback(VTKeyEventCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_soft_key_event_listener(std::function callback) { - softKeyEventCallbacks.push_back(value); + return softKeyEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::remove_vt_soft_key_event_callback(VTKeyEventCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_button_event_listener(std::function callback) { - auto callbackLocation = std::find(softKeyEventCallbacks.begin(), softKeyEventCallbacks.end(), value); - - if (softKeyEventCallbacks.end() != callbackLocation) - { - softKeyEventCallbacks.erase(callbackLocation); - } + return buttonEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::register_vt_button_event_callback(VTKeyEventCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_pointing_event_listener(std::function callback) { - buttonEventCallbacks.push_back(value); + return pointingEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::remove_vt_button_event_callback(VTKeyEventCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_select_input_object_event_listener(std::function callback) { - auto callbackLocation = std::find(buttonEventCallbacks.begin(), buttonEventCallbacks.end(), value); - - if (buttonEventCallbacks.end() != callbackLocation) - { - buttonEventCallbacks.erase(callbackLocation); - } + return selectInputObjectEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::register_vt_pointing_event_callback(VTPointingEventCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_esc_message_event_listener(std::function callback) { - pointingEventCallbacks.push_back(value); + return escMessageEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::remove_vt_pointing_event_callback(VTPointingEventCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_change_numeric_value_event_listener(std::function callback) { - auto callbackLocation = std::find(pointingEventCallbacks.begin(), pointingEventCallbacks.end(), value); - - if (pointingEventCallbacks.end() != callbackLocation) - { - pointingEventCallbacks.erase(callbackLocation); - } + return changeNumericValueEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::register_vt_select_input_object_event_callback(VTSelectInputObjectCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_change_active_mask_event_listener(std::function callback) { - selectInputObjectCallbacks.push_back(value); + return changeActiveMaskEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::remove_vt_selection_input_object_event_callback(VTSelectInputObjectCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_change_soft_key_mask_event_listener(std::function callback) { - auto callbackLocation = std::find(selectInputObjectCallbacks.begin(), selectInputObjectCallbacks.end(), value); - - if (selectInputObjectCallbacks.end() != callbackLocation) - { - selectInputObjectCallbacks.erase(callbackLocation); - } + return changeSoftKeyMaskEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::register_vt_esc_message_event_callback(VTESCMessageCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_change_string_value_event_listener(std::function callback) { - escMessageCallbacks.push_back(value); - } - - void VirtualTerminalClient::remove_vt_esc_message_event_callback(VTESCMessageCallback value) - { - auto callbackLocation = std::find(escMessageCallbacks.begin(), escMessageCallbacks.end(), value); - - if (escMessageCallbacks.end() != callbackLocation) - { - escMessageCallbacks.erase(callbackLocation); - } + return changeStringValueEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::register_vt_change_numeric_value_event_callback(VTChangeNumericValueCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_user_layout_hide_show_event_listener(std::function callback) { - changeNumericValueCallbacks.push_back(value); + return userLayoutHideShowEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::remove_vt_change_numeric_value_event_callback(VTChangeNumericValueCallback value) + std::shared_ptr VirtualTerminalClient::add_vt_control_audio_signal_termination_event_listener(std::function callback) { - auto callbackLocation = std::find(changeNumericValueCallbacks.begin(), changeNumericValueCallbacks.end(), value); - - if (changeNumericValueCallbacks.end() != callbackLocation) - { - changeNumericValueCallbacks.erase(callbackLocation); - } + return audioSignalTerminationEventDispatcher.add_listener(callback); } - void VirtualTerminalClient::register_vt_change_active_mask_event_callback(VTChangeActiveMaskCallback value) + std::shared_ptr VirtualTerminalClient::add_auxiliary_function_event_listener(std::function callback) { - changeActiveMaskCallbacks.push_back(value); - } - - void VirtualTerminalClient::remove_vt_change_active_mask_event_callback(VTChangeActiveMaskCallback value) - { - auto callbackLocation = std::find(changeActiveMaskCallbacks.begin(), changeActiveMaskCallbacks.end(), value); - - if (changeActiveMaskCallbacks.end() != callbackLocation) - { - changeActiveMaskCallbacks.erase(callbackLocation); - } - } - - void VirtualTerminalClient::register_vt_change_soft_key_mask_event_callback(VTChangeSoftKeyMaskCallback value) - { - changeSoftKeyMaskCallbacks.push_back(value); - } - - void VirtualTerminalClient::remove_vt_change_soft_key_mask_event_callback(VTChangeSoftKeyMaskCallback value) - { - auto callbackLocation = std::find(changeSoftKeyMaskCallbacks.begin(), changeSoftKeyMaskCallbacks.end(), value); - - if (changeSoftKeyMaskCallbacks.end() != callbackLocation) - { - changeSoftKeyMaskCallbacks.erase(callbackLocation); - } - } - - void VirtualTerminalClient::register_vt_change_string_value_event_callback(VTChangeStringValueCallback value) - { - changeStringValueCallbacks.push_back(value); - } - - void VirtualTerminalClient::remove_vt_change_string_value_event_callback(VTChangeStringValueCallback value) - { - auto callbackLocation = std::find(changeStringValueCallbacks.begin(), changeStringValueCallbacks.end(), value); - - if (changeStringValueCallbacks.end() != callbackLocation) - { - changeStringValueCallbacks.erase(callbackLocation); - } - } - - void VirtualTerminalClient::register_vt_user_layout_hide_show_event_callback(VTUserLayoutHideShowCallback value) - { - userLayoutHideShowCallbacks.push_back(value); - } - - void VirtualTerminalClient::remove_vt_user_layout_hide_show_callback(VTUserLayoutHideShowCallback value) - { - auto callbackLocation = std::find(userLayoutHideShowCallbacks.begin(), userLayoutHideShowCallbacks.end(), value); - - if (userLayoutHideShowCallbacks.end() != callbackLocation) - { - userLayoutHideShowCallbacks.erase(callbackLocation); - } - } - - void VirtualTerminalClient::register_vt_control_audio_signal_termination_event_callback(VTAudioSignalTerminationCallback value) - { - audioSignalTerminationCallbacks.push_back(value); - } - - void VirtualTerminalClient::remove_vt_control_audio_signal_termination_event_callback(VTAudioSignalTerminationCallback value) - { - auto callbackLocation = std::find(audioSignalTerminationCallbacks.begin(), audioSignalTerminationCallbacks.end(), value); - - if (audioSignalTerminationCallbacks.end() != callbackLocation) - { - audioSignalTerminationCallbacks.erase(callbackLocation); - } - } - - void VirtualTerminalClient::register_auxiliary_function_event_callback(AuxiliaryFunctionCallback value) - { - if (state == StateMachineState::Disconnected) - { - auxiliaryFunctionCallbacks.push_back(value); - } - else - { - // Limitation right now due to the preferred assignment command relying on the presence of auxiliary function callbacks - CANStackLogger::error("[AUX-N] Error registering auxiliary function callback... can only be done when disconnected"); - } - } - - void VirtualTerminalClient::remove_auxiliary_function_event_callback(AuxiliaryFunctionCallback value) - { - auto callbackLocation = std::find(auxiliaryFunctionCallbacks.begin(), auxiliaryFunctionCallbacks.end(), value); - - if (auxiliaryFunctionCallbacks.end() != callbackLocation) - { - auxiliaryFunctionCallbacks.erase(callbackLocation); - } + return auxiliaryFunctionEventDispatcher.add_listener(callback); } void VirtualTerminalClient::set_auxiliary_input_model_identification_code(std::uint16_t modelIdentificationCode) @@ -2597,168 +2470,6 @@ namespace isobus } } - void VirtualTerminalClient::process_button_event_callback(KeyActivationCode keyEvent, std::uint8_t keyNumber, std::uint16_t objectID, std::uint16_t parentObjectID, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->buttonEventCallbacks.size(); i++) - { - if (nullptr != parentPointer->buttonEventCallbacks[i]) - { - parentPointer->buttonEventCallbacks[i](keyEvent, keyNumber, objectID, parentObjectID, parentPointer); - } - } - } - - void VirtualTerminalClient::process_softkey_event_callback(KeyActivationCode keyEvent, std::uint8_t keyNumber, std::uint16_t objectID, std::uint16_t parentObjectID, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->softKeyEventCallbacks.size(); i++) - { - if (nullptr != parentPointer->softKeyEventCallbacks[i]) - { - parentPointer->softKeyEventCallbacks[i](keyEvent, keyNumber, objectID, parentObjectID, parentPointer); - } - } - } - - void VirtualTerminalClient::process_pointing_event_callback(KeyActivationCode keyEvent, - std::uint16_t xPosition, - std::uint16_t yPosition, - std::uint16_t parentMaskObjectID, - VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->pointingEventCallbacks.size(); i++) - { - if (nullptr != parentPointer->pointingEventCallbacks[i]) - { - parentPointer->pointingEventCallbacks[i](keyEvent, xPosition, yPosition, parentMaskObjectID, parentPointer); - } - } - } - - void VirtualTerminalClient::process_select_input_object_callback(std::uint16_t objectID, bool objectSelected, bool objectOpenForInput, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->selectInputObjectCallbacks.size(); i++) - { - if (nullptr != parentPointer->selectInputObjectCallbacks[i]) - { - parentPointer->selectInputObjectCallbacks[i](objectID, objectSelected, objectOpenForInput, parentPointer); - } - } - } - - void VirtualTerminalClient::process_esc_message_callback(std::uint16_t objectID, ESCMessageErrorCode errorCode, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->escMessageCallbacks.size(); i++) - { - if (nullptr != parentPointer->escMessageCallbacks[i]) - { - parentPointer->escMessageCallbacks[i](objectID, errorCode, parentPointer); - } - } - } - - void VirtualTerminalClient::process_change_numeric_value_callback(std::uint16_t objectID, std::uint32_t value, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->changeNumericValueCallbacks.size(); i++) - { - if (nullptr != parentPointer->changeNumericValueCallbacks[i]) - { - parentPointer->changeNumericValueCallbacks[i](objectID, value, parentPointer); - } - } - } - - void VirtualTerminalClient::process_change_active_mask_callback(std::uint16_t maskObjectID, - std::uint16_t errorObjectID, - std::uint16_t parentObjectID, - bool missingObjects, - bool maskOrChildHasErrors, - bool anyOtherEror, - bool poolDeleted, - VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->changeActiveMaskCallbacks.size(); i++) - { - if (nullptr != parentPointer->changeActiveMaskCallbacks[i]) - { - parentPointer->changeActiveMaskCallbacks[i](maskObjectID, - errorObjectID, - parentObjectID, - missingObjects, - maskOrChildHasErrors, - anyOtherEror, - poolDeleted, - parentPointer); - } - } - } - - void VirtualTerminalClient::process_change_soft_key_mask_callback(std::uint16_t dataOrAlarmMaskObjectID, - std::uint16_t softKeyMaskObjectID, - bool missingObjects, - bool maskOrChildHasErrors, - bool anyOtherEror, - bool poolDeleted, - VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->changeSoftKeyMaskCallbacks.size(); i++) - { - if (nullptr != parentPointer->changeSoftKeyMaskCallbacks[i]) - { - parentPointer->changeSoftKeyMaskCallbacks[i](dataOrAlarmMaskObjectID, - softKeyMaskObjectID, - missingObjects, - maskOrChildHasErrors, - anyOtherEror, - poolDeleted, - parentPointer); - } - } - } - - void VirtualTerminalClient::process_change_string_value_callback(std::uint16_t objectID, std::string value, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->changeStringValueCallbacks.size(); i++) - { - if (nullptr != parentPointer->changeStringValueCallbacks[i]) - { - parentPointer->changeStringValueCallbacks[i](objectID, value, parentPointer); - } - } - } - - void VirtualTerminalClient::process_user_layout_hide_show_callback(std::uint16_t objectID, bool isHidden, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->userLayoutHideShowCallbacks.size(); i++) - { - if (nullptr != parentPointer->userLayoutHideShowCallbacks[i]) - { - parentPointer->userLayoutHideShowCallbacks[i](objectID, isHidden, parentPointer); - } - } - } - - void VirtualTerminalClient::process_audio_signal_termination_callback(bool isTerminated, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->audioSignalTerminationCallbacks.size(); i++) - { - if (nullptr != parentPointer->audioSignalTerminationCallbacks[i]) - { - parentPointer->audioSignalTerminationCallbacks[i](isTerminated, parentPointer); - } - } - } - - void VirtualTerminalClient::process_auxiliary_input_callback(AssignedAuxiliaryFunction function, std::uint32_t value1, std::uint32_t value2, VirtualTerminalClient *parentPointer) - { - for (std::size_t i = 0; i < parentPointer->auxiliaryFunctionCallbacks.size(); i++) - { - if (nullptr != parentPointer->auxiliaryFunctionCallbacks[i]) - { - parentPointer->auxiliaryFunctionCallbacks[i](function, value1, value2, parentPointer); - } - } - } - void VirtualTerminalClient::process_flags(std::uint32_t flag, void *parent) { if ((flag <= static_cast(TransmitFlags::NumberFlags)) && @@ -2849,7 +2560,7 @@ namespace isobus //! @todo process TAN } - parentVT->process_softkey_event_callback(static_cast(keyCode), keyNumber, objectID, parentObjectID, parentVT); + parentVT->softKeyEventDispatcher.invoke({ static_cast(keyCode), keyNumber, objectID, parentObjectID, parentVT }); } } break; @@ -2866,7 +2577,7 @@ namespace isobus { //! @todo process TAN } - parentVT->process_button_event_callback(static_cast(keyCode), keyNumber, objectID, parentObjectID, parentVT); + parentVT->buttonEventDispatcher.invoke({ static_cast(keyCode), keyNumber, objectID, parentObjectID, parentVT }); } } break; @@ -2893,11 +2604,7 @@ namespace isobus if (touchState <= static_cast(KeyActivationCode::ButtonPressAborted)) { - parentVT->process_pointing_event_callback(static_cast(touchState), - xPosition, - yPosition, - partenMaskObjectID, - parentVT); + parentVT->pointingEventDispatcher.invoke({ static_cast(touchState), xPosition, yPosition, partenMaskObjectID, parentVT }); } } break; @@ -2918,7 +2625,7 @@ namespace isobus //! @todo process TAN } - parentVT->process_select_input_object_callback(objectID, objectSelected, objectOpenForInput, parentVT); + parentVT->selectInputObjectEventDispatcher.invoke({ objectID, objectSelected, objectOpenForInput, parentVT }); } break; @@ -2934,7 +2641,7 @@ namespace isobus //! @todo process TAN } - parentVT->process_esc_message_callback(objectID, static_cast(errorCode), parentVT); + parentVT->escMessageEventDispatcher.invoke({ objectID, static_cast(errorCode), parentVT }); } } break; @@ -2948,7 +2655,7 @@ namespace isobus { //! @todo process TAN } - parentVT->process_change_numeric_value_callback(objectID, value, parentVT); + parentVT->changeNumericValueEventDispatcher.invoke({ objectID, value, parentVT }); } break; @@ -2964,14 +2671,14 @@ namespace isobus std::uint16_t errorObjectID = message->get_uint16_at(4); std::uint16_t parentObjectID = message->get_uint16_at(6); - parentVT->process_change_active_mask_callback(maskObjectID, - errorObjectID, - parentObjectID, - missingObjects, - maskOrChildHasErrors, - anyOtherError, - poolDeleted, - parentVT); + parentVT->changeActiveMaskEventDispatcher.invoke({ maskObjectID, + errorObjectID, + parentObjectID, + missingObjects, + maskOrChildHasErrors, + anyOtherError, + poolDeleted, + parentVT }); } break; @@ -2985,13 +2692,13 @@ namespace isobus bool anyOtherError = message->get_bool_at(5, 4); bool poolDeleted = message->get_bool_at(5, 5); - parentVT->process_change_soft_key_mask_callback(dataOrAlarmMaskID, - softKeyMaskID, - missingObjects, - maskOrChildHasErrors, - anyOtherError, - poolDeleted, - parentVT); + parentVT->changeSoftKeyMaskEventDispatcher.invoke({ dataOrAlarmMaskID, + softKeyMaskID, + missingObjects, + maskOrChildHasErrors, + anyOtherError, + poolDeleted, + parentVT }); } break; @@ -3001,7 +2708,7 @@ namespace isobus std::uint8_t stringLength = message->get_uint8_at(3); std::string value = std::string(message->get_data().begin() + 4, message->get_data().begin() + 4 + stringLength); - parentVT->process_change_string_value_callback(objectID, value, parentVT); + parentVT->changeStringValueEventDispatcher.invoke({ objectID, value, parentVT }); } break; @@ -3010,14 +2717,14 @@ namespace isobus std::uint16_t objectID = message->get_uint16_at(1); bool hidden = !message->get_bool_at(3, 0); - parentVT->process_user_layout_hide_show_callback(objectID, hidden, parentVT); + parentVT->userLayoutHideShowEventDispatcher.invoke({ objectID, hidden, parentVT }); // There could be two layout messages in one packet objectID = message->get_uint16_at(4); if (objectID != NULL_OBJECT_ID) { hidden = !message->get_bool_at(6, 0); - parentVT->process_user_layout_hide_show_callback(objectID, hidden, parentVT); + parentVT->userLayoutHideShowEventDispatcher.invoke({ objectID, hidden, parentVT }); } if (parentVT->get_vt_version_supported(VTVersion::Version6)) @@ -3031,7 +2738,7 @@ namespace isobus { bool terminated = message->get_bool_at(1, 0); - parentVT->process_audio_signal_termination_callback(terminated, parentVT); + parentVT->audioSignalTerminationEventDispatcher.invoke({ terminated, parentVT }); if (parentVT->get_vt_version_supported(VTVersion::Version6)) { @@ -3192,7 +2899,7 @@ namespace isobus }); if (aux.functions.end() != result) { - parentVT->process_auxiliary_input_callback(*result, value1, value2, parentVT); + parentVT->auxiliaryFunctionEventDispatcher.invoke({ *result, value1, value2, parentVT }); } } } @@ -3398,7 +3105,7 @@ namespace isobus parentVT->set_state(StateMachineState::Connected); //! @todo maybe a better way available than relying on aux function callbacks registered? - if (!parentVT->auxiliaryFunctionCallbacks.empty()) + if (parentVT->auxiliaryFunctionEventDispatcher.get_listener_count() > 0) { if (parentVT->send_auxiliary_functions_preferred_assignment()) { @@ -3522,7 +3229,7 @@ namespace isobus parentVT->set_state(StateMachineState::Connected); } //! @todo maybe a better way available than relying on aux function callbacks registered? - if (!parentVT->auxiliaryFunctionCallbacks.empty()) + if (parentVT->auxiliaryFunctionEventDispatcher.get_listener_count() > 0) { if (parentVT->send_auxiliary_functions_preferred_assignment()) { diff --git a/test/event_dispatcher_tests.cpp b/test/event_dispatcher_tests.cpp new file mode 100644 index 00000000..3f5a4222 --- /dev/null +++ b/test/event_dispatcher_tests.cpp @@ -0,0 +1,52 @@ +#include + +#include "isobus/utility/event_dispatcher.hpp" + +#include +#include + +using namespace isobus; + +TEST(EVENT_DISPATCHER_TESTS, AddRemoveListener) +{ + EventDispatcher dispatcher; + + std::function callback = [](bool) {}; + + // Use different scopes to test the lifetime of the listeners. + { + auto listener = dispatcher.add_listener(callback); + EXPECT_EQ(dispatcher.get_listener_count(), 1); + { + auto listener2 = dispatcher.add_listener(callback); + EXPECT_EQ(dispatcher.get_listener_count(), 2); + } + EXPECT_EQ(dispatcher.get_listener_count(), 2); + + // Invoke is required to automatically remove expired listeners. + dispatcher.invoke({ true }); + EXPECT_EQ(dispatcher.get_listener_count(), 1); + } + + // Invoke is required to automatically remove expired listeners. + dispatcher.invoke({ true }); + EXPECT_EQ(dispatcher.get_listener_count(), 0); +} + +TEST(EVENT_DISPATCHER_TESTS, InvokeEvent) +{ + EventDispatcher dispatcher; + + int count = 0; + std::function callback = [&count](bool value) { + ASSERT_TRUE(value); + count += 1; + }; + auto listener = dispatcher.add_listener(callback); + + dispatcher.invoke({ true }); + ASSERT_EQ(count, 1); + + dispatcher.invoke({ true }); + ASSERT_EQ(count, 2); +} \ No newline at end of file diff --git a/utility/CMakeLists.txt b/utility/CMakeLists.txt index c2765743..0fb655d0 100644 --- a/utility/CMakeLists.txt +++ b/utility/CMakeLists.txt @@ -17,7 +17,7 @@ prepend(UTILITY_SRC ${UTILITY_SRC_DIR} ${UTILITY_SRC}) # Set the include files set(UTILITY_INCLUDE "system_timing.hpp" "processing_flags.hpp" "iop_file_interface.hpp" - "to_string.hpp" "platform_endianness.hpp") + "to_string.hpp" "platform_endianness.hpp" "event_dispatcher.hpp") # Prepend the include directory path to all the include files prepend(UTILITY_INCLUDE ${UTILITY_INCLUDE_DIR} ${UTILITY_INCLUDE}) diff --git a/utility/include/isobus/utility/event_dispatcher.hpp b/utility/include/isobus/utility/event_dispatcher.hpp new file mode 100644 index 00000000..410d8518 --- /dev/null +++ b/utility/include/isobus/utility/event_dispatcher.hpp @@ -0,0 +1,93 @@ +//================================================================================================ +/// @file event_dispatcher.hpp +/// +/// @brief An object to represent a dispatcher that can invoke callbacks in a thread-safe manner. +/// @author Daan Steenbergen +/// +/// @copyright 2023 Adrian Del Grosso +//================================================================================================ +#ifndef EVENT_DISPATCHER_HPP +#define EVENT_DISPATCHER_HPP + +#include +#include +#include +#include + +namespace isobus +{ + //================================================================================================ + /// @class EventDispatcher + /// + /// @brief A dispatcher that notifies listeners when an event is invoked. + //================================================================================================ + template + class EventDispatcher + { + public: + /// @brief Register a callback to be invoked when the event is invoked. + /// @param callback The callback to register. + /// @return A shared pointer to the callback. + std::shared_ptr> add_listener(std::function &callback) + { + auto shared = std::make_shared>(callback); + callbacks.push_back(shared); + return shared; + } + + /// @brief Get the number of listeners registered to this event. + /// @return The number of listeners + std::size_t get_listener_count() const + { + return callbacks.size(); + } + + /// @brief Invokes an event and notify all listeners. + /// @param args The arguments to pass to the listeners. + /// @return True if the event was successfully invoked, false otherwise. + bool invoke(E &&args) + { + // Remove all callbacks that are gone, only if we are not dispatching. + if (0 == concurrent_invokes_count) + { + for (auto it = callbacks.begin(); it != callbacks.end();) + { + if (it->expired()) + { + it = callbacks.erase(it); + } + else + { + ++it; + } + } + } + + concurrent_invokes_count++; + try + { + std::size_t current = 0; + while (current < callbacks.size()) + { + if (auto callback = callbacks[current++].lock()) + { + (*callback)(args); + } + } + concurrent_invokes_count--; + } + catch (...) + { + concurrent_invokes_count--; + return false; + } + return true; + } + + private: + std::vector>> callbacks; ///< The callbacks to invoke + std::atomic concurrent_invokes_count = { 0 }; ///< The number of concurrent invoke calls in progress + }; +} // namespace isobus + +#endif // EVENT_DISPATCHER_HPP