@@ -324,6 +324,7 @@ class TransportImpl final : private TransportDelegate, public ICanTransport
324324 return MemoryError{};
325325 }
326326
327+ const auto now_us = std::chrono::duration_cast<std::chrono::microseconds>(executor_.now ().time_since_epoch ());
327328 const auto deadline_us = std::chrono::duration_cast<std::chrono::microseconds>(deadline.time_since_epoch ());
328329
329330 for (Media& media : media_array_)
@@ -335,7 +336,8 @@ class TransportImpl final : private TransportDelegate, public ICanTransport
335336 &canardInstance (),
336337 static_cast <CanardMicrosecond>(deadline_us.count ()),
337338 &metadata,
338- {payload.size (), payload.data ()}); // NOSONAR cpp:S5356
339+ {payload.size (), payload.data ()}, // NOSONAR cpp:S5356
340+ static_cast <CanardMicrosecond>(now_us.count ()));
339341
340342 cetl::optional<AnyFailure> failure =
341343 tryHandleTransientCanardResult<TransientErrorReport::CanardTxPush>(media, result);
@@ -572,77 +574,84 @@ class TransportImpl final : private TransportDelegate, public ICanTransport
572574 }
573575 }
574576
575- // / @brief Tries to push next frame from TX queue to media.
576- // /
577- void pushNextFrameToMedia (Media& media)
577+ std::int8_t handleMediaTxFrame (Media& media, const CanardMicrosecond deadline, CanardMutableFrame& frame)
578578 {
579- TimePoint tx_deadline;
580- while (CanardTxQueueItem* const tx_item = peekFirstValidTxItem (media.canard_tx_queue (), tx_deadline))
579+ //
580+ // Move the payload from the frame to the media payload - `media.push` might take ownership of it.
581+ // No Sonar `cpp:S5356` and `cpp:S5357` b/c we integrate here with C libcanard API.
582+ //
583+ MediaPayload payload{frame.payload .size ,
584+ static_cast <cetl::byte*>(frame.payload .data ), // NOSONAR cpp:S5356 cpp:S5357
585+ frame.payload .allocated_size ,
586+ &media.interface ().getTxMemoryResource ()};
587+ frame.payload = {0 , nullptr , 0 };
588+
589+ auto push_result = media.interface ().push (TimePoint{std::chrono::microseconds{deadline}}, //
590+ frame.extended_can_id ,
591+ payload);
592+
593+ if (const auto * const push = cetl::get_if<IMedia::PushResult::Success>(&push_result))
581594 {
582- // Move the payload from the frame to the media payload - `media.push` might take ownership of it.
583- // No Sonar `cpp:S5356` and `cpp:S5357` b/c we integrate here with C libcanard API.
584- //
585- auto & frame_payload = tx_item->frame .payload ;
586- MediaPayload payload{frame_payload.size ,
587- static_cast <cetl::byte*>(frame_payload.data ), // NOSONAR cpp:S5356 cpp:S5357
588- frame_payload.allocated_size ,
589- &media.interface ().getTxMemoryResource ()};
590- frame_payload = {0 , nullptr , 0 };
591-
592- auto push_result = media.interface ().push (tx_deadline, tx_item->frame .extended_can_id , payload);
593-
594- // In case of media push error, we are going to drop this problematic frame
595- // (b/c it looks like media can't handle this frame),
596- // but we will continue to process with another transfer frame.
597- // Note that media not being ready/able to push a frame just yet (aka temporary)
598- // is not reported as an error (see `is_pushed` below).
599- //
600- auto * const push_failure = cetl::get_if<IMedia::PushResult::Failure>(&push_result);
601- if (nullptr == push_failure)
595+ if (!push->is_accepted )
602596 {
603- const auto push = cetl::get<IMedia::PushResult::Success>(push_result);
604- if (push.is_accepted )
605- {
606- popAndFreeCanardTxQueueItem (media.canard_tx_queue (),
607- canardInstance (),
608- tx_item,
609- false /* single frame */ );
610- }
611- else
612- {
613- // Media has not accepted the frame, so we need return original payload back to the item,
614- // so that in the future potential retry could try to push it again.
615- const auto org_payload = payload.release ();
616- frame_payload.size = std::get<0 >(org_payload);
617- frame_payload.data = std::get<1 >(org_payload);
618- frame_payload.allocated_size = std::get<2 >(org_payload);
619- }
620-
621- // If needed schedule (recursively!) next frame to push.
622- // Already existing callback will be called by executor when media TX is ready to push more.
623- //
624- if (!media.tx_callback ())
625- {
626- media.tx_callback () = media.interface ().registerPushCallback ([this , &media](const auto &) {
627- //
628- pushNextFrameToMedia (media);
629- });
630- }
631- return ;
597+ // Media has not accepted the frame, so we need return original payload back to the item,
598+ // so that in the future potential retry could try to push it again.
599+ // No Sonar `cpp:S5356` b/c we need to pass payload as a raw data to the libcanard.
600+ const auto org_payload = payload.release ();
601+ frame.payload .size = org_payload.size ;
602+ frame.payload .data = org_payload.data ; // NOSONAR cpp:S5356
603+ frame.payload .allocated_size = org_payload.allocated_size ;
632604 }
633605
634- // Release whole problematic transfer from the TX queue,
635- // so that other transfers in TX queue have their chance.
636- // Otherwise, we would be stuck in an execution loop trying to send the same frame.
637- popAndFreeCanardTxQueueItem (media.canard_tx_queue (), canardInstance (), tx_item, true /* whole transfer */ );
606+ // If needed schedule (recursively!) next frame to push.
607+ // Already existing callback will be called by executor when media TX is ready to push more.
608+ //
609+ if (!media.tx_callback ())
610+ {
611+ media.tx_callback () = media.interface ().registerPushCallback ([this , &media](const auto &) {
612+ //
613+ pushNextFrameToMedia (media);
614+ });
615+ }
616+ return push->is_accepted ? 1 : 0 ;
617+ }
638618
639- using Report = TransientErrorReport::MediaPush;
640- tryHandleTransientMediaFailure<Report>(media, std::move (*push_failure));
619+ using Report = TransientErrorReport::MediaPush;
620+ tryHandleTransientMediaFailure<Report>(media, cetl::get<IMedia::PushResult::Failure>(std::move (push_result)));
621+ return -1 ;
622+ }
641623
642- } // for a valid tx item
624+ // / @brief Tries to push next frame from TX queue to media.
625+ // /
626+ void pushNextFrameToMedia (Media& media)
627+ {
628+ auto frame_handler = [this , &media](const CanardMicrosecond deadline,
629+ CanardMutableFrame& frame) -> std::int8_t {
630+ //
631+ return handleMediaTxFrame (media, deadline, frame);
632+ };
643633
644- // There is nothing to send anymore, so we are done with this media TX - no more callbacks for now.
645- media.tx_callback ().reset ();
634+ // In case of a media failure we gonna try to push another frame from the next transfer in the queue, so
635+ // that at least (and at most) one new frame will be succesfully attempted to be pushed in the end.
636+ // Everytime we poll the queue, its size surely decrements (when `result != 0`),
637+ // so there is no risk of infinite loop here.
638+ //
639+ std::int8_t result = -1 ;
640+ while (result < 0 )
641+ {
642+ // No Sonar `cpp:S5356` & `cpp:S5356` b/c we integrate with Canard C api.
643+ result = ::canardTxPoll ( //
644+ &media.canard_tx_queue (),
645+ &canardInstance (),
646+ static_cast <CanardMicrosecond>(executor_.now ().time_since_epoch ().count ()),
647+ &frame_handler, // NOSONAR cpp:S5356
648+ [](auto * const user_reference, const auto deadline, auto * frame) {
649+ //
650+ auto * const frame_handler_ptr =
651+ static_cast <decltype (frame_handler)*>(user_reference); // NOSONAR cpp:S5356, cpp:S5357
652+ return (*frame_handler_ptr)(deadline, *frame);
653+ });
654+ }
646655 }
647656
648657 // / @brief Tries to peek the first TX item from the media TX queue which is not expired.
0 commit comments