Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/pgn_requests/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,13 @@ int main()
}

// Register a callback to handle PROPA PGN Requests
TestInternalECU->get_pgn_request_protocol().lock()->register_pgn_request_callback(static_cast<std::uint32_t>(isobus::CANLibParameterGroupNumber::ProprietaryA), example_proprietary_a_pgn_request_handler, nullptr);
TestInternalECU->get_pgn_request_protocol()->register_pgn_request_callback(static_cast<std::uint32_t>(isobus::CANLibParameterGroupNumber::ProprietaryA), example_proprietary_a_pgn_request_handler, nullptr);

// Now, if you send a PGN request for EF00 to our internal control function, the stack will acknowledge it. Other requests will be NACK'ed (negative acknowledged)
// NOTE the device you send from MUST have address claimed.

// Now we'll set up a callback to handle requests for repetition rate for the PROPA PGN
TestInternalECU->get_pgn_request_protocol().lock()->register_request_for_repetition_rate_callback(static_cast<std::uint32_t>(isobus::CANLibParameterGroupNumber::ProprietaryA), example_proprietary_a_request_for_repetition_rate_handler, nullptr);
TestInternalECU->get_pgn_request_protocol()->register_request_for_repetition_rate_callback(static_cast<std::uint32_t>(isobus::CANLibParameterGroupNumber::ProprietaryA), example_proprietary_a_request_for_repetition_rate_handler, nullptr);

// Now we'll get a callback when someone requests a repetition rate for PROPA.
// The application (not the stack) must handle these requests, as the CAN stack does not know what data to send when responding.
Expand All @@ -159,7 +159,7 @@ int main()

// This is how you would request a PGN from someone else. In this example, we request it from the broadcast address.
// Generally you'd want to replace nullptr with your partner control function as its a little nicer than just asking everyone on the bus for a PGN
isobus::ParameterGroupNumberRequestProtocol::request_parameter_group_number(static_cast<std::uint32_t>(isobus::CANLibParameterGroupNumber::ProprietaryA), TestInternalECU, nullptr);
TestInternalECU->get_pgn_request_protocol()->request_parameter_group_number(static_cast<std::uint32_t>(isobus::CANLibParameterGroupNumber::ProprietaryA), nullptr);

while (running)
{
Expand Down
1 change: 1 addition & 0 deletions examples/seeder_example/seeder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "isobus/hardware_integration/available_can_drivers.hpp"
#include "isobus/hardware_integration/can_hardware_interface.hpp"
#include "isobus/isobus/can_network_manager.hpp"
#include "isobus/isobus/isobus_diagnostic_protocol.hpp"
#include "isobus/isobus/isobus_standard_data_description_indices.hpp"
#include "isobus/isobus/isobus_task_controller_client.hpp"
Expand Down
2 changes: 1 addition & 1 deletion examples/seeder_example/vt_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ bool SeederVtApplication::initialize()
update_section_objects(i);
}

speedMessages.initialize();
isobus::CANNetworkManager::CANNetwork.get_can_message_handler().add_consumer(&speedMessages);
speedMessages.get_machine_selected_speed_data_event_publisher().add_listener([this](const std::shared_ptr<isobus::SpeedMessagesInterface::MachineSelectedSpeedData> mssData, bool changed) { this->handle_machine_selected_speed(mssData, changed); });
speedMessages.get_ground_based_machine_speed_data_event_publisher().add_listener([this](const std::shared_ptr<isobus::SpeedMessagesInterface::GroundBasedSpeedData> gbsData, bool changed) { this->handle_ground_based_speed(gbsData, changed); });
speedMessages.get_wheel_based_machine_speed_data_event_publisher().add_listener([this](const std::shared_ptr<isobus::SpeedMessagesInterface::WheelBasedMachineSpeedData> wbsData, bool changed) { this->handle_wheel_based_speed(wbsData, changed); });
Expand Down
6 changes: 4 additions & 2 deletions isobus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ set(ISOBUS_SRC
"nmea2000_message_definitions.cpp"
"nmea2000_message_interface.cpp"
"isobus_device_descriptor_object_pool_helpers.cpp"
"can_message_data.cpp")
"can_message_data.cpp"
"can_message_handling.cpp")

