From 32e713b2a1f215d5b3b5ac1ff1e0e40be8895968 Mon Sep 17 00:00:00 2001 From: Daan Steenbergen Date: Fri, 10 Nov 2023 14:39:46 +0100 Subject: [PATCH] feat(core): add more concrete ways of sending data --- examples/transport_layer/main.cpp | 4 +- .../isobus/isobus/can_message_data.hpp | 104 +++++++ .../isobus/isobus/can_network_manager.hpp | 159 ++++++++++- .../src/can_address_claim_state_machine.cpp | 40 +-- isobus/src/can_network_manager.cpp | 267 +++++++++++++++--- isobus/src/isobus_diagnostic_protocol.cpp | 72 ++--- .../src/isobus_maintain_power_interface.cpp | 9 +- isobus/src/isobus_task_controller_client.cpp | 5 +- isobus/src/isobus_virtual_terminal_client.cpp | 5 +- 9 files changed, 549 insertions(+), 116 deletions(-) create mode 100644 isobus/include/isobus/isobus/can_message_data.hpp diff --git a/examples/transport_layer/main.cpp b/examples/transport_layer/main.cpp index fa920606..8f57f86a 100644 --- a/examples/transport_layer/main.cpp +++ b/examples/transport_layer/main.cpp @@ -98,7 +98,7 @@ int main() } // Send a classic CAN message to global (0xFF) (8 bytes or less) - if (running && isobus::CANNetworkManager::CANNetwork.send_can_message(0xEF00, ETPTestBuffer, isobus::CAN_DATA_LENGTH, TestInternalECU)) + if (running && isobus::CANNetworkManager::CANNetwork.send_can_message_global(0xEF00, ETPTestBuffer, isobus::CAN_DATA_LENGTH, TestInternalECU)) { std::cout << "Sent a broadcast CAN Message with length 8" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(4)); // Arbitrary @@ -139,7 +139,7 @@ int main() } // Send message - if (isobus::CANNetworkManager::CANNetwork.send_can_message(0xEF00, TPTestBuffer, i, TestInternalECU)) + if (isobus::CANNetworkManager::CANNetwork.send_can_message_global(0xEF00, TPTestBuffer, i, TestInternalECU)) { std::cout << "Started BAM Session with length " << i << std::endl; } diff --git a/isobus/include/isobus/isobus/can_message_data.hpp b/isobus/include/isobus/isobus/can_message_data.hpp new file mode 100644 index 00000000..5930969b --- /dev/null +++ b/isobus/include/isobus/isobus/can_message_data.hpp @@ -0,0 +1,104 @@ +//================================================================================================ +/// @file can_message_data.hpp +/// +/// @brief Contains common types and functions for working with the data of a CAN message. +/// @author Daan Steenbergen +/// +/// @copyright 2022 Open Agriculture +//================================================================================================ +#ifndef CAN_MESSAGE_DATA_HPP +#define CAN_MESSAGE_DATA_HPP + +#include +#include + +namespace isobus +{ + //================================================================================================ + /// @class DataSpan + /// + /// @brief A class that represents a span of data of arbitrary length. + //================================================================================================ + template + class DataSpan + { + public: + /// @brief Construct a new DataSpan object of a writeable array. + /// @param ptr pointer to the buffer to use. + /// @param len The number of elements in the buffer. + DataSpan(T *ptr, std::size_t size) : + ptr(ptr), + _size(size) + { + } + + /// @brief Get the element at the given index. + /// @param index The index of the element to get. + /// @return The element at the given index. + T &operator[](std::size_t index) + { + return ptr[index * sizeof(T)]; + } + + /// @brief Get the element at the given index. + /// @param index The index of the element to get. + /// @return The element at the given index. + T const &operator[](std::size_t index) const + { + return ptr[index * sizeof(T)]; + } + + /// @brief Get the size of the data span. + /// @return The size of the data span. + std::size_t size() const + { + return _size; + } + + /// @brief Get the begin iterator. + /// @return The begin iterator. + T *begin() + { + return ptr; + } + + /// @brief Get the end iterator. + /// @return The end iterator. + T *end() + { + return ptr + _size * sizeof(T); + } + + private: + T *ptr; + std::size_t _size; + }; + + //================================================================================================ + /// @class DataSpanFactory + /// + /// @brief A class that can be used to construct a DataSpan from various types. + //================================================================================================ + class DataSpanFactory + { + public: + /// @brief Factory method to create a DataSpan from a std::array. + /// @param array The array to create the DataSpan from. + /// @return The created DataSpan. + template + static DataSpan fromArray(std::array &array) + { + return DataSpan(array.data(), N); + } + + /// @brief Factory method to create a DataSpan of consts elements from a std::array of non-const elements. + /// @param array The array to create the DataSpan from. + /// @return The created DataSpan of const elements. + template + static DataSpan> cfromArray(std::array &array) + { + return DataSpan>(array.data(), N); + } + }; +} +#endif // CAN_MESSAGE_DATA_HPP diff --git a/isobus/include/isobus/isobus/can_network_manager.hpp b/isobus/include/isobus/isobus/can_network_manager.hpp index 09ab54c9..a10218f7 100644 --- a/isobus/include/isobus/isobus/can_network_manager.hpp +++ b/isobus/include/isobus/isobus/can_network_manager.hpp @@ -20,6 +20,7 @@ #include "isobus/isobus/can_identifier.hpp" #include "isobus/isobus/can_internal_control_function.hpp" #include "isobus/isobus/can_message.hpp" +#include "isobus/isobus/can_message_data.hpp" #include "isobus/isobus/can_message_frame.hpp" #include "isobus/isobus/can_network_configuration.hpp" #include "isobus/isobus/can_transport_protocol.hpp" @@ -101,21 +102,156 @@ namespace isobus /// @returns Estimated busload over the last 1 second float get_estimated_busload(std::uint8_t canChannel); - /// @brief This is the main way to send a CAN message of any length. + /// @brief Sends a CAN message with the specified parameter group number and data buffer to the specified destination control function. + /// @deprecated Use the `send_can_message` function with a DataSpan instead. /// @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. - /// @returns `true` if the message was sent, otherwise `false` + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param dataBuffer The data buffer containing the data to be sent. + /// @param dataLength The length of the data buffer. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param destinationControlFunction The destination control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. bool send_can_message(std::uint32_t parameterGroupNumber, const std::uint8_t *dataBuffer, std::uint32_t dataLength, std::shared_ptr sourceControlFunction, - std::shared_ptr destinationControlFunction = nullptr, + std::shared_ptr destinationControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); + + /// @brief Sends a CAN message with the specified parameter group number and data from chunk callback + /// to the specified destination control function. + /// @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 + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param frameChunkCallback The callback function to be called to get the data to be sent in chunks. + /// @param dataLength The total length of the data to be sent. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param destinationControlFunction The destination control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message(std::uint32_t parameterGroupNumber, + DataChunkCallback frameChunkCallback, + std::uint32_t dataLength, + std::shared_ptr sourceControlFunction, + std::shared_ptr destinationControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); + + /// @brief Sends a CAN message with the specified parameter group number and data to the specified destination control function. + /// @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 + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param data The data to be sent. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param destinationControlFunction The destination control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message(std::uint32_t parameterGroupNumber, + DataSpan data, + std::shared_ptr sourceControlFunction, + std::shared_ptr destinationControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); + + /// @brief Sends a CAN message with the specified parameter group number and data to the specified destination control function. + /// @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 + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param data The data to be sent. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param destinationControlFunction The destination control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message(std::uint32_t parameterGroupNumber, + std::initializer_list data, + std::shared_ptr sourceControlFunction, + std::shared_ptr destinationControlFunction, CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, TransmitCompleteCallback txCompleteCallback = nullptr, - void *parentPointer = nullptr, - DataChunkCallback frameChunkCallback = nullptr); + void *parentPointer = nullptr); + + /// @brief Broadcast a CAN message with the specified parameter group number and data buffer. (Destination Address=0xFF) + /// @deprecated Use the `send_can_message_global` function with a DataSpan instead. + /// @details This function will automatically choose an appropriate transport protocol if needed. + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param dataBuffer The data buffer containing the data to be sent. + /// @param dataLength The length of the data buffer. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message_global(std::uint32_t parameterGroupNumber, + const std::uint8_t *dataBuffer, + std::uint32_t dataLength, + std::shared_ptr sourceControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); + + /// @brief Broadcast a CAN message with the specified parameter group number and data from chunk callback. (Destination Address=0xFF) + /// @details This function will automatically choose an appropriate transport protocol if needed. + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param frameChunkCallback The callback function to be called to get the data to be sent in chunks. + /// @param dataLength The total length of the data to be sent. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message_global(std::uint32_t parameterGroupNumber, + DataChunkCallback frameChunkCallback, + std::uint32_t dataLength, + std::shared_ptr sourceControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); + + /// @brief Broadcast a CAN message with the specified parameter group number and data. (Destination Address=0xFF) + /// @details This function will automatically choose an appropriate transport protocol if needed. + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param data The data to be sent. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message_global(std::uint32_t parameterGroupNumber, + DataSpan data, + std::shared_ptr sourceControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); + + /// @brief Broadcast a CAN message with the specified parameter group number and data. + /// @details This function will automatically choose an appropriate transport protocol if needed. + /// @param parameterGroupNumber The parameter group number of the CAN message. + /// @param data The data to be sent. + /// @param sourceControlFunction The source control function of the CAN message. + /// @param priority The priority of the CAN message. + /// @param txCompleteCallback The callback function to be called when the transmission is complete. + /// @param parentPointer A pointer to the parent object that gets passed to the txCompleteCallback. + /// @return True if the message is successfully enqueued, false otherwise. + bool send_can_message_global(std::uint32_t parameterGroupNumber, + std::initializer_list data, + std::shared_ptr sourceControlFunction, + CANIdentifier::CANPriority priority = CANIdentifier::CANPriority::PriorityDefault6, + TransmitCompleteCallback txCompleteCallback = nullptr, + void *parentPointer = nullptr); /// @brief This is the main function used by the stack to receive CAN messages and add them to a queue. /// @details This function is called by the stack itself when you call can_lib_process_rx_message. @@ -219,8 +355,7 @@ namespace isobus std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, - const void *data, - std::uint32_t size, + DataSpan data, CANLibBadge) const; /// @brief Processes completed protocol messages. Causes PGN callbacks to trigger. @@ -269,8 +404,7 @@ namespace isobus std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, - const void *data, - std::uint32_t size) const; + DataSpan data) const; /// @brief Returns a control function based on a CAN address and channel index /// @param[in] channelIndex The CAN channel index of the CAN message being processed @@ -344,8 +478,7 @@ namespace isobus std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, - const void *data, - std::uint32_t size) const; + DataSpan data) const; /// @brief Gets a PGN callback for the global address by index /// @param[in] index The index of the callback to get diff --git a/isobus/src/can_address_claim_state_machine.cpp b/isobus/src/can_address_claim_state_machine.cpp index 62fb5bdb..5d8c6a31 100644 --- a/isobus/src/can_address_claim_state_machine.cpp +++ b/isobus/src/can_address_claim_state_machine.cpp @@ -8,6 +8,7 @@ //================================================================================================ #include "isobus/isobus/can_address_claim_state_machine.hpp" #include "isobus/isobus/can_general_parameter_group_numbers.hpp" +#include "isobus/isobus/can_message_data.hpp" #include "isobus/isobus/can_network_manager.hpp" #include "isobus/isobus/can_stack_logger.hpp" #include "isobus/utility/system_timing.hpp" @@ -329,21 +330,20 @@ namespace isobus if (get_enabled()) { - const std::uint8_t addressClaimRequestLength = 3; + static const std::uint8_t ADDRESS_CLAIM_MESSAGE_LENGTH = 3; const auto PGN = static_cast(CANLibParameterGroupNumber::AddressClaim); - std::uint8_t dataBuffer[addressClaimRequestLength]; - - dataBuffer[0] = (PGN & std::numeric_limits::max()); - dataBuffer[1] = ((PGN >> 8) & std::numeric_limits::max()); - dataBuffer[2] = ((PGN >> 16) & std::numeric_limits::max()); + std::array dataBuffer{ + PGN & 0xFF, + (PGN >> 8) & 0xFF, + (PGN >> 16) & 0xFF, + }; retVal = CANNetworkManager::CANNetwork.send_can_message_raw(m_portIndex, NULL_CAN_ADDRESS, BROADCAST_CAN_ADDRESS, static_cast(CANLibParameterGroupNumber::ParameterGroupNumberRequest), static_cast(CANIdentifier::CANPriority::PriorityDefault6), - dataBuffer, - 3, + DataSpanFactory::fromArray(dataBuffer), {}); } return retVal; @@ -358,23 +358,23 @@ namespace isobus if (get_enabled()) { std::uint64_t isoNAME = m_isoname.get_full_name(); - std::uint8_t dataBuffer[CAN_DATA_LENGTH]; - - dataBuffer[0] = static_cast(isoNAME); - dataBuffer[1] = static_cast(isoNAME >> 8); - dataBuffer[2] = static_cast(isoNAME >> 16); - dataBuffer[3] = static_cast(isoNAME >> 24); - dataBuffer[4] = static_cast(isoNAME >> 32); - dataBuffer[5] = static_cast(isoNAME >> 40); - dataBuffer[6] = static_cast(isoNAME >> 48); - dataBuffer[7] = static_cast(isoNAME >> 56); + std::array dataBuffer{ + static_cast(isoNAME), + static_cast(isoNAME >> 8), + static_cast(isoNAME >> 16), + static_cast(isoNAME >> 24), + static_cast(isoNAME >> 32), + static_cast(isoNAME >> 40), + static_cast(isoNAME >> 48), + static_cast(isoNAME >> 56), + }; + retVal = CANNetworkManager::CANNetwork.send_can_message_raw(m_portIndex, address, BROADCAST_CAN_ADDRESS, static_cast(CANLibParameterGroupNumber::AddressClaim), static_cast(CANIdentifier::CANPriority::PriorityDefault6), - dataBuffer, - CAN_DATA_LENGTH, + DataSpanFactory::cfromArray(dataBuffer), {}); if (retVal) { diff --git a/isobus/src/can_network_manager.cpp b/isobus/src/can_network_manager.cpp index fdc772c5..75665465 100644 --- a/isobus/src/can_network_manager.cpp +++ b/isobus/src/can_network_manager.cpp @@ -118,19 +118,59 @@ namespace isobus std::shared_ptr sourceControlFunction, std::shared_ptr destinationControlFunction, CANIdentifier::CANPriority priority, - TransmitCompleteCallback transmitCompleteCallback, - void *parentPointer, - DataChunkCallback frameChunkCallback) + TransmitCompleteCallback txCompleteCallback, + void *parentPointer) + { + return send_can_message(parameterGroupNumber, + DataSpan(dataBuffer, dataLength), + sourceControlFunction, + destinationControlFunction, + priority, + txCompleteCallback, + parentPointer); + } + + bool CANNetworkManager::send_can_message(std::uint32_t parameterGroupNumber, + DataChunkCallback frameChunkCallback, + std::uint32_t dataLength, + std::shared_ptr sourceControlFunction, + std::shared_ptr destinationControlFunction, + CANIdentifier::CANPriority priority, + TransmitCompleteCallback txCompleteCallback, + void *parentPointer) { bool retVal = false; - if (((nullptr != dataBuffer) || - (nullptr != frameChunkCallback)) && - (dataLength > 0) && - (dataLength <= CANMessage::ABSOLUTE_MAX_MESSAGE_LENGTH) && - (nullptr != sourceControlFunction) && - ((parameterGroupNumber == static_cast(CANLibParameterGroupNumber::AddressClaim)) || - (sourceControlFunction->get_address_valid()))) + const std::uint8_t destinationAddress = (nullptr != destinationControlFunction) ? destinationControlFunction->get_address() : BROADCAST_CAN_ADDRESS; + + if (frameChunkCallback == nullptr) + { + // If the chunk callback is null, then we can't send a meaningful message + CANStackLogger::warn("[NM]: Cannot send message with null chunk callback, source: %hu, destination %hu, pgn: 0x%05x", + sourceControlFunction->get_address(), + destinationAddress, + parameterGroupNumber); + retVal = false; + } + else if ((nullptr == sourceControlFunction) || !sourceControlFunction->get_address_valid()) + { + // If the source control function is null, then we can't send a meaningful message + CANStackLogger::warn("[NM]: Cannot send message with invalid source control function, destination %hu, pgn: 0x%05x", + destinationAddress, + parameterGroupNumber); + retVal = false; + } + else if ((dataLength == 0) || (dataLength > CANMessage::ABSOLUTE_MAX_MESSAGE_LENGTH)) + { + // If the data length is zero or too long, then we can't send a meaningful message + CANStackLogger::warn("[NM]: Cannot send message with data length out-of-bounds, message-length %d, source: %hu, destination %hu, pgn: 0x%05x", + dataLength, + sourceControlFunction->get_address(), + destinationAddress, + parameterGroupNumber); + retVal = false; + } + else { CANLibProtocol *currentProtocol; @@ -140,11 +180,11 @@ namespace isobus if (CANLibProtocol::get_protocol(i, currentProtocol)) { retVal = currentProtocol->protocol_transmit_message(parameterGroupNumber, - dataBuffer, + nullptr, dataLength, sourceControlFunction, destinationControlFunction, - transmitCompleteCallback, + txCompleteCallback, parentPointer, frameChunkCallback); @@ -155,31 +195,187 @@ namespace isobus } } - //! @todo Allow sending 8 byte message with the frameChunkCallback - if ((!retVal) && - (nullptr != dataBuffer)) + if (!retVal && (dataLength <= CAN_DATA_LENGTH)) { - if (nullptr == destinationControlFunction) + // Request the buffer of equal or less than 8 bytes from the chunk callback + std::array dataBuffer; + frameChunkCallback(0, 0, dataLength, dataBuffer.data(), parentPointer); + + retVal = send_can_message_raw(sourceControlFunction->get_can_port(), + sourceControlFunction->get_address(), + destinationAddress, + parameterGroupNumber, + priority, + DataSpanFactory::cfromArray(dataBuffer)); + + if (retVal && (nullptr != txCompleteCallback)) { - // Todo move binding of dest address to hardware layer - retVal = send_can_message_raw(sourceControlFunction->get_can_port(), sourceControlFunction->get_address(), 0xFF, parameterGroupNumber, priority, dataBuffer, dataLength); + // Message is sent within a single frame, so handle the tx callback now + txCompleteCallback(parameterGroupNumber, dataLength, sourceControlFunction, destinationControlFunction, true, parentPointer); } - else if (destinationControlFunction->get_address_valid()) + } + } + return retVal; + } + + bool CANNetworkManager::send_can_message(std::uint32_t parameterGroupNumber, + DataSpan data, + std::shared_ptr sourceControlFunction, + std::shared_ptr destinationControlFunction, + CANIdentifier::CANPriority priority, + TransmitCompleteCallback txCompleteCallback, + void *parentPointer) + { + bool retVal = false; + + const std::uint8_t destinationAddress = (nullptr != destinationControlFunction) ? destinationControlFunction->get_address() : BROADCAST_CAN_ADDRESS; + + if (data.begin() == nullptr) + { + // If the data buffer is null, then we can't send a meaningful message + CANStackLogger::warn("[NM]: Cannot send message with null data buffer, source: %hu, destination %hu, pgn: 0x%05x", + sourceControlFunction->get_address(), + destinationAddress, + parameterGroupNumber); + retVal = false; + } + else if ((nullptr == sourceControlFunction) || !sourceControlFunction->get_address_valid()) + { + // If the source control function is null, then we can't send a meaningful message + CANStackLogger::warn("[NM]: Cannot send message with invalid source control function, destination %hu, pgn: 0x%05x", + destinationAddress, + parameterGroupNumber); + retVal = false; + } + else if ((0 == data.size()) || (data.size() > CANMessage::ABSOLUTE_MAX_MESSAGE_LENGTH)) + { + // If the data length is zero or too long, then we can't send a meaningful message + CANStackLogger::warn("[NM]: Cannot send message with data length out-of-bounds, message-length %d, source: %hu, destination %hu, pgn: 0x%05x", + data.size(), + sourceControlFunction->get_address(), + destinationAddress, + parameterGroupNumber); + retVal = false; + } + else + { + CANLibProtocol *currentProtocol; + + // See if any transport layer protocol can handle this message + for (std::uint32_t i = 0; i < CANLibProtocol::get_number_protocols(); i++) + { + if (CANLibProtocol::get_protocol(i, currentProtocol)) { - retVal = send_can_message_raw(sourceControlFunction->get_can_port(), sourceControlFunction->get_address(), destinationControlFunction->get_address(), parameterGroupNumber, priority, dataBuffer, dataLength); + retVal = currentProtocol->protocol_transmit_message(parameterGroupNumber, + data.begin(), + data.size(), + sourceControlFunction, + destinationControlFunction, + txCompleteCallback, + parentPointer, + nullptr); + + if (retVal) + { + break; + } } + } - if ((retVal) && - (nullptr != transmitCompleteCallback)) + if (!retVal && (data.size() <= CAN_DATA_LENGTH)) + { + retVal = send_can_message_raw(sourceControlFunction->get_can_port(), + sourceControlFunction->get_address(), + destinationAddress, + parameterGroupNumber, + priority, + data); + + if (retVal && (nullptr != txCompleteCallback)) { - // Message was not sent via a protocol, so handle the tx callback now - transmitCompleteCallback(parameterGroupNumber, dataLength, sourceControlFunction, destinationControlFunction, retVal, parentPointer); + // Message is sent within a single frame, so handle the tx callback now + txCompleteCallback(parameterGroupNumber, data.size(), sourceControlFunction, destinationControlFunction, retVal, parentPointer); } } } return retVal; } + bool isobus::CANNetworkManager::send_can_message(std::uint32_t parameterGroupNumber, + std::initializer_list data, + std::shared_ptr sourceControlFunction, + std::shared_ptr destinationControlFunction, + CANIdentifier::CANPriority priority, + TransmitCompleteCallback txCompleteCallback, + void *parentPointer) + { + return send_can_message(parameterGroupNumber, + DataSpan(data.begin(), data.size()), + sourceControlFunction, + destinationControlFunction, + priority, + txCompleteCallback, + parentPointer); + } + + bool isobus::CANNetworkManager::send_can_message_global(std::uint32_t parameterGroupNumber, + const std::uint8_t *dataBuffer, + std::uint32_t dataLength, + std::shared_ptr sourceControlFunction, + CANIdentifier::CANPriority priority, + TransmitCompleteCallback txCompleteCallback, + void *parentPointer) + { + return send_can_message(parameterGroupNumber, + dataBuffer, + dataLength, + sourceControlFunction, + nullptr, // To denote a global message we pass a null destination + priority, + txCompleteCallback, + parentPointer); + } + + bool isobus::CANNetworkManager::send_can_message_global(std::uint32_t parameterGroupNumber, + DataChunkCallback frameChunkCallback, + std::uint32_t dataLength, + std::shared_ptr sourceControlFunction, + CANIdentifier::CANPriority priority, + TransmitCompleteCallback txCompleteCallback, + void *parentPointer) + { + return send_can_message(parameterGroupNumber, + frameChunkCallback, + dataLength, + sourceControlFunction, + nullptr, // To denote a global message we pass a null destination + priority, + txCompleteCallback, + parentPointer); + } + + bool isobus::CANNetworkManager::send_can_message_global(std::uint32_t parameterGroupNumber, DataSpan data, std::shared_ptr sourceControlFunction, CANIdentifier::CANPriority priority, TransmitCompleteCallback txCompleteCallback, void *parentPointer) + { + return send_can_message(parameterGroupNumber, + data, + sourceControlFunction, + nullptr, // To denote a global message we pass a null destination + priority, + txCompleteCallback, + parentPointer); + } + + bool isobus::CANNetworkManager::send_can_message_global(std::uint32_t parameterGroupNumber, std::initializer_list data, std::shared_ptr sourceControlFunction, CANIdentifier::CANPriority priority, TransmitCompleteCallback txCompleteCallback, void *parentPointer) + { + return send_can_message(parameterGroupNumber, + DataSpan(data.begin(), data.size()), + sourceControlFunction, + nullptr, // To denote a global message we pass a null destination + priority, + txCompleteCallback, + parentPointer); + } + void CANNetworkManager::receive_can_message(const CANMessage &message) { if (initialized) @@ -233,11 +429,10 @@ namespace isobus std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, - const void *data, - std::uint32_t size, + DataSpan data, CANLibBadge) const { - return send_can_message_raw(portIndex, sourceAddress, destAddress, parameterGroupNumber, priority, data, size); + return send_can_message_raw(portIndex, sourceAddress, destAddress, parameterGroupNumber, priority, data); } ParameterGroupNumberCallbackData CANNetworkManager::get_global_parameter_group_number_callback(std::uint32_t index) const @@ -736,13 +931,12 @@ namespace isobus std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, - const void *data, - std::uint32_t size) const + DataSpan data) const { CANMessageFrame txFrame; txFrame.identifier = DEFAULT_IDENTIFIER; - if ((NULL_CAN_ADDRESS != destAddress) && (priority <= static_cast(CANIdentifier::CANPriority::PriorityLowest7)) && (size <= CAN_DATA_LENGTH) && (nullptr != data)) + if ((NULL_CAN_ADDRESS != destAddress) && (priority <= static_cast(CANIdentifier::CANPriority::PriorityLowest7)) && (data.size() <= CAN_DATA_LENGTH) && (nullptr != data.begin())) { std::uint32_t identifier = 0; @@ -782,8 +976,8 @@ namespace isobus if (DEFAULT_IDENTIFIER != identifier) { txFrame.channel = portIndex; - memcpy(reinterpret_cast(txFrame.data), data, size); - txFrame.dataLength = size; + memcpy(txFrame.data, data.begin(), data.size()); + txFrame.dataLength = data.size(); txFrame.isExtendedFrame = true; txFrame.identifier = identifier & 0x1FFFFFFF; } @@ -1008,9 +1202,14 @@ namespace isobus } } - bool CANNetworkManager::send_can_message_raw(std::uint32_t portIndex, std::uint8_t sourceAddress, std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, const void *data, std::uint32_t size) const + bool CANNetworkManager::send_can_message_raw(std::uint32_t portIndex, + std::uint8_t sourceAddress, + std::uint8_t destAddress, + std::uint32_t parameterGroupNumber, + std::uint8_t priority, + DataSpan data) const { - CANMessageFrame tempFrame = construct_frame(portIndex, sourceAddress, destAddress, parameterGroupNumber, priority, data, size); + CANMessageFrame tempFrame = construct_frame(portIndex, sourceAddress, destAddress, parameterGroupNumber, priority, data); bool retVal = false; if ((DEFAULT_IDENTIFIER != tempFrame.identifier) && diff --git a/isobus/src/isobus_diagnostic_protocol.cpp b/isobus/src/isobus_diagnostic_protocol.cpp index 9c45f583..a774b2a5 100644 --- a/isobus/src/isobus_diagnostic_protocol.cpp +++ b/isobus/src/isobus_diagnostic_protocol.cpp @@ -679,10 +679,10 @@ namespace isobus buffer[5] = 0x00; buffer[6] = 0xFF; buffer[7] = 0xFF; - retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::DiagnosticMessage1), - buffer.data(), - CAN_DATA_LENGTH, - myControlFunction); + retVal = CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::DiagnosticMessage1), + buffer.data(), + CAN_DATA_LENGTH, + myControlFunction); } else { @@ -701,10 +701,10 @@ namespace isobus payloadSize = CAN_DATA_LENGTH; } - retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::DiagnosticMessage1), - buffer.data(), - payloadSize, - myControlFunction); + retVal = CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::DiagnosticMessage1), + buffer.data(), + payloadSize, + myControlFunction); } } } @@ -767,10 +767,10 @@ namespace isobus buffer[5] = 0x00; buffer[6] = 0xFF; buffer[7] = 0xFF; - retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::DiagnosticMessage2), - buffer.data(), - CAN_DATA_LENGTH, - myControlFunction); + retVal = CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::DiagnosticMessage2), + buffer.data(), + CAN_DATA_LENGTH, + myControlFunction); } else { @@ -789,10 +789,10 @@ namespace isobus payloadSize = CAN_DATA_LENGTH; } - retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::DiagnosticMessage2), - buffer.data(), - payloadSize, - myControlFunction); + retVal = CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::DiagnosticMessage2), + buffer.data(), + payloadSize, + myControlFunction); } } } @@ -811,10 +811,10 @@ namespace isobus buffer.fill(0xFF); // Reserved bytes buffer[0] = SUPPORTED_DIAGNOSTIC_PROTOCOLS_BITFIELD; - retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::DiagnosticProtocolIdentification), - buffer.data(), - CAN_DATA_LENGTH, - myControlFunction); + retVal = CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::DiagnosticProtocolIdentification), + buffer.data(), + CAN_DATA_LENGTH, + myControlFunction); } return retVal; } @@ -831,10 +831,10 @@ namespace isobus 0xFF, 0xFF }; - return CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::DiagnosticMessage13), - buffer.data(), - buffer.size(), - myControlFunction); + return CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::DiagnosticMessage13), + buffer.data(), + buffer.size(), + myControlFunction); } bool DiagnosticProtocol::send_ecu_identification() const @@ -848,10 +848,10 @@ namespace isobus } std::vector buffer(ecuIdString.begin(), ecuIdString.end()); - return CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::ECUIdentificationInformation), - buffer.data(), - buffer.size(), - myControlFunction); + return CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::ECUIdentificationInformation), + buffer.data(), + buffer.size(), + myControlFunction); } bool DiagnosticProtocol::send_product_identification() const @@ -859,10 +859,10 @@ namespace isobus std::string productIdString = productIdentificationCode + "*" + productIdentificationBrand + "*" + productIdentificationModel + "*"; std::vector buffer(productIdString.begin(), productIdString.end()); - return CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::ProductIdentification), - buffer.data(), - buffer.size(), - myControlFunction); + return CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::ProductIdentification), + buffer.data(), + buffer.size(), + myControlFunction); } bool DiagnosticProtocol::send_software_identification() const @@ -881,10 +881,10 @@ namespace isobus }); std::vector buffer(softIDString.begin(), softIDString.end()); - retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::SoftwareIdentification), - buffer.data(), - buffer.size(), - myControlFunction); + retVal = CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::SoftwareIdentification), + buffer.data(), + buffer.size(), + myControlFunction); } return retVal; } diff --git a/isobus/src/isobus_maintain_power_interface.cpp b/isobus/src/isobus_maintain_power_interface.cpp index 0ff607a2..c5604505 100644 --- a/isobus/src/isobus_maintain_power_interface.cpp +++ b/isobus/src/isobus_maintain_power_interface.cpp @@ -209,11 +209,10 @@ namespace isobus 0xFF, 0xFF }; - - return CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::MaintainPower), - buffer.data(), - buffer.size(), - std::static_pointer_cast(maintainPowerTransmitData.get_sender_control_function())); + return CANNetworkManager::CANNetwork.send_can_message_global(static_cast(CANLibParameterGroupNumber::MaintainPower), + buffer.data(), + buffer.size(), + std::static_pointer_cast(maintainPowerTransmitData.get_sender_control_function())); } void MaintainPowerInterface::process_flags(std::uint32_t flag, void *parentPointer) diff --git a/isobus/src/isobus_task_controller_client.cpp b/isobus/src/isobus_task_controller_client.cpp index ff1ece33..323feea7 100644 --- a/isobus/src/isobus_task_controller_client.cpp +++ b/isobus/src/isobus_task_controller_client.cpp @@ -645,14 +645,13 @@ namespace isobus assert(0 != dataLength); transmitSuccessful = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::ProcessData), - nullptr, + process_internal_object_pool_upload_callback, dataLength, myControlFunction, partnerControlFunction, CANIdentifier::CANPriority::PriorityLowest7, process_tx_callback, - this, - process_internal_object_pool_upload_callback); + this); if (transmitSuccessful) { set_state(StateMachineState::WaitForDDOPTransfer); diff --git a/isobus/src/isobus_virtual_terminal_client.cpp b/isobus/src/isobus_virtual_terminal_client.cpp index d7f05ab3..9cf91ffe 100644 --- a/isobus/src/isobus_virtual_terminal_client.cpp +++ b/isobus/src/isobus_virtual_terminal_client.cpp @@ -1869,14 +1869,13 @@ namespace isobus if (!objectPools[i].uploaded) { bool transmitSuccessful = CANNetworkManager::CANNetwork.send_can_message(static_cast(CANLibParameterGroupNumber::ECUtoVirtualTerminal), - nullptr, + process_internal_object_pool_upload_callback, objectPools[i].objectPoolSize + 1, // Account for Mux byte myControlFunction, partnerControlFunction, CANIdentifier::CANPriority::PriorityLowest7, process_callback, - this, - process_internal_object_pool_upload_callback); + this); if (transmitSuccessful) {