From 46563f861068c1c44c351419adcbdae1fa98a546 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 13 Oct 2020 15:28:30 +0200 Subject: [PATCH 01/12] Updating to libcanard v1.0 --- src/libcanard/canard.c | 54 +++++++++---------- src/libcanard/canard.h | 40 +++++++------- src/libcanard/canard_dsdl.c | 105 ++++++++++++++++++++---------------- src/libcanard/canard_dsdl.h | 17 ++++++ 4 files changed, 125 insertions(+), 91 deletions(-) diff --git a/src/libcanard/canard.c b/src/libcanard/canard.c index a3dfcd07..13103826 100644 --- a/src/libcanard/canard.c +++ b/src/libcanard/canard.c @@ -22,8 +22,8 @@ # define CANARD_PRIVATE static #endif -#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 201112L) -# error "Unsupported language: ISO C11 or a newer version is required." +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# error "Unsupported language: ISO C99 or a newer version is required." #endif // --------------------------------------------- COMMON CONSTANTS --------------------------------------------- @@ -119,7 +119,8 @@ CANARD_PRIVATE uint32_t txMakeMessageSessionSpecifier(const CanardPortID subject { CANARD_ASSERT(src_node_id <= CANARD_NODE_ID_MAX); CANARD_ASSERT(subject_id <= CANARD_SUBJECT_ID_MAX); - return src_node_id | ((uint32_t) subject_id << OFFSET_SUBJECT_ID); + const uint32_t tmp = subject_id | (CANARD_SUBJECT_ID_MAX + 1) | ((CANARD_SUBJECT_ID_MAX + 1) * 2); + return src_node_id | (tmp << OFFSET_SUBJECT_ID); } CANARD_PRIVATE uint32_t txMakeServiceSessionSpecifier(const CanardPortID service_id, @@ -238,7 +239,7 @@ CANARD_PRIVATE uint8_t txMakeTailByte(const bool start_of_transfer, const bool toggle, const CanardTransferID transfer_id) { - CANARD_ASSERT(start_of_transfer ? toggle : true); + CANARD_ASSERT(start_of_transfer ? (toggle == INITIAL_TOGGLE_STATE) : true); return (uint8_t)((start_of_transfer ? TAIL_START_OF_TRANSFER : 0U) | (end_of_transfer ? TAIL_END_OF_TRANSFER : 0U) | (toggle ? TAIL_TOGGLE : 0U) | (transfer_id & CANARD_TRANSFER_ID_MAX)); } @@ -637,28 +638,28 @@ CANARD_PRIVATE uint8_t rxComputeTransferIDDifference(const uint8_t a, const uint CANARD_PRIVATE int8_t rxSessionWritePayload(CanardInstance* const ins, CanardInternalRxSession* const rxs, - const size_t payload_size_max, + const size_t extent, const size_t payload_size, const void* const payload); CANARD_PRIVATE int8_t rxSessionWritePayload(CanardInstance* const ins, CanardInternalRxSession* const rxs, - const size_t payload_size_max, + const size_t extent, const size_t payload_size, const void* const payload) { CANARD_ASSERT(ins != NULL); CANARD_ASSERT(rxs != NULL); CANARD_ASSERT((payload != NULL) || (payload_size == 0U)); - CANARD_ASSERT(rxs->payload_size <= payload_size_max); // This invariant is enforced by the subscription logic. + CANARD_ASSERT(rxs->payload_size <= extent); // This invariant is enforced by the subscription logic. CANARD_ASSERT(rxs->payload_size <= rxs->total_payload_size); rxs->total_payload_size += payload_size; // Allocate the payload lazily, as late as possible. - if ((NULL == rxs->payload) && (payload_size_max > 0U)) + if ((NULL == rxs->payload) && (extent > 0U)) { CANARD_ASSERT(rxs->payload_size == 0); - rxs->payload = ins->memory_allocate(ins, payload_size_max); + rxs->payload = ins->memory_allocate(ins, extent); } int8_t out = 0; @@ -666,11 +667,11 @@ CANARD_PRIVATE int8_t rxSessionWritePayload(CanardInstance* const ins, { // Copy the payload into the contiguous buffer. Apply the implicit truncation rule if necessary. size_t bytes_to_copy = payload_size; - if ((rxs->payload_size + bytes_to_copy) > payload_size_max) + if ((rxs->payload_size + bytes_to_copy) > extent) { - CANARD_ASSERT(rxs->payload_size <= payload_size_max); - bytes_to_copy = payload_size_max - rxs->payload_size; - CANARD_ASSERT((rxs->payload_size + bytes_to_copy) == payload_size_max); + CANARD_ASSERT(rxs->payload_size <= extent); + bytes_to_copy = extent - rxs->payload_size; + CANARD_ASSERT((rxs->payload_size + bytes_to_copy) == extent); CANARD_ASSERT(bytes_to_copy < payload_size); } // This memcpy() call here is one of the two variable-complexity operations in the RX pipeline; @@ -681,12 +682,12 @@ CANARD_PRIVATE int8_t rxSessionWritePayload(CanardInstance* const ins, // We ignore it because the safe functions are poorly supported; reliance on them may limit the portability. (void) memcpy(&rxs->payload[rxs->payload_size], payload, bytes_to_copy); // NOLINT NOSONAR rxs->payload_size += bytes_to_copy; - CANARD_ASSERT(rxs->payload_size <= payload_size_max); + CANARD_ASSERT(rxs->payload_size <= extent); } else { CANARD_ASSERT(rxs->payload_size == 0); - out = (payload_size_max > 0U) ? -CANARD_ERROR_OUT_OF_MEMORY : 0; + out = (extent > 0U) ? -CANARD_ERROR_OUT_OF_MEMORY : 0; } CANARD_ASSERT(out <= 0); return out; @@ -710,12 +711,12 @@ CANARD_PRIVATE void rxSessionRestart(CanardInstance* const ins, CanardInternalRx CANARD_PRIVATE int8_t rxSessionAcceptFrame(CanardInstance* const ins, CanardInternalRxSession* const rxs, const RxFrameModel* const frame, - const size_t payload_size_max, + const size_t extent, CanardTransfer* const out_transfer); CANARD_PRIVATE int8_t rxSessionAcceptFrame(CanardInstance* const ins, CanardInternalRxSession* const rxs, const RxFrameModel* const frame, - const size_t payload_size_max, + const size_t extent, CanardTransfer* const out_transfer) { CANARD_ASSERT(ins != NULL); @@ -738,7 +739,7 @@ CANARD_PRIVATE int8_t rxSessionAcceptFrame(CanardInstance* const ins, rxs->calculated_crc = crcAdd(rxs->calculated_crc, frame->payload_size, frame->payload); } - int8_t out = rxSessionWritePayload(ins, rxs, payload_size_max, frame->payload_size, frame->payload); + int8_t out = rxSessionWritePayload(ins, rxs, extent, frame->payload_size, frame->payload); if (out < 0) { CANARD_ASSERT(-CANARD_ERROR_OUT_OF_MEMORY == out); @@ -787,14 +788,14 @@ CANARD_PRIVATE int8_t rxSessionUpdate(CanardInstance* const ins, const RxFrameModel* const frame, const uint8_t redundant_transport_index, const CanardMicrosecond transfer_id_timeout_usec, - const size_t payload_size_max, + const size_t extent, CanardTransfer* const out_transfer); CANARD_PRIVATE int8_t rxSessionUpdate(CanardInstance* const ins, CanardInternalRxSession* const rxs, const RxFrameModel* const frame, const uint8_t redundant_transport_index, const CanardMicrosecond transfer_id_timeout_usec, - const size_t payload_size_max, + const size_t extent, CanardTransfer* const out_transfer) { CANARD_ASSERT(ins != NULL); @@ -834,7 +835,7 @@ CANARD_PRIVATE int8_t rxSessionUpdate(CanardInstance* const ins, const bool correct_tid = (frame->transfer_id == rxs->transfer_id); if (correct_transport && correct_toggle && correct_tid) { - out = rxSessionAcceptFrame(ins, rxs, frame, payload_size_max, out_transfer); + out = rxSessionAcceptFrame(ins, rxs, frame, extent, out_transfer); } } return out; @@ -895,7 +896,7 @@ CANARD_PRIVATE int8_t rxAcceptFrame(CanardInstance* const ins, frame, redundant_transport_index, subscription->_transfer_id_timeout_usec, - subscription->_payload_size_max, + subscription->_extent, out_transfer); } } @@ -905,9 +906,8 @@ CANARD_PRIVATE int8_t rxAcceptFrame(CanardInstance* const ins, // Anonymous transfers are stateless. No need to update the state machine, just blindly accept it. // We have to copy the data into an allocated storage because the API expects it: the lifetime shall be // independent of the input data and the memory shall be free-able. - const size_t payload_size = (subscription->_payload_size_max < frame->payload_size) - ? subscription->_payload_size_max - : frame->payload_size; + const size_t payload_size = + (subscription->_extent < frame->payload_size) ? subscription->_extent : frame->payload_size; void* const payload = ins->memory_allocate(ins, payload_size); if (payload != NULL) { @@ -1071,7 +1071,7 @@ int8_t canardRxAccept(CanardInstance* const ins, int8_t canardRxSubscribe(CanardInstance* const ins, const CanardTransferKind transfer_kind, const CanardPortID port_id, - const size_t payload_size_max, + const size_t extent, const CanardMicrosecond transfer_id_timeout_usec, CanardRxSubscription* const out_subscription) { @@ -1093,7 +1093,7 @@ int8_t canardRxSubscribe(CanardInstance* const ins, out_subscription->_sessions[i] = NULL; } out_subscription->_transfer_id_timeout_usec = transfer_id_timeout_usec; - out_subscription->_payload_size_max = payload_size_max; + out_subscription->_extent = extent; out_subscription->_port_id = port_id; out_subscription->_next = ins->_rx_subscriptions[tk]; ins->_rx_subscriptions[tk] = out_subscription; diff --git a/src/libcanard/canard.h b/src/libcanard/canard.h index 6440e390..0a6ecc17 100644 --- a/src/libcanard/canard.h +++ b/src/libcanard/canard.h @@ -10,7 +10,7 @@ /// It is designed for use in robust deterministic embedded systems equipped with at least 32K ROM and 4..8K RAM. /// The codebase follows the MISRA C rules, has 100% test coverage, and is validated by at least two static analyzers. /// The library is designed to be compatible with any target platform and instruction set architecture, from 8 to 64 -/// bit, little- and big-endian, RTOS-based or baremetal, etc., as long as there is a standards-compliant C11 compiler. +/// bit, little- and big-endian, RTOS-based or baremetal, etc., as long as there is a standards-compliant compiler. /// /// INTEGRATION /// @@ -108,8 +108,8 @@ extern "C" { /// Semantic version of this library (not the UAVCAN specification). /// API will be backward compatible within the same major version. -#define CANARD_VERSION_MAJOR 0 -#define CANARD_VERSION_MINOR 100 +#define CANARD_VERSION_MAJOR 1 +#define CANARD_VERSION_MINOR 0 /// The version number of the UAVCAN specification implemented by this library. #define CANARD_UAVCAN_SPECIFICATION_VERSION_MAJOR 1 @@ -130,7 +130,7 @@ extern "C" { #define CANARD_MTU_CAN_FD 64U /// Parameter ranges are inclusive; the lower bound is zero for all. See UAVCAN/CAN Specification for background. -#define CANARD_SUBJECT_ID_MAX 32767U +#define CANARD_SUBJECT_ID_MAX 8191U #define CANARD_SERVICE_ID_MAX 511U #define CANARD_NODE_ID_MAX 127U #define CANARD_PRIORITY_MAX 7U @@ -289,7 +289,7 @@ typedef struct CanardRxSubscription struct CanardInternalRxSession* _sessions[CANARD_NODE_ID_MAX + 1U]; CanardMicrosecond _transfer_id_timeout_usec; ///< Internal use only. - size_t _payload_size_max; ///< Internal use only. + size_t _extent; ///< Internal use only. CanardPortID _port_id; ///< Internal use only. } CanardRxSubscription; @@ -480,10 +480,11 @@ void canardTxPop(CanardInstance* const ins); /// was already allocated at the time. /// This event occurs when a transport frame that matches a known subscription is received and it begins a /// new transfer (that is, the start-of-frame flag is set and it is not a duplicate). -/// The amount of the allocated memory is payload_size_max as configured via canardRxSubscribe(). +/// The amount of the allocated memory equals the extent as configured via canardRxSubscribe(); please read +/// its documentation for further information about the extent and related edge cases. /// The worst case occurs when every node on the bus initiates a multi-frame transfer for which there is a -/// matching subscription: in this case, the library will allocate number_of_nodes allocations of size -/// payload_size_max. +/// matching subscription: in this case, the library will allocate number_of_nodes allocations, where each +/// allocation is the same size as the configured extent. /// /// 3. Memory allocated for the transfer payload buffer may be deallocated at the discretion of the library. /// This operation does not increase the worst case execution time and does not improve the worst case memory @@ -492,9 +493,9 @@ void canardTxPop(CanardInstance* const ins); /// /// The worst case dynamic memory consumption per subscription is: /// -/// (sizeof(session instance) + payload_size_max) * number_of_nodes +/// (sizeof(session instance) + extent) * number_of_nodes /// -/// Where sizeof(session instance) and payload_size_max are defined above, and number_of_nodes is the number of remote +/// Where sizeof(session instance) and extent are defined above, and number_of_nodes is the number of remote /// nodes emitting transfers that match the subscription (which cannot exceed (CANARD_NODE_ID_MAX-1) by design). /// If the dynamic memory pool is sized correctly, the application is guaranteed to never encounter an /// out-of-memory (OOM) error at runtime. The actual size of the dynamic memory pool is typically larger; @@ -514,7 +515,7 @@ void canardTxPop(CanardInstance* const ins); /// are stored into out_transfer, and the transfer payload buffer ownership is passed to that object. The lifetime /// of the resulting transfer object is not related to the lifetime of the input transport frame (that is, even if /// it is a single-frame transfer, its payload is copied out into a new dynamically allocated buffer storage). -/// If the payload_size_max is zero, the payload pointer may be NULL, since there is no data to store and so a +/// If the extent is zero, the payload pointer may be NULL, since there is no data to store and so a /// buffer is not needed. The application is responsible for deallocating the payload buffer when the processing /// is done by invoking memory_free on the transfer payload pointer. /// @@ -525,8 +526,8 @@ void canardTxPop(CanardInstance* const ins); /// - The payload pointer of the input frame is NULL while its size is non-zero. /// - The CAN ID of the input frame is not less than 2**29=0x20000000. /// -/// The function drops the frame and returns zero if any of the following conditions are true (the general policy is -/// that protocol errors are not escalated because they do not construe a node-local error): +/// The function returns zero if any of the following conditions are true (the general policy is that protocol +/// errors are not escalated because they do not construe a node-local error): /// - The received frame is not a valid UAVCAN/CAN transport frame. /// - The received frame is a valid UAVCAN/CAN transport frame, but there is no matching subscription, /// the frame did not complete a transfer, the frame forms an invalid frame sequence, the frame is a duplicate, @@ -554,11 +555,14 @@ int8_t canardRxAccept(CanardInstance* const ins, /// If such subscription already exists, it will be removed first as if canardRxUnsubscribe() was /// invoked by the application, and then re-created anew with the new parameters. /// -/// The payload_size_max defines the size of the transfer payload memory buffer. Transfers that carry larger payloads -/// will be accepted but the excess payload will be truncated, as mandated by the Specification. This behavior is -/// called the Implicit Truncation Rule (ITR) and it is intended to facilitate extensibility of data types while -/// preserving backward compatibility. The transfer CRC is validated regardless of whether its payload is truncated. +/// The extent defines the size of the transfer payload memory buffer; or, in other words, the maximum possible size +/// of received objects, considering also possible future versions with new fields. It is safe to pick larger values. +/// Note well that the extent is not the same thing as the maximum size of the object, it is usually larger! +/// Transfers that carry payloads that exceed the specified extent will be accepted anyway but the excess payload +/// will be truncated away, as mandated by the Specification. The transfer CRC is always validated regardless of +/// whether its payload is truncated. /// +/// The default transfer-ID timeout value is defined as CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC; use it if not sure. /// The redundant transport fail-over timeout (if redundant transports are used) is the same as the transfer-ID timeout. /// It may be reduced in a future release of the library, but it will not affect the backward compatibility. /// @@ -581,7 +585,7 @@ int8_t canardRxAccept(CanardInstance* const ins, int8_t canardRxSubscribe(CanardInstance* const ins, const CanardTransferKind transfer_kind, const CanardPortID port_id, - const size_t payload_size_max, + const size_t extent, const CanardMicrosecond transfer_id_timeout_usec, CanardRxSubscription* const out_subscription); diff --git a/src/libcanard/canard_dsdl.c b/src/libcanard/canard_dsdl.c index de171ffe..98cd1c4c 100644 --- a/src/libcanard/canard_dsdl.c +++ b/src/libcanard/canard_dsdl.c @@ -31,8 +31,17 @@ # define CANARD_PRIVATE static #endif -#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 201112L) -# error "Unsupported language: ISO C11 or a newer version is required." +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# error "Unsupported language: ISO C99 or a newer version is required." +#endif + +/// In general, _Static_assert is not present on C99 compilers, except for gnu99 +#if !defined(static_assert) +// Intentional violation of MISRA: static assertion macro cannot be replaced with a function definition. +# define static_assert(x, ...) typedef char _static_assert_gl(_static_assertion_, __LINE__)[(x) ? 1 : -1] // NOSONAR +# define _static_assert_gl(a, b) _static_assert_gl_impl(a, b) // NOSONAR +// Intentional violation of MISRA: the paste operator ## cannot be avoided in this context. +# define _static_assert_gl_impl(a, b) a##b // NOSONAR #endif /// Detect whether the target platform is compatible with IEEE 754. @@ -59,22 +68,31 @@ CANARD_PRIVATE size_t chooseMin(size_t a, size_t b) return (a < b) ? a : b; } -/// The algorithm was originally designed by Ben Dyer for Libuavcan v0: -/// https://github.com/UAVCAN/libuavcan/blob/ba696029f9625d7ea3eb00/libuavcan/src/marshal/uc_bit_array_copy.cpp#L12-L58 -/// This version is modified for v1 where the bit order is the opposite. -/// If both offsets and the length are byte-aligned, the algorithm degenerates to memcpy(). -/// The source and the destination shall not overlap. -CANARD_PRIVATE void copyBitArray(const size_t length_bit, - const size_t src_offset_bit, - const size_t dst_offset_bit, - const uint8_t* const src, - uint8_t* const dst); -CANARD_PRIVATE void copyBitArray(const size_t length_bit, - const size_t src_offset_bit, - const size_t dst_offset_bit, - const uint8_t* const src, - uint8_t* const dst) +CANARD_PRIVATE size_t getBitCopySize(const size_t buf_size_bytes, + const size_t offset_bit, + const size_t requested_length_bit, + const uint8_t value_length_bit); +CANARD_PRIVATE size_t getBitCopySize(const size_t buf_size_bytes, + const size_t offset_bit, + const size_t requested_length_bit, + const uint8_t value_length_bit) +{ + const size_t buf_size_bit = buf_size_bytes * BYTE_WIDTH; + const size_t remaining_bit = buf_size_bit - chooseMin(buf_size_bit, offset_bit); + return chooseMin(remaining_bit, chooseMin(requested_length_bit, value_length_bit)); +} + +// --------------------------------------------- PUBLIC API - BIT ARRAY --------------------------------------------- + +void canardDSDLCopyBits(const size_t length_bit, + const size_t src_offset_bit, + const size_t dst_offset_bit, + const uint8_t* const src, + uint8_t* const dst) { + // The algorithm was originally designed by Ben Dyer for Libuavcan v0: + // https://github.com/UAVCAN/libuavcan/blob/ba6929f9625d7ea3eb00/libuavcan/src/marshal/uc_bit_array_copy.cpp#L12-L58 + // This version is modified for v1 where the bit order is the opposite. CANARD_ASSERT((src != NULL) && (dst != NULL) && (src != dst)); CANARD_ASSERT((src < dst) ? ((src + ((src_offset_bit + length_bit + BYTE_WIDTH) / BYTE_WIDTH)) <= dst) : ((dst + ((dst_offset_bit + length_bit + BYTE_WIDTH) / BYTE_WIDTH)) <= src)); @@ -128,36 +146,23 @@ CANARD_PRIVATE void copyBitArray(const size_t length_bit, } } -CANARD_PRIVATE size_t getBitCopySize(const size_t buf_size_bytes, - const size_t offset_bit, - const size_t requested_length_bit, - const uint8_t value_length_bit); -CANARD_PRIVATE size_t getBitCopySize(const size_t buf_size_bytes, - const size_t offset_bit, - const size_t requested_length_bit, - const uint8_t value_length_bit) -{ - const size_t buf_size_bit = buf_size_bytes * BYTE_WIDTH; - const size_t remaining_bit = buf_size_bit - chooseMin(buf_size_bit, offset_bit); - return chooseMin(remaining_bit, chooseMin(requested_length_bit, value_length_bit)); -} - // --------------------------------------------- PUBLIC API - INTEGER --------------------------------------------- void canardDSDLSetBit(uint8_t* const buf, const size_t off_bit, const bool value) { CANARD_ASSERT(buf != NULL); const uint8_t val = value ? 1U : 0U; - copyBitArray(1U, 0U, off_bit, &val, buf); + canardDSDLCopyBits(1U, 0U, off_bit, &val, buf); } +static_assert(WIDTH64 == (sizeof(uint64_t) * BYTE_WIDTH), "Unexpected size of uint64_t"); + void canardDSDLSetUxx(uint8_t* const buf, const size_t off_bit, const uint64_t value, const uint8_t len_bit) { - _Static_assert(WIDTH64 == (sizeof(uint64_t) * BYTE_WIDTH), "Unexpected size of uint64_t"); CANARD_ASSERT(buf != NULL); const size_t saturated_len_bit = chooseMin(len_bit, WIDTH64); #if CANARD_DSDL_CONFIG_LITTLE_ENDIAN - copyBitArray(saturated_len_bit, 0U, off_bit, (const uint8_t*) &value, buf); + canardDSDLCopyBits(saturated_len_bit, 0U, off_bit, (const uint8_t*) &value, buf); #else const uint8_t tmp[sizeof(uint64_t)] = { (uint8_t)((value >> 0U) & BYTE_MAX), // Suppress warnings about the magic numbers. Their purpose is clear. @@ -169,7 +174,7 @@ void canardDSDLSetUxx(uint8_t* const buf, const size_t off_bit, const uint64_t v (uint8_t)((value >> 48U) & BYTE_MAX), // NOLINT NOSONAR (uint8_t)((value >> 56U) & BYTE_MAX), // NOLINT NOSONAR }; - copyBitArray(saturated_len_bit, 0U, off_bit, &tmp[0], buf); + canardDSDLCopyBits(saturated_len_bit, 0U, off_bit, &tmp[0], buf); #endif } @@ -192,7 +197,7 @@ uint8_t canardDSDLGetU8(const uint8_t* const buf, const size_t buf_size, const s const size_t copy_size = getBitCopySize(buf_size, off_bit, len_bit, BYTE_WIDTH); CANARD_ASSERT(copy_size <= (sizeof(uint8_t) * BYTE_WIDTH)); uint8_t val = 0; - copyBitArray(copy_size, off_bit, 0U, buf, &val); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, &val); return val; } @@ -203,11 +208,11 @@ uint16_t canardDSDLGetU16(const uint8_t* const buf, const size_t buf_size, const CANARD_ASSERT(copy_size <= (sizeof(uint16_t) * BYTE_WIDTH)); #if CANARD_DSDL_CONFIG_LITTLE_ENDIAN uint16_t val = 0U; - copyBitArray(copy_size, off_bit, 0U, buf, (uint8_t*) &val); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, (uint8_t*) &val); return val; #else uint8_t tmp[sizeof(uint16_t)] = {0}; - copyBitArray(copy_size, off_bit, 0U, buf, &tmp[0]); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, &tmp[0]); return (uint16_t)(tmp[0] | (uint16_t)(((uint16_t) tmp[1]) << BYTE_WIDTH)); #endif } @@ -219,11 +224,11 @@ uint32_t canardDSDLGetU32(const uint8_t* const buf, const size_t buf_size, const CANARD_ASSERT(copy_size <= (sizeof(uint32_t) * BYTE_WIDTH)); #if CANARD_DSDL_CONFIG_LITTLE_ENDIAN uint32_t val = 0U; - copyBitArray(copy_size, off_bit, 0U, buf, (uint8_t*) &val); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, (uint8_t*) &val); return val; #else uint8_t tmp[sizeof(uint32_t)] = {0}; - copyBitArray(copy_size, off_bit, 0U, buf, &tmp[0]); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, &tmp[0]); return (uint32_t)(tmp[0] | // Suppress warnings about the magic numbers. ((uint32_t) tmp[1] << 8U) | // NOLINT NOSONAR ((uint32_t) tmp[2] << 16U) | // NOLINT NOSONAR @@ -238,11 +243,11 @@ uint64_t canardDSDLGetU64(const uint8_t* const buf, const size_t buf_size, const CANARD_ASSERT(copy_size <= (sizeof(uint64_t) * BYTE_WIDTH)); #if CANARD_DSDL_CONFIG_LITTLE_ENDIAN uint64_t val = 0U; - copyBitArray(copy_size, off_bit, 0U, buf, (uint8_t*) &val); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, (uint8_t*) &val); return val; #else uint8_t tmp[sizeof(uint64_t)] = {0}; - copyBitArray(copy_size, off_bit, 0U, buf, &tmp[0]); + canardDSDLCopyBits(copy_size, off_bit, 0U, buf, &tmp[0]); return (uint64_t)(tmp[0] | // Suppress warnings about the magic numbers. ((uint64_t) tmp[1] << 8U) | // NOLINT NOSONAR ((uint64_t) tmp[2] << 16U) | // NOLINT NOSONAR @@ -294,7 +299,7 @@ int64_t canardDSDLGetI64(const uint8_t* const buf, const size_t buf_size, const #if CANARD_DSDL_PLATFORM_IEEE754_FLOAT -_Static_assert(WIDTH32 == (sizeof(CanardDSDLFloat32) * BYTE_WIDTH), "Unsupported floating point model"); +static_assert(WIDTH32 == (sizeof(CanardDSDLFloat32) * BYTE_WIDTH), "Unsupported floating point model"); // Intentional violation of MISRA: we need this union because the alternative is far more error prone. // We have to rely on low-level data representation details to do the conversion; unions are helpful. @@ -319,7 +324,15 @@ CANARD_PRIVATE uint16_t float16Pack(const CanardDSDLFloat32 value) uint16_t out = 0; if (in.bits >= f32inf.bits) { - out = (in.bits > f32inf.bits) ? (uint16_t) 0x7FFFU : (uint16_t) 0x7C00U; // NOLINT NOSONAR + // The no-lint statements suppress the warnings about magic numbers. + if ((in.bits & 0x7FFFFFUL) != 0) // NOLINT NOSONAR + { + out = 0x7E00U; // NOLINT NOSONAR + } + else + { + out = (in.bits > f32inf.bits) ? (uint16_t) 0x7FFFU : (uint16_t) 0x7C00U; // NOLINT NOSONAR + } } else { @@ -369,7 +382,7 @@ CanardDSDLFloat32 canardDSDLGetF16(const uint8_t* const buf, const size_t buf_si #if CANARD_DSDL_PLATFORM_IEEE754_FLOAT -_Static_assert(WIDTH32 == (sizeof(CanardDSDLFloat32) * BYTE_WIDTH), "Unsupported floating point model"); +static_assert(WIDTH32 == (sizeof(CanardDSDLFloat32) * BYTE_WIDTH), "Unsupported floating point model"); void canardDSDLSetF32(uint8_t* const buf, const size_t off_bit, const CanardDSDLFloat32 value) { @@ -403,7 +416,7 @@ CanardDSDLFloat32 canardDSDLGetF32(const uint8_t* const buf, const size_t buf_si #if CANARD_DSDL_PLATFORM_IEEE754_DOUBLE -_Static_assert(WIDTH64 == (sizeof(CanardDSDLFloat64) * BYTE_WIDTH), "Unsupported floating point model"); +static_assert(WIDTH64 == (sizeof(CanardDSDLFloat64) * BYTE_WIDTH), "Unsupported floating point model"); CanardDSDLFloat64 canardDSDLGetF64(const uint8_t* const buf, const size_t buf_size, const size_t off_bit) { diff --git a/src/libcanard/canard_dsdl.h b/src/libcanard/canard_dsdl.h index 482f0d1e..b863dacb 100644 --- a/src/libcanard/canard_dsdl.h +++ b/src/libcanard/canard_dsdl.h @@ -41,6 +41,23 @@ extern "C" { typedef float CanardDSDLFloat32; typedef double CanardDSDLFloat64; +/// Copy the specified number of bits from the source buffer into the destination buffer in accordance with the +/// DSDL bit-level serialization specification. The offsets may be arbitrary (may exceed 8 bits). +/// If both offsets and the length are byte-aligned, the algorithm degenerates to memcpy(). +/// If the source and the destination overlap, the behavior is undefined. +/// If either source or destination pointers are NULL, the behavior is undefined. +/// Arguments: +/// length_bit The number of bits to copy. Both source and destination shall be large enough. +/// src_offset_bit Offset in bits from the source pointer. May exceed 8. +/// dst_offset_bit Offset in bits from the destination pointer. May exceed 8. +/// src Source buffer. Shall be at least ceil(length_bit/8) bytes large. +/// dst Destination buffer. Shall be at least ceil(length_bit/8) bytes large. +void canardDSDLCopyBits(const size_t length_bit, + const size_t src_offset_bit, + const size_t dst_offset_bit, + const uint8_t* const src, + uint8_t* const dst); + /// Serialize a DSDL field value at the specified bit offset from the beginning of the destination buffer. /// The behavior is undefined if the input pointer is NULL. The time complexity is linear of the bit length. /// One-bit-wide signed integers are processed without raising an error but the result is unspecified. From 7e7614424220b7f8c5bfe670d9c7c1259066299f Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 09:41:52 +0100 Subject: [PATCH 02/12] Updating to the latest code generated by the nnvg C-serialiation. --- .../src/test_ExecuteCommand_ServiceClient.cpp | 16 +- .../src/test_ExecuteCommand_ServiceServer.cpp | 14 +- extras/test/src/test_Heartbeat.cpp | 6 +- extras/test/src/test_ID.cpp | 2 +- extras/test/src/test_Version.cpp | 2 +- src/nunavut/support/serialization.h | 580 +++++++++++++----- .../node/ExecuteCommand.1.0.Request.cpp | 13 +- .../uavcan/node/ExecuteCommand.1.0.Request.h | 6 +- .../node/ExecuteCommand.1.0.Response.cpp | 13 +- .../uavcan/node/ExecuteCommand.1.0.Response.h | 20 +- .../uavcan/node/ExecuteCommand.1.0.nnvg.h | 536 ++++++++++------ src/types/uavcan/node/Health_1_0.nnvg.h | 199 ++++++ src/types/uavcan/node/Heartbeat.1.0.cpp | 17 +- src/types/uavcan/node/Heartbeat.1.0.h | 21 +- src/types/uavcan/node/Heartbeat.1.0.nnvg.h | 379 ++++++++---- src/types/uavcan/node/ID.1.0.hpp | 2 +- src/types/uavcan/node/ID.1.0.ipp | 13 +- src/types/uavcan/node/ID.1.0.nnvg.h | 204 ++++-- src/types/uavcan/node/Mode_1_0.nnvg.h | 199 ++++++ src/types/uavcan/node/Version.1.0.hpp | 2 +- src/types/uavcan/node/Version.1.0.ipp | 13 +- src/types/uavcan/node/Version.1.0.nnvg.h | 238 ++++--- 22 files changed, 1833 insertions(+), 662 deletions(-) create mode 100644 src/types/uavcan/node/Health_1_0.nnvg.h create mode 100644 src/types/uavcan/node/Mode_1_0.nnvg.h diff --git a/extras/test/src/test_ExecuteCommand_ServiceClient.cpp b/extras/test/src/test_ExecuteCommand_ServiceClient.cpp index 72ad3b33..fd80fccd 100644 --- a/extras/test/src/test_ExecuteCommand_ServiceClient.cpp +++ b/extras/test/src/test_ExecuteCommand_ServiceClient.cpp @@ -27,7 +27,7 @@ static CanardNodeID const REMOTE_NODE_ID = 27; **************************************************************************************/ static util::CanFrameVect can_frame_vect; -static uavcan_node_ExecuteCommand_1_0_Response response; +static uavcan_node_ExecuteCommand_Response_1_0 response; /************************************************************************************** * PRIVATE FUNCTION DEFINITION @@ -53,14 +53,16 @@ static void onExecuteCommand_1_0_Response_Received(CanardTransfer const & transf TEST_CASE("A '435.ExecuteCommand.1.0' request is sent to a server", "[execute-command-client-01]") { - uavcan_node_ExecuteCommand_1_0_Response_init(&response); + uavcan_node_ExecuteCommand_Response_1_0_initialize_(&response); ArduinoUAVCAN uavcan(util::LOCAL_NODE_ID, transmitCanFrame); std::string const cmd_1_param = "I want a double espresso with cream"; ExecuteCommand_1_0::Request req_1; req_1.data.command = 0xCAFE; - req_1.data.parameter_length = std::min(cmd_1_param.length(), uavcan_node_ExecuteCommand_1_0_Request_parameter_array_capacity()); - std::copy(cmd_1_param.c_str(), cmd_1_param.c_str() + req_1.data.parameter_length, req_1.data.parameter); + req_1.data.parameter.count = std::min(cmd_1_param.length(), (size_t)uavcan_node_ExecuteCommand_Request_1_0_parameter_ARRAY_CAPACITY_); + std::copy(cmd_1_param.c_str(), + cmd_1_param.c_str() + req_1.data.parameter.count, + req_1.data.parameter.elements); REQUIRE(uavcan.request(req_1, REMOTE_NODE_ID, onExecuteCommand_1_0_Response_Received) == true); @@ -105,8 +107,10 @@ TEST_CASE("A '435.ExecuteCommand.1.0' request is sent to a server", "[execute-co std::string const cmd_2_param = "I do not need coffee anymore"; ExecuteCommand_1_0::Request req_2; req_2.data.command = 0xDEAD; - req_2.data.parameter_length = std::min(cmd_2_param.length(), uavcan_node_ExecuteCommand_1_0_Request_parameter_array_capacity()); - std::copy(cmd_2_param.c_str(), cmd_2_param.c_str() + req_2.data.parameter_length, req_2.data.parameter); + req_2.data.parameter.count = std::min(cmd_2_param.length(), (size_t)uavcan_node_ExecuteCommand_Request_1_0_parameter_ARRAY_CAPACITY_); + std::copy(cmd_2_param.c_str(), + cmd_2_param.c_str() + req_2.data.parameter.count, + req_2.data.parameter.elements); REQUIRE(uavcan.request(req_2, REMOTE_NODE_ID, onExecuteCommand_1_0_Response_Received) == true); /* Transmit all the CAN frames. */ diff --git a/extras/test/src/test_ExecuteCommand_ServiceServer.cpp b/extras/test/src/test_ExecuteCommand_ServiceServer.cpp index 332f9bfb..be798876 100644 --- a/extras/test/src/test_ExecuteCommand_ServiceServer.cpp +++ b/extras/test/src/test_ExecuteCommand_ServiceServer.cpp @@ -27,7 +27,7 @@ static CanardNodeID const REMOTE_NODE_ID = 27; **************************************************************************************/ static util::CanFrame response_can_frame; -static uavcan_node_ExecuteCommand_1_0_Request request; +static uavcan_node_ExecuteCommand_Request_1_0 request; /************************************************************************************** * PRIVATE FUNCTION DEFINITION @@ -49,8 +49,10 @@ static void onExecuteCommand_1_0_Request_Received(CanardTransfer const & transfe * have them in your real application. */ request.command = received_request.data.command; - request.parameter_length = received_request.data.parameter_length; - std::copy(received_request.data.parameter, received_request.data.parameter + received_request.data.parameter_length, request.parameter); + request.parameter.count = received_request.data.parameter.count; + std::copy(received_request.data.parameter.elements, + received_request.data.parameter.elements + received_request.data.parameter.count, + request.parameter.elements); /* Deal with the command ... */ @@ -67,7 +69,7 @@ static void onExecuteCommand_1_0_Request_Received(CanardTransfer const & transfe TEST_CASE("A '435.ExecuteCommand.1.0' request is received from a client", "[execute-command-server-01]") { - uavcan_node_ExecuteCommand_1_0_Request_init(&request); + uavcan_node_ExecuteCommand_Request_1_0_initialize_(&request); ArduinoUAVCAN uavcan(util::LOCAL_NODE_ID, transmitCanFrame); /* Subscribe to incoming server requests. */ @@ -98,8 +100,8 @@ TEST_CASE("A '435.ExecuteCommand.1.0' request is received from a client", "[exec REQUIRE(request.command == 0xCAFE); std::string const EXP_CMD_PARAM_STR = "I want a double espresso with cream"; std::vector const EXP_CMD_PARAM_VECT(EXP_CMD_PARAM_STR.begin(), EXP_CMD_PARAM_STR.end()); - REQUIRE(request.parameter_length == EXP_CMD_PARAM_VECT.size()); - REQUIRE(std::equal(EXP_CMD_PARAM_VECT.begin(), EXP_CMD_PARAM_VECT.end(), request.parameter) == true); + REQUIRE(request.parameter.count == EXP_CMD_PARAM_VECT.size()); + REQUIRE(std::equal(EXP_CMD_PARAM_VECT.begin(), EXP_CMD_PARAM_VECT.end(), request.parameter.elements) == true); /* We should now have one CAN frame in the transmit pipeline */ REQUIRE(uavcan.transmitCanFrame() == true); diff --git a/extras/test/src/test_Heartbeat.cpp b/extras/test/src/test_Heartbeat.cpp index 0420492f..08ff2114 100644 --- a/extras/test/src/test_Heartbeat.cpp +++ b/extras/test/src/test_Heartbeat.cpp @@ -85,7 +85,7 @@ TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is sent", "[heartbeat-01]") TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is received", "[heartbeat-02]") { - uavcan_node_Heartbeat_1_0_init(&hb_data); + uavcan_node_Heartbeat_1_0_initialize_(&hb_data); ArduinoUAVCAN uavcan(util::LOCAL_NODE_ID, nullptr); REQUIRE(uavcan.subscribe(onHeatbeat_1_0_Received)); @@ -104,7 +104,7 @@ TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is received", "[heartbeat-02]" REQUIRE(hb_node_id == 59); REQUIRE(hb_data.uptime == 1337); - REQUIRE(hb_data.health == arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Health::CAUTION)); - REQUIRE(hb_data.mode == arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Mode::OFFLINE)); + REQUIRE(hb_data.health.value == arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Health::CAUTION)); + REQUIRE(hb_data.mode.value == 7); /* TODO: FIX THIS (uint8_t)arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Mode::OFFLINE)); */ REQUIRE(hb_data.vendor_specific_status_code == 42); } diff --git a/extras/test/src/test_ID.cpp b/extras/test/src/test_ID.cpp index 47499dfa..8e1343f9 100644 --- a/extras/test/src/test_ID.cpp +++ b/extras/test/src/test_ID.cpp @@ -71,7 +71,7 @@ TEST_CASE("A 'ID.1.0.uavcan' message is sent", "[id-01]") TEST_CASE("A 'ID.1.0.uavcan' message is received", "[id-02]") { - uavcan_node_ID_1_0_init(&id); + uavcan_node_ID_1_0_initialize_(&id); ArduinoUAVCAN uavcan(util::LOCAL_NODE_ID, transmitCanFrame); REQUIRE(uavcan.subscribe>(onID_1_0_Received)); diff --git a/extras/test/src/test_Version.cpp b/extras/test/src/test_Version.cpp index 74e9ce3f..fde92c1e 100644 --- a/extras/test/src/test_Version.cpp +++ b/extras/test/src/test_Version.cpp @@ -73,7 +73,7 @@ TEST_CASE("A 'Version.1.0.uavcan' message is sent", "[version-01]") TEST_CASE("A 'Version.1.0.uavcan' message is received", "[version-02]") { - uavcan_node_Version_1_0_init(&version); + uavcan_node_Version_1_0_initialize_(&version); ArduinoUAVCAN uavcan(util::LOCAL_NODE_ID, transmitCanFrame); REQUIRE(uavcan.subscribe>(onVersion_1_0_Received)); diff --git a/src/nunavut/support/serialization.h b/src/nunavut/support/serialization.h index 283982f3..737e5333 100644 --- a/src/nunavut/support/serialization.h +++ b/src/nunavut/support/serialization.h @@ -1,11 +1,10 @@ -/* - * +-+ +-+ - * UAVCAN common serialization support routines. | | | | - * \ - / - * This software is distributed under the terms of the MIT License. --- - * o - * +------------------------------------------------------------------------------------------------------------------+ - */ +// UAVCAN common serialization support routines. +-+ +-+ +// This file is based on canard_dsdl.h, which is part of Libcanard. | | | | +// \ - / +// AUTOGENERATED, DO NOT EDIT. --- +// o +//--------------------------------------------------------------------------------------------------------------------- + #ifndef NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED #define NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED @@ -22,109 +21,144 @@ extern "C" #endif #include +#include +#include // For isfinite(). #include #include -#ifndef __cplusplus -#include -#endif +#include // For static_assert (C11) and assert() if NUNAVUT_ASSERT is used. + +static_assert(sizeof(size_t) >= sizeof(size_t), + "The bit-length type used by Nunavut, size_t, " + "is smaller than this platform's size_t type. " + "Nunavut serialization relies on size_t to size_t conversions " + "that do not lose data. You will need to regenerate Nunavut serialization support with a larger " + "unsigned_bit_length type specified."); + +/// Nunavut returns 0 for success and < 0 for any failure. It is always adequate to check that error_value < 0 +/// to detect errors or error_value == 0 for success. +/// +/// Nunavut serialization will never define more than 127 errors and the reserved error numbers are [-1,-127] +/// (-128 is not used). Error code 1 is currently also not used to avoid conflicts with 3rd-party software. +/// +/// Return values > 0 for Nunavut serialization are undefined. +#define NUNAVUT_SUCCESS 0 +// API usage errors: +#define NUNAVUT_ERROR_INVALID_ARGUMENT 2 +#define NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL 3 +// Invalid representation (caused by bad input data, not API misuse): +#define NUNAVUT_ERROR_REPRESENTATION_BAD_ARRAY_LENGTH 10 +#define NUNAVUT_ERROR_REPRESENTATION_BAD_UNION_TAG 11 +#define NUNAVUT_ERROR_REPRESENTATION_BAD_DELIMITER_HEADER 12 + +/// Detect whether the target platform is compatible with IEEE 754. +#define NUNAVUT_PLATFORM_IEEE754_FLOAT \ + ((FLT_RADIX == 2) && (FLT_MANT_DIG == 24) && (FLT_MIN_EXP == -125) && (FLT_MAX_EXP == 128)) +#define NUNAVUT_PLATFORM_IEEE754_DOUBLE \ + ((FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MIN_EXP == -1021) && (DBL_MAX_EXP == 1024)) -/// Serialization errors -/// Returned as negative numbers -#define NUNAVUT_ERR_INVALID_BUF 1 /// Indicates a NULL pointer to a data buffer was given. -#define NUNAVUT_ERR_INVALID_LEN 2 /// Indicates a variable length array was serialized with an - /// invalid length tag. -#define NUNAVUT_ERR_BUF_OVERFLOW 3 /// Indicates the data being deserialized overflows the given - /// buffer. -#define NUNAVUT_ERR_INVALID_TAG 4 /// Indicates that a union field was assigned an invalid tag. #ifndef NUNAVUT_ASSERT -#define NUNAVUT_ASSERT(expr) +# define NUNAVUT_ASSERT(expr) #endif -// --------------------------------------------- PRIMITIVE SERIALIZATION --------------------------------------------- -/// Calculate the number of bits to safely copy from serialized buffer. -/// Returns the smallest bit length based on requested parameters and available space. +// ---------------------------------------------------- HELPERS ---------------------------------------------------- + +/// Returns the smallest value. +static inline size_t nunavutChooseMin(const size_t a, const size_t b) +{ + return (a < b) ? a : b; +} + +/// Calculate the number of bits to safely copy from/to a serialized buffer. +/// Mind the units! By convention, buffer size is specified in bytes, but fragment length and offset are in bits. /// -/// @param buf_size_bytes Size of the input buffer in bytes. -/// @param offset_bit Offset (in bits) of data type in message. -/// @param requested_length_bit Size (in bits) of the data type requested. -/// @param value_length_bit Size (in bits) of the data type that is being extracted into. +/// buffer buffer +/// origin end +/// [------------------------- buffer_size_bytes ------------------------] +/// [--------------- fragment_offset_bits ---------------][--- fragment_length_bits ---] +/// [-- out bits --] /// -/// @returns Number of bits to safely read from buffer. -static inline uint32_t nunavutInternalGetBitCopySize( - const size_t buf_size_bytes, - const uint32_t offset_bit, - const uint32_t requested_length_bit, - const uint8_t value_length_bit) +static inline size_t nunavutSaturateBufferFragmentBitLength( + const size_t buffer_size_bytes, const size_t fragment_offset_bits, const size_t fragment_length_bits) { - const uint32_t buf_size_bit = (uint32_t)buf_size_bytes * 8U; - const uint32_t remaining_bit = buf_size_bit - (((buf_size_bit) < (offset_bit)) ? (buf_size_bit) : (offset_bit)); - const uint32_t min_length_bit = (((requested_length_bit) < (value_length_bit)) ? (requested_length_bit) : (value_length_bit)); - const uint32_t min_remaining_length = (((remaining_bit) < (min_length_bit)) ? (remaining_bit) : (min_length_bit)); - return min_remaining_length; + const size_t size_bits = (size_t)buffer_size_bytes * 8U; + const size_t tail_bits = size_bits - nunavutChooseMin(size_bits, fragment_offset_bits); + return nunavutChooseMin(fragment_length_bits, tail_bits); } -// --------------------------------------------- PUBLIC API - BIT ARRAY --------------------------------------------- +// ---------------------------------------------------- BIT ARRAY ---------------------------------------------------- -static inline void nunavutCopyBits(const uint32_t length_bit, - const uint32_t src_offset_bit, - const uint32_t dst_offset_bit, - const uint8_t* const src, - uint8_t* const dst) +/// Copy the specified number of bits from the source buffer into the destination buffer in accordance with the +/// DSDL bit-level serialization specification. The offsets may be arbitrary (may exceed 8 bits). +/// If both offsets are byte-aligned, the function invokes memmove() and possibly adjusts the last byte separately. +/// If the source and the destination overlap AND the offsets are not byte-aligned, the behavior is undefined. +/// If either source or destination pointers are NULL, the behavior is undefined. +/// Arguments: +/// dst Destination buffer. Shall be at least ceil(length_bits/8) bytes large. +/// dst_offset_bits Offset in bits from the destination pointer. May exceed 8. +/// length_bits The number of bits to copy. Both source and destination shall be large enough. +/// src Source buffer. Shall be at least ceil(length_bits/8) bytes large. +/// src_offset_bits Offset in bits from the source pointer. May exceed 8. +static inline void nunavutCopyBits(void* const dst, + const size_t dst_offset_bits, + const size_t length_bits, + const void* const src, + const size_t src_offset_bits) { - // The algorithm was originally designed by Ben Dyer for Libuavcan v0: - // https://github.com/UAVCAN/libuavcan/blob/legacy-v0/libuavcan/src/marshal/uc_bit_array_copy.cpp - // This version is modified for v1 where the bit order is the opposite. NUNAVUT_ASSERT(src != NULL); NUNAVUT_ASSERT(dst != NULL); NUNAVUT_ASSERT(src != dst); - NUNAVUT_ASSERT(((src < dst) \ - ? (src + ((src_offset_bit + length_bit + 4U) / 8U) <= dst) : 1)); - NUNAVUT_ASSERT(((src > dst) \ - ? (dst + ((dst_offset_bit + length_bit + 4U) / 8U) <= src) : 1)); - if ((0U == (length_bit % 8U)) && // - (0U == (src_offset_bit % 8U)) && // - (0U == (dst_offset_bit % 8U))) + if ((0U == (src_offset_bits % 8U)) && (0U == (dst_offset_bits % 8U))) // Aligned copy, optimized, most common case. { - // Intentional violation of MISRA: Pointer arithmetics. - // This is done to remove the API constraint that offsets be under 8 bits. - // Fewer constraints reduce the chance of API misuse. - (void) memcpy(dst + (dst_offset_bit / 8U), // NOSONAR NOLINT - src + (src_offset_bit / 8U), // NOSONAR - length_bit / 8U); + const size_t length_bytes = (size_t)(length_bits / 8U); + // Intentional violation of MISRA: Pointer arithmetics. This is done to remove the API constraint that + // offsets be under 8 bits. Fewer constraints reduce the chance of API misuse. + const uint8_t* const psrc = (src_offset_bits / 8U) + (const uint8_t*) src; // NOSONAR NOLINT + uint8_t* const pdst = (dst_offset_bits / 8U) + (uint8_t*) dst; // NOSONAR NOLINT + (void) memmove(pdst, psrc, length_bytes); + const uint8_t length_mod = (uint8_t)(length_bits % 8U); + if (0U != length_mod) // If the length is unaligned, the last byte requires special treatment. + { + // Intentional violation of MISRA: Pointer arithmetics. It is unavoidable in this context. + const uint8_t* const last_src = psrc + length_bytes; // NOLINT NOSONAR + uint8_t* const last_dst = pdst + length_bytes; // NOLINT NOSONAR + NUNAVUT_ASSERT(length_mod < 8U); + const uint8_t mask = (uint8_t)((1U << length_mod) - 1U); + *last_dst = (*last_dst & (uint8_t)~mask) | (*last_src & mask); + } } else { - uint32_t src_off = src_offset_bit; - uint32_t dst_off = dst_offset_bit; - const uint32_t last_bit = src_off + length_bit; + // The algorithm was originally designed by Ben Dyer for Libuavcan v0: + // https://github.com/UAVCAN/libuavcan/blob/legacy-v0/libuavcan/src/marshal/uc_bit_array_copy.cpp + // This version is modified for v1 where the bit order is the opposite. + const uint8_t* const psrc = (const uint8_t*) src; + uint8_t* const pdst = (uint8_t*) dst; + size_t src_off = src_offset_bits; + size_t dst_off = dst_offset_bits; + const size_t last_bit = src_off + length_bits; + NUNAVUT_ASSERT(((psrc < pdst) ? ((uintptr_t)(psrc + ((src_offset_bits + length_bits + 8U) / 8U)) <= (uintptr_t)pdst) : 1)); + NUNAVUT_ASSERT(((psrc > pdst) ? ((uintptr_t)(pdst + ((dst_offset_bits + length_bits + 8U) / 8U)) <= (uintptr_t)psrc) : 1)); while (last_bit > src_off) { const uint8_t src_mod = (uint8_t)(src_off % 8U); const uint8_t dst_mod = (uint8_t)(dst_off % 8U); const uint8_t max_mod = (src_mod > dst_mod) ? src_mod : dst_mod; - - const uint8_t size = (uint8_t) (((8U - max_mod) < (last_bit - src_off)) ? (8U - max_mod) : (last_bit - src_off)); + const uint8_t size = (uint8_t) nunavutChooseMin(8U - max_mod, last_bit - src_off); NUNAVUT_ASSERT(size > 0U); NUNAVUT_ASSERT(size <= 8U); - // Suppress a false warning from Clang-Tidy & Sonar that size is being over-shifted. It's not. const uint8_t mask = (uint8_t)((((1U << size) - 1U) << dst_mod) & 0xFFU); // NOLINT NOSONAR NUNAVUT_ASSERT(mask > 0U); - // Intentional violation of MISRA: indexing on a pointer. // This simplifies the implementation greatly and avoids pointer arithmetics. - const uint8_t in = (uint8_t)((uint8_t)(src[src_off / 8U] >> src_mod) << dst_mod) & - 0xFFU; // NOSONAR - + const uint8_t in = (uint8_t)((uint8_t)(psrc[src_off / 8U] >> src_mod) << dst_mod) & 0xFFU; // NOSONAR // Intentional violation of MISRA: indexing on a pointer. // This simplifies the implementation greatly and avoids pointer arithmetics. - const uint8_t a = dst[dst_off / 8U] & ((uint8_t) ~mask); // NOSONAR + const uint8_t a = pdst[dst_off / 8U] & ((uint8_t) ~mask); // NOSONAR const uint8_t b = in & mask; - // Intentional violation of MISRA: indexing on a pointer. // This simplifies the implementation greatly and avoids pointer arithmetics. - dst[dst_off / 8U] = a | b; // NOSONAR - + pdst[dst_off / 8U] = a | b; // NOSONAR src_off += size; dst_off += size; } @@ -132,154 +166,392 @@ static inline void nunavutCopyBits(const uint32_t length_bit, } } -// --------------------------------------------- PUBLIC API - INTEGER --------------------------------------------- +/// This function is intended for deserialization of contiguous sequences of zero-cost primitives. +/// It extracts (len_bits) bits that are offset by (off_bits) from the origin of (buf) whose size is (buf_size_bytes). +/// If the requested (len_bits+off_bits) overruns the buffer, the missing bits are implicitly zero-extended. +/// If (len_bits % 8 != 0), the output buffer is right-zero-padded up to the next byte boundary. +/// If (off_bits % 8 == 0), the operation is delegated to memmove(); otherwise, a much slower unaligned bit copy +/// algorithm is employed. See @ref nunavutCopyBits() for further details. +static inline void nunavutGetBits(void* const output, + const void* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const size_t len_bits) +{ + NUNAVUT_ASSERT(output != NULL); + NUNAVUT_ASSERT(buf != NULL); + const size_t sat_bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, len_bits); + // Apply implicit zero extension. Normally, this is a no-op unless (len_bits > sat_bits) or (len_bits % 8 != 0). + // The former case ensures that if we're copying <8 bits, the MSB in the destination will be zeroed out. + (void) memset(((uint8_t*)output) + (sat_bits / 8U), 0, ((len_bits + 7U) / 8U) - (sat_bits / 8U)); + nunavutCopyBits(output, 0U, sat_bits, buf, off_bits); +} -static inline void nunavutSetBit(uint8_t* const buf, const uint32_t off_bit, const bool value) +// ---------------------------------------------------- INTEGER ---------------------------------------------------- + +/// Serialize a DSDL field value at the specified bit offset from the beginning of the destination buffer. +/// The behavior is undefined if the input pointer is NULL. The time complexity is linear of the bit length. +/// One-bit-wide signed integers are processed without raising an error but the result is unspecified. +/// +/// Arguments: +/// buf Destination buffer where the result will be stored. +/// buf_size_bytes Size of the above, in bytes. +/// off_bits Offset, in bits, from the beginning of the buffer. May exceed one byte. +/// value The value itself (in case of integers it is promoted to 64-bit for unification). +/// len_bits Length of the serialized representation, in bits. Zero has no effect. Values >64 bit saturated. + +static inline int8_t nunavutSetBit( + uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const bool value) { NUNAVUT_ASSERT(buf != NULL); + if ((buf_size_bytes * 8) <= off_bits) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } const uint8_t val = value ? 1U : 0U; - nunavutCopyBits(1U, 0U, off_bit, &val, buf); + nunavutCopyBits(buf, off_bits, 1U, &val, 0U); + return NUNAVUT_SUCCESS; } -static inline void nunavutSetUxx(uint8_t* const buf, - const uint32_t off_bit, - const uint64_t value, - const uint8_t len_bit) +static inline int8_t nunavutSetUxx( + uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint64_t value, + const uint8_t len_bits) { static_assert(64U == (sizeof(uint64_t) * 8U), "Unexpected size of uint64_t"); NUNAVUT_ASSERT(buf != NULL); - const uint32_t saturated_len_bit = (((len_bit) < (64U)) ? (len_bit) : (64U)); - nunavutCopyBits(saturated_len_bit, 0U, off_bit, (const uint8_t*) &value, buf); + if ((buf_size_bytes * 8) < (off_bits + len_bits)) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + const size_t saturated_len_bits = nunavutChooseMin(len_bits, 64U); + nunavutCopyBits(buf, off_bits, saturated_len_bits, (const uint8_t*) &value, 0U); + return NUNAVUT_SUCCESS; } -static inline void nunavutSetIxx(uint8_t* const buf, - const uint32_t off_bit, - const int64_t value, - const uint8_t len_bit) +static inline int8_t nunavutSetIxx( + uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const int64_t value, + const uint8_t len_bits) { // The naive sign conversion is safe and portable according to the C standard: // 6.3.1.3.3: if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more // than the maximum value that can be represented in the new type until the value is in the range of the new type. - nunavutSetUxx(buf, off_bit, (uint64_t) value, len_bit); + return nunavutSetUxx(buf, buf_size_bytes, off_bits, (uint64_t) value, len_bits); } -static inline uint8_t nunavutGetU8(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit); +/// Deserialize a DSDL field value located at the specified bit offset from the beginning of the source buffer. +/// If the deserialized value extends beyond the end of the buffer, the missing bits are taken as zero, as required +/// by the DSDL specification (see Implicit Zero Extension Rule, IZER). +/// +/// If len_bits is greater than the return type, extra bits will be truncated per standard narrowing conversion rules. +/// If len_bits is shorter than the return type, missing bits will be zero per standard integer promotion rules. +/// Essentially, for integers, it would be enough to have 64-bit versions only; narrower variants exist only to avoid +/// narrowing type conversions of the result and for some performance gains. +/// +/// The behavior is undefined if the input pointer is NULL. The time complexity is linear of the bit length. +/// One-bit-wide signed integers are processed without raising an error but the result is unspecified. +/// +/// Arguments: +/// buf Source buffer where the serialized representation will be read from. +/// buf_size_bytes The size of the source buffer, in bytes. Reads past this limit will return zero bits. +/// off_bits Offset, in bits, from the beginning of the buffer. May exceed one byte. +/// len_bits Length of the serialized representation, in bits. Zero returns 0. Out-of-range values saturated. + +static inline uint8_t nunavutGetU8(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits); -static inline bool nunavutGetBit(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit) +static inline bool nunavutGetBit(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits) { - return 1U == nunavutGetU8(buf, buf_size, off_bit, 1U); + return 1U == nunavutGetU8(buf, buf_size_bytes, off_bits, 1U); } -static inline uint8_t nunavutGetU8(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline uint8_t nunavutGetU8(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { NUNAVUT_ASSERT(buf != NULL); - const uint32_t copy_size = - nunavutInternalGetBitCopySize(buf_size, off_bit, len_bit, 8U); - NUNAVUT_ASSERT(copy_size <= (sizeof(uint8_t) * 8U)); + const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 8U)); + NUNAVUT_ASSERT(bits <= (sizeof(uint8_t) * 8U)); uint8_t val = 0; - nunavutCopyBits(copy_size, off_bit, 0U, buf, &val); + nunavutCopyBits(&val, 0U, bits, buf, off_bits); return val; } -static inline uint16_t nunavutGetU16(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline uint16_t nunavutGetU16(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { NUNAVUT_ASSERT(buf != NULL); - const uint32_t copy_size = - nunavutInternalGetBitCopySize(buf_size, off_bit, len_bit, 16U); - NUNAVUT_ASSERT(copy_size <= (sizeof(uint16_t) * 8U)); + const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 16U)); + NUNAVUT_ASSERT(bits <= (sizeof(uint16_t) * 8U)); uint16_t val = 0U; - nunavutCopyBits(copy_size, off_bit, 0U, buf, (uint8_t*) &val); + nunavutCopyBits(&val, 0U, bits, buf, off_bits); return val; } -static inline uint32_t nunavutGetU32(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline uint32_t nunavutGetU32(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { NUNAVUT_ASSERT(buf != NULL); - const uint32_t copy_size = - nunavutInternalGetBitCopySize(buf_size, off_bit, len_bit, 32U); - NUNAVUT_ASSERT(copy_size <= (sizeof(uint32_t) * 8U)); + const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 32U)); + NUNAVUT_ASSERT(bits <= (sizeof(uint32_t) * 8U)); uint32_t val = 0U; - nunavutCopyBits(copy_size, off_bit, 0U, buf, (uint8_t*) &val); + nunavutCopyBits(&val, 0U, bits, buf, off_bits); return val; } -static inline uint64_t nunavutGetU64(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline uint64_t nunavutGetU64(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { NUNAVUT_ASSERT(buf != NULL); - const uint32_t copy_size = - nunavutInternalGetBitCopySize(buf_size, off_bit, len_bit, 64U); - NUNAVUT_ASSERT(copy_size <= (sizeof(uint64_t) * 8U)); + const size_t bits = nunavutSaturateBufferFragmentBitLength(buf_size_bytes, off_bits, nunavutChooseMin(len_bits, 64U)); + NUNAVUT_ASSERT(bits <= (sizeof(uint64_t) * 8U)); uint64_t val = 0U; - nunavutCopyBits(copy_size, off_bit, 0U, buf, (uint8_t*) &val); + nunavutCopyBits(&val, 0U, bits, buf, off_bits); return val; } -static inline int8_t nunavutGetI8(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline int8_t nunavutGetI8(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { - const uint8_t sat = (uint8_t) (((len_bit) < (8U)) ? (len_bit) : (8U)); - uint8_t val = nunavutGetU8(buf, buf_size, off_bit, sat); + const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 8U); + uint8_t val = nunavutGetU8(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); val = ((sat < 8U) && neg) ? (uint8_t)(val | ~((1U << sat) - 1U)) : val; // Sign extension return neg ? (int8_t)((-(int8_t)(uint8_t) ~val) - 1) : (int8_t) val; } -static inline int16_t nunavutGetI16(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline int16_t nunavutGetI16(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { - const uint8_t sat = (uint8_t) (((len_bit) < (16U)) ? (len_bit) : (16U)); - uint16_t val = nunavutGetU16(buf, buf_size, off_bit, sat); + const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 16U); + uint16_t val = nunavutGetU16(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); val = ((sat < 16U) && neg) ? (uint16_t)(val | ~((1U << sat) - 1U)) : val; // Sign extension return neg ? (int16_t)((-(int16_t)(uint16_t) ~val) - 1) : (int16_t) val; } -static inline int32_t nunavutGetI32(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline int32_t nunavutGetI32(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { - const uint8_t sat = (uint8_t) (((len_bit) < (32U)) ? (len_bit) : (32U)); - uint32_t val = nunavutGetU32(buf, buf_size, off_bit, sat); + const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 32U); + uint32_t val = nunavutGetU32(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); val = ((sat < 32U) && neg) ? (uint32_t)(val | ~((1UL << sat) - 1U)) : val; // Sign extension return neg ? (int32_t)((-(int32_t) ~val) - 1) : (int32_t) val; } -static inline int64_t nunavutGetI64(const uint8_t* const buf, - const size_t buf_size, - const uint32_t off_bit, - const uint8_t len_bit) +static inline int64_t nunavutGetI64(const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const uint8_t len_bits) { - const uint8_t sat = (uint8_t) (((len_bit) < (64U)) ? (len_bit) : (64U)); - uint64_t val = nunavutGetU64(buf, buf_size, off_bit, sat); + const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 64U); + uint64_t val = nunavutGetU64(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); val = ((sat < 64U) && neg) ? (uint64_t)(val | ~((1ULL << sat) - 1U)) : val; // Sign extension return neg ? (int64_t)((-(int64_t) ~val) - 1) : (int64_t) val; } -// --------------------------------------------- PUBLIC API - FLOAT16 ---------------------------------------------// --------------------------------------------- PUBLIC API - FLOAT32 ---------------------------------------------// --------------------------------------------- PUBLIC API - FLOAT64 ---------------------------------------------#ifdef __cplusplus +// ---------------------------------------------------- FLOAT16 ---------------------------------------------------- + +static_assert(NUNAVUT_PLATFORM_IEEE754_FLOAT, + "The target platform does not support IEEE754 floating point operations."); +static_assert(32U == (sizeof(float) * 8U), "Unsupported floating point model"); + +/// Converts a single-precision float into the binary representation of the value as a half-precision IEEE754 value. +static inline uint16_t nunavutFloat16Pack(const float value) +{ + typedef union // NOSONAR + { + uint32_t bits; + float real; + } Float32Bits; + + // The no-lint statements suppress the warning about the use of union. This is required for low-level bit access. + const uint32_t round_mask = ~(uint32_t) 0x0FFFU; + Float32Bits f32inf; // NOSONAR + Float32Bits f16inf; // NOSONAR + Float32Bits magic; // NOSONAR + Float32Bits in; // NOSONAR + f32inf.bits = ((uint32_t) 255U) << 23U; + f16inf.bits = ((uint32_t) 31U) << 23U; + magic.bits = ((uint32_t) 15U) << 23U; + in.real = value; + const uint32_t sign = in.bits & (((uint32_t) 1U) << 31U); + in.bits ^= sign; + uint16_t out = 0; + if (in.bits >= f32inf.bits) + { + if ((in.bits & 0x7FFFFFUL) != 0) + { + out = 0x7E00U; + } + else + { + out = (in.bits > f32inf.bits) ? (uint16_t) 0x7FFFU : (uint16_t) 0x7C00U; + } + } + else + { + in.bits &= round_mask; + in.real *= magic.real; + in.bits -= round_mask; + if (in.bits > f16inf.bits) + { + in.bits = f16inf.bits; + } + out = (uint16_t)(in.bits >> 13U); + } + out |= (uint16_t)(sign >> 16U); + return out; +} + +static inline float nunavutFloat16Unpack(const uint16_t value) +{ + typedef union // NOSONAR + { + uint32_t bits; + float real; + } Float32Bits; + + // The no-lint statements suppress the warning about the use of union. This is required for low-level bit access. + Float32Bits magic; // NOSONAR + Float32Bits inf_nan; // NOSONAR + Float32Bits out; // NOSONAR + magic.bits = ((uint32_t) 0xEFU) << 23U; + inf_nan.bits = ((uint32_t) 0x8FU) << 23U; + out.bits = ((uint32_t)(value & 0x7FFFU)) << 13U; + out.real *= magic.real; + if (out.real >= inf_nan.real) + { + out.bits |= ((uint32_t) 0xFFU) << 23U; + } + out.bits |= ((uint32_t)(value & 0x8000U)) << 16U; + return out.real; +} + +static inline int8_t nunavutSetF16( + uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const float value) +{ + return nunavutSetUxx(buf, buf_size_bytes, off_bits, nunavutFloat16Pack(value), 16U); } -#endif /* NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED */ +static inline float nunavutGetF16( + const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits) +{ + return nunavutFloat16Unpack(nunavutGetU16(buf, buf_size_bytes, off_bits, 16U)); +} + +// ---------------------------------------------------- FLOAT32 ---------------------------------------------------- + +static_assert(NUNAVUT_PLATFORM_IEEE754_FLOAT, + "The target platform does not support IEEE754 floating point operations."); +static_assert(32U == (sizeof(float) * 8U), "Unsupported floating point model"); + +static inline int8_t nunavutSetF32( + uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const float value) +{ + // Intentional violation of MISRA: use union to perform fast conversion from an IEEE 754-compatible native + // representation into a serializable integer. The assumptions about the target platform properties are made + // clear. In the future we may add a more generic conversion that is platform-invariant. + union // NOSONAR + { + float fl; + uint32_t in; + } const tmp = {value}; // NOSONAR + return nunavutSetUxx(buf, buf_size_bytes, off_bits, tmp.in, sizeof(tmp) * 8U); +} + +static inline float nunavutGetF32( + const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits) +{ + // Intentional violation of MISRA: use union to perform fast conversion to an IEEE 754-compatible native + // representation into a serializable integer. The assumptions about the target platform properties are made + // clear. In the future we may add a more generic conversion that is platform-invariant. + union // NOSONAR + { + uint32_t in; + float fl; + } const tmp = {nunavutGetU32(buf, buf_size_bytes, off_bits, 32U)}; + return tmp.fl; +} + +// ---------------------------------------------------- FLOAT64 ---------------------------------------------------- + +static_assert(NUNAVUT_PLATFORM_IEEE754_DOUBLE, + "The target platform does not support IEEE754 double-precision floating point operations."); +static_assert(64U == (sizeof(double) * 8U), "Unsupported floating point model"); + +static inline int8_t nunavutSetF64( + uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits, + const double value) +{ + // Intentional violation of MISRA: use union to perform fast conversion from an IEEE 754-compatible native + // representation into a serializable integer. The assumptions about the target platform properties are made + // clear. In the future we may add a more generic conversion that is platform-invariant. + union // NOSONAR + { + double fl; + uint64_t in; + } const tmp = {value}; // NOSONAR + return nunavutSetUxx(buf, buf_size_bytes, off_bits, tmp.in, sizeof(tmp) * 8U); +} + +static inline double nunavutGetF64( + const uint8_t* const buf, + const size_t buf_size_bytes, + const size_t off_bits) +{ + // Intentional violation of MISRA: use union to perform fast conversion to an IEEE 754-compatible native + // representation into a serializable integer. The assumptions about the target platform properties are made + // clear. In the future we may add a more generic conversion that is platform-invariant. + union // NOSONAR + { + uint64_t in; + double fl; + } const tmp = {nunavutGetU64(buf, buf_size_bytes, off_bits, 64U)}; + return tmp.fl; +} + +#ifdef __cplusplus +} +#endif + +#endif // NUNAVUT_SUPPORT_SERIALIZATION_H_INCLUDED diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp index 521f5892..5b255e56 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp @@ -38,7 +38,7 @@ constexpr CanardTransferKind Request::TRANSFER_KIND; Request::Request() { - uavcan_node_ExecuteCommand_1_0_Request_init(&data); + uavcan_node_ExecuteCommand_Request_1_0_initialize_(&data); } Request::Request(Request const & other) @@ -53,14 +53,19 @@ Request::Request(Request const & other) Request Request::create(CanardTransfer const & transfer) { Request r; - uavcan_node_ExecuteCommand_1_0_Request_deserialize(&r.data, 0, (uint8_t *)(transfer.payload), transfer.payload_size); + size_t inout_buffer_size_bytes = transfer.payload_size; + uavcan_node_ExecuteCommand_Request_1_0_deserialize_(&r.data, (uint8_t *)(transfer.payload), &inout_buffer_size_bytes); return r; } size_t Request::encode(uint8_t * payload) const { - size_t const offset = uavcan_node_ExecuteCommand_1_0_Request_serialize(&data, 0, payload); - return (offset / 8); + size_t inout_buffer_size_bytes = Request::MAX_PAYLOAD_SIZE; + + if (uavcan_node_ExecuteCommand_Request_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) + return 0; + else + return inout_buffer_size_bytes; } /************************************************************************************** diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.h b/src/types/uavcan/node/ExecuteCommand.1.0.Request.h index b5406ab1..55545757 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.h @@ -32,10 +32,10 @@ class Request public: - uavcan_node_ExecuteCommand_1_0_Request data; + uavcan_node_ExecuteCommand_Request_1_0 data; - static constexpr CanardPortID PORT_ID = uavcan_node_ExecuteCommand_1_0_FIXED_PORT_ID; - static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_ExecuteCommand_1_0_Request_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES; + static constexpr CanardPortID PORT_ID = uavcan_node_ExecuteCommand_1_0_FIXED_PORT_ID_; + static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_ExecuteCommand_Request_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; static constexpr CanardTransferKind TRANSFER_KIND = CanardTransferKindRequest; Request(); diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp index ab75cd1b..83953bc7 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp @@ -34,7 +34,7 @@ constexpr CanardTransferKind Response::TRANSFER_KIND; Response::Response() { - uavcan_node_ExecuteCommand_1_0_Response_init(&data); + uavcan_node_ExecuteCommand_Response_1_0_initialize_(&data); } Response::Response(Response const & other) @@ -49,14 +49,19 @@ Response::Response(Response const & other) Response Response::create(CanardTransfer const & transfer) { Response r; - uavcan_node_ExecuteCommand_1_0_Response_deserialize(&r.data, 0, (uint8_t *)(transfer.payload), transfer.payload_size); + size_t inout_buffer_size_bytes = transfer.payload_size; + uavcan_node_ExecuteCommand_Response_1_0_deserialize_(&r.data, (uint8_t *)(transfer.payload), &inout_buffer_size_bytes); return r; } size_t Response::encode(uint8_t * payload) const { - size_t const offset = uavcan_node_ExecuteCommand_1_0_Response_serialize(&data, 0, payload); - return (offset / 8); + size_t inout_buffer_size_bytes = Response::MAX_PAYLOAD_SIZE; + + if (uavcan_node_ExecuteCommand_Response_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) + return 0; + else + return inout_buffer_size_bytes; } void Response::operator = (Status const status) diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.h b/src/types/uavcan/node/ExecuteCommand.1.0.Response.h index 74325b2d..6611b0a2 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.h @@ -34,19 +34,19 @@ class Response enum class Status : uint8_t { - SUCCESS = 0, - FAILURE = 1, - NOT_AUTHORIZED = 2, - BAD_COMMAND = 3, - BAD_PARAMETER = 4, - BAD_STATE = 5, - INTERNAL_ERROR = 6, + SUCCESS = uavcan_node_ExecuteCommand_Response_1_0_STATUS_SUCCESS, + FAILURE = uavcan_node_ExecuteCommand_Response_1_0_STATUS_FAILURE, + NOT_AUTHORIZED = uavcan_node_ExecuteCommand_Response_1_0_STATUS_NOT_AUTHORIZED, + BAD_COMMAND = uavcan_node_ExecuteCommand_Response_1_0_STATUS_BAD_COMMAND, + BAD_PARAMETER = uavcan_node_ExecuteCommand_Response_1_0_STATUS_BAD_PARAMETER, + BAD_STATE = uavcan_node_ExecuteCommand_Response_1_0_STATUS_BAD_STATE, + INTERNAL_ERROR = uavcan_node_ExecuteCommand_Response_1_0_STATUS_INTERNAL_ERROR, }; - uavcan_node_ExecuteCommand_1_0_Response data; + uavcan_node_ExecuteCommand_Response_1_0 data; - static constexpr CanardPortID PORT_ID = uavcan_node_ExecuteCommand_1_0_FIXED_PORT_ID; - static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_ExecuteCommand_1_0_Response_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES; + static constexpr CanardPortID PORT_ID = uavcan_node_ExecuteCommand_1_0_FIXED_PORT_ID_; + static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_ExecuteCommand_Response_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; static constexpr CanardTransferKind TRANSFER_KIND = CanardTransferKindResponse; Response(); diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.nnvg.h b/src/types/uavcan/node/ExecuteCommand.1.0.nnvg.h index 35c7f5c2..8d47554b 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.nnvg.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.nnvg.h @@ -1,28 +1,30 @@ -// UAVCAN data structure definition. +-+ +-+ -// | | | | -// AUTOGENERATED, DO NOT EDIT. \ - / -// --- -// o -// +------------------------------------------------------------------------------------------------------------------+ +// This is an AUTO-GENERATED UAVCAN DSDL data type implementation. Curious? See https://uavcan.org. +// You shouldn't attempt to edit this file. // -// Generator: -// nunavut-0.3.9 (serialization was enabled) +// Checking this file under version control is not recommended unless it is used as part of a high-SIL +// safety-critical codebase. The typical usage scenario is to generate it as part of the build process. // +// To avoid conflicts with definitions given in the source DSDL file, all entities created by the code generator +// are named with an underscore at the end, like foo_bar_(). // -// Source File: -// /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/435.ExecuteCommand.1.0.uavcan -// -// Template: -// ServiceType.j2 -// -// Generated at: 2020-09-15 13:51:27.537809 UTC -// Is deprecated: no -// Fixed port ID: 435 +// Generator: nunavut-0.5.1 (serialization was enabled) +// Source file: /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/435.ExecuteCommand.1.0.uavcan +// Generated at: 2020-11-11 05:20:32.234712 UTC +// Is deprecated: yes +// Fixed port-ID: 435 // Full name: uavcan.node.ExecuteCommand // Version: 1.0 +// _____ ______ _____ _____ ______ _____ _______ ______ _____ +// | __ `| ____| __ `| __ `| ____/ ____| /`|__ __| ____| __ ` +// | | | | |__ | |__) | |__) | |__ | | / ` | | | |__ | | | | +// | | | | __| | ___/| _ /| __|| | / /` ` | | | __| | | | | +// | |__| | |____| | | | ` `| |___| |____ / ____ `| | | |____| |__| | +// |_____/|______|_| |_| `_`______`_____/_/ `_`_| |______|_____/ +// +// WARNING: this data type is deprecated and is nearing the end of its life cycle. Seek replacement. -#ifndef UAVCAN_NODE_EXECUTE_COMMAND_1_0_INCLUDED -#define UAVCAN_NODE_EXECUTE_COMMAND_1_0_INCLUDED +#ifndef UAVCAN_NODE_EXECUTE_COMMAND_1_0_INCLUDED_ +#define UAVCAN_NODE_EXECUTE_COMMAND_1_0_INCLUDED_ #include #include @@ -30,232 +32,386 @@ #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ -// +------------------------------------------------------------------------------------------------------------------+ -// | Begin service definitions for uavcan_node_ExecuteCommand_1_0 -// +------------------------------------------------------------------------------------------------------------------+ - -#define uavcan_node_ExecuteCommand_1_0_Request_COMMAND_RESTART (65535) -#define uavcan_node_ExecuteCommand_1_0_Request_COMMAND_POWER_OFF (65534) -#define uavcan_node_ExecuteCommand_1_0_Request_COMMAND_BEGIN_SOFTWARE_UPDATE (65533) -#define uavcan_node_ExecuteCommand_1_0_Request_COMMAND_FACTORY_RESET (65532) -#define uavcan_node_ExecuteCommand_1_0_Request_COMMAND_EMERGENCY_STOP (65531) -#define uavcan_node_ExecuteCommand_1_0_Request_COMMAND_STORE_PERSISTENT_STATES (65530) +#endif + +#define uavcan_node_ExecuteCommand_1_0_HAS_FIXED_PORT_ID_ true +#define uavcan_node_ExecuteCommand_1_0_FIXED_PORT_ID_ 435U + +#define uavcan_node_ExecuteCommand_1_0_FULL_NAME_ "uavcan.node.ExecuteCommand" +#define uavcan_node_ExecuteCommand_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.ExecuteCommand.1.0" + +#define uavcan_node_ExecuteCommand_Request_1_0_FULL_NAME_ "uavcan.node.ExecuteCommand.Request" +#define uavcan_node_ExecuteCommand_Request_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.ExecuteCommand.Request.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_ExecuteCommand_Request_1_0_EXTENT_BYTES_ 300UL +#define uavcan_node_ExecuteCommand_Request_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 115UL +static_assert(uavcan_node_ExecuteCommand_Request_1_0_EXTENT_BYTES_ >= uavcan_node_ExecuteCommand_Request_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); + +/// saturated uint16 COMMAND_RESTART = 65535 +#define uavcan_node_ExecuteCommand_Request_1_0_COMMAND_RESTART (65535U) +/// saturated uint16 COMMAND_POWER_OFF = 65534 +#define uavcan_node_ExecuteCommand_Request_1_0_COMMAND_POWER_OFF (65534U) +/// saturated uint16 COMMAND_BEGIN_SOFTWARE_UPDATE = 65533 +#define uavcan_node_ExecuteCommand_Request_1_0_COMMAND_BEGIN_SOFTWARE_UPDATE (65533U) +/// saturated uint16 COMMAND_FACTORY_RESET = 65532 +#define uavcan_node_ExecuteCommand_Request_1_0_COMMAND_FACTORY_RESET (65532U) +/// saturated uint16 COMMAND_EMERGENCY_STOP = 65531 +#define uavcan_node_ExecuteCommand_Request_1_0_COMMAND_EMERGENCY_STOP (65531U) +/// saturated uint16 COMMAND_STORE_PERSISTENT_STATES = 65530 +#define uavcan_node_ExecuteCommand_Request_1_0_COMMAND_STORE_PERSISTENT_STATES (65530U) + +/// Array metadata for: saturated uint8[<=112] parameter +#define uavcan_node_ExecuteCommand_Request_1_0_parameter_ARRAY_CAPACITY_ 112U +#define uavcan_node_ExecuteCommand_Request_1_0_parameter_ARRAY_IS_VARIABLE_LENGTH_ true typedef struct { + /// saturated uint16 command uint16_t command; - size_t parameter_length; - uint8_t parameter[112]; -} uavcan_node_ExecuteCommand_1_0_Request; -/// The maximum capacity of uavcan_node_ExecuteCommand_1_0_Request.parameter. -static inline size_t uavcan_node_ExecuteCommand_1_0_Request_parameter_array_capacity(void) + /// saturated uint8[<=112] parameter + struct /// Array address equivalence guarantee: &elements[0] == ¶meter + { + uint8_t elements[112]; + size_t count; + } parameter; +} uavcan_node_ExecuteCommand_Request_1_0; + +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. +/// +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_ExecuteCommand_Request_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_ExecuteCommand_Request_1_0_serialize_( + const uavcan_node_ExecuteCommand_Request_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - return (112U); -} + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } -/// If true then uavcan_node_ExecuteCommand_1_0_Request.parameter's length can vary otherwise it will -/// always be the same as its capacity. -static inline bool uavcan_node_ExecuteCommand_1_0_Request_parameter_array_is_variable_length(void) -{ - return (true); -} + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 920UL) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + + { // saturated uint16 command + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 16ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + (void) memmove(&buffer[offset_bits / 8U], &obj->command, 2U); + offset_bits += 16U; + } -/// If uavcan_node_ExecuteCommand_1_0_Request.parameter is a variable length array then this is the current -/// number of items populated in the array starting from index 0. If parameter is not a variable length array -/// then this is the same as its capacity. -static inline size_t uavcan_node_ExecuteCommand_1_0_Request_parameter_array_length( - const uavcan_node_ExecuteCommand_1_0_Request* const instance) -{ - return (instance == NULL) ? 0 : instance->parameter_length; + { // saturated uint8[<=112] parameter + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 904ULL) <= (capacity_bytes * 8U)); + if (obj->parameter.count > 112) + { + return -NUNAVUT_ERROR_REPRESENTATION_BAD_ARRAY_LENGTH; + } + // Array length prefix: truncated uint8 + buffer[offset_bits / 8U] = (uint8_t)(obj->parameter.count); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 8U; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + // Optimization prospect: this item is aligned at the byte boundary, so it is possible to use memmove(). + nunavutCopyBits(&buffer[0], offset_bits, obj->parameter.count * 8U, &obj->parameter.elements[0], 0U); + offset_bits += obj->parameter.count * 8U; + } + + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. + { + const uint8_t _pad0_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad0_ > 0); + const int8_t _err0_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad0_); // Optimize? + if (_err0_ < 0) + { + return _err0_; + } + offset_bits += _pad0_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + + NUNAVUT_ASSERT(offset_bits >= 24ULL); + NUNAVUT_ASSERT(offset_bits <= 920ULL); + + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); + + return NUNAVUT_SUCCESS; } -/// Initialize an uavcan_node_ExecuteCommand_1_0_Request instance to default values. -/// No memory is allocated within this method. -/// Does nothing if @ref out_instance is NULL. +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. +/// +/// @param obj The object to update from the provided serialized representation. +/// +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. /// -/// @param out_instance A structure instance to Initialize. -static inline void uavcan_node_ExecuteCommand_1_0_Request_init(uavcan_node_ExecuteCommand_1_0_Request* const out_instance) +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_ExecuteCommand_Request_1_0_deserialize_( + uavcan_node_ExecuteCommand_Request_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - if (out_instance != NULL) + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) { - out_instance->command = 0; - out_instance->parameter_length = 0; + return -NUNAVUT_ERROR_INVALID_ARGUMENT; } -} -/// Contains the maximum number of bytes needed to serialize a(n) uavcan_node_ExecuteCommand_1_0_Request -/// instance. -#define uavcan_node_ExecuteCommand_1_0_Request_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES \ - (115U) + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; + // saturated uint16 command + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + out_obj->command = nunavutGetU16(&buffer[0], capacity_bytes, offset_bits, 16); + offset_bits += 16U; -// +------------------------------------------------------------------------------------------------------------------+ + // saturated uint8[<=112] parameter + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + // Array length prefix: truncated uint8 + if ((offset_bits + 8U) <= capacity_bits) + { + out_obj->parameter.count = buffer[offset_bits / 8U] & 255U; + } + else + { + out_obj->parameter.count = 0U; + } + offset_bits += 8U; + if (out_obj->parameter.count > 112) + { + return -NUNAVUT_ERROR_REPRESENTATION_BAD_ARRAY_LENGTH; + } + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + nunavutGetBits(&out_obj->parameter.elements[0], &buffer[0], capacity_bytes, offset_bits, out_obj->parameter.count * 8U); + offset_bits += out_obj->parameter.count * 8U; -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_SUCCESS (0) -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_FAILURE (1) -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_NOT_AUTHORIZED (2) -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_BAD_COMMAND (3) -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_BAD_PARAMETER (4) -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_BAD_STATE (5) -#define uavcan_node_ExecuteCommand_1_0_Response_STATUS_INTERNAL_ERROR (6) + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); -typedef struct -{ - uint8_t status; -} uavcan_node_ExecuteCommand_1_0_Response; + return NUNAVUT_SUCCESS; +} -/// Initialize an uavcan_node_ExecuteCommand_1_0_Response instance to default values. -/// No memory is allocated within this method. -/// Does nothing if @ref out_instance is NULL. -/// -/// @param out_instance A structure instance to Initialize. -static inline void uavcan_node_ExecuteCommand_1_0_Response_init(uavcan_node_ExecuteCommand_1_0_Response* const out_instance) +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_ExecuteCommand_Request_1_0_initialize_(uavcan_node_ExecuteCommand_Request_1_0* const out_obj) { - if (out_instance != NULL) + if (out_obj != NULL) { - out_instance->status = 0; - /* Ignoring 48 padding bit(s). */ + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_ExecuteCommand_Request_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; } } -/// Contains the maximum number of bytes needed to serialize a(n) uavcan_node_ExecuteCommand_1_0_Response -/// instance. -#define uavcan_node_ExecuteCommand_1_0_Response_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES \ - (7U) - - +#define uavcan_node_ExecuteCommand_Response_1_0_FULL_NAME_ "uavcan.node.ExecuteCommand.Response" +#define uavcan_node_ExecuteCommand_Response_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.ExecuteCommand.Response.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_ExecuteCommand_Response_1_0_EXTENT_BYTES_ 48UL +#define uavcan_node_ExecuteCommand_Response_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 1UL +static_assert(uavcan_node_ExecuteCommand_Response_1_0_EXTENT_BYTES_ >= uavcan_node_ExecuteCommand_Response_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); + +/// saturated uint8 STATUS_SUCCESS = 0 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_SUCCESS (0U) +/// saturated uint8 STATUS_FAILURE = 1 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_FAILURE (1U) +/// saturated uint8 STATUS_NOT_AUTHORIZED = 2 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_NOT_AUTHORIZED (2U) +/// saturated uint8 STATUS_BAD_COMMAND = 3 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_BAD_COMMAND (3U) +/// saturated uint8 STATUS_BAD_PARAMETER = 4 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_BAD_PARAMETER (4U) +/// saturated uint8 STATUS_BAD_STATE = 5 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_BAD_STATE (5U) +/// saturated uint8 STATUS_INTERNAL_ERROR = 6 +#define uavcan_node_ExecuteCommand_Response_1_0_STATUS_INTERNAL_ERROR (6U) -/// uavcan_node_ExecuteCommand_1_0 can only be used with the fixed port identifier defined here. -#define uavcan_node_ExecuteCommand_1_0_FIXED_PORT_ID (435U) +typedef struct +{ + /// saturated uint8 status + uint8_t status; +} uavcan_node_ExecuteCommand_Response_1_0; -/// Serialization of uavcan_node_ExecuteCommand_1_0_Request instance into a byte array. +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. /// -/// @param in_instance A structure instance to serialize. -/// @param offset Offset (in bits) of data type in message. -/// @param out_buffer Output message buffer. +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_ExecuteCommand_Response_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ /// -/// @returns Size of encoded data (in bits) -static inline int32_t uavcan_node_ExecuteCommand_1_0_Request_serialize(const uavcan_node_ExecuteCommand_1_0_Request* in_instance, uint32_t offset, uint8_t* const out_buffer) +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_ExecuteCommand_Response_1_0_serialize_( + const uavcan_node_ExecuteCommand_Response_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - - if (out_buffer == NULL) + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) { - return -NUNAVUT_ERR_INVALID_BUF; + return -NUNAVUT_ERROR_INVALID_ARGUMENT; } - // Begin Structure: saturated uint16 - nunavutSetUxx(out_buffer, offset, in_instance->command, 16); - offset += 16; - // End Structure: saturated uint16 - // Begin Structure: saturated uint8[<=112] - if (in_instance->parameter_length > 112) + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 8UL) { - return -NUNAVUT_ERR_INVALID_LEN; + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + + { // saturated uint8 status + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 8ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + buffer[offset_bits / 8U] = (uint8_t)(obj->status); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 8U; } - nunavutSetUxx(out_buffer, offset, in_instance->parameter_length, 8); - offset += 8; - memcpy((void*)((uint8_t*)out_buffer + (offset>> 3)), (void*)in_instance->parameter, in_instance->parameter_length * 1); - offset += (uint32_t)(in_instance->parameter_length * 8); - // End Structure: saturated uint8[<=112] - - return offset; -} - -/// Deserialization of byte-array encoding into uavcan_node_ExecuteCommand_1_0_Request instance. -/// -/// @param out_instance Structure instance to write data to. -/// @param offset Bit offset of structure in data buffer. -/// @param in_buffer Message buffer to deserialize. -/// @param buf_size_bytes Length of input buffer (in bytes) to avoid overflow. -/// -/// @returns Size of decoded data (in bits) -static inline int32_t uavcan_node_ExecuteCommand_1_0_Request_deserialize(uavcan_node_ExecuteCommand_1_0_Request* const out_instance, uint32_t offset, uint8_t* const in_buffer, const size_t buf_size_bytes) -{ - if (in_buffer == NULL) + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. { - return -NUNAVUT_ERR_INVALID_BUF; + const uint8_t _pad1_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad1_ > 0); + const int8_t _err1_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad1_); // Optimize? + if (_err1_ < 0) + { + return _err1_; + } + offset_bits += _pad1_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. - // Begin Structure: saturated uint16 - out_instance->command = nunavutGetU16(in_buffer, buf_size_bytes, offset, 16); - offset += 16; - // End Structure: saturated uint16 - // Begin Structure: saturated uint8[<=112] - out_instance->parameter_length = nunavutGetU8(in_buffer, buf_size_bytes, offset, 8);; - offset += 8; - if (out_instance->parameter_length > 112) - { - return -NUNAVUT_ERR_INVALID_LEN; - }memcpy((void*)out_instance->parameter, (void*)((uint8_t*)in_buffer + (offset >> 3)), out_instance->parameter_length * 1); - offset += (uint32_t)out_instance->parameter_length * 8; - // End Structure: saturated uint8[<=112] + NUNAVUT_ASSERT(offset_bits == 8ULL); - return offset; -} + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); + return NUNAVUT_SUCCESS; +} -/// Serialization of uavcan_node_ExecuteCommand_1_0_Response instance into a byte array. +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. +/// +/// @param obj The object to update from the provided serialized representation. +/// +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. /// -/// @param in_instance A structure instance to serialize. -/// @param offset Offset (in bits) of data type in message. -/// @param out_buffer Output message buffer. +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. /// -/// @returns Size of encoded data (in bits) -static inline int32_t uavcan_node_ExecuteCommand_1_0_Response_serialize(const uavcan_node_ExecuteCommand_1_0_Response* in_instance, uint32_t offset, uint8_t* const out_buffer) +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_ExecuteCommand_Response_1_0_deserialize_( + uavcan_node_ExecuteCommand_Response_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } - if (out_buffer == NULL) + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; + + // saturated uint8 status + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + if ((offset_bits + 8U) <= capacity_bits) + { + out_obj->status = buffer[offset_bits / 8U] & 255U; + } + else { - return -NUNAVUT_ERR_INVALID_BUF; + out_obj->status = 0U; } + offset_bits += 8U; - // Begin Structure: saturated uint8 - nunavutSetUxx(out_buffer, offset, in_instance->status, 8); - offset += 8; - // End Structure: saturated uint8 - // Begin Structure: void48 - offset += 48; - // End Structure: void48 + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); - return offset; + return NUNAVUT_SUCCESS; } -/// Deserialization of byte-array encoding into uavcan_node_ExecuteCommand_1_0_Response instance. -/// -/// @param out_instance Structure instance to write data to. -/// @param offset Bit offset of structure in data buffer. -/// @param in_buffer Message buffer to deserialize. -/// @param buf_size_bytes Length of input buffer (in bytes) to avoid overflow. -/// -/// @returns Size of decoded data (in bits) -static inline int32_t uavcan_node_ExecuteCommand_1_0_Response_deserialize(uavcan_node_ExecuteCommand_1_0_Response* const out_instance, uint32_t offset, uint8_t* const in_buffer, const size_t buf_size_bytes) +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_ExecuteCommand_Response_1_0_initialize_(uavcan_node_ExecuteCommand_Response_1_0* const out_obj) { - - if (in_buffer == NULL) + if (out_obj != NULL) { - return -NUNAVUT_ERR_INVALID_BUF; + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_ExecuteCommand_Response_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; } - - // Begin Structure: saturated uint8 - out_instance->status = nunavutGetU8(in_buffer, buf_size_bytes, offset, 8); - offset += 8; - // End Structure: saturated uint8 - // Begin Structure: void48 - offset += 48; - // End Structure: void48 - - return offset; } - -// +------------------------------------------------------------------------------------------------------------------+ -// | End service definitions for uavcan_node_ExecuteCommand_1_0 -// +------------------------------------------------------------------------------------------------------------------+ - #ifdef __cplusplus -} /* End extern "C" */ -#endif /* __cplusplus */ -#endif /* UAVCAN_NODE_EXECUTE_COMMAND_1_0_INCLUDED */ +} +#endif +#endif // UAVCAN_NODE_EXECUTE_COMMAND_1_0_INCLUDED_ diff --git a/src/types/uavcan/node/Health_1_0.nnvg.h b/src/types/uavcan/node/Health_1_0.nnvg.h new file mode 100644 index 00000000..66377cc7 --- /dev/null +++ b/src/types/uavcan/node/Health_1_0.nnvg.h @@ -0,0 +1,199 @@ +// This is an AUTO-GENERATED UAVCAN DSDL data type implementation. Curious? See https://uavcan.org. +// You shouldn't attempt to edit this file. +// +// Checking this file under version control is not recommended unless it is used as part of a high-SIL +// safety-critical codebase. The typical usage scenario is to generate it as part of the build process. +// +// To avoid conflicts with definitions given in the source DSDL file, all entities created by the code generator +// are named with an underscore at the end, like foo_bar_(). +// +// Generator: nunavut-0.5.1 (serialization was enabled) +// Source file: /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/Health.1.0.uavcan +// Generated at: 2020-11-11 05:20:33.126672 UTC +// Is deprecated: no +// Fixed port-ID: None +// Full name: uavcan.node.Health +// Version: 1.0 + +#ifndef UAVCAN_NODE_HEALTH_1_0_INCLUDED_ +#define UAVCAN_NODE_HEALTH_1_0_INCLUDED_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// This type does not have a fixed port-ID. See https://forum.uavcan.org/t/choosing-message-and-service-ids/889 +#define uavcan_node_Health_1_0_HAS_FIXED_PORT_ID_ false + +#define uavcan_node_Health_1_0_FULL_NAME_ "uavcan.node.Health" +#define uavcan_node_Health_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.Health.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_Health_1_0_EXTENT_BYTES_ 1UL +#define uavcan_node_Health_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 1UL +static_assert(uavcan_node_Health_1_0_EXTENT_BYTES_ >= uavcan_node_Health_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); + +/// saturated uint2 NOMINAL = 0 +#define uavcan_node_Health_1_0_NOMINAL (0U) +/// saturated uint2 ADVISORY = 1 +#define uavcan_node_Health_1_0_ADVISORY (1U) +/// saturated uint2 CAUTION = 2 +#define uavcan_node_Health_1_0_CAUTION (2U) +/// saturated uint2 WARNING = 3 +#define uavcan_node_Health_1_0_WARNING (3U) + +typedef struct +{ + /// saturated uint2 value + uint8_t value; +} uavcan_node_Health_1_0; + +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. +/// +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_Health_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Health_1_0_serialize_( + const uavcan_node_Health_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) +{ + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } + + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 8UL) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + + { // saturated uint2 value + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 2ULL) <= (capacity_bytes * 8U)); + uint8_t _sat0_ = obj->value; + if (_sat0_ > 3U) + { + _sat0_ = 3U; + } + buffer[offset_bits / 8U] = (uint8_t)(_sat0_); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 2U; + } + + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. + { + const uint8_t _pad0_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad0_ > 0); + const int8_t _err0_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad0_); // Optimize? + if (_err0_ < 0) + { + return _err0_; + } + offset_bits += _pad0_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + + NUNAVUT_ASSERT(offset_bits == 8ULL); + + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); + + return NUNAVUT_SUCCESS; +} + +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. +/// +/// @param obj The object to update from the provided serialized representation. +/// +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Health_1_0_deserialize_( + uavcan_node_Health_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) +{ + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } + + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; + + // saturated uint2 value + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + if ((offset_bits + 2U) <= capacity_bits) + { + out_obj->value = buffer[offset_bits / 8U] & 3U; + } + else + { + out_obj->value = 0U; + } + offset_bits += 2U; + + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); + + return NUNAVUT_SUCCESS; +} + +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_Health_1_0_initialize_(uavcan_node_Health_1_0* const out_obj) +{ + if (out_obj != NULL) + { + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_Health_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; + } +} + +#ifdef __cplusplus +} +#endif +#endif // UAVCAN_NODE_HEALTH_1_0_INCLUDED_ diff --git a/src/types/uavcan/node/Heartbeat.1.0.cpp b/src/types/uavcan/node/Heartbeat.1.0.cpp index cf4ce5bf..7c6731eb 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.cpp +++ b/src/types/uavcan/node/Heartbeat.1.0.cpp @@ -27,7 +27,7 @@ constexpr CanardTransferKind Heartbeat_1_0::TRANSFER_KIND; Heartbeat_1_0::Heartbeat_1_0() { - uavcan_node_Heartbeat_1_0_init(&data); + uavcan_node_Heartbeat_1_0_initialize_(&data); } Heartbeat_1_0::Heartbeat_1_0(Heartbeat_1_0 const & other) @@ -42,22 +42,27 @@ Heartbeat_1_0::Heartbeat_1_0(Heartbeat_1_0 const & other) Heartbeat_1_0 Heartbeat_1_0::create(CanardTransfer const & transfer) { Heartbeat_1_0 h; - uavcan_node_Heartbeat_1_0_deserialize(&h.data, 0, (uint8_t *)(transfer.payload), transfer.payload_size); + size_t inout_buffer_size_bytes = transfer.payload_size; + uavcan_node_Heartbeat_1_0_deserialize_(&h.data, (uint8_t *)(transfer.payload), &inout_buffer_size_bytes); return h; } size_t Heartbeat_1_0::encode(uint8_t * payload) const { - size_t const offset = uavcan_node_Heartbeat_1_0_serialize(&data, 0, payload); - return (offset / 8); + size_t inout_buffer_size_bytes = Heartbeat_1_0::MAX_PAYLOAD_SIZE; + + if (uavcan_node_Heartbeat_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) + return 0; + else + return inout_buffer_size_bytes; } void Heartbeat_1_0::operator = (Health const health) { - data.health = arduino::_107_::uavcan::to_integer(health); + data.health.value = arduino::_107_::uavcan::to_integer(health); } void Heartbeat_1_0::operator = (Mode const mode) { - data.mode = arduino::_107_::uavcan::to_integer(mode); + data.mode.value = arduino::_107_::uavcan::to_integer(mode); } diff --git a/src/types/uavcan/node/Heartbeat.1.0.h b/src/types/uavcan/node/Heartbeat.1.0.h index 034e507c..e747b8ba 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.h +++ b/src/types/uavcan/node/Heartbeat.1.0.h @@ -27,25 +27,24 @@ class Heartbeat_1_0 enum class Health : uint8_t { - NOMINAL = uavcan_node_Heartbeat_1_0_HEALTH_NOMINAL, - ADVISORY = uavcan_node_Heartbeat_1_0_HEALTH_ADVISORY, - CAUTION = uavcan_node_Heartbeat_1_0_HEALTH_CAUTION, - WARNING = uavcan_node_Heartbeat_1_0_HEALTH_WARNING, + NOMINAL = uavcan_node_Health_1_0_NOMINAL, + ADVISORY = uavcan_node_Health_1_0_ADVISORY, + CAUTION = uavcan_node_Health_1_0_CAUTION, + WARNING = uavcan_node_Health_1_0_WARNING, }; enum class Mode : uint8_t { - OPERATIONAL = uavcan_node_Heartbeat_1_0_MODE_OPERATIONAL, - INITIALIZATION = uavcan_node_Heartbeat_1_0_MODE_INITIALIZATION, - MAINTENANCE = uavcan_node_Heartbeat_1_0_MODE_MAINTENANCE, - SOFTWARE_UPDATE = uavcan_node_Heartbeat_1_0_MODE_SOFTWARE_UPDATE, - OFFLINE = uavcan_node_Heartbeat_1_0_MODE_OFFLINE, + OPERATIONAL = uavcan_node_Mode_1_0_OPERATIONAL, + INITIALIZATION = uavcan_node_Mode_1_0_INITIALIZATION, + MAINTENANCE = uavcan_node_Mode_1_0_MAINTENANCE, + SOFTWARE_UPDATE = uavcan_node_Mode_1_0_SOFTWARE_UPDATE, }; uavcan_node_Heartbeat_1_0 data; - static constexpr CanardPortID PORT_ID = uavcan_node_Heartbeat_1_0_FIXED_PORT_ID; - static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_Heartbeat_1_0_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES; + static constexpr CanardPortID PORT_ID = uavcan_node_Heartbeat_1_0_FIXED_PORT_ID_; + static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_Health_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; static constexpr CanardTransferKind TRANSFER_KIND = CanardTransferKindMessage; Heartbeat_1_0(); diff --git a/src/types/uavcan/node/Heartbeat.1.0.nnvg.h b/src/types/uavcan/node/Heartbeat.1.0.nnvg.h index 72eab8a2..ae45be4c 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.nnvg.h +++ b/src/types/uavcan/node/Heartbeat.1.0.nnvg.h @@ -1,155 +1,314 @@ -// UAVCAN data structure definition. +-+ +-+ -// | | | | -// AUTOGENERATED, DO NOT EDIT. \ - / -// --- -// o -// +------------------------------------------------------------------------------------------------------------------+ +// This is an AUTO-GENERATED UAVCAN DSDL data type implementation. Curious? See https://uavcan.org. +// You shouldn't attempt to edit this file. // -// Generator: -// nunavut-0.3.9 (serialization was enabled) +// Checking this file under version control is not recommended unless it is used as part of a high-SIL +// safety-critical codebase. The typical usage scenario is to generate it as part of the build process. // +// To avoid conflicts with definitions given in the source DSDL file, all entities created by the code generator +// are named with an underscore at the end, like foo_bar_(). // -// Source File: -// /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/32085.Heartbeat.1.0.uavcan -// -// Template: -// StructureType.j2 -// -// Generated at: 2020-09-15 12:16:39.449037 UTC +// Generator: nunavut-0.5.1 (serialization was enabled) +// Source file: /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/7509.Heartbeat.1.0.uavcan +// Generated at: 2020-11-11 05:20:33.286763 UTC // Is deprecated: no -// Fixed port ID: 32085 +// Fixed port-ID: 7509 // Full name: uavcan.node.Heartbeat // Version: 1.0 -#ifndef UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED -#define UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED +#ifndef UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED_ +#define UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED_ #include +#include "Health_1_0.nnvg.h" +#include "Mode_1_0.nnvg.h" #include #include #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ - -#define uavcan_node_Heartbeat_1_0_MAX_PUBLICATION_PERIOD (1) -#define uavcan_node_Heartbeat_1_0_OFFLINE_TIMEOUT (3) -#define uavcan_node_Heartbeat_1_0_HEALTH_NOMINAL (0) -#define uavcan_node_Heartbeat_1_0_HEALTH_ADVISORY (1) -#define uavcan_node_Heartbeat_1_0_HEALTH_CAUTION (2) -#define uavcan_node_Heartbeat_1_0_HEALTH_WARNING (3) -#define uavcan_node_Heartbeat_1_0_MODE_OPERATIONAL (0) -#define uavcan_node_Heartbeat_1_0_MODE_INITIALIZATION (1) -#define uavcan_node_Heartbeat_1_0_MODE_MAINTENANCE (2) -#define uavcan_node_Heartbeat_1_0_MODE_SOFTWARE_UPDATE (3) -#define uavcan_node_Heartbeat_1_0_MODE_OFFLINE (7) +#endif + +#define uavcan_node_Heartbeat_1_0_HAS_FIXED_PORT_ID_ true +#define uavcan_node_Heartbeat_1_0_FIXED_PORT_ID_ 7509U + +#define uavcan_node_Heartbeat_1_0_FULL_NAME_ "uavcan.node.Heartbeat" +#define uavcan_node_Heartbeat_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.Heartbeat.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_Heartbeat_1_0_EXTENT_BYTES_ 12UL +#define uavcan_node_Heartbeat_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 7UL +static_assert(uavcan_node_Heartbeat_1_0_EXTENT_BYTES_ >= uavcan_node_Heartbeat_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); + +/// saturated uint16 MAX_PUBLICATION_PERIOD = 1 +#define uavcan_node_Heartbeat_1_0_MAX_PUBLICATION_PERIOD (1U) +/// saturated uint16 OFFLINE_TIMEOUT = 3 +#define uavcan_node_Heartbeat_1_0_OFFLINE_TIMEOUT (3U) typedef struct { + /// saturated uint32 uptime uint32_t uptime; - uint8_t health; - uint8_t mode; - uint32_t vendor_specific_status_code; + + /// uavcan.node.Health.1.0 health + uavcan_node_Health_1_0 health; + + /// uavcan.node.Mode.1.0 mode + uavcan_node_Mode_1_0 mode; + + /// saturated uint8 vendor_specific_status_code + uint8_t vendor_specific_status_code; } uavcan_node_Heartbeat_1_0; -/// Initialize an uavcan_node_Heartbeat_1_0 instance to default values. -/// No memory is allocated within this method. -/// Does nothing if @ref out_instance is NULL. +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. +/// +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_Heartbeat_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ /// -/// @param out_instance A structure instance to Initialize. -static inline void uavcan_node_Heartbeat_1_0_init(uavcan_node_Heartbeat_1_0* const out_instance) +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Heartbeat_1_0_serialize_( + const uavcan_node_Heartbeat_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - if (out_instance != NULL) + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) { - out_instance->uptime = 0; - out_instance->health = 0; - out_instance->mode = 0; - out_instance->vendor_specific_status_code = 0; + return -NUNAVUT_ERROR_INVALID_ARGUMENT; } -} -/// Contains the maximum number of bytes needed to serialize a(n) uavcan_node_Heartbeat_1_0 -/// instance. -#define uavcan_node_Heartbeat_1_0_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES \ - (7U) + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 56UL) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + { // saturated uint32 uptime + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 32ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + (void) memmove(&buffer[offset_bits / 8U], &obj->uptime, 4U); + offset_bits += 32U; + } + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. + { + const uint8_t _pad0_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad0_ > 0); + const int8_t _err0_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad0_); // Optimize? + if (_err0_ < 0) + { + return _err0_; + } + offset_bits += _pad0_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + } -/// uavcan_node_Heartbeat_1_0 can only be used with the fixed port identifier defined here. -#define uavcan_node_Heartbeat_1_0_FIXED_PORT_ID (32085U) -/// Serialization of uavcan_node_Heartbeat_1_0 instance into a byte array. -/// -/// @param in_instance A structure instance to serialize. -/// @param offset Offset (in bits) of data type in message. -/// @param out_buffer Output message buffer. -/// -/// @returns Size of encoded data (in bits) -static inline int32_t uavcan_node_Heartbeat_1_0_serialize(const uavcan_node_Heartbeat_1_0* in_instance, uint32_t offset, uint8_t* const out_buffer) -{ + { // uavcan.node.Health.1.0 health + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 8ULL) <= (capacity_bytes * 8U)); + size_t _size_bytes0_ = 1UL; // Nested object (max) size, in bytes. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits / 8U + _size_bytes0_) <= capacity_bytes); + int8_t _err1_ = uavcan_node_Health_1_0_serialize_( + &obj->health, &buffer[offset_bits / 8U], &_size_bytes0_); + if (_err1_ < 0) + { + return _err1_; + } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + NUNAVUT_ASSERT((_size_bytes0_ * 8U) == 8ULL); + offset_bits += _size_bytes0_ * 8U; // Advance by the size of the nested object. + NUNAVUT_ASSERT(offset_bits <= (capacity_bytes * 8U)); + } + + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. + { + const uint8_t _pad1_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad1_ > 0); + const int8_t _err2_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad1_); // Optimize? + if (_err2_ < 0) + { + return _err2_; + } + offset_bits += _pad1_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + } + + { // uavcan.node.Mode.1.0 mode + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 8ULL) <= (capacity_bytes * 8U)); + size_t _size_bytes1_ = 1UL; // Nested object (max) size, in bytes. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits / 8U + _size_bytes1_) <= capacity_bytes); + int8_t _err3_ = uavcan_node_Mode_1_0_serialize_( + &obj->mode, &buffer[offset_bits / 8U], &_size_bytes1_); + if (_err3_ < 0) + { + return _err3_; + } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + NUNAVUT_ASSERT((_size_bytes1_ * 8U) == 8ULL); + offset_bits += _size_bytes1_ * 8U; // Advance by the size of the nested object. + NUNAVUT_ASSERT(offset_bits <= (capacity_bytes * 8U)); + } - if (out_buffer == NULL) + { // saturated uint8 vendor_specific_status_code + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 8ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + buffer[offset_bits / 8U] = (uint8_t)(obj->vendor_specific_status_code); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 8U; + } + + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. { - return -NUNAVUT_ERR_INVALID_BUF; + const uint8_t _pad2_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad2_ > 0); + const int8_t _err4_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad2_); // Optimize? + if (_err4_ < 0) + { + return _err4_; + } + offset_bits += _pad2_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + + NUNAVUT_ASSERT(offset_bits == 56ULL); + + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); - // Begin Structure: saturated uint32 - nunavutSetUxx(out_buffer, offset, in_instance->uptime, 32); - offset += 32; - // End Structure: saturated uint32 - // Begin Structure: truncated uint2 - nunavutSetUxx(out_buffer, offset, in_instance->health, 2); - offset += 2; - // End Structure: truncated uint2 - // Begin Structure: truncated uint3 - nunavutSetUxx(out_buffer, offset, in_instance->mode, 3); - offset += 3; - // End Structure: truncated uint3 - // Begin Structure: truncated uint19 - nunavutSetUxx(out_buffer, offset, in_instance->vendor_specific_status_code, 19); - offset += 19; - // End Structure: truncated uint19 - - return offset; + return NUNAVUT_SUCCESS; } -/// Deserialization of byte-array encoding into uavcan_node_Heartbeat_1_0 instance. +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. /// -/// @param out_instance Structure instance to write data to. -/// @param offset Bit offset of structure in data buffer. -/// @param in_buffer Message buffer to deserialize. -/// @param buf_size_bytes Length of input buffer (in bytes) to avoid overflow. +/// @param obj The object to update from the provided serialized representation. /// -/// @returns Size of decoded data (in bits) -static inline int32_t uavcan_node_Heartbeat_1_0_deserialize(uavcan_node_Heartbeat_1_0* const out_instance, uint32_t offset, uint8_t* const in_buffer, const size_t buf_size_bytes) +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Heartbeat_1_0_deserialize_( + uavcan_node_Heartbeat_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } + + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; + + // saturated uint32 uptime + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + out_obj->uptime = nunavutGetU32(&buffer[0], capacity_bytes, offset_bits, 32); + offset_bits += 32U; + + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. - if (in_buffer == NULL) + // uavcan.node.Health.1.0 health + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT(offset_bits % 8U == 0U); { - return -NUNAVUT_ERR_INVALID_BUF; + size_t _size_bytes2_ = (size_t)(capacity_bytes - nunavutChooseMin((offset_bits / 8U), capacity_bytes)); + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + const int8_t _err5_ = uavcan_node_Health_1_0_deserialize_( + &out_obj->health, &buffer[offset_bits / 8U], &_size_bytes2_); + if (_err5_ < 0) + { + return _err5_; + } + offset_bits += _size_bytes2_ * 8U; // Advance by the size of the nested serialized representation. } - // Begin Structure: saturated uint32 - out_instance->uptime = nunavutGetU32(in_buffer, buf_size_bytes, offset, 32); - offset += 32; - // End Structure: saturated uint32 - // Begin Structure: truncated uint2 - out_instance->health = nunavutGetU8(in_buffer, buf_size_bytes, offset, 2); - offset += 2; - // End Structure: truncated uint2 - // Begin Structure: truncated uint3 - out_instance->mode = nunavutGetU8(in_buffer, buf_size_bytes, offset, 3); - offset += 3; - // End Structure: truncated uint3 - // Begin Structure: truncated uint19 - out_instance->vendor_specific_status_code = nunavutGetU32(in_buffer, buf_size_bytes, offset, 19); - offset += 19; - // End Structure: truncated uint19 - - return offset; + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + + // uavcan.node.Mode.1.0 mode + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + { + size_t _size_bytes3_ = (size_t)(capacity_bytes - nunavutChooseMin((offset_bits / 8U), capacity_bytes)); + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + const int8_t _err6_ = uavcan_node_Mode_1_0_deserialize_( + &out_obj->mode, &buffer[offset_bits / 8U], &_size_bytes3_); + if (_err6_ < 0) + { + return _err6_; + } + offset_bits += _size_bytes3_ * 8U; // Advance by the size of the nested serialized representation. + } + + // saturated uint8 vendor_specific_status_code + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + if ((offset_bits + 8U) <= capacity_bits) + { + out_obj->vendor_specific_status_code = buffer[offset_bits / 8U] & 255U; + } + else + { + out_obj->vendor_specific_status_code = 0U; + } + offset_bits += 8U; + + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); + + return NUNAVUT_SUCCESS; } +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_Heartbeat_1_0_initialize_(uavcan_node_Heartbeat_1_0* const out_obj) +{ + if (out_obj != NULL) + { + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_Heartbeat_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; + } +} #ifdef __cplusplus -} /* End extern "C" */ -#endif /* __cplusplus */ -#endif /* UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED */ +} +#endif +#endif // UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED_ + diff --git a/src/types/uavcan/node/ID.1.0.hpp b/src/types/uavcan/node/ID.1.0.hpp index 9a910e58..37b15ca3 100644 --- a/src/types/uavcan/node/ID.1.0.hpp +++ b/src/types/uavcan/node/ID.1.0.hpp @@ -29,7 +29,7 @@ class ID_1_0 uavcan_node_ID_1_0 data; static constexpr CanardPortID PORT_ID = ID; - static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_ID_1_0_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES; + static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_ID_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; static constexpr CanardTransferKind TRANSFER_KIND = CanardTransferKindMessage; ID_1_0(); diff --git a/src/types/uavcan/node/ID.1.0.ipp b/src/types/uavcan/node/ID.1.0.ipp index 8c63f2b6..14bbdc19 100644 --- a/src/types/uavcan/node/ID.1.0.ipp +++ b/src/types/uavcan/node/ID.1.0.ipp @@ -20,7 +20,7 @@ template constexpr CanardTransferKind ID_1_0::TRANSFER_KIN template ID_1_0::ID_1_0() { - uavcan_node_ID_1_0_init(&data); + uavcan_node_ID_1_0_initialize_(&data); } template @@ -37,13 +37,18 @@ template ID_1_0 ID_1_0::create(CanardTransfer const & transfer) { ID_1_0 i; - uavcan_node_ID_1_0_deserialize(&i.data, 0, (uint8_t *)(transfer.payload), transfer.payload_size); + size_t inout_buffer_size_bytes = transfer.payload_size; + uavcan_node_ID_1_0_deserialize_(&i.data, (uint8_t *)(transfer.payload), &inout_buffer_size_bytes); return i; } template size_t ID_1_0::encode(uint8_t * payload) const { - size_t const offset = uavcan_node_ID_1_0_serialize(&data, 0, payload); - return (offset / 8); + size_t inout_buffer_size_bytes = ID_1_0::MAX_PAYLOAD_SIZE; + + if (uavcan_node_ID_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) + return 0; + else + return inout_buffer_size_bytes; } diff --git a/src/types/uavcan/node/ID.1.0.nnvg.h b/src/types/uavcan/node/ID.1.0.nnvg.h index 86ae011b..d945f6ff 100644 --- a/src/types/uavcan/node/ID.1.0.nnvg.h +++ b/src/types/uavcan/node/ID.1.0.nnvg.h @@ -1,28 +1,22 @@ -// UAVCAN data structure definition. +-+ +-+ -// | | | | -// AUTOGENERATED, DO NOT EDIT. \ - / -// --- -// o -// +------------------------------------------------------------------------------------------------------------------+ +// This is an AUTO-GENERATED UAVCAN DSDL data type implementation. Curious? See https://uavcan.org. +// You shouldn't attempt to edit this file. // -// Generator: -// nunavut-0.3.9 (serialization was enabled) +// Checking this file under version control is not recommended unless it is used as part of a high-SIL +// safety-critical codebase. The typical usage scenario is to generate it as part of the build process. // +// To avoid conflicts with definitions given in the source DSDL file, all entities created by the code generator +// are named with an underscore at the end, like foo_bar_(). // -// Source File: -// /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/ID.1.0.uavcan -// -// Template: -// StructureType.j2 -// -// Generated at: 2020-09-15 13:51:27.934955 UTC +// Generator: nunavut-0.5.1 (serialization was enabled) +// Source file: /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/ID.1.0.uavcan +// Generated at: 2020-11-11 05:20:33.457377 UTC // Is deprecated: no -// Fixed port ID: None +// Fixed port-ID: None // Full name: uavcan.node.ID // Version: 1.0 -#ifndef UAVCAN_NODE_ID_1_0_INCLUDED -#define UAVCAN_NODE_ID_1_0_INCLUDED +#ifndef UAVCAN_NODE_ID_1_0_INCLUDED_ +#define UAVCAN_NODE_ID_1_0_INCLUDED_ #include #include @@ -30,82 +24,156 @@ #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ - +#endif + +/// This type does not have a fixed port-ID. See https://forum.uavcan.org/t/choosing-message-and-service-ids/889 +#define uavcan_node_ID_1_0_HAS_FIXED_PORT_ID_ false + +#define uavcan_node_ID_1_0_FULL_NAME_ "uavcan.node.ID" +#define uavcan_node_ID_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.ID.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_ID_1_0_EXTENT_BYTES_ 2UL +#define uavcan_node_ID_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 2UL +static_assert(uavcan_node_ID_1_0_EXTENT_BYTES_ >= uavcan_node_ID_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); typedef struct { + /// saturated uint16 value uint16_t value; } uavcan_node_ID_1_0; -/// Initialize an uavcan_node_ID_1_0 instance to default values. -/// No memory is allocated within this method. -/// Does nothing if @ref out_instance is NULL. +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. /// -/// @param out_instance A structure instance to Initialize. -static inline void uavcan_node_ID_1_0_init(uavcan_node_ID_1_0* const out_instance) +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_ID_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_ID_1_0_serialize_( + const uavcan_node_ID_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - if (out_instance != NULL) + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) { - out_instance->value = 0; + return -NUNAVUT_ERROR_INVALID_ARGUMENT; } -} -/// Contains the maximum number of bytes needed to serialize a(n) uavcan_node_ID_1_0 -/// instance. -#define uavcan_node_ID_1_0_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES \ - (2U) - - -/// Serialization of uavcan_node_ID_1_0 instance into a byte array. -/// -/// @param in_instance A structure instance to serialize. -/// @param offset Offset (in bits) of data type in message. -/// @param out_buffer Output message buffer. -/// -/// @returns Size of encoded data (in bits) -static inline int32_t uavcan_node_ID_1_0_serialize(const uavcan_node_ID_1_0* in_instance, uint32_t offset, uint8_t* const out_buffer) -{ + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 16UL) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + + { // saturated uint16 value + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 16ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + (void) memmove(&buffer[offset_bits / 8U], &obj->value, 2U); + offset_bits += 16U; + } - if (out_buffer == NULL) + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. { - return -NUNAVUT_ERR_INVALID_BUF; + const uint8_t _pad0_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad0_ > 0); + const int8_t _err0_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad0_); // Optimize? + if (_err0_ < 0) + { + return _err0_; + } + offset_bits += _pad0_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. - // Begin Structure: saturated uint16 - nunavutSetUxx(out_buffer, offset, in_instance->value, 16); - offset += 16; - // End Structure: saturated uint16 + NUNAVUT_ASSERT(offset_bits == 16ULL); - return offset; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); + + return NUNAVUT_SUCCESS; } -/// Deserialization of byte-array encoding into uavcan_node_ID_1_0 instance. +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. +/// +/// @param obj The object to update from the provided serialized representation. /// -/// @param out_instance Structure instance to write data to. -/// @param offset Bit offset of structure in data buffer. -/// @param in_buffer Message buffer to deserialize. -/// @param buf_size_bytes Length of input buffer (in bytes) to avoid overflow. +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. /// -/// @returns Size of decoded data (in bits) -static inline int32_t uavcan_node_ID_1_0_deserialize(uavcan_node_ID_1_0* const out_instance, uint32_t offset, uint8_t* const in_buffer, const size_t buf_size_bytes) +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_ID_1_0_deserialize_( + uavcan_node_ID_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - - if (in_buffer == NULL) + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) { - return -NUNAVUT_ERR_INVALID_BUF; + return -NUNAVUT_ERROR_INVALID_ARGUMENT; } - // Begin Structure: saturated uint16 - out_instance->value = nunavutGetU16(in_buffer, buf_size_bytes, offset, 16); - offset += 16; - // End Structure: saturated uint16 + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; + + // saturated uint16 value + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + out_obj->value = nunavutGetU16(&buffer[0], capacity_bytes, offset_bits, 16); + offset_bits += 16U; - return offset; + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); + + return NUNAVUT_SUCCESS; } +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_ID_1_0_initialize_(uavcan_node_ID_1_0* const out_obj) +{ + if (out_obj != NULL) + { + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_ID_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; + } +} #ifdef __cplusplus -} /* End extern "C" */ -#endif /* __cplusplus */ -#endif /* UAVCAN_NODE_ID_1_0_INCLUDED */ +} +#endif +#endif // UAVCAN_NODE_ID_1_0_INCLUDED_ diff --git a/src/types/uavcan/node/Mode_1_0.nnvg.h b/src/types/uavcan/node/Mode_1_0.nnvg.h new file mode 100644 index 00000000..781e115b --- /dev/null +++ b/src/types/uavcan/node/Mode_1_0.nnvg.h @@ -0,0 +1,199 @@ +// This is an AUTO-GENERATED UAVCAN DSDL data type implementation. Curious? See https://uavcan.org. +// You shouldn't attempt to edit this file. +// +// Checking this file under version control is not recommended unless it is used as part of a high-SIL +// safety-critical codebase. The typical usage scenario is to generate it as part of the build process. +// +// To avoid conflicts with definitions given in the source DSDL file, all entities created by the code generator +// are named with an underscore at the end, like foo_bar_(). +// +// Generator: nunavut-0.5.1 (serialization was enabled) +// Source file: /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/Mode.1.0.uavcan +// Generated at: 2020-11-11 05:20:33.776711 UTC +// Is deprecated: no +// Fixed port-ID: None +// Full name: uavcan.node.Mode +// Version: 1.0 + +#ifndef UAVCAN_NODE_MODE_1_0_INCLUDED_ +#define UAVCAN_NODE_MODE_1_0_INCLUDED_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// This type does not have a fixed port-ID. See https://forum.uavcan.org/t/choosing-message-and-service-ids/889 +#define uavcan_node_Mode_1_0_HAS_FIXED_PORT_ID_ false + +#define uavcan_node_Mode_1_0_FULL_NAME_ "uavcan.node.Mode" +#define uavcan_node_Mode_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.Mode.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_Mode_1_0_EXTENT_BYTES_ 1UL +#define uavcan_node_Mode_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 1UL +static_assert(uavcan_node_Mode_1_0_EXTENT_BYTES_ >= uavcan_node_Mode_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); + +/// saturated uint3 OPERATIONAL = 0 +#define uavcan_node_Mode_1_0_OPERATIONAL (0U) +/// saturated uint3 INITIALIZATION = 1 +#define uavcan_node_Mode_1_0_INITIALIZATION (1U) +/// saturated uint3 MAINTENANCE = 2 +#define uavcan_node_Mode_1_0_MAINTENANCE (2U) +/// saturated uint3 SOFTWARE_UPDATE = 3 +#define uavcan_node_Mode_1_0_SOFTWARE_UPDATE (3U) + +typedef struct +{ + /// saturated uint3 value + uint8_t value; +} uavcan_node_Mode_1_0; + +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. +/// +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_Mode_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Mode_1_0_serialize_( + const uavcan_node_Mode_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) +{ + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } + + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 8UL) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + + { // saturated uint3 value + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 3ULL) <= (capacity_bytes * 8U)); + uint8_t _sat0_ = obj->value; + if (_sat0_ > 7U) + { + _sat0_ = 7U; + } + buffer[offset_bits / 8U] = (uint8_t)(_sat0_); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 3U; + } + + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. + { + const uint8_t _pad0_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad0_ > 0); + const int8_t _err0_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad0_); // Optimize? + if (_err0_ < 0) + { + return _err0_; + } + offset_bits += _pad0_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + + NUNAVUT_ASSERT(offset_bits == 8ULL); + + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); + + return NUNAVUT_SUCCESS; +} + +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. +/// +/// @param obj The object to update from the provided serialized representation. +/// +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Mode_1_0_deserialize_( + uavcan_node_Mode_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) +{ + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } + + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; + + // saturated uint3 value + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + if ((offset_bits + 3U) <= capacity_bits) + { + out_obj->value = buffer[offset_bits / 8U] & 7U; + } + else + { + out_obj->value = 0U; + } + offset_bits += 3U; + + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); + + return NUNAVUT_SUCCESS; +} + +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_Mode_1_0_initialize_(uavcan_node_Mode_1_0* const out_obj) +{ + if (out_obj != NULL) + { + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_Mode_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; + } +} + +#ifdef __cplusplus +} +#endif +#endif // UAVCAN_NODE_MODE_1_0_INCLUDED_ diff --git a/src/types/uavcan/node/Version.1.0.hpp b/src/types/uavcan/node/Version.1.0.hpp index bce6f5e4..1b4cf163 100644 --- a/src/types/uavcan/node/Version.1.0.hpp +++ b/src/types/uavcan/node/Version.1.0.hpp @@ -29,7 +29,7 @@ class Version_1_0 uavcan_node_Version_1_0 data; static constexpr CanardPortID PORT_ID = ID; - static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_Version_1_0_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES; + static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_Version_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; static constexpr CanardTransferKind TRANSFER_KIND = CanardTransferKindMessage; Version_1_0(); diff --git a/src/types/uavcan/node/Version.1.0.ipp b/src/types/uavcan/node/Version.1.0.ipp index f837b5cc..a138a733 100644 --- a/src/types/uavcan/node/Version.1.0.ipp +++ b/src/types/uavcan/node/Version.1.0.ipp @@ -20,7 +20,7 @@ template constexpr CanardTransferKind Version_1_0::TRANSFE template Version_1_0::Version_1_0() { - uavcan_node_Version_1_0_init(&data); + uavcan_node_Version_1_0_initialize_(&data); } template @@ -37,13 +37,18 @@ template Version_1_0 Version_1_0::create(CanardTransfer const & transfer) { Version_1_0 v; - uavcan_node_Version_1_0_deserialize(&v.data, 0, (uint8_t *)(transfer.payload), transfer.payload_size); + size_t inout_buffer_size_bytes = transfer.payload_size; + uavcan_node_Version_1_0_deserialize_(&v.data, (uint8_t *)(transfer.payload), &inout_buffer_size_bytes); return v; } template size_t Version_1_0::encode(uint8_t * payload) const { - size_t const offset = uavcan_node_Version_1_0_serialize(&data, 0, payload); - return (offset / 8); + size_t inout_buffer_size_bytes = Version_1_0::MAX_PAYLOAD_SIZE; + + if (uavcan_node_Version_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) + return 0; + else + return inout_buffer_size_bytes; } diff --git a/src/types/uavcan/node/Version.1.0.nnvg.h b/src/types/uavcan/node/Version.1.0.nnvg.h index 89d72b10..84240a05 100644 --- a/src/types/uavcan/node/Version.1.0.nnvg.h +++ b/src/types/uavcan/node/Version.1.0.nnvg.h @@ -1,28 +1,22 @@ -// UAVCAN data structure definition. +-+ +-+ -// | | | | -// AUTOGENERATED, DO NOT EDIT. \ - / -// --- -// o -// +------------------------------------------------------------------------------------------------------------------+ +// This is an AUTO-GENERATED UAVCAN DSDL data type implementation. Curious? See https://uavcan.org. +// You shouldn't attempt to edit this file. // -// Generator: -// nunavut-0.3.9 (serialization was enabled) +// Checking this file under version control is not recommended unless it is used as part of a high-SIL +// safety-critical codebase. The typical usage scenario is to generate it as part of the build process. // +// To avoid conflicts with definitions given in the source DSDL file, all entities created by the code generator +// are named with an underscore at the end, like foo_bar_(). // -// Source File: -// /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/Version.1.0.uavcan -// -// Template: -// StructureType.j2 -// -// Generated at: 2020-09-15 13:51:28.069217 UTC +// Generator: nunavut-0.5.1 (serialization was enabled) +// Source file: /home/alex/projects/107-systems/public_regulated_data_types/uavcan/node/Version.1.0.uavcan +// Generated at: 2020-11-11 05:20:33.936346 UTC // Is deprecated: no -// Fixed port ID: None +// Fixed port-ID: None // Full name: uavcan.node.Version // Version: 1.0 -#ifndef UAVCAN_NODE_VERSION_1_0_INCLUDED -#define UAVCAN_NODE_VERSION_1_0_INCLUDED +#ifndef UAVCAN_NODE_VERSION_1_0_INCLUDED_ +#define UAVCAN_NODE_VERSION_1_0_INCLUDED_ #include #include @@ -30,92 +24,186 @@ #ifdef __cplusplus extern "C" { -#endif /* __cplusplus */ - +#endif + +/// This type does not have a fixed port-ID. See https://forum.uavcan.org/t/choosing-message-and-service-ids/889 +#define uavcan_node_Version_1_0_HAS_FIXED_PORT_ID_ false + +#define uavcan_node_Version_1_0_FULL_NAME_ "uavcan.node.Version" +#define uavcan_node_Version_1_0_FULL_NAME_AND_VERSION_ "uavcan.node.Version.1.0" + +/// Extent is the minimum amount of memory required to hold any serialized representation of any compatible +/// version of the data type; or, on other words, it is the the maximum possible size of received objects of this type. +/// The size is specified in bytes (rather than bits) because by definition, extent is an integer number of bytes long. +/// When allocating a deserialization (RX) buffer for this data type, it should be at least extent bytes large. +/// When allocating a serialization (TX) buffer, it is safe to use the size of the largest serialized representation +/// instead of the extent because it provides a tighter bound of the object size; it is safe because the concrete type +/// is always known during serialization (unlike deserialization). If not sure, use extent everywhere. +#define uavcan_node_Version_1_0_EXTENT_BYTES_ 2UL +#define uavcan_node_Version_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ 2UL +static_assert(uavcan_node_Version_1_0_EXTENT_BYTES_ >= uavcan_node_Version_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_, + "Internal constraint violation"); typedef struct { + /// saturated uint8 major uint8_t major; + + /// saturated uint8 minor uint8_t minor; } uavcan_node_Version_1_0; -/// Initialize an uavcan_node_Version_1_0 instance to default values. -/// No memory is allocated within this method. -/// Does nothing if @ref out_instance is NULL. +/// Serialize an instance into the provided buffer. +/// The lifetime of the resulting serialized representation is independent of the original instance. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original object where possible. +/// +/// @param obj The object to serialize. /// -/// @param out_instance A structure instance to Initialize. -static inline void uavcan_node_Version_1_0_init(uavcan_node_Version_1_0* const out_instance) +/// @param buffer The destination buffer. There are no alignment requirements. +/// @see uavcan_node_Version_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_ +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the buffer in bytes. +/// Upon return this value will be updated with the size of the constructed serialized +/// representation (in bytes); this value is then to be passed over to the transport +/// layer. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Version_1_0_serialize_( + const uavcan_node_Version_1_0* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { - if (out_instance != NULL) + if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) { - out_instance->major = 0; - out_instance->minor = 0; + return -NUNAVUT_ERROR_INVALID_ARGUMENT; } -} - -/// Contains the maximum number of bytes needed to serialize a(n) uavcan_node_Version_1_0 -/// instance. -#define uavcan_node_Version_1_0_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES \ - (2U) + const size_t capacity_bytes = *inout_buffer_size_bytes; + if ((8U * (size_t) capacity_bytes) < 16UL) + { + return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL; + } + // Notice that fields that are not an integer number of bytes long may overrun the space allocated for them + // in the serialization buffer up to the next byte boundary. This is by design and is guaranteed to be safe. + size_t offset_bits = 0U; + + { // saturated uint8 major + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 8ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + buffer[offset_bits / 8U] = (uint8_t)(obj->major); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 8U; + } -/// Serialization of uavcan_node_Version_1_0 instance into a byte array. -/// -/// @param in_instance A structure instance to serialize. -/// @param offset Offset (in bits) of data type in message. -/// @param out_buffer Output message buffer. -/// -/// @returns Size of encoded data (in bits) -static inline int32_t uavcan_node_Version_1_0_serialize(const uavcan_node_Version_1_0* in_instance, uint32_t offset, uint8_t* const out_buffer) -{ + { // saturated uint8 minor + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + NUNAVUT_ASSERT((offset_bits + 8ULL) <= (capacity_bytes * 8U)); + // Saturation code not emitted -- native representation matches the serialized representation. + buffer[offset_bits / 8U] = (uint8_t)(obj->minor); // C std, 6.3.1.3 Signed and unsigned integers + offset_bits += 8U; + } - if (out_buffer == NULL) + if (offset_bits % 8U != 0U) // Pad to 8 bits. TODO: Eliminate redundant padding checks. { - return -NUNAVUT_ERR_INVALID_BUF; + const uint8_t _pad0_ = (uint8_t)(8U - offset_bits % 8U); + NUNAVUT_ASSERT(_pad0_ > 0); + const int8_t _err0_ = nunavutSetUxx(&buffer[0], capacity_bytes, offset_bits, 0U, _pad0_); // Optimize? + if (_err0_ < 0) + { + return _err0_; + } + offset_bits += _pad0_; + NUNAVUT_ASSERT(offset_bits % 8U == 0U); } + // It is assumed that we know the exact type of the serialized entity, hence we expect the size to match. + + NUNAVUT_ASSERT(offset_bits == 16ULL); - // Begin Structure: saturated uint8 - nunavutSetUxx(out_buffer, offset, in_instance->major, 8); - offset += 8; - // End Structure: saturated uint8 - // Begin Structure: saturated uint8 - nunavutSetUxx(out_buffer, offset, in_instance->minor, 8); - offset += 8; - // End Structure: saturated uint8 + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (offset_bits / 8U); - return offset; + return NUNAVUT_SUCCESS; } -/// Deserialization of byte-array encoding into uavcan_node_Version_1_0 instance. +/// Deserialize an instance from the provided buffer. +/// The lifetime of the resulting object is independent of the original buffer. +/// This method may be slow for large objects (e.g., images, point clouds, radar samples), so in a later revision +/// we may define a zero-copy alternative that keeps references to the original buffer where possible. /// -/// @param out_instance Structure instance to write data to. -/// @param offset Bit offset of structure in data buffer. -/// @param in_buffer Message buffer to deserialize. -/// @param buf_size_bytes Length of input buffer (in bytes) to avoid overflow. +/// @param obj The object to update from the provided serialized representation. /// -/// @returns Size of decoded data (in bits) -static inline int32_t uavcan_node_Version_1_0_deserialize(uavcan_node_Version_1_0* const out_instance, uint32_t offset, uint8_t* const in_buffer, const size_t buf_size_bytes) +/// @param buffer The source buffer containing the serialized representation. There are no alignment requirements. +/// If the buffer is shorter or longer than expected, it will be implicitly zero-extended or truncated, +/// respectively; see Specification for "implicit zero extension" and "implicit truncation" rules. +/// +/// @param inout_buffer_size_bytes When calling, this is a pointer to the size of the supplied serialized +/// representation, in bytes. Upon return this value will be updated with the +/// size of the consumed fragment of the serialized representation (in bytes), +/// which may be smaller due to the implicit truncation rule, but it is guaranteed +/// to never exceed the original buffer size even if the implicit zero extension rule +/// was activated. In case of error this value is undefined. +/// +/// @returns Negative on error, zero on success. +static inline int8_t uavcan_node_Version_1_0_deserialize_( + uavcan_node_Version_1_0* const out_obj, const uint8_t* const buffer, size_t* const inout_buffer_size_bytes) { + if ((out_obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL)) + { + return -NUNAVUT_ERROR_INVALID_ARGUMENT; + } + + const size_t capacity_bytes = *inout_buffer_size_bytes; + const size_t capacity_bits = capacity_bytes * (size_t) 8U; + size_t offset_bits = 0U; - if (in_buffer == NULL) + // saturated uint8 major + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + if ((offset_bits + 8U) <= capacity_bits) { - return -NUNAVUT_ERR_INVALID_BUF; + out_obj->major = buffer[offset_bits / 8U] & 255U; } + else + { + out_obj->major = 0U; + } + offset_bits += 8U; - // Begin Structure: saturated uint8 - out_instance->major = nunavutGetU8(in_buffer, buf_size_bytes, offset, 8); - offset += 8; - // End Structure: saturated uint8 - // Begin Structure: saturated uint8 - out_instance->minor = nunavutGetU8(in_buffer, buf_size_bytes, offset, 8); - offset += 8; - // End Structure: saturated uint8 + // saturated uint8 minor + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + if ((offset_bits + 8U) <= capacity_bits) + { + out_obj->minor = buffer[offset_bits / 8U] & 255U; + } + else + { + out_obj->minor = 0U; + } + offset_bits += 8U; - return offset; + offset_bits = (offset_bits + 7U) & ~(size_t) 7U; // Align on 8 bits. + NUNAVUT_ASSERT(offset_bits % 8U == 0U); + *inout_buffer_size_bytes = (size_t) (nunavutChooseMin(offset_bits, capacity_bits) / 8U); + NUNAVUT_ASSERT(capacity_bytes >= *inout_buffer_size_bytes); + + return NUNAVUT_SUCCESS; } +/// Initialize an instance to default values. Does nothing if @param out_obj is NULL. +/// This function intentionally leaves inactive elements uninitialized; for example, members of a variable-length +/// array beyond its length are left uninitialized; aliased union memory that is not used by the first union field +/// is left uninitialized, etc. If full zero-initialization is desired, just use memset(&obj, 0, sizeof(obj)). +static inline void uavcan_node_Version_1_0_initialize_(uavcan_node_Version_1_0* const out_obj) +{ + if (out_obj != NULL) + { + size_t size_bytes = 0; + const uint8_t buf = 0; + const int8_t err = uavcan_node_Version_1_0_deserialize_(out_obj, &buf, &size_bytes); + NUNAVUT_ASSERT(err >= 0); + (void) err; + } +} #ifdef __cplusplus -} /* End extern "C" */ -#endif /* __cplusplus */ -#endif /* UAVCAN_NODE_VERSION_1_0_INCLUDED */ +} +#endif +#endif // UAVCAN_NODE_VERSION_1_0_INCLUDED_ From f672d82dd18789b1db43f9b42235bc16abfc3e93 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 15:10:16 +0100 Subject: [PATCH 03/12] Updating 'nnvg' command invocation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b6ea7b2..29a2d7b6 100644 --- a/README.md +++ b/README.md @@ -150,5 +150,5 @@ python3.8 -m pip install . cd ~ git clone https://github.com/UAVCAN/public_regulated_data_types && cd public_regulated_data_types -nnvg --outdir include --templates c_jinja --pp-trim-trailing-whitespace -e .h uavcan +nnvg --target-language c --pp-max-emptylines=1 --pp-trim-trailing-whitespace --target-endianness=little --enable-serialization-asserts --outdir include uavcan ``` From 0e46be7fd2b8a97c68d12457d2664c8bd2e03486 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 15:13:10 +0100 Subject: [PATCH 04/12] Updating link pointing to UAVCAN specification pointing to v1.0-beta specification document --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29a2d7b6..9c241a49 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![General Formatting Checks](https://github.com/107-systems/107-Arduino-UAVCAN/workflows/General%20Formatting%20Checks/badge.svg)](https://github.com/107-systems/107-Arduino-UAVCAN/actions?workflow=General+Formatting+Checks) [![Spell Check](https://github.com/107-systems/107-Arduino-UAVCAN/workflows/Spell%20Check/badge.svg)](https://github.com/107-systems/107-Arduino-UAVCAN/actions?workflow=Spell+Check) -Arduino library for providing a convenient C++ interface for accessing [UAVCAN](https://uavcan.org/) ([v1.0-alpha](https://uavcan.org/specification/UAVCAN_Specification_v1.0-alpha.pdf)) utilizing [libcanard](https://github.com/UAVCAN/libcanard). +Arduino library for providing a convenient C++ interface for accessing [UAVCAN](https://uavcan.org/) ([v1.0-beta](https://uavcan.org/specification/UAVCAN_Specification_v1.0-beta.pdf)) utilizing [libcanard](https://github.com/UAVCAN/libcanard).