# Prepend the source directory path to all the source files
prepend(ISOBUS_SRC ${ISOBUS_SRC_DIR} ${ISOBUS_SRC})
Expand Down Expand Up @@ -98,7 +99,8 @@ set(ISOBUS_INCLUDE
"nmea2000_message_interface.hpp"
"isobus_preferred_addresses.hpp"
"isobus_device_descriptor_object_pool_helpers.hpp"
"can_message_data.hpp")
"can_message_data.hpp"
"can_message_handling.hpp")
# Prepend the include directory path to all the include files
prepend(ISOBUS_INCLUDE ${ISOBUS_INCLUDE_DIR} ${ISOBUS_INCLUDE})

Expand Down
12 changes: 7 additions & 5 deletions isobus/include/isobus/isobus/can_internal_control_function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ namespace isobus

/// @brief Gets the PGN request protocol for this ICF
/// @returns The PGN request protocol for this ICF
std::weak_ptr<ParameterGroupNumberRequestProtocol> get_pgn_request_protocol() const;
std::shared_ptr<ParameterGroupNumberRequestProtocol> get_pgn_request_protocol() const;

/// @brief Sets the PGN request protocol for this ICF, only use this if you have advanced needs.
/// Normally, when you create the internal control function via the network manager, it will set this for you.
/// @param[in] protocol The PGN request protocol for this ICF
void set_pgn_request_protocol(std::shared_ptr<ParameterGroupNumberRequestProtocol> protocol);

/// @brief Validates that a CAN message has not caused an address violation for this ICF.
/// If a violation is found, a re-claim will be executed for as is required by ISO 11783-5,
Expand All @@ -88,10 +93,6 @@ namespace isobus
/// @returns true if the message caused an address violation, otherwise false
bool process_rx_message_for_address_violation(const CANMessage &message);

protected:
friend class CANNetworkManager; ///< Allow the network manager to access the pgn request protocol
std::shared_ptr<ParameterGroupNumberRequestProtocol> pgnRequestProtocol; ///< The PGN request protocol for this ICF

private:
/// @brief Sends the PGN request for the address claim PGN
/// @returns true if the message was sent, otherwise false
Expand Down Expand Up @@ -119,6 +120,7 @@ namespace isobus
std::uint8_t preferredAddress; ///< The address we'd prefer to claim as (we may not get it)
std::uint8_t randomClaimDelay_ms; ///< The random delay before claiming an address as required by the ISO11783 standard
EventDispatcher<std::uint8_t> addressClaimedDispatcher; ///< The event dispatcher for when an address is claimed
std::shared_ptr<ParameterGroupNumberRequestProtocol> pgnRequestProtocol = nullptr; ///< The PGN request protocol for this ICF
};

} // namespace isobus
Expand Down
156 changes: 156 additions & 0 deletions isobus/include/isobus/isobus/can_message_handling.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//================================================================================================
/// @file can_message_handling.hpp
///
/// @brief Defines an interface for interacting with incoming and outgoing CAN messages. This is
/// used to abstract the CAN messaging layer from the rest of the application. This allows for
/// easy testing and swapping out of the CAN messaging layer. Furthermore, it ensures that the
/// implementing class is not intertwined with the CAN messaging layer.
///
/// @details The interfaces are more generic than raw CAN messaging, and is designed to be used
/// with J1939 and ISOBUS protocols.
///
///
/// @author Daan Steenbergen
///
/// @copyright 2023 The Open-Agriculture Developers
//================================================================================================
#ifndef CAN_MESSAGE_HANDLER_HPP
#define CAN_MESSAGE_HANDLER_HPP

#include "isobus/isobus/can_callbacks.hpp"
#include "isobus/isobus/can_internal_control_function.hpp"
#include "isobus/isobus/can_message.hpp"

