Skip to content

Commit

Permalink
refactor(tp): rework transport protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
GwnDaan committed Nov 25, 2023
1 parent 16b71ac commit b8b8926
Show file tree
Hide file tree
Showing 25 changed files with 1,628 additions and 959 deletions.
2 changes: 1 addition & 1 deletion examples/nmea2000/fast_packet_protocol/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ int main()
while (running)
{
// Send a fast packet message
isobus::CANNetworkManager::CANNetwork.get_fast_packet_protocol().send_multipacket_message(0x1F001, testMessageData, TEST_MESSAGE_LENGTH, TestInternalECU, nullptr, isobus::CANIdentifier::PriorityLowest7, nmea2k_transmit_complete_callback);
isobus::CANNetworkManager::CANNetwork.get_fast_packet_protocol().send_multipacket_message(0x1F001, testMessageData, TEST_MESSAGE_LENGTH, TestInternalECU, nullptr, isobus::CANIdentifier::CANPriority::PriorityLowest7, nmea2k_transmit_complete_callback);

// Sleep for a while
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ namespace isobus
/// @brief Connects to the socket
void open() override;

/// @brief Returns a frame from the hardware (synchronous), or `false` if no frame can be read.
/// @brief Returns a frame from the hardware (synchronous), or `false` if no frame can be read. Times out after 1 second.
/// @param[in, out] canFrame The CAN frame that was read
/// @returns `true` if a CAN frame was read, otherwise `false`
bool read_frame(isobus::CANMessageFrame &canFrame) override;

/// @brief Returns a frame from the hardware (synchronous), or `false` if no frame can be read.
/// @param[in, out] canFrame The CAN frame that was read
/// @param[in] timeout The timeout in milliseconds
/// @returns `true` if a CAN frame was read, otherwise `false`
bool read_frame(isobus::CANMessageFrame &canFrame, std::uint32_t timeout) const;

/// @brief Writes a frame to the bus (synchronous)
/// @param[in] canFrame The frame to write to the bus
/// @returns `true` if the frame was written, otherwise `false`
Expand All @@ -71,10 +77,13 @@ namespace isobus
/// @param[in] canFrame The frame to write to the bus
void write_frame_as_if_received(const isobus::CANMessageFrame &canFrame) const;

/// @brief Returns if the internal message queue is empty or not
/// @returns `true` if the internal message queue is empty, otherwise false
/// @brief Returns if the internal received message queue is empty or not
/// @returns `true` if the internal received message queue is empty, otherwise false
bool get_queue_empty() const;

/// @brief Clear the internal received message queue
void clear_queue() const;

private:
/// @brief A struct holding information about a virtual CAN device
struct VirtualDevice
Expand All @@ -95,4 +104,4 @@ namespace isobus
std::atomic_bool running; ///< If `true`, the driver is running
};
}
#endif // VIRTUAL_CAN_PLUGIN_HPP
#endif // VIRTUAL_CAN_PLUGIN_HPP
13 changes: 12 additions & 1 deletion hardware_integration/src/virtual_can_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,14 @@ namespace isobus
}

bool VirtualCANPlugin::read_frame(isobus::CANMessageFrame &canFrame)
{
return read_frame(canFrame, 1000);
}

bool VirtualCANPlugin::read_frame(isobus::CANMessageFrame &canFrame, std::uint32_t timeout) const
{
std::unique_lock<std::mutex> lock(mutex);
ourDevice->condition.wait_for(lock, std::chrono::milliseconds(1000), [this] { return !ourDevice->queue.empty() || !running; });
ourDevice->condition.wait_for(lock, std::chrono::milliseconds(timeout), [this] { return !ourDevice->queue.empty() || !running; });
if (!ourDevice->queue.empty())
{
canFrame = ourDevice->queue.front();
Expand All @@ -94,4 +99,10 @@ namespace isobus
const std::lock_guard<std::mutex> lock(mutex);
return ourDevice->queue.empty();
}

void VirtualCANPlugin::clear_queue() const
{
const std::lock_guard<std::mutex> lock(mutex);
ourDevice->queue.clear();
}
}
6 changes: 4 additions & 2 deletions isobus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ set(ISOBUS_SRC
"isobus_speed_distance_messages.cpp"
"isobus_maintain_power_interface.cpp"
"nmea2000_message_definitions.cpp"
"nmea2000_message_interface.cpp")
"nmea2000_message_interface.cpp"
"can_message_data.cpp")