From b66cc5660ab2b7c4942131e2323a4a8f1099c5c7 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 15:14:46 +0100 Subject: [PATCH 05/12] Too many examples make looking at the README discouraging, therefore leaving only one simple case. --- README.md | 96 ------------------------------------------------------- 1 file changed, 96 deletions(-) diff --git a/README.md b/README.md index 9c241a49..187231ec 100644 --- a/README.md +++ b/README.md @@ -18,29 +18,6 @@ This library works for * [ArduinoCore-mbed](https://github.com/arduino/ArduinoCore-mbed): [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano 33 BLE`](https://store.arduino.cc/arduino-nano-33-ble) :heavy_check_mark: ## Example -### Subscribe -```C++ -#include -/* ... */ -ArduinoUAVCAN uavcan(13, nullptr); -/* ... */ -void setup() { - /* ... */ - uavcan.subscribe(onHeatbeat_1_0_Received); -} -/* ... */ -void onHeatbeat_1_0_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) -{ - Heartbeat_1_0 const hb = Heartbeat_1_0::create(transfer); - - char msg[64]; - snprintf(msg, 64, "ID %02X, Uptime = %d, Health = %d, Mode = %d, VSSC = %d", transfer.remote_node_id, hb.data.uptime, hb.data.health, hb.data.mode, hb.data.vendor_specific_status_code); - - Serial.println(msg); -} -``` - -### Publish ```C++ #include /* ... */ @@ -69,79 +46,6 @@ bool transmitCanFrame(CanardFrame const & frame) { } ``` -### Service Client -```C++ -#include -/* ... */ -ArduinoUAVCAN uavcan(13, transmitCanFrame); -/* ... */ -void setup() { - /* ... */ - /* Request some coffee. */ - char const cmd_param[] = "I want a double espresso with cream"; - ExecuteCommand_1_0::Request req: - req.data.command = 0xCAFE; - req.data.parameter_length = std::min(cmd_param.length(), uavcan_node_ExecuteCommand_1_0_Request_parameter_array_capacity()); - std::copy(cmd_param.c_str(), cmd_param.c_str() + req.data.parameter_length, req.data.parameter); - - uavcan.request(req, 27 /* remote node id */, onExecuteCommand_1_0_Response_Received); -} - -void loop() { - /* Transmit all enqeued CAN frames */ - while(uavcan.transmitCanFrame()) { } -} -/* ... */ -void onExecuteCommand_1_0_Response_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - ExecuteCommand_1_0::Response const rsp = ExecuteCommand_1_0::Response::create(transfer); - - if (rsp.data.status == ExecuteCommand_1_0::Response::Status::SUCCESS) - Serial.println("Coffee successful retrieved"); - else - Serial.println("Error when retrieving coffee"); -} -/* ... */ -bool transmitCanFrame(CanardFrame const & frame) { - /* ... */ -} -``` - - -### Service Server -```C++ -#include -/* ... */ -ArduinoUAVCAN uavcan(13, transmitCanFrame); -/* ... */ -void setup() { - /* ... */ - /* Subscribe to incoming service requests */ - uavcan.subscribe(onExecuteCommand_1_0_Request_Received); -} - -void loop() { - /* Transmit all enqeued CAN frames */ - while(uavcan.transmitCanFrame()) { } -} -/* ... */ -void onExecuteCommand_1_0_Request_Received(CanardTransfer const & transfer, ArduinoUAVCAN & uavcan) { - ExecuteCommand_1_0::Request req = ExecuteCommand_1_0::Request::create(transfer); - ExecuteCommand_1_0::Response rsp; - - if (req.command() == 0xCAFE) { - rsp = ExecuteCommand_1_0::Response::Status::SUCCESS; - uavcan.respond(rsp, transfer.remote_node_id, transfer.transfer_id); - } else { - rsp = ExecuteCommand_1_0::Response::Status::NOT_AUTHORIZED; - uavcan.respond(rsp, transfer.remote_node_id, transfer.transfer_id); - } -} -/* ... */ -bool transmitCanFrame(CanardFrame const & frame) { - /* ... */ -} -``` - ### How to generate C header files from DSDL via nunavut/nnvg ```bash cd ~ From 6e873b09af7159de7d57bdeb57af08a14b1136c9 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 15:31:58 +0100 Subject: [PATCH 06/12] Moving dev-related documentation to wiki --- README.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 187231ec..4c93d169 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This library works for * [ArduinoCore-samd](https://github.com/arduino/ArduinoCore-samd): [`Arduino Zero`](https://store.arduino.cc/arduino-zero), [`MKR 1000`](https://store.arduino.cc/arduino-mkr1000-wifi), [`MKR WiFi 1010`](https://store.arduino.cc/arduino-mkr-wifi-1010), [`Nano 33 IoT`](https://store.arduino.cc/arduino-nano-33-iot), [`MKR GSM 1400`](https://store.arduino.cc/arduino-mkr-gsm-1400-1415), [`MKR NB 1500`](https://store.arduino.cc/arduino-mkr-nb-1500-1413), [`MKR WAN 1300/1310`](https://store.arduino.cc/mkr-wan-1310) :heavy_check_mark: * [ArduinoCore-mbed](https://github.com/arduino/ArduinoCore-mbed): [`Portenta H7`](https://store.arduino.cc/portenta-h7), [`Nano 33 BLE`](https://store.arduino.cc/arduino-nano-33-ble) :heavy_check_mark: -## Example +### Example ```C++ #include /* ... */ @@ -46,13 +46,6 @@ bool transmitCanFrame(CanardFrame const & frame) { } ``` -### How to generate C header files from DSDL via nunavut/nnvg -```bash -cd ~ -https://github.com/UAVCAN/nunavut && cd nunavut -python3.8 -m pip install . +### Contribution +Please take a look at the [wiki](https://github.com/107-systems/107-Arduino-UAVCAN/wiki) for notes pertaining development of `107-Arduino-UAVCAN`. -cd ~ -git clone https://github.com/UAVCAN/public_regulated_data_types && cd public_regulated_data_types -nnvg --target-language c --pp-max-emptylines=1 --pp-trim-trailing-whitespace --target-endianness=little --enable-serialization-asserts --outdir include uavcan -``` From b047aaed16fb53f846a97115e2e73baec5ca536d Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 20:42:54 +0100 Subject: [PATCH 07/12] Eliminiate trailing whitespaces --- src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp | 2 +- src/types/uavcan/node/Heartbeat.1.0.cpp | 2 +- src/types/uavcan/node/ID.1.0.ipp | 2 +- src/types/uavcan/node/Version.1.0.ipp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp index 5b255e56..673586df 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp @@ -61,7 +61,7 @@ Request Request::create(CanardTransfer const & transfer) size_t Request::encode(uint8_t * payload) const { size_t inout_buffer_size_bytes = Request::MAX_PAYLOAD_SIZE; - + if (uavcan_node_ExecuteCommand_Request_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) return 0; else diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp index 83953bc7..cdbb2165 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp @@ -57,7 +57,7 @@ Response Response::create(CanardTransfer const & transfer) size_t Response::encode(uint8_t * payload) const { size_t inout_buffer_size_bytes = Response::MAX_PAYLOAD_SIZE; - + if (uavcan_node_ExecuteCommand_Response_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) return 0; else diff --git a/src/types/uavcan/node/Heartbeat.1.0.cpp b/src/types/uavcan/node/Heartbeat.1.0.cpp index 7c6731eb..a25889a2 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.cpp +++ b/src/types/uavcan/node/Heartbeat.1.0.cpp @@ -50,7 +50,7 @@ Heartbeat_1_0 Heartbeat_1_0::create(CanardTransfer const & transfer) size_t Heartbeat_1_0::encode(uint8_t * payload) const { size_t inout_buffer_size_bytes = Heartbeat_1_0::MAX_PAYLOAD_SIZE; - + if (uavcan_node_Heartbeat_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) return 0; else diff --git a/src/types/uavcan/node/ID.1.0.ipp b/src/types/uavcan/node/ID.1.0.ipp index 14bbdc19..227af126 100644 --- a/src/types/uavcan/node/ID.1.0.ipp +++ b/src/types/uavcan/node/ID.1.0.ipp @@ -46,7 +46,7 @@ template size_t ID_1_0::encode(uint8_t * payload) const { size_t inout_buffer_size_bytes = ID_1_0::MAX_PAYLOAD_SIZE; - + if (uavcan_node_ID_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) return 0; else diff --git a/src/types/uavcan/node/Version.1.0.ipp b/src/types/uavcan/node/Version.1.0.ipp index a138a733..bf1a08df 100644 --- a/src/types/uavcan/node/Version.1.0.ipp +++ b/src/types/uavcan/node/Version.1.0.ipp @@ -46,7 +46,7 @@ template size_t Version_1_0::encode(uint8_t * payload) const { size_t inout_buffer_size_bytes = Version_1_0::MAX_PAYLOAD_SIZE; - + if (uavcan_node_Version_1_0_serialize_(&data, payload, &inout_buffer_size_bytes) < NUNAVUT_SUCCESS) return 0; else From 79bf35fb9139a150a599aaaaa514fde103fe3d36 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 20:44:24 +0100 Subject: [PATCH 08/12] Remove blank line at file end --- README.md | 1 - src/types/uavcan/node/Heartbeat.1.0.nnvg.h | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index 4c93d169..a23fa4ec 100644 --- a/README.md +++ b/README.md @@ -48,4 +48,3 @@ bool transmitCanFrame(CanardFrame const & frame) { ### Contribution Please take a look at the [wiki](https://github.com/107-systems/107-Arduino-UAVCAN/wiki) for notes pertaining development of `107-Arduino-UAVCAN`. - diff --git a/src/types/uavcan/node/Heartbeat.1.0.nnvg.h b/src/types/uavcan/node/Heartbeat.1.0.nnvg.h index ae45be4c..dd5e2ac1 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.nnvg.h +++ b/src/types/uavcan/node/Heartbeat.1.0.nnvg.h @@ -311,4 +311,3 @@ static inline void uavcan_node_Heartbeat_1_0_initialize_(uavcan_node_Heartbeat_1 } #endif #endif // UAVCAN_NODE_HEARTBEAT_1_0_INCLUDED_ - From 59cad05255bddfdb6326cf74c50014a4a609d935 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 11 Nov 2020 20:49:54 +0100 Subject: [PATCH 09/12] Fixing examples to compile again --- examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino b/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino index 066565a1..f7b0922f 100644 --- a/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino +++ b/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino @@ -75,8 +75,10 @@ void setup() char const cmd_param[] = "I want a double espresso with cream"; ExecuteCommand_1_0::Request req; req.data.command = 0xCAFE; - req.data.parameter_length = std::min(sizeof(cmd_param), uavcan_node_ExecuteCommand_1_0_Request_parameter_array_capacity()); - std::copy(cmd_param, cmd_param + req.data.parameter_length, req.data.parameter); + req.data.parameter.count = std::min(strlen(cmd_param), (size_t)uavcan_node_ExecuteCommand_Request_1_0_parameter_ARRAY_CAPACITY_); + std::copy(cmd_param, + cmd_param + req.data.parameter.count, + req.data.parameter.elements); uavcan.request(req, 27 /* remote node id */, onExecuteCommand_1_0_Response_Received); } From ca24be9917de4b6c6b792f955a1711ca1fa394c7 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 17 Nov 2020 08:29:42 +0100 Subject: [PATCH 10/12] Fixing test cases due to changes from UAVCAN v1.0-alpha to v1.0-beta --- .../test/src/test_ExecuteCommand_ServiceServer.cpp | 2 +- extras/test/src/test_Heartbeat.cpp | 14 +++++++------- extras/test/src/test_ID.cpp | 2 +- extras/test/src/test_Version.cpp | 10 +++++----- src/types/uavcan/node/Heartbeat.1.0.h | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extras/test/src/test_ExecuteCommand_ServiceServer.cpp b/extras/test/src/test_ExecuteCommand_ServiceServer.cpp index be798876..d479363e 100644 --- a/extras/test/src/test_ExecuteCommand_ServiceServer.cpp +++ b/extras/test/src/test_ExecuteCommand_ServiceServer.cpp @@ -108,5 +108,5 @@ TEST_CASE("A '435.ExecuteCommand.1.0' request is received from a client", "[exec /* Check if the sent response is identical with what we expect. */ REQUIRE(response_can_frame.id == 0x126CCD8D); - REQUIRE(response_can_frame.data == std::vector{0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE0}); + REQUIRE(response_can_frame.data == std::vector{0x02, 0xE0}); } diff --git a/extras/test/src/test_Heartbeat.cpp b/extras/test/src/test_Heartbeat.cpp index 08ff2114..1290bc85 100644 --- a/extras/test/src/test_Heartbeat.cpp +++ b/extras/test/src/test_Heartbeat.cpp @@ -65,10 +65,10 @@ TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is sent", "[heartbeat-01]") uavcan.publish(hb); while(uavcan.transmitCanFrame()) { } /* - * pyuavcan publish 32085.uavcan.node.Heartbeat.1.0 '{uptime: 9876, health: 0, mode: 3, vendor_specific_status_code: 5}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' + * pyuavcan publish 7509.uavcan.node.Heartbeat.1.0 '{uptime: 9876, health: {value: 0}, mode: {value: 3}, vendor_specific_status_code: 5}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' */ REQUIRE(can_frame.id == 0x107D550D); - REQUIRE(can_frame.data == std::vector{0x94, 0x26, 0x00, 0x00, 0xAC, 0x00, 0x00, 0xE0}); + REQUIRE(can_frame.data == std::vector{0x94, 0x26, 0x00, 0x00, 0x00, 0x03, 0x05, 0xE0}); hb.data.uptime = 9881; hb = Heartbeat_1_0::Health::ADVISORY; @@ -77,10 +77,10 @@ TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is sent", "[heartbeat-01]") uavcan.publish(hb); while(uavcan.transmitCanFrame()) { } /* - * pyuavcan publish 32085.uavcan.node.Heartbeat.1.0 '{uptime: 9881, health: 1, mode: 2, vendor_specific_status_code: 123}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' + * pyuavcan publish 7509.uavcan.node.Heartbeat.1.0 '{uptime: 9881, health: {value: 1}, mode: {value: 2}, vendor_specific_status_code: 123}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' */ REQUIRE(can_frame.id == 0x107D550D); - REQUIRE(can_frame.data == std::vector{0x99, 0x26, 0x00, 0x00, 0x69, 0x0F, 0x00, 0xE1}); + REQUIRE(can_frame.data == std::vector{0x99, 0x26, 0x00, 0x00, 0x01, 0x02, 0x7B, 0xE1}); } TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is received", "[heartbeat-02]") @@ -91,7 +91,7 @@ TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is received", "[heartbeat-02]" REQUIRE(uavcan.subscribe(onHeatbeat_1_0_Received)); /* Create: - * pyuavcan publish 32085.uavcan.node.Heartbeat.1.0 '{uptime: 1337, health: 2, mode: 7, vendor_specific_status_code: 42}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),59)' + * pyuavcan publish 7509.uavcan.node.Heartbeat.1.0 '{uptime: 1337, health: {value: 2}, mode: {value: 2}, vendor_specific_status_code: 42}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),59)' * * Capture: * sudo modprobe vcan @@ -99,12 +99,12 @@ TEST_CASE("A '32085.Heartbeat.1.0.uavcan' message is received", "[heartbeat-02]" * sudo ip link set up vcan0 * candump -decaxta vcan0 */ - std::vector const data{0x39, 0x05, 0x00, 0x00, 0x5E, 0x05, 0x00, 0xE1}; + std::vector const data{0x39, 0x05, 0x00, 0x00, 0x02, 0x02, 0x2A, 0xE1}; uavcan.onCanFrameReceived(util::toCanardFrame(0x107D553B, data)); REQUIRE(hb_node_id == 59); REQUIRE(hb_data.uptime == 1337); REQUIRE(hb_data.health.value == arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Health::CAUTION)); - REQUIRE(hb_data.mode.value == 7); /* TODO: FIX THIS (uint8_t)arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Mode::OFFLINE)); */ + REQUIRE(hb_data.mode.value == arduino::_107_::uavcan::to_integer(Heartbeat_1_0::Mode::MAINTENANCE)); REQUIRE(hb_data.vendor_specific_status_code == 42); } diff --git a/extras/test/src/test_ID.cpp b/extras/test/src/test_ID.cpp index 8e1343f9..c3278bc1 100644 --- a/extras/test/src/test_ID.cpp +++ b/extras/test/src/test_ID.cpp @@ -65,7 +65,7 @@ TEST_CASE("A 'ID.1.0.uavcan' message is sent", "[id-01]") /* * pyuavcan publish 1337.uavcan.node.ID.1.0 '{value: 65}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' */ - REQUIRE(can_frame.id == 0x1005390D); + REQUIRE(can_frame.id == 0x1065390D); REQUIRE(can_frame.data == std::vector{0x41, 0x00, 0xE0}); } diff --git a/extras/test/src/test_Version.cpp b/extras/test/src/test_Version.cpp index fde92c1e..21c8aacc 100644 --- a/extras/test/src/test_Version.cpp +++ b/extras/test/src/test_Version.cpp @@ -20,7 +20,7 @@ * CONSTANTS **************************************************************************************/ -static CanardPortID const VERSION_PORT_ID = 12345; +static CanardPortID const VERSION_PORT_ID = 1234; /************************************************************************************** * PRIVATE GLOBAL VARIABLES @@ -65,9 +65,9 @@ TEST_CASE("A 'Version.1.0.uavcan' message is sent", "[version-01]") uavcan.publish(version); uavcan.transmitCanFrame(); /* - * pyuavcan publish 12345.uavcan.node.Version.1.0 '{major: 0xCA, minor: 0xFE}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' + * pyuavcan publish 1234.uavcan.node.Version.1.0 '{major: 0xCA, minor: 0xFE}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),13)' */ - REQUIRE(can_frame.id == 0x1030390D); + REQUIRE(can_frame.id == 0x1064D20D); REQUIRE(can_frame.data == std::vector{0xCA, 0xFE, 0xE0}); } @@ -78,10 +78,10 @@ TEST_CASE("A 'Version.1.0.uavcan' message is received", "[version-02]") REQUIRE(uavcan.subscribe>(onVersion_1_0_Received)); /* - * pyuavcan publish 12345.uavcan.node.Version.1.0 '{major: 0x13, minor: 0x37}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),27)' + * pyuavcan publish 1234.uavcan.node.Version.1.0 '{major: 0x13, minor: 0x37}' --tr='CAN(can.media.socketcan.SocketCANMedia("vcan0",8),27)' */ std::vector const data{0x13, 0x37, 0xE0}; - uavcan.onCanFrameReceived(util::toCanardFrame(0x1030391B, data)); + uavcan.onCanFrameReceived(util::toCanardFrame(0x1064D21B, data)); REQUIRE(version_node_id == 27); REQUIRE(version.major == 0x13); diff --git a/src/types/uavcan/node/Heartbeat.1.0.h b/src/types/uavcan/node/Heartbeat.1.0.h index e747b8ba..69c5adee 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.h +++ b/src/types/uavcan/node/Heartbeat.1.0.h @@ -44,7 +44,7 @@ class Heartbeat_1_0 uavcan_node_Heartbeat_1_0 data; static constexpr CanardPortID PORT_ID = uavcan_node_Heartbeat_1_0_FIXED_PORT_ID_; - static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_Health_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; + static constexpr size_t MAX_PAYLOAD_SIZE = uavcan_node_Heartbeat_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_; static constexpr CanardTransferKind TRANSFER_KIND = CanardTransferKindMessage; Heartbeat_1_0(); From ee7e76b0ccaf7721fd5e14e77cc98c326a813d81 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 17 Nov 2020 08:31:41 +0100 Subject: [PATCH 11/12] Rename 'create' to 'deserialize' --- .../UAVCAN-Heartbeat-Subscribe/UAVCAN-Heartbeat-Subscribe.ino | 2 +- examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino | 2 +- examples/UAVCAN-Service-Server/UAVCAN-Service-Server.ino | 2 +- extras/test/src/test_ExecuteCommand_ServiceClient.cpp | 2 +- extras/test/src/test_ExecuteCommand_ServiceServer.cpp | 2 +- extras/test/src/test_Heartbeat.cpp | 2 +- extras/test/src/test_ID.cpp | 2 +- extras/test/src/test_Version.cpp | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Request.h | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Response.h | 2 +- src/types/uavcan/node/Heartbeat.1.0.cpp | 2 +- src/types/uavcan/node/Heartbeat.1.0.h | 2 +- src/types/uavcan/node/ID.1.0.hpp | 2 +- src/types/uavcan/node/ID.1.0.ipp | 2 +- src/types/uavcan/node/Version.1.0.hpp | 2 +- src/types/uavcan/node/Version.1.0.ipp | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/UAVCAN-Heartbeat-Subscribe/UAVCAN-Heartbeat-Subscribe.ino b/examples/UAVCAN-Heartbeat-Subscribe/UAVCAN-Heartbeat-Subscribe.ino index b62482e6..f4ae57c2 100644 --- a/examples/UAVCAN-Heartbeat-Subscribe/UAVCAN-Heartbeat-Subscribe.ino +++ b/examples/UAVCAN-Heartbeat-Subscribe/UAVCAN-Heartbeat-Subscribe.ino @@ -109,7 +109,7 @@ void onReceiveBufferFull(CanardFrame const & frame) void onHeatbeat_1_0_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - Heartbeat_1_0 const hb = Heartbeat_1_0::create(transfer); + Heartbeat_1_0 const hb = Heartbeat_1_0::deserialize(transfer); char msg[64]; snprintf(msg, 64, diff --git a/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino b/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino index f7b0922f..53f42210 100644 --- a/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino +++ b/examples/UAVCAN-Service-Client/UAVCAN-Service-Client.ino @@ -125,7 +125,7 @@ bool transmitCanFrame(CanardFrame const & frame) void onExecuteCommand_1_0_Response_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - ExecuteCommand_1_0::Response const rsp = ExecuteCommand_1_0::Response::create(transfer); + ExecuteCommand_1_0::Response const rsp = ExecuteCommand_1_0::Response::deserialize(transfer); if (rsp.data.status == arduino::_107_::uavcan::to_integer(ExecuteCommand_1_0::Response::Status::SUCCESS)) Serial.println("Coffee successful retrieved"); diff --git a/examples/UAVCAN-Service-Server/UAVCAN-Service-Server.ino b/examples/UAVCAN-Service-Server/UAVCAN-Service-Server.ino index 98a0ff26..540b4330 100644 --- a/examples/UAVCAN-Service-Server/UAVCAN-Service-Server.ino +++ b/examples/UAVCAN-Service-Server/UAVCAN-Service-Server.ino @@ -117,7 +117,7 @@ bool transmitCanFrame(CanardFrame const & frame) void onExecuteCommand_1_0_Request_Received(CanardTransfer const & transfer, ArduinoUAVCAN & uavcan) { - ExecuteCommand_1_0::Request req = ExecuteCommand_1_0::Request::create(transfer); + ExecuteCommand_1_0::Request req = ExecuteCommand_1_0::Request::deserialize(transfer); if (req.data.command == 0xCAFE) { diff --git a/extras/test/src/test_ExecuteCommand_ServiceClient.cpp b/extras/test/src/test_ExecuteCommand_ServiceClient.cpp index fd80fccd..747365bb 100644 --- a/extras/test/src/test_ExecuteCommand_ServiceClient.cpp +++ b/extras/test/src/test_ExecuteCommand_ServiceClient.cpp @@ -43,7 +43,7 @@ static bool transmitCanFrame(CanardFrame const & f) static void onExecuteCommand_1_0_Response_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - ExecuteCommand_1_0::Response const received_response = ExecuteCommand_1_0::Response::create(transfer); + ExecuteCommand_1_0::Response const received_response = ExecuteCommand_1_0::Response::deserialize(transfer); response.status = received_response.data.status; } diff --git a/extras/test/src/test_ExecuteCommand_ServiceServer.cpp b/extras/test/src/test_ExecuteCommand_ServiceServer.cpp index d479363e..a8cf6881 100644 --- a/extras/test/src/test_ExecuteCommand_ServiceServer.cpp +++ b/extras/test/src/test_ExecuteCommand_ServiceServer.cpp @@ -43,7 +43,7 @@ static bool transmitCanFrame(CanardFrame const & f) static void onExecuteCommand_1_0_Request_Received(CanardTransfer const & transfer, ArduinoUAVCAN & uavcan) { - ExecuteCommand_1_0::Request received_request = ExecuteCommand_1_0::Request::create(transfer); + ExecuteCommand_1_0::Request received_request = ExecuteCommand_1_0::Request::deserialize(transfer); /* The next 2 lines are just for test purposes, you won't * have them in your real application. diff --git a/extras/test/src/test_Heartbeat.cpp b/extras/test/src/test_Heartbeat.cpp index 1290bc85..f8feb3ac 100644 --- a/extras/test/src/test_Heartbeat.cpp +++ b/extras/test/src/test_Heartbeat.cpp @@ -40,7 +40,7 @@ static bool transmitCanFrame(CanardFrame const & f) static void onHeatbeat_1_0_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - Heartbeat_1_0 const received_hb = Heartbeat_1_0::create(transfer); + Heartbeat_1_0 const received_hb = Heartbeat_1_0::deserialize(transfer); hb_node_id = transfer.remote_node_id; hb_data.uptime = received_hb.data.uptime; diff --git a/extras/test/src/test_ID.cpp b/extras/test/src/test_ID.cpp index c3278bc1..62a662f7 100644 --- a/extras/test/src/test_ID.cpp +++ b/extras/test/src/test_ID.cpp @@ -44,7 +44,7 @@ static bool transmitCanFrame(CanardFrame const & f) static void onID_1_0_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - ID_1_0 const received_id = ID_1_0::create(transfer); + ID_1_0 const received_id = ID_1_0::deserialize(transfer); id_node_id = transfer.remote_node_id; id.value = received_id.data.value; diff --git a/extras/test/src/test_Version.cpp b/extras/test/src/test_Version.cpp index 21c8aacc..44f0837d 100644 --- a/extras/test/src/test_Version.cpp +++ b/extras/test/src/test_Version.cpp @@ -44,7 +44,7 @@ static bool transmitCanFrame(CanardFrame const & f) static void onVersion_1_0_Received(CanardTransfer const & transfer, ArduinoUAVCAN & /* uavcan */) { - Version_1_0 const received_version = Version_1_0::create(transfer); + Version_1_0 const received_version = Version_1_0::deserialize(transfer); version_node_id = transfer.remote_node_id; version.major = received_version.data.major; diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp index 673586df..6cf29ba0 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp @@ -50,7 +50,7 @@ Request::Request(Request const & other) * PUBLIC MEMBER FUNCTIONS **************************************************************************************/ -Request Request::create(CanardTransfer const & transfer) +Request Request::deserialize(CanardTransfer const & transfer) { Request r; size_t inout_buffer_size_bytes = transfer.payload_size; diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.h b/src/types/uavcan/node/ExecuteCommand.1.0.Request.h index 55545757..35575ca6 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.h @@ -41,7 +41,7 @@ class Request Request(); Request(Request const & other); - static Request create(CanardTransfer const & transfer); + static Request deserialize(CanardTransfer const & transfer); size_t encode(uint8_t * payload) const; }; diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp index cdbb2165..f4922534 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp @@ -46,7 +46,7 @@ Response::Response(Response const & other) * PUBLIC MEMBER FUNCTIONS **************************************************************************************/ -Response Response::create(CanardTransfer const & transfer) +Response Response::deserialize(CanardTransfer const & transfer) { Response r; size_t inout_buffer_size_bytes = transfer.payload_size; diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.h b/src/types/uavcan/node/ExecuteCommand.1.0.Response.h index 6611b0a2..39621567 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.h @@ -52,7 +52,7 @@ class Response Response(); Response(Response const & other); - static Response create(CanardTransfer const & transfer); + static Response deserialize(CanardTransfer const & transfer); size_t encode(uint8_t * payload) const; void operator = (Status const status); diff --git a/src/types/uavcan/node/Heartbeat.1.0.cpp b/src/types/uavcan/node/Heartbeat.1.0.cpp index a25889a2..e3258f46 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.cpp +++ b/src/types/uavcan/node/Heartbeat.1.0.cpp @@ -39,7 +39,7 @@ Heartbeat_1_0::Heartbeat_1_0(Heartbeat_1_0 const & other) * PUBLIC MEMBER FUNCTIONS **************************************************************************************/ -Heartbeat_1_0 Heartbeat_1_0::create(CanardTransfer const & transfer) +Heartbeat_1_0 Heartbeat_1_0::deserialize(CanardTransfer const & transfer) { Heartbeat_1_0 h; size_t inout_buffer_size_bytes = transfer.payload_size; diff --git a/src/types/uavcan/node/Heartbeat.1.0.h b/src/types/uavcan/node/Heartbeat.1.0.h index 69c5adee..7c400a66 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.h +++ b/src/types/uavcan/node/Heartbeat.1.0.h @@ -50,7 +50,7 @@ class Heartbeat_1_0 Heartbeat_1_0(); Heartbeat_1_0(Heartbeat_1_0 const & other); - static Heartbeat_1_0 create(CanardTransfer const & transfer); + static Heartbeat_1_0 deserialize(CanardTransfer const & transfer); size_t encode(uint8_t * payload) const; void operator = (Health const health); diff --git a/src/types/uavcan/node/ID.1.0.hpp b/src/types/uavcan/node/ID.1.0.hpp index 37b15ca3..0700c02e 100644 --- a/src/types/uavcan/node/ID.1.0.hpp +++ b/src/types/uavcan/node/ID.1.0.hpp @@ -35,7 +35,7 @@ class ID_1_0 ID_1_0(); ID_1_0(ID_1_0 const & other); - static ID_1_0 create(CanardTransfer const & transfer); + static ID_1_0 deserialize(CanardTransfer const & transfer); size_t encode(uint8_t * payload) const; }; diff --git a/src/types/uavcan/node/ID.1.0.ipp b/src/types/uavcan/node/ID.1.0.ipp index 227af126..a200904e 100644 --- a/src/types/uavcan/node/ID.1.0.ipp +++ b/src/types/uavcan/node/ID.1.0.ipp @@ -34,7 +34,7 @@ ID_1_0::ID_1_0(ID_1_0 const & other) **************************************************************************************/ template -ID_1_0 ID_1_0::create(CanardTransfer const & transfer) +ID_1_0 ID_1_0::deserialize(CanardTransfer const & transfer) { ID_1_0 i; size_t inout_buffer_size_bytes = transfer.payload_size; diff --git a/src/types/uavcan/node/Version.1.0.hpp b/src/types/uavcan/node/Version.1.0.hpp index 1b4cf163..c05994c5 100644 --- a/src/types/uavcan/node/Version.1.0.hpp +++ b/src/types/uavcan/node/Version.1.0.hpp @@ -35,7 +35,7 @@ class Version_1_0 Version_1_0(); Version_1_0(Version_1_0 const & other); - static Version_1_0 create(CanardTransfer const & transfer); + static Version_1_0 deserialize(CanardTransfer const & transfer); size_t encode(uint8_t * payload) const; }; diff --git a/src/types/uavcan/node/Version.1.0.ipp b/src/types/uavcan/node/Version.1.0.ipp index bf1a08df..37a29850 100644 --- a/src/types/uavcan/node/Version.1.0.ipp +++ b/src/types/uavcan/node/Version.1.0.ipp @@ -34,7 +34,7 @@ Version_1_0::Version_1_0(Version_1_0 const & other) **************************************************************************************/ template -Version_1_0 Version_1_0::create(CanardTransfer const & transfer) +Version_1_0 Version_1_0::deserialize(CanardTransfer const & transfer) { Version_1_0 v; size_t inout_buffer_size_bytes = transfer.payload_size; From 3eab238679b85d3e767c8b7178749a84b5c2f06b Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 17 Nov 2020 08:32:33 +0100 Subject: [PATCH 12/12] Renaming 'encode' to 'serialize' --- src/ArduinoUAVCAN.ipp | 6 +++--- src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Request.h | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp | 2 +- src/types/uavcan/node/ExecuteCommand.1.0.Response.h | 2 +- src/types/uavcan/node/Heartbeat.1.0.cpp | 2 +- src/types/uavcan/node/Heartbeat.1.0.h | 2 +- src/types/uavcan/node/ID.1.0.hpp | 2 +- src/types/uavcan/node/ID.1.0.ipp | 2 +- src/types/uavcan/node/Version.1.0.hpp | 2 +- src/types/uavcan/node/Version.1.0.ipp | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ArduinoUAVCAN.ipp b/src/ArduinoUAVCAN.ipp index bee7cc2b..6da7a28b 100644 --- a/src/ArduinoUAVCAN.ipp +++ b/src/ArduinoUAVCAN.ipp @@ -22,7 +22,7 @@ bool ArduinoUAVCAN::publish(T_MSG const & msg) std::array payload_buf; payload_buf.fill(0); - size_t const payload_size = msg.encode(payload_buf.data()); + size_t const payload_size = msg.serialize(payload_buf.data()); CanardTransferID const transfer_id = getNextTransferId(T_MSG::PORT_ID); return enqeueTransfer(CANARD_NODE_ID_UNSET, T_MSG::TRANSFER_KIND, T_MSG::PORT_ID, payload_size, payload_buf.data(), transfer_id); @@ -35,7 +35,7 @@ bool ArduinoUAVCAN::respond(T_RSP const & rsp, CanardNodeID const remote_node_id std::array payload_buf; payload_buf.fill(0); - size_t const payload_size = rsp.encode(payload_buf.data()); + size_t const payload_size = rsp.serialize(payload_buf.data()); return enqeueTransfer(remote_node_id, T_RSP::TRANSFER_KIND, T_RSP::PORT_ID, payload_size, payload_buf.data(), transfer_id); } @@ -48,7 +48,7 @@ bool ArduinoUAVCAN::request(T_REQ const & req, CanardNodeID const remote_node_id std::array payload_buf; payload_buf.fill(0); - size_t const payload_size = req.encode(payload_buf.data()); + size_t const payload_size = req.serialize(payload_buf.data()); CanardTransferID const transfer_id = getNextTransferId(T_REQ::PORT_ID); if (!enqeueTransfer(remote_node_id, T_REQ::TRANSFER_KIND, T_REQ::PORT_ID, payload_size, payload_buf.data(), transfer_id)) diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp index 6cf29ba0..dc8dd6a0 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.cpp @@ -58,7 +58,7 @@ Request Request::deserialize(CanardTransfer const & transfer) return r; } -size_t Request::encode(uint8_t * payload) const +size_t Request::serialize(uint8_t * payload) const { size_t inout_buffer_size_bytes = Request::MAX_PAYLOAD_SIZE; diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Request.h b/src/types/uavcan/node/ExecuteCommand.1.0.Request.h index 35575ca6..bddaf933 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Request.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Request.h @@ -42,7 +42,7 @@ class Request Request(Request const & other); static Request deserialize(CanardTransfer const & transfer); - size_t encode(uint8_t * payload) const; + size_t serialize(uint8_t * payload) const; }; /************************************************************************************** diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp index f4922534..0e2a85b6 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.cpp @@ -54,7 +54,7 @@ Response Response::deserialize(CanardTransfer const & transfer) return r; } -size_t Response::encode(uint8_t * payload) const +size_t Response::serialize(uint8_t * payload) const { size_t inout_buffer_size_bytes = Response::MAX_PAYLOAD_SIZE; diff --git a/src/types/uavcan/node/ExecuteCommand.1.0.Response.h b/src/types/uavcan/node/ExecuteCommand.1.0.Response.h index 39621567..dab2fecf 100644 --- a/src/types/uavcan/node/ExecuteCommand.1.0.Response.h +++ b/src/types/uavcan/node/ExecuteCommand.1.0.Response.h @@ -53,7 +53,7 @@ class Response Response(Response const & other); static Response deserialize(CanardTransfer const & transfer); - size_t encode(uint8_t * payload) const; + size_t serialize(uint8_t * payload) const; void operator = (Status const status); }; diff --git a/src/types/uavcan/node/Heartbeat.1.0.cpp b/src/types/uavcan/node/Heartbeat.1.0.cpp index e3258f46..c25b59a0 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.cpp +++ b/src/types/uavcan/node/Heartbeat.1.0.cpp @@ -47,7 +47,7 @@ Heartbeat_1_0 Heartbeat_1_0::deserialize(CanardTransfer const & transfer) return h; } -size_t Heartbeat_1_0::encode(uint8_t * payload) const +size_t Heartbeat_1_0::serialize(uint8_t * payload) const { size_t inout_buffer_size_bytes = Heartbeat_1_0::MAX_PAYLOAD_SIZE; diff --git a/src/types/uavcan/node/Heartbeat.1.0.h b/src/types/uavcan/node/Heartbeat.1.0.h index 7c400a66..8f6717a1 100644 --- a/src/types/uavcan/node/Heartbeat.1.0.h +++ b/src/types/uavcan/node/Heartbeat.1.0.h @@ -51,7 +51,7 @@ class Heartbeat_1_0 Heartbeat_1_0(Heartbeat_1_0 const & other); static Heartbeat_1_0 deserialize(CanardTransfer const & transfer); - size_t encode(uint8_t * payload) const; + size_t serialize(uint8_t * payload) const; void operator = (Health const health); void operator = (Mode const mode); diff --git a/src/types/uavcan/node/ID.1.0.hpp b/src/types/uavcan/node/ID.1.0.hpp index 0700c02e..fb546b44 100644 --- a/src/types/uavcan/node/ID.1.0.hpp +++ b/src/types/uavcan/node/ID.1.0.hpp @@ -36,7 +36,7 @@ class ID_1_0 ID_1_0(ID_1_0 const & other); static ID_1_0 deserialize(CanardTransfer const & transfer); - size_t encode(uint8_t * payload) const; + size_t serialize(uint8_t * payload) const; }; diff --git a/src/types/uavcan/node/ID.1.0.ipp b/src/types/uavcan/node/ID.1.0.ipp index a200904e..6bf07025 100644 --- a/src/types/uavcan/node/ID.1.0.ipp +++ b/src/types/uavcan/node/ID.1.0.ipp @@ -43,7 +43,7 @@ ID_1_0 ID_1_0::deserialize(CanardTransfer const & transfer) } template -size_t ID_1_0::encode(uint8_t * payload) const +size_t ID_1_0::serialize(uint8_t * payload) const { size_t inout_buffer_size_bytes = ID_1_0::MAX_PAYLOAD_SIZE; diff --git a/src/types/uavcan/node/Version.1.0.hpp b/src/types/uavcan/node/Version.1.0.hpp index c05994c5..7ec02f27 100644 --- a/src/types/uavcan/node/Version.1.0.hpp +++ b/src/types/uavcan/node/Version.1.0.hpp @@ -36,7 +36,7 @@ class Version_1_0 Version_1_0(Version_1_0 const & other); static Version_1_0 deserialize(CanardTransfer const & transfer); - size_t encode(uint8_t * payload) const; + size_t serialize(uint8_t * payload) const; }; diff --git a/src/types/uavcan/node/Version.1.0.ipp b/src/types/uavcan/node/Version.1.0.ipp index 37a29850..cb581ebd 100644 --- a/src/types/uavcan/node/Version.1.0.ipp +++ b/src/types/uavcan/node/Version.1.0.ipp @@ -43,7 +43,7 @@ Version_1_0 Version_1_0::deserialize(CanardTransfer const & transfer) } template -size_t Version_1_0::encode(uint8_t * payload) const +size_t Version_1_0::serialize(uint8_t * payload) const { size_t inout_buffer_size_bytes = Version_1_0::MAX_PAYLOAD_SIZE;