namespace isobus
{
/// @brief An interface that provides a way to send CAN messages to the bus.
/// @note This is a pure virtual interface, and must be implemented by a concrete class.
class CANMessagingProvider
{
public:
/// @brief Default destructor
virtual ~CANMessagingProvider() = default;

/// @brief This is the main way to send a CAN message of any length.
/// @details This function will automatically choose an appropriate transport protocol if needed.
/// If you don't specify a destination (or use nullptr) you message will be sent as a broadcast
/// if it is valid to do so.
/// You can also get a callback on success or failure of the transmit.
/// @param[in] parameterGroupNumber The PGN to use when sending the message
/// @param[in] dataBuffer A pointer to the data buffer to send from
/// @param[in] dataLength The size of the message to send
/// @param[in] sourceControlFunction The control function that is sending the message
/// @param[in] destinationControlFunction The control function that the message is destined for or nullptr if broadcast
/// @param[in] priority The CAN priority of the message being sent
/// @param[in] txCompleteCallback A callback to be called when the message is sent or fails to send
/// @param[in] parentPointer A generic context variable that helps identify what object the callback is destined for
/// @param[in] frameChunkCallback A callback which can be supplied to have the tack call you back to get chunks of the message as they are sent
/// @returns `true` if the message was sent, otherwise `false`
virtual bool send_can_message(std::uint32_t parameterGroupNumber,
const std::uint8_t *dataBuffer,
std::uint32_t dataLength,
std::shared_ptr<InternalControlFunction> sourceControlFunction,
std::shared_ptr<ControlFunction> destinationControlFunction = nullptr,
CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6,
TransmitCompleteCallback txCompleteCallback = nullptr,
void *parentPointer = nullptr,
DataChunkCallback frameChunkCallback = nullptr) = 0;
};

/// @brief A class that provides a way to interact with incoming and outgoing CAN messages.
/// @details This should be extended by a class that wants to interact with incoming and outgoing
/// CAN messages. It provides a way to process incoming and outgoing messages, and send messages
/// to the bus.
class CANMessagingConsumer
{
public:
/// @brief Default destructor
virtual ~CANMessagingConsumer() = default;

/// @brief Processes incoming CAN messages
virtual void process_rx_message(const CANMessage &)
{
// Override this function in the derived class, if you want to process incoming messages
}

/// @brief Processes outgoing CAN messages
virtual void process_tx_message(const CANMessage &)
{
// Override this function in the derived class, if you want to process outgoing messages
}

protected:
/// @brief This is the main way to send a CAN message of any length.
/// @details This function will automatically choose an appropriate transport protocol if needed.
/// If you don't specify a destination (or use nullptr) you message will be sent as a broadcast
/// if it is valid to do so.
/// You can also get a callback on success or failure of the transmit.
/// @param[in] parameterGroupNumber The PGN to use when sending the message
/// @param[in] dataBuffer A pointer to the data buffer to send from
/// @param[in] dataLength The size of the message to send
/// @param[in] sourceControlFunction The control function that is sending the message
/// @param[in] destinationControlFunction The control function that the message is destined for or nullptr if broadcast
/// @param[in] priority The CAN priority of the message being sent
/// @param[in] txCompleteCallback A callback to be called when the message is sent or fails to send
/// @param[in] parentPointer A generic context variable that helps identify what object the callback is destined for
/// @param[in] frameChunkCallback A callback which can be supplied to have the tack call you back to get chunks of the message as they are sent
/// @returns `true` if the message was sent, otherwise `false`
bool send_can_message(std::uint32_t parameterGroupNumber,
const std::uint8_t *dataBuffer,
std::uint32_t dataLength,
std::shared_ptr<InternalControlFunction> sourceControlFunction,
std::shared_ptr<ControlFunction> destinationControlFunction = nullptr,
CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6,
TransmitCompleteCallback txCompleteCallback = nullptr,
void *parentPointer = nullptr,
DataChunkCallback frameChunkCallback = nullptr) const;

friend class CANMessageHandler; ///< Allow the CANMessageHandler to modify the messaging provider

//! @todo: change to std::weak_ptr once network manager is no longer a singleton
CANMessagingProvider *messagingProvider = nullptr; ///< The messaging provider to use for sending messages
};

/// @brief A class for managing the routing of incoming and outgoing CAN messages
class CANMessageHandler : public CANMessagingConsumer
{
public:
/// @brief Processes incoming CAN messages
/// @param message The incoming CAN message to process
void process_rx_message(const CANMessage &message) override;

/// @brief Processes outgoing CAN messages
/// @param message The outgoing CAN message to process
void process_tx_message(const CANMessage &message) override;

/// @brief Adds a consumer to the list of consumers
/// @param consumer The consumer to add
void add_consumer(std::shared_ptr<CANMessagingConsumer> consumer);

/// @brief Removes a consumer from the list of consumers
/// @param consumer The consumer to remove
void remove_consumer(std::shared_ptr<CANMessagingConsumer> consumer);

/// @brief Adds a raw pointer to a consumer to the list of consumers (not recommended)
/// @note This is an unsafe operation, and should only be used when the consumer is guaranteed to exist.
/// Where possible, use the shared pointer version of this function.
/// @param consumer The consumer to add
void add_consumer(CANMessagingConsumer *consumer);

/// @brief Removes a raw pointer to a consumer from the list of consumers (not recommended)
/// @note This is an unsafe operation, and should only be used when the consumer is guaranteed to exist.
/// Where possible, use the shared pointer version of this function.
/// @param consumer The consumer to remove
void remove_consumer(CANMessagingConsumer *consumer);

/// @brief Sets the messaging provider to use for sending messages
/// @param provider The messaging provider to use for sending messages
void set_messaging_provider(CANMessagingProvider *provider);

private:
std::vector<std::weak_ptr<CANMessagingConsumer>> consumers; ///< The list of consumers to route messages to
std::vector<CANMessagingConsumer *> rawConsumers; ///< The list of consumers to (unsafely) route messages to
};
} // namespace isobus

