Skip to content

Commit

Permalink
canardTxPoll
Browse files Browse the repository at this point in the history
  • Loading branch information
serges147 committed Dec 4, 2024
1 parent 78b62fa commit df974af
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 9 deletions.
74 changes: 65 additions & 9 deletions libcanard/canard.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,25 @@ CANARD_PRIVATE int32_t txPushMultiFrame(struct CanardTxQueue* const que,
return out;
}

CANARD_PRIVATE void txPopAndFreeTransfer(struct CanardTxQueue* const que,
const struct CanardInstance* const ins,
struct CanardTxQueueItem* tx_item,
const bool drop_whole_transfer)
{
struct CanardTxQueueItem* tx_item_to_free = NULL;
while (NULL != (tx_item_to_free = canardTxPop(que, tx_item)))
{
tx_item = tx_item_to_free->next_in_transfer;
canardTxFree(que, ins, tx_item_to_free);

if (!drop_whole_transfer)
{
break;
}
que->stats.dropped_frames++;
}
}

/// Flushes expired transfers by comparing deadline timestamps of the pending transfers with the current time.
CANARD_PRIVATE void txFlushExpiredTransfers(struct CanardTxQueue* const que,
const struct CanardInstance* const ins,
Expand All @@ -609,15 +628,8 @@ CANARD_PRIVATE void txFlushExpiredTransfers(struct CanardTxQueue* const q
break;
}

// All frames of the transfer are released at once b/c they all have the same deadline.
struct CanardTxQueueItem* tx_item_to_free = NULL;
while (NULL != (tx_item_to_free = canardTxPop(que, tx_item)))
{
tx_item = tx_item_to_free->next_in_transfer;
canardTxFree(que, ins, tx_item_to_free);

que->stats.dropped_frames++;
}
// All frames of the transfer are dropped at once b/c they all have the same deadline.
txPopAndFreeTransfer(que, ins, tx_item, true); // drop the whole transfer
}
}

Expand Down Expand Up @@ -1242,6 +1254,50 @@ void canardTxFree(struct CanardTxQueue* const que,
}
}

int8_t canardTxPoll(struct CanardTxQueue* const que,
const struct CanardInstance* const ins,
const CanardMicrosecond now_usec,
void* const user_reference,
const CanardTxFrameHandler frame_handler)
{
int8_t out = 0;

// Before peeking a frame to transmit, we need to try to flush any expired transfers.
// This will not only ensure asap freeing of the queue capacity, but also makes sure that the following
// `canardTxPeek` will return a not expired item (if any), so we don't need to check the deadline again.
// The flushing is done by comparing deadline timestamps of the pending transfers with the current time,
// which makes sense only if the current time is known (bigger than zero).
if (now_usec > 0)
{
txFlushExpiredTransfers(que, ins, now_usec);
}

if (frame_handler != NULL)
{
struct CanardTxQueueItem* const tx_item = canardTxPeek(que);
if (tx_item != NULL)
{
// No need to check the deadline again, as we have already flushed all expired transfers.
out = frame_handler(user_reference, tx_item->tx_deadline_usec, &tx_item->frame);

// We gonna release (pop and free) the frame if the handler returned:
// - either a positive value - the frame has been successfully accepted by the handler;
// - or a negative value - the frame has been rejected by the handler due to failure.
// Zero value means that the handler cannot accept the frame at the moment, so we keep it in the queue.
if (out != 0)
{
// In case of a failure, it makes sense to drop the whole transfer immediately
// b/c at least one this frame has been rejected, so the whole transfer is useless.
const bool drop_whole_transfer = (out < 0);
txPopAndFreeTransfer(que, ins, tx_item, drop_whole_transfer);
}
}
}

CANARD_ASSERT(out <= 1);
return out;
}

int8_t canardRxAccept(struct CanardInstance* const ins,
const CanardMicrosecond timestamp_usec,
const struct CanardFrame* const frame,
Expand Down
48 changes: 48 additions & 0 deletions libcanard/canard.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,29 @@ struct CanardTxQueueStats
size_t dropped_frames;
};

/// Defines the signature of the TX frame handler function.
///
/// The handler function is intended to be invoked from Canard TX polling (see details for the `canardTxPoll()`).
///
/// @param user_reference The user reference passed to `canardTxPoll()`.
/// @param deadline_usec The deadline of the frame that is being handled.
/// @param frame The mutable frame that is being handled.
/// @return The result of the handling operation:
/// - Any positive value: the frame was successfully handled.
/// This indicates that the frame payload was accepted (and its ownership could be potentially moved,
/// see `canardTxPeek` for the details), and the frame can be safely removed from the queue.
/// - Zero: the frame was not handled, and so the frame should be kept in the queue.
/// It will be retried on some future `canardTxPoll()` call according to the queue state in the future.
/// This case is useful when TX hardware is busy, and the frame should be retried later.
/// - Any negative value: the frame was rejected due to an unrecoverable failure.
/// This indicates to the caller (`canardTxPoll`) that the frame should be dropped from the queue,
/// as well as all other frames belonging to the same transfer. The `dropped_frames` counter in the TX queue stats
/// will be incremented for each frame dropped in this way.
///
typedef int8_t (*CanardTxFrameHandler)(void* const user_reference,
const CanardMicrosecond deadline_usec,
struct CanardMutableFrame* frame);

/// Prioritized transmission queue that keeps CAN frames destined for transmission via one CAN interface.
/// Applications with redundant interfaces are expected to have one instance of this type per interface.
/// Applications that are not interested in transmission may have zero queues.
Expand Down Expand Up @@ -613,6 +636,31 @@ void canardTxFree(struct CanardTxQueue* const que,
const struct CanardInstance* const ins,
struct CanardTxQueueItem* const item);

/// This is a helper that combines several Canard TX calls (`canardTxPeek`, `canardTxPop` and `canardTxFree`)
/// into one "polling" algorythm. It simplifies the whole process of transmitting frames to just two function calls:
/// - `canardTxPush` to enqueue the frames
/// - `canardTxPoll` to dequeue, transmit and free a single frame
///
/// The algorythm implements a typical pattern of de-queuing, transmitting and freeing a TX queue item,
/// as well as handling transmission failures, retries, and deadline timeouts.
///
/// The function is intended to be periodically called, most probably on a signal that the previous TX frame
/// transmission has been completed, and so the next TX frame (if any) could be polled from the TX queue.
///
/// @param que The TX queue to poll.
/// @param ins The Canard instance.
/// @param now_usec The current time in microseconds. It is used to determine if the frame has timed out.
/// @param user_reference The user reference to be passed to the frame handler.
/// @param frame_handler The frame handler function that will be called to transmit the frame.
/// @return Zero if the queue is empty or there is no frame handler (NULL).
/// Otherwise, the result from the frame handler call. See `CanardTxFrameHandler` documentation.
///
int8_t canardTxPoll(struct CanardTxQueue* const que,
const struct CanardInstance* const ins,
const CanardMicrosecond now_usec,
void* const user_reference,
const CanardTxFrameHandler frame_handler);

/// This function implements the transfer reassembly logic. It accepts a transport frame from any of the redundant
/// interfaces, locates the appropriate subscription state, and, if found, updates it. If the frame completed a
/// transfer, the return value is 1 (one) and the out_transfer pointer is populated with the parameters of the
Expand Down

0 comments on commit df974af

Please sign in to comment.