Skip to content

Commit

Permalink
feat(vt-client): add attribute tracking and updating
Browse files Browse the repository at this point in the history
- introduced callbacks for transmitted messages by our device from any CF
- make event dispatcher follow explicit behavior when (de-)registering listeners Currently, when the returned shared_ptr goes out of scope, the listener is automatically cleanup up. But in many cases, this is not the flow you expect/want. That is why I removed this functionality and so it is easy to follow what is happening.
- refactored the ifdef checks for when threads are disabled to a conciser macro.
  • Loading branch information
GwnDaan committed Feb 8, 2024
1 parent 0192b52 commit 867d2c0
Show file tree
Hide file tree
Showing 32 changed files with 592 additions and 247 deletions.
2 changes: 1 addition & 1 deletion examples/diagnostic_protocol/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ int main()

// Important: we need to update the diagnostic protocol using the hardware interface periodic update event,
// otherwise the diagnostic protocol will not be able to update its internal state.
auto listenerHandle = isobus::CANHardwareInterface::get_periodic_update_event_dispatcher().add_listener([&diagnosticProtocol]() {
isobus::CANHardwareInterface::get_periodic_update_event_dispatcher().add_listener([&diagnosticProtocol]() {
diagnosticProtocol.update();
});

Expand Down
5 changes: 2 additions & 3 deletions examples/guidance/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,8 @@ int main()
isobus::AgriculturalGuidanceInterface TestGuidanceInterface(nullptr, nullptr);

// Register listeners for the (guidance) events we want to receive
//! @note That the listeners are removed automatically when the returned `shared_ptr` goes out of scope!!!
auto guidanceMachineInfoListener = TestGuidanceInterface.get_guidance_machine_info_event_publisher().add_listener(on_guidance_machine_info_message);
auto guidanceSystemCommandListener = TestGuidanceInterface.get_guidance_system_command_event_publisher().add_listener(on_guidance_system_command_message);
TestGuidanceInterface.get_guidance_machine_info_event_publisher().add_listener(on_guidance_machine_info_message);
TestGuidanceInterface.get_guidance_system_command_event_publisher().add_listener(on_guidance_system_command_message);

// Finally we can initialize the guidance interface to start sending and receiving messages
TestGuidanceInterface.initialize();
Expand Down
15 changes: 7 additions & 8 deletions examples/nmea2000/nmea2000_parser/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,13 @@ int main()
isobus::NMEA2000MessageInterface n2kInterface(TestInternalECU, false, false, false, false, false, false, false);
n2kInterface.initialize();

// Listen to incoming NMEA2K messages. Note how need to keep the returned listener object alive for as long as we want to receive messages.
// If we don't, the listener will be removed and we will simply not receive messages.
auto cog_sog_listener = n2kInterface.get_course_speed_over_ground_rapid_update_event_publisher().add_listener(on_cog_sog_update);
auto datum_listener = n2kInterface.get_datum_event_publisher().add_listener(on_datum_update);
auto position_listener = n2kInterface.get_gnss_position_data_event_publisher().add_listener(on_position_update);
auto position_rapid_listener = n2kInterface.get_position_rapid_update_event_publisher().add_listener(on_position_rapid_update);
auto turn_rate_listener = n2kInterface.get_rate_of_turn_event_publisher().add_listener(on_turn_rate_update);
auto vessel_heading_listener = n2kInterface.get_vessel_heading_event_publisher().add_listener(on_vessel_heading_update);
// Listen to incoming NMEA2K messages
n2kInterface.get_course_speed_over_ground_rapid_update_event_publisher().add_listener(on_cog_sog_update);
n2kInterface.get_datum_event_publisher().add_listener(on_datum_update);
n2kInterface.get_gnss_position_data_event_publisher().add_listener(on_position_update);
n2kInterface.get_position_rapid_update_event_publisher().add_listener(on_position_rapid_update);
n2kInterface.get_rate_of_turn_event_publisher().add_listener(on_turn_rate_update);
n2kInterface.get_vessel_heading_event_publisher().add_listener(on_vessel_heading_update);

std::cout << "Starting to parse NMEA2K messages. Press Ctrl+C to stop." << std::endl;
while (running)
Expand Down
2 changes: 1 addition & 1 deletion examples/virtual_terminal/aux_functions/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ int main()

TestVirtualTerminalClient = std::make_shared<isobus::VirtualTerminalClient>(TestPartnerVT, TestInternalECU);
TestVirtualTerminalClient->set_object_pool(0, testPool.data(), testPool.size(), objectPoolHash);
auto auxFunctionListener = TestVirtualTerminalClient->add_auxiliary_function_event_listener(handle_aux_function_input);
TestVirtualTerminalClient->get_auxiliary_function_event_dispatcher().add_listener(handle_aux_function_input);
TestVirtualTerminalClient->initialize(true);

while (running)
Expand Down
2 changes: 1 addition & 1 deletion examples/virtual_terminal/aux_inputs/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ int main()
return -2;
}

auto updateListener = isobus::CANHardwareInterface::get_periodic_update_event_dispatcher().add_listener(on_periodic_update);
isobus::CANHardwareInterface::get_periodic_update_event_dispatcher().add_listener(on_periodic_update);
std::this_thread::sleep_for(std::chrono::milliseconds(250));

isobus::NAME TestDeviceNAME(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ extern "C" void app_main()

virtualTerminalClient = std::make_shared<isobus::VirtualTerminalClient>(TestPartnerVT, TestInternalECU);
virtualTerminalClient->set_object_pool(0, testPool, (object_pool_end - object_pool_start) - 1, "ais1");
auto softKeyListener = virtualTerminalClient->add_vt_soft_key_event_listener(handleVTKeyEvents);
auto buttonListener = virtualTerminalClient->add_vt_button_event_listener(handleVTKeyEvents);
virtualTerminalClient->get_vt_soft_key_event_dispatcher().add_listener(handleVTKeyEvents);
virtualTerminalClient->get_vt_button_event_dispatcher().add_listener(handleVTKeyEvents);
virtualTerminalClient->initialize(true);

virtualTerminalUpdateHelper = std::make_shared<isobus::VirtualTerminalClientUpdateHelper>(virtualTerminalClient);
Expand Down
4 changes: 2 additions & 2 deletions examples/virtual_terminal/version3_object_pool/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ int main()

virtualTerminalClient = std::make_shared<isobus::VirtualTerminalClient>(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->get_vt_soft_key_event_dispatcher().add_listener(handleVTKeyEvents);
virtualTerminalClient->get_vt_button_event_dispatcher().add_listener(handleVTKeyEvents);
virtualTerminalClient->initialize(true);

virtualTerminalUpdateHelper = std::make_shared<isobus::VirtualTerminalClientUpdateHelper>(virtualTerminalClient);
Expand Down
3 changes: 3 additions & 0 deletions hardware_integration/src/can_hardware_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ namespace isobus
return false;
}
stop_threads();
frameReceivedEventDispatcher.clear_listeners();
frameTransmittedEventDispatcher.clear_listeners();
periodicUpdateEventDispatcher.clear_listeners();

std::lock_guard<std::mutex> channelsLock(hardwareChannelsMutex);
std::for_each(hardwareChannels.begin(), hardwareChannels.end(), [](const std::unique_ptr<CANHardware> &channel) {
Expand Down
16 changes: 16 additions & 0 deletions isobus/include/isobus/isobus/can_network_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ namespace isobus
/// @param[in] parent A generic context variable that helps identify what object the callback was destined for
void remove_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent);

/// @brief Returns the network manager's event dispatcher for notifying consumers whenever a
/// message is transmitted by our application
/// @returns An event dispatcher which can be used to get notified about transmitted messages
EventDispatcher<CANMessage> &get_transmitted_message_event_dispatcher();

/// @brief Returns an internal control function if the passed-in control function is an internal type
/// @param[in] controlFunction The control function to get the internal control function from
/// @returns An internal control function casted from the passed in control function
Expand Down Expand Up @@ -299,6 +304,11 @@ namespace isobus
/// @returns The message that was at the front of the queue, or an invalid message if the queue is empty
CANMessage get_next_can_message_from_rx_queue();

/// @brief Get the next CAN message from the received message queue, and remove it from the queue
/// @note This will only ever get an 8 byte message because they are directly translated from CAN frames.
/// @returns The message that was at the front of the queue, or an invalid message if the queue is empty
CANMessage get_next_can_message_from_tx_queue();

/// @brief Informs the network manager that a control function object has been created
/// @param[in] controlFunction The control function that was created
void on_control_function_created(std::shared_ptr<ControlFunction> controlFunction);
Expand Down Expand Up @@ -338,6 +348,9 @@ namespace isobus
/// @brief Processes the internal received message queue
void process_rx_messages();

/// @brief Processes the internal transmitted message queue
void process_tx_messages();

/// @brief Checks to see if any control function didn't claim during a round of
/// address claiming and removes it if needed.
void prune_inactive_control_functions();
Expand Down Expand Up @@ -383,9 +396,11 @@ namespace isobus

std::list<ParameterGroupNumberCallbackData> protocolPGNCallbacks; ///< A list of PGN callback registered by CAN protocols
std::queue<CANMessage> receivedMessageQueue; ///< A queue of received messages to process
std::queue<CANMessage> transmittedMessageQueue; ///< A queue of transmitted messages to process (already sent, so changes to the message won't affect the bus)
std::list<ControlFunctionStateCallback> controlFunctionStateCallbacks; ///< List of all control function state callbacks
std::vector<ParameterGroupNumberCallbackData> globalParameterGroupNumberCallbacks; ///< A list of all global PGN callbacks
std::vector<ParameterGroupNumberCallbackData> anyControlFunctionParameterGroupNumberCallbacks; ///< A list of all global PGN callbacks
EventDispatcher<CANMessage> messageTransmittedEventDispatcher; ///< An event dispatcher for notifying consumers about transmitted messages by our application
EventDispatcher<std::shared_ptr<InternalControlFunction>> addressViolationEventDispatcher; ///< An event dispatcher for notifying consumers about address violations
#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
std::mutex receivedMessageQueueMutex; ///< A mutex for receive messages thread safety
Expand All @@ -394,6 +409,7 @@ namespace isobus
std::mutex busloadUpdateMutex; ///< A mutex that protects the busload metrics since we calculate it on our own thread
std::mutex controlFunctionStatusCallbacksMutex; ///< A Mutex that protects access to the control function status callback list
#endif
Mutex transmittedMessageQueueMutex; ///< A mutex for protecting the transmitted message queue
std::uint32_t busloadUpdateTimestamp_ms = 0; ///< Tracks a time window for determining approximate busload
std::uint32_t updateTimestamp_ms = 0; ///< Keeps track of the last time the CAN stack was update in milliseconds
bool initialized = false; ///< True if the network manager has been initialized by the update function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ namespace isobus
static void process_flags(std::uint32_t flag, void *parentPointer);

std::shared_ptr<InternalControlFunction> myControlFunction; ///< The internal control function that this protocol will send from
std::shared_ptr<void> addressViolationEventHandle; ///< Stores the handle from registering for address violation events
EventCallbackHandle addressViolationEventHandle; ///< Stores the handle from registering for address violation events
NetworkType networkType; ///< The diagnostic network type that this protocol will use
std::vector<DiagnosticTroubleCode> activeDTCList; ///< Keeps track of all the active DTCs
std::vector<DiagnosticTroubleCode> inactiveDTCList; ///< Keeps track of all the previously active DTCs
Expand Down
102 changes: 45 additions & 57 deletions isobus/include/isobus/isobus/isobus_virtual_terminal_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,68 +517,56 @@ namespace isobus
std::uint16_t value2; ///< The second 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<void> add_vt_soft_key_event_listener(std::function<void(const VTKeyEvent &)> 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<void> add_vt_button_event_listener(std::function<void(const VTKeyEvent &)> 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<void> add_vt_pointing_event_listener(std::function<void(const VTPointingEvent &)> 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<void> add_vt_select_input_object_event_listener(std::function<void(const VTSelectInputObjectEvent &)> 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<void> add_vt_esc_message_event_listener(std::function<void(const VTESCMessageEvent &)> 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<void> add_vt_change_numeric_value_event_listener(std::function<void(const VTChangeNumericValueEvent &)> callback);

/// @brief Add a listener for when the active mask is changed
/// @brief The event dispatcher for when a soft key is pressed or released
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTKeyEvent> &get_vt_soft_key_event_dispatcher();

/// @brief The event dispatcher for when a button is pressed or released
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTKeyEvent> &get_vt_button_event_dispatcher();

/// @brief The event dispatcher for when a pointing event is "pressed or released"
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTPointingEvent> &get_vt_pointing_event_dispatcher();

/// @brief The event dispatcher for when an input object event is triggered
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTSelectInputObjectEvent> &get_vt_select_input_object_event_dispatcher();

/// @brief The event dispatcher for when an ESC message is received, e.g. an open object input is closed
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTESCMessageEvent> &get_vt_esc_message_event_dispatcher();

/// @brief The event dispatcher for when a numeric value is changed in an input object
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTChangeNumericValueEvent> &get_vt_change_numeric_value_event_dispatcher();

/// @brief The event dispatcher 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<void> add_vt_change_active_mask_event_listener(std::function<void(const VTChangeActiveMaskEvent &)> callback);
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTChangeActiveMaskEvent> &get_vt_change_active_mask_event_dispatcher();

/// @brief Add a listener for when the soft key mask is changed
/// @brief The event dispatcher 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<void> add_vt_change_soft_key_mask_event_listener(std::function<void(const VTChangeSoftKeyMaskEvent &)> callback);
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTChangeSoftKeyMaskEvent> &get_vt_change_soft_key_mask_event_dispatcher();

/// @brief Add a listener for when a string value is changed
/// @brief The event dispatcher 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<void> add_vt_change_string_value_event_listener(std::function<void(const VTChangeStringValueEvent &)> 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<void> add_vt_user_layout_hide_show_event_listener(std::function<void(const VTUserLayoutHideShowEvent &)> 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<void> add_vt_control_audio_signal_termination_event_listener(std::function<void(const VTAudioSignalTerminationEvent &)> 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<void> add_auxiliary_function_event_listener(std::function<void(const AuxiliaryFunctionEvent &)> callback);
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTChangeStringValueEvent> &get_vt_change_string_value_event_dispatcher();

/// @brief The event dispatcher for when a user-layout object is hidden or shown
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTUserLayoutHideShowEvent> &get_vt_user_layout_hide_show_event_dispatcher();

/// @brief The event dispatcher for when an audio signal is terminated
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<VTAudioSignalTerminationEvent> &get_vt_control_audio_signal_termination_event_dispatcher();

/// @brief The event dispatcher for for when a change in auxiliary input for a function is received
/// @returns A reference to the event dispatcher, used to add listeners
EventDispatcher<AuxiliaryFunctionEvent> &get_auxiliary_function_event_dispatcher();

/// @brief Set the model identification code of our auxiliary input device.
/// @details The model identification code is used to allow other devices identify
Expand Down
Loading

0 comments on commit 867d2c0

Please sign in to comment.