#endif // CAN_MESSAGE_HANDLER_HPP
13 changes: 10 additions & 3 deletions isobus/include/isobus/isobus/can_network_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "isobus/isobus/can_internal_control_function.hpp"
#include "isobus/isobus/can_message.hpp"
#include "isobus/isobus/can_message_frame.hpp"
#include "isobus/isobus/can_message_handling.hpp"
#include "isobus/isobus/can_network_configuration.hpp"
#include "isobus/isobus/can_partnered_control_function.hpp"
#include "isobus/isobus/can_transport_protocol.hpp"
Expand All @@ -45,7 +46,7 @@ namespace isobus
/// @brief The main CAN network manager object, handles protocol management and updating other
/// stack components. Provides an interface for sending CAN messages.
//================================================================================================
class CANNetworkManager
class CANNetworkManager : public CANMessagingProvider
{
public:
static CANNetworkManager CANNetwork; ///< Static singleton of the one network manager. Use this to access stack functionality.
Expand Down Expand Up @@ -152,7 +153,7 @@ namespace isobus
CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6,
TransmitCompleteCallback txCompleteCallback = nullptr,
void *parentPointer = nullptr,
DataChunkCallback frameChunkCallback = nullptr);
DataChunkCallback frameChunkCallback = nullptr) override;

/// @brief The main update function for the network manager. Updates all protocols.
void update();
Expand Down Expand Up @@ -213,6 +214,10 @@ namespace isobus
/// @returns An event dispatcher which can be used to get notified about address violations
EventDispatcher<std::shared_ptr<InternalControlFunction>> &get_address_violation_event_dispatcher();

/// @brief Returns the message handler to subscribe to incoming and outgoing messages from the network manager
/// @returns The message handler
CANMessageHandler &get_can_message_handler();

protected:
// Using protected region to allow protocols use of special functions from the network manager
friend class InternalControlFunction; ///< Allows the network manager to work closely with the address claiming process
Expand Down Expand Up @@ -390,7 +395,7 @@ namespace isobus
std::array<std::unique_ptr<TransportProtocolManager>, CAN_PORT_MAXIMUM> transportProtocols; ///< One instance of the transport protocol manager for each channel
std::array<std::unique_ptr<ExtendedTransportProtocolManager>, CAN_PORT_MAXIMUM> extendedTransportProtocols; ///< One instance of the extended transport protocol manager for each channel
std::array<std::unique_ptr<FastPacketProtocol>, CAN_PORT_MAXIMUM> fastPacketProtocol; ///< One instance of the fast packet protocol for each channel
std::array<std::unique_ptr<HeartbeatInterface>, CAN_PORT_MAXIMUM> heartBeatInterfaces; ///< Manages ISOBUS heartbeat requests, one per channel
std::array<std::shared_ptr<HeartbeatInterface>, CAN_PORT_MAXIMUM> heartBeatInterfaces; ///< Manages ISOBUS heartbeat requests, one per channel

std::array<std::deque<std::uint32_t>, CAN_PORT_MAXIMUM> busloadMessageBitsHistory; ///< Stores the approximate number of bits processed on each channel over multiple previous time windows
std::array<std::uint32_t, CAN_PORT_MAXIMUM> currentBusloadBitAccumulator; ///< Accumulates the approximate number of bits processed on each channel during the current time window
Expand All @@ -407,6 +412,8 @@ namespace isobus
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
CANMessageHandler messageHandler; ///< The message handler driven by the network manager
std::shared_ptr<CANMessagingProvider> messagingProvider; ///< The messaging provider for the network manager, currently set to itself
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
Mutex receivedMessageQueueMutex; ///< A mutex for receive messages thread safety
Expand Down
Loading