# Prepend the source directory path to all the source files
prepend(ISOBUS_SRC ${ISOBUS_SRC_DIR} ${ISOBUS_SRC})
Expand Down Expand Up @@ -80,7 +81,8 @@ set(ISOBUS_INCLUDE
"isobus_speed_distance_messages.hpp"
"isobus_maintain_power_interface.hpp"
"nmea2000_message_definitions.hpp"
"nmea2000_message_interface.hpp")
"nmea2000_message_interface.hpp"
"can_message_data.hpp")
# Prepend the include directory path to all the include files
prepend(ISOBUS_INCLUDE ${ISOBUS_INCLUDE_DIR} ${ISOBUS_INCLUDE})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ namespace isobus
ECUtoVirtualTerminal = 0xE700,
Acknowledge = 0xE800,
ParameterGroupNumberRequest = 0xEA00,
TransportProtocolData = 0xEB00,
TransportProtocolCommand = 0xEC00,
TransportProtocolDataTransfer = 0xEB00,
TransportProtocolConnectionManagement = 0xEC00,
AddressClaim = 0xEE00,
ProprietaryA = 0xEF00,
MachineSelectedSpeed = 0xF022,
Expand Down
15 changes: 3 additions & 12 deletions isobus/include/isobus/isobus/can_identifier.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace isobus
{
public:
/// @brief Defines all the CAN frame priorities that can be encoded in a frame ID
enum CANPriority
enum class CANPriority
{
PriorityHighest0 = 0, ///< Highest CAN priority
Priority1 = 1, ///< Priority highest - 1
Expand All @@ -37,7 +37,7 @@ namespace isobus
};

/// @brief Defines if a frame is a standard (11 bit) or extended (29 bit) ID frame
enum Type
enum class Type
{
Standard = 0, ///< Frame is an 11bit ID standard (legacy) message with no PGN and highest priority
Extended = 1 ///< Frame is a modern 29 bit ID CAN frame
Expand All @@ -59,17 +59,8 @@ namespace isobus
std::uint8_t destinationAddress,
std::uint8_t sourceAddress);

/// @brief Copy constructor for a CAN Identifier
/// @param[in] copiedObject The object to copy
CANIdentifier(const CANIdentifier &copiedObject);

/// @brief Destructor for the CANIdentifier
~CANIdentifier();

/// @brief Assignment operator for a CAN identifier
/// @param[in] obj rhs of the operator
/// @returns The lhs of the operator, now assigned the rhs value
CANIdentifier &operator=(const CANIdentifier &obj);
~CANIdentifier() = default;

/// @brief Returns the raw encoded ID of the CAN identifier
/// @returns The raw encoded ID of the CAN identifier
Expand Down
23 changes: 22 additions & 1 deletion isobus/include/isobus/isobus/can_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,31 @@ namespace isobus
/// @returns The source control function that the message is from
std::shared_ptr<ControlFunction> get_source_control_function() const;

/// @brief Returns whether the message is sent by a device that claimed its address on the bus.
/// @returns True if the source of the message is valid, false otherwise
bool has_valid_source_control_function() const;

/// @brief Gets the destination control function that the message is to
/// @returns The destination control function that the message is to
std::shared_ptr<ControlFunction> get_destination_control_function() const;

/// @brief Returns whether the message is sent to a specific device on the bus.
/// @returns True if the destination of the message is valid, false otherwise
bool has_valid_destination_control_function() const;

/// @brief Returns whether the message is sent as a broadcast message / to all devices on the bus.
/// @returns True if the destination of the message is global, false otherwise
bool is_destination_global() const;

/// @brief Returns whether the message is destined for our device on the bus.
/// @returns True if the message is destined for our device, false otherwise
bool is_destination_our_device() const;

/// @brief Returns whether the message is destined for the control function.
/// @param[in] controlFunction The control function to check
/// @returns True if the message is destined for the control function, false otherwise
bool is_destination(std::shared_ptr<ControlFunction> controlFunction) const;

/// @brief Returns the identifier of the message
/// @returns The identifier of the message
CANIdentifier get_identifier() const;
Expand Down Expand Up @@ -110,7 +131,7 @@ namespace isobus

/// @brief Sets the CAN ID of the message
/// @param[in] value The CAN ID for the message
void set_identifier(CANIdentifier value);
void set_identifier(const CANIdentifier &value);

/// @brief Get a 8-bit unsigned byte from the buffer at a specific index.
/// A 8-bit unsigned byte can hold a value between 0 and 255.
Expand Down
171 changes: 171 additions & 0 deletions isobus/include/isobus/isobus/can_message_data.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//================================================================================================
/// @file can_message_data.hpp
///
/// @brief An interface class that represents data payload of a CAN message of arbitrary length.
/// @author Daan Steenbergen
///
/// @copyright 2023 - The OpenAgriculture Developers
//================================================================================================

#ifndef CAN_MESSAGE_DATA_HPP
#define CAN_MESSAGE_DATA_HPP

#include "isobus/isobus/can_callbacks.hpp"
#include "isobus/isobus/can_control_function.hpp"
#include "isobus/isobus/can_message.hpp"
#include "isobus/utility/data_span.hpp"

#include <cstdint>
#include <memory>
#include <vector>

namespace isobus
{
//================================================================================================
/// @class CANMessageData
///
/// @brief A interface class that represents data payload of a CAN message of arbitrary length.
//================================================================================================
class CANMessageData
{
public:
/// @brief Default destructor.
virtual ~CANMessageData() = default;

/// @brief Get the size of the data.
virtual std::size_t size() const = 0;

/// @brief Get the byte at the given index.
/// @param[in] index The index of the byte to get.
/// @return The byte at the given index.
virtual std::uint8_t get_byte(std::size_t index) = 0;

/// @brief If the data isn't owned by this class, make a copy of the data.
/// @param[in] self A pointer to this object.
/// @return A copy of the data if it isn't owned by this class, otherwise a moved pointer.
virtual std::unique_ptr<CANMessageData> copy_if_not_owned(std::unique_ptr<CANMessageData> self) const = 0;
};

//================================================================================================
/// @class CANMessageDataVector
///
/// @brief A class that represents data of a CAN message by holding a vector of bytes.
//================================================================================================
class CANMessageDataVector : public CANMessageData
, public std::vector<std::uint8_t>
{
public:
/// @brief Construct a new CANMessageDataVector object.
/// @param[in] size The size of the data.
explicit CANMessageDataVector(std::size_t size);

/// @brief Construct a new CANMessageDataVector object.
/// @param[in] data The data to copy.
explicit CANMessageDataVector(const std::vector<std::uint8_t> &data);

/// @brief Construct a new CANMessageDataVector object.
/// @param[in] data A pointer to the data to copy.
/// @param[in] size The size of the data to copy.
CANMessageDataVector(const std::uint8_t *data, std::size_t size);

/// @brief Get the size of the data.
/// @return The size of the data.
std::size_t size() const override;

/// @brief Get the byte at the given index.
/// @param[in] index The index of the byte to get.
/// @return The byte at the given index.
std::uint8_t get_byte(std::size_t index) override;

/// @brief Set the byte at the given index.
/// @param[in] index The index of the byte to set.
/// @param[in] value The value to set the byte to.
void set_byte(std::size_t index, std::uint8_t value);

/// @brief Get the data span.
/// @return The data span.
CANDataSpan data() const;

/// @brief If the data isn't owned by this class, make a copy of the data.
/// @param[in] self A pointer to this object.
/// @return A copy of the data if it isn't owned by this class, otherwise it returns itself.
std::unique_ptr<CANMessageData> copy_if_not_owned(std::unique_ptr<CANMessageData> self) const override;
};

//================================================================================================
/// @class CANMessageDataView
///
/// @brief A class that represents data of a CAN message by holding a view of an array of bytes.
/// The view is not owned by this class, it is simply holding a pointer to the array of bytes.
//================================================================================================
class CANMessageDataView : public CANMessageData
, public CANDataSpan
{
public:
/// @brief Construct a new CANMessageDataView object.
/// @param[in] ptr The pointer to the array of bytes.
/// @param[in] len The length of the array of bytes.
CANMessageDataView(const std::uint8_t *ptr, std::size_t len);

/// @brief Get the size of the data.
/// @return The size of the data.
std::size_t size() const override;

/// @brief Get the byte at the given index.
/// @param[in] index The index of the byte to get.
/// @return The byte at the given index.
std::uint8_t get_byte(std::size_t index) override;

/// @brief Get the data span.
/// @return The data span.
CANDataSpan data() const;

/// @brief If the data isn't owned by this class, make a copy of the data.
/// @param[in] self A pointer to this object.
/// @return A copy of the data if it isn't owned by this class, otherwise it returns itself.
std::unique_ptr<CANMessageData> copy_if_not_owned(std::unique_ptr<CANMessageData> self) const override;
};

//================================================================================================
/// @class CANMessageDataCallback
///
/// @brief A class that represents data of a CAN message by using a callback function.
//================================================================================================
class CANMessageDataCallback : public CANMessageData
{
public:
/// @brief Constructor for transport data that uses a callback function.
/// @param[in] size The size of the data.
/// @param[in] callback The callback function to be called for each data chunk.
/// @param[in] parentPointer The parent object that owns this callback (optional).
/// @param[in] chunkSize The size of each data chunk (optional, default is 7).
CANMessageDataCallback(std::size_t size,
DataChunkCallback callback,
void *parentPointer = nullptr,
std::size_t chunkSize = 7);

/// @brief Get the size of the data.
/// @return The size of the data.
std::size_t size() const override;

/// @brief Get the byte at the given index.
/// @param[in] index The index of the byte to get.
/// @return The byte at the given index.
std::uint8_t get_byte(std::size_t index) override;

/// @brief If the data isn't owned by this class, make a copy of the data.
/// @param[in] self A pointer to this object.
/// @return A copy of the data if it isn't owned by this class, otherwise it returns itself.
std::unique_ptr<CANMessageData> copy_if_not_owned(std::unique_ptr<CANMessageData> self) const override;

private:
std::size_t totalSize; //< The total size of the data.
DataChunkCallback callback; //< The callback function to be called for each data chunk.
void *parentPointer; //< The parent object that gets passed to the callback function.
std::vector<std::uint8_t> buffer; //< The buffer to store the data chunks.
std::size_t bufferSize; //< The size of the buffer.
std::size_t dataOffset = 0; //< The offset of the data in the buffer.
};
} // namespace isobus

#endif // CAN_MESSAGE_DATA_HPP
7 changes: 2 additions & 5 deletions isobus/include/isobus/isobus/can_network_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,6 @@ namespace isobus
/// @param[in] parameterGroupNumber The PGN to use when sending the message
/// @param[in] priority The CAN priority of the message being sent
/// @param[in] data A pointer to the data buffer to send from
/// @param[in] size The size of the message to send
/// @returns `true` if the message was sent, otherwise `false`
bool send_can_message_raw(std::uint32_t portIndex,
std::uint8_t sourceAddress,
Expand Down Expand Up @@ -327,7 +326,6 @@ namespace isobus
/// @param[in] parameterGroupNumber The PGN to use when sending the message
/// @param[in] priority The CAN priority of the message being sent
/// @param[in] data A pointer to the data buffer to send from
/// @param[in] size The size of the message to send
/// @returns The constructed frame based on the inputs
CANMessageFrame construct_frame(std::uint32_t portIndex,
std::uint8_t sourceAddress,
Expand Down Expand Up @@ -401,7 +399,6 @@ namespace isobus
/// @param[in] parameterGroupNumber The PGN to use when sending the message
/// @param[in] priority The CAN priority of the message being sent
/// @param[in] data A pointer to the data buffer to send from
/// @param[in] size The size of the message to send
/// @returns `true` if the message was sent, otherwise `false`
bool send_can_message_raw(std::uint32_t portIndex,
std::uint8_t sourceAddress,
Expand All @@ -419,9 +416,9 @@ namespace isobus
static constexpr std::uint32_t BUSLOAD_UPDATE_FREQUENCY_MS = 100; ///< Bus load bit accumulation happens over a 100ms window

CANNetworkConfiguration configuration; ///< The configuration for this network manager
ExtendedTransportProtocolManager extendedTransportProtocol; ///< Static instance of the protocol manager
ExtendedTransportProtocolManager extendedTransportProtocol; ///< Instance of the extended transport protocol manager
FastPacketProtocol fastPacketProtocol; ///< Instance of the fast packet protocol
TransportProtocolManager transportProtocol; ///< Static instance of the transport protocol manager
TransportProtocolManager transportProtocol; ///< Instance of the transport protocol manager

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 Down
Loading

0 comments on commit b8b8926

Please sign in to comment.