Skip to content

Commit d05d0f3

Browse files
authored
Merge pull request #4 from keningatamazon/main
Implementation of multi-frame transfer
2 parents bb9052f + ff804bf commit d05d0f3

File tree

8 files changed

+665
-52
lines changed

8 files changed

+665
-52
lines changed

libudpard/udpard.c

Lines changed: 258 additions & 36 deletions
Large diffs are not rendered by default.

libudpard/udpard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ extern "C" {
4242
/// The error code 1 is not used because -1 is often used as a generic error code in 3rd-party code.
4343
#define UDPARD_ERROR_INVALID_ARGUMENT 2
4444
#define UDPARD_ERROR_OUT_OF_MEMORY 3
45+
#define UDPARD_ERROR_OUT_OF_ORDER 4
4546

4647
/// In the case that we still need error codes but need to mutate an input we will default to a success code
4748
#define UDPARD_SUCCESS 0
4849

4950
/// MTU values for the supported protocols.
5051
#define UDPARD_MTU_UDP_IPV4 576U
5152
#define UDPARD_MTU_UDP_IPV6 1280U
53+
#define UDPARD_MTU_UDP_IPV4_TEMP 64U
5254
#define UDPARD_MTU_MAX UDPARD_MTU_UDP_IPV6 /// We may want to set this to 1400/1500 for Ethernet MTU
5355

5456
/// Parameter ranges are inclusive; the lower bound is zero for all. See Cyphal/UDP Specification for background.

tests/exposed.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,16 @@ struct RxFrameModel
7171
bool end_of_transfer = false;
7272
std::size_t payload_size = 0U;
7373
const std::uint8_t* payload = nullptr;
74+
uint32_t frame_index = 0U;
7475
};
7576

7677
// Extern C effectively discards the outer namespaces.
7778
extern "C" {
7879

7980
auto crcAdd(const std::uint32_t crc, const std::size_t size, const void* const bytes) -> std::uint32_t;
8081

82+
auto crcValue(const std::uint32_t crc) -> std::uint32_t;
83+
8184
auto txMakeMessageSessionSpecifier(const UdpardPortID subject_id,
8285
const UdpardNodeID src_node_id,
8386
const UdpardIPv4Addr local_node_addr,

tests/test_private_crc.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
TEST_CASE("TransferCRC")
99
{
1010
using exposed::crcAdd;
11+
using exposed::crcValue;
1112
std::uint32_t crc = 0xFFFFFFFFU;
1213

1314
crc = crcAdd(crc, 1, "1");
14-
REQUIRE(0x90F599E3U == crc);
15+
REQUIRE(0x90F599E3U == crcValue(crc));
1516
crc = crcAdd(crc, 1, "2");
16-
REQUIRE(0x7355C460U == crc);
17+
REQUIRE(0x7355C460U == crcValue(crc));
1718
crc = crcAdd(crc, 1, "3");
18-
REQUIRE(0x107B2FB2U == crc);
19+
REQUIRE(0x107B2FB2U == crcValue(crc));
1920

2021
crc = crcAdd(crc, 6, "456789");
21-
REQUIRE(0xE3069283U == crc);
22+
REQUIRE(0xE3069283U == crcValue(crc));
2223
}

tests/test_private_rx.cpp

Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,9 @@ TEST_CASE("rxSessionUpdate")
425425

426426
Instance ins;
427427
ins.getAllocator().setAllocationCeiling(16);
428+
Instance ins_1;
429+
ins_1.getAllocator().setAllocationCeiling(16);
430+
428431

429432
RxFrameModel frame;
430433
frame.timestamp_usec = 10'000'000;
@@ -440,8 +443,11 @@ TEST_CASE("rxSessionUpdate")
440443
frame.payload = reinterpret_cast<const uint8_t*>("\x01\x01\x01");
441444

442445
RxSession rxs;
446+
RxSession rxs_1;
443447
rxs.transfer_id = 31;
444448
rxs.redundant_transport_index = 1;
449+
rxs_1.transfer_id = 32;
450+
rxs_1.redundant_transport_index = 1;
445451

446452
UdpardRxTransfer transfer{};
447453

@@ -457,7 +463,19 @@ TEST_CASE("rxSessionUpdate")
457463
&transfer);
458464
};
459465

460-
// const auto crc = [](const char* const string) { return crcAdd(0xFFFF, std::strlen(string), string); };
466+
const auto updateAnotherSession = [&](const std::uint8_t redundant_transport_index,
467+
const std::uint64_t tid_timeout_usec,
468+
const std::size_t extent) {
469+
return rxSessionUpdate(&ins_1.getInstance(),
470+
&rxs_1,
471+
&frame,
472+
redundant_transport_index,
473+
tid_timeout_usec,
474+
extent,
475+
&transfer);
476+
};
477+
478+
const auto crc = [](const char* const string) { return crcAdd(0xFFFFFFFFU, std::strlen(string), string); };
461479

462480
// Accept one transfer.
463481
REQUIRE(1 == update(1, 1'000'000, 16));
@@ -548,6 +566,120 @@ TEST_CASE("rxSessionUpdate")
548566
REQUIRE(ins.getAllocator().getTotalAllocatedAmount() == 16);
549567
ins.getAllocator().deallocate(transfer.payload);
550568

569+
// Multi-frame, first frame
570+
frame.timestamp_usec = 20'000'100;
571+
frame.transfer_id = 13;
572+
frame.end_of_transfer = false;
573+
frame.payload_size = 7;
574+
frame.frame_index = 1;
575+
frame.payload = reinterpret_cast<const uint8_t*>("\x06\x06\x06\x06\x06\x06\x06");
576+
REQUIRE(0 == update(0, 1'000'000, 16));
577+
578+
REQUIRE(rxs.transfer_timestamp_usec == 20'000'100);
579+
REQUIRE(rxs.payload_size == 7);
580+
REQUIRE(0 == std::memcmp(rxs.payload, "\x06\x06\x06\x06\x06\x06\x06", 7));
581+
REQUIRE(rxs.calculated_crc == crc("\x06\x06\x06\x06\x06\x06\x06"));
582+
REQUIRE(rxs.transfer_id == 13U);
583+
REQUIRE(rxs.redundant_transport_index == 0);
584+
REQUIRE(ins.getAllocator().getNumAllocatedFragments() == 1);
585+
REQUIRE(ins.getAllocator().getTotalAllocatedAmount() == 16);
586+
587+
// Update another session using same frame.
588+
REQUIRE(0 == updateAnotherSession(1, 1'000'000, 16));
589+
REQUIRE(rxs_1.transfer_timestamp_usec == 20'000'100);
590+
REQUIRE(rxs_1.payload_size == 7);
591+
REQUIRE(0 == std::memcmp(rxs_1.payload, "\x06\x06\x06\x06\x06\x06\x06", 7));
592+
REQUIRE(rxs_1.calculated_crc == crc("\x06\x06\x06\x06\x06\x06\x06"));
593+
REQUIRE(rxs_1.transfer_id == 13U);
594+
REQUIRE(rxs_1.redundant_transport_index == 1);
595+
REQUIRE(ins_1.getAllocator().getNumAllocatedFragments() == 1);
596+
REQUIRE(ins_1.getAllocator().getTotalAllocatedAmount() == 16);
597+
598+
// Multi-frame, bad middle, out-of-order
599+
frame.timestamp_usec = 20'000'200;
600+
frame.start_of_transfer = false;
601+
frame.end_of_transfer = false;
602+
frame.frame_index = 3 + (uint32_t) (1U << (uint32_t) 31U);
603+
frame.payload_size = 2;
604+
frame.payload = reinterpret_cast<const uint8_t*>("\x09\x09");
605+
REQUIRE(-UDPARD_ERROR_OUT_OF_ORDER == update(0, 1'000'000, 16));
606+
// Update another session using same frame, fail.
607+
REQUIRE(-UDPARD_ERROR_OUT_OF_ORDER == updateAnotherSession(1, 1'000'000, 16));
608+
609+
// Multi-frame, middle.
610+
frame.timestamp_usec = 20'000'200;
611+
frame.start_of_transfer = false;
612+
frame.end_of_transfer = false;
613+
frame.frame_index = 2;
614+
frame.payload_size = 7;
615+
frame.payload = reinterpret_cast<const uint8_t*>("\x07\x07\x07\x07\x07\x07\x07");
616+
REQUIRE(0 == update(0, 1'000'000, 16));
617+
REQUIRE(rxs.transfer_timestamp_usec == 20'000'100);
618+
REQUIRE(rxs.payload_size == 14);
619+
REQUIRE(0 == std::memcmp(rxs.payload, "\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07", 14));
620+
REQUIRE(rxs.calculated_crc == crc("\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07"));
621+
REQUIRE(rxs.transfer_id == 13U);
622+
REQUIRE(rxs.redundant_transport_index == 0);
623+
REQUIRE(ins.getAllocator().getNumAllocatedFragments() == 1);
624+
REQUIRE(ins.getAllocator().getTotalAllocatedAmount() == 16);
625+
626+
// Update another session using same frame.
627+
REQUIRE(0 == updateAnotherSession(1, 1'000'000, 16));
628+
REQUIRE(rxs_1.transfer_timestamp_usec == 20'000'100);
629+
REQUIRE(rxs_1.payload_size == 14);
630+
REQUIRE(0 == std::memcmp(rxs_1.payload, "\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07", 14));
631+
REQUIRE(rxs_1.calculated_crc == crc("\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07"));
632+
REQUIRE(rxs_1.transfer_id == 13U);
633+
REQUIRE(rxs_1.redundant_transport_index == 1);
634+
REQUIRE(ins_1.getAllocator().getNumAllocatedFragments() == 1);
635+
REQUIRE(ins_1.getAllocator().getTotalAllocatedAmount() == 16);
636+
637+
// Multi-frame, last.
638+
frame.timestamp_usec = 20'000'400;
639+
frame.start_of_transfer = false;
640+
frame.end_of_transfer = true;
641+
frame.frame_index = 3 + (uint32_t) (1U << (uint32_t) 31U);
642+
frame.payload_size = 8; // The payload is IMPLICITLY TRUNCATED, and the CRC IS STILL VALIDATED.
643+
frame.payload = reinterpret_cast<const uint8_t*>("\x09\x09\x09\x09\x32\x98\x04\x7B");
644+
REQUIRE(1 == update(0, 1'000'000, 16));
645+
REQUIRE(rxs.transfer_timestamp_usec == 20'000'100); // First frame.
646+
REQUIRE(rxs.payload_size == 0);
647+
REQUIRE(rxs.payload == nullptr);
648+
REQUIRE(rxs.calculated_crc == 0xFFFFFFFFU);
649+
REQUIRE(rxs.transfer_id == 14U);
650+
REQUIRE(rxs.redundant_transport_index == 0);
651+
REQUIRE(transfer.timestamp_usec == 20'000'100);
652+
REQUIRE(transfer.metadata.priority == UdpardPrioritySlow);
653+
REQUIRE(transfer.metadata.transfer_kind == UdpardTransferKindMessage);
654+
REQUIRE(transfer.metadata.port_id == 2'222);
655+
REQUIRE(transfer.metadata.remote_node_id == 55);
656+
REQUIRE(transfer.metadata.transfer_id == 13);
657+
REQUIRE(transfer.payload_size == 16);
658+
REQUIRE(0 == std::memcmp(transfer.payload, "\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07\x09\x09", 16));
659+
REQUIRE(ins.getAllocator().getNumAllocatedFragments() == 1);
660+
REQUIRE(ins.getAllocator().getTotalAllocatedAmount() == 16);
661+
ins.getAllocator().deallocate(transfer.payload);
662+
663+
// Update another session using same frame.
664+
REQUIRE(1 == updateAnotherSession(1, 1'000'000, 16));
665+
REQUIRE(rxs_1.transfer_timestamp_usec == 20'000'100); // First frame.
666+
REQUIRE(rxs_1.payload_size == 0);
667+
REQUIRE(rxs_1.payload == nullptr);
668+
REQUIRE(rxs_1.calculated_crc == 0xFFFFFFFFU);
669+
REQUIRE(rxs_1.transfer_id == 14U);
670+
REQUIRE(rxs_1.redundant_transport_index == 1);
671+
REQUIRE(transfer.timestamp_usec == 20'000'100);
672+
REQUIRE(transfer.metadata.priority == UdpardPrioritySlow);
673+
REQUIRE(transfer.metadata.transfer_kind == UdpardTransferKindMessage);
674+
REQUIRE(transfer.metadata.port_id == 2'222);
675+
REQUIRE(transfer.metadata.remote_node_id == 55);
676+
REQUIRE(transfer.metadata.transfer_id == 13);
677+
REQUIRE(transfer.payload_size == 16);
678+
REQUIRE(0 == std::memcmp(transfer.payload, "\x06\x06\x06\x06\x06\x06\x06\x07\x07\x07\x07\x07\x07\x07\x09\x09", 16));
679+
REQUIRE(ins_1.getAllocator().getNumAllocatedFragments() == 1);
680+
REQUIRE(ins_1.getAllocator().getTotalAllocatedAmount() == 16);
681+
ins_1.getAllocator().deallocate(transfer.payload);
682+
551683
// Restart by TID timeout, not the first frame.
552684
frame.timestamp_usec = 30'000'000;
553685
frame.transfer_id = 12; // Goes back.
@@ -556,7 +688,7 @@ TEST_CASE("rxSessionUpdate")
556688
frame.payload_size = 7;
557689
frame.payload = reinterpret_cast<const uint8_t*>("\x0A\x0A\x0A\x0A\x0A\x0A\x0A");
558690
REQUIRE(0 == update(2, 1'000'000, 16));
559-
REQUIRE(rxs.transfer_timestamp_usec == 20'000'000); // No change.
691+
REQUIRE(rxs.transfer_timestamp_usec == 20'000'100); // No change.
560692
REQUIRE(rxs.payload_size == 0);
561693
REQUIRE(rxs.payload == nullptr);
562694
REQUIRE(rxs.calculated_crc == 0xFFFFFFFFU);

tests/test_public_roundtrip.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,8 @@ TEST_CASE("RoundtripSimple")
9595
(tran.transfer_kind == UdpardTransferKindMessage) ? UDPARD_NODE_ID_UNSET : ins_rx.getNodeID();
9696
tran.transfer_id = (st.transfer_id++) & UDPARD_TRANSFER_ID_MAX;
9797

98-
/* We will use a random MTU when multiframe transfers are implemented.
9998
// Use a random MTU.
100-
que_tx.setMTU(static_cast<std::uint8_t>(getRandomNatural(256U)));
101-
*/
102-
// Set the MTU to the max for testing
103-
que_tx.setMTU(UDPARD_MTU_MAX);
99+
que_tx.setMTU(static_cast<std::uint8_t>(getRandomNatural(256U - sizeof(UdpardFrameHeader)) + sizeof(UdpardFrameHeader)) + 1U);
104100

105101
// Push the transfer.
106102
bool sleep = false;
@@ -176,12 +172,17 @@ TEST_CASE("RoundtripSimple")
176172
UdpardRxSubscription* subscription = nullptr;
177173
std::int8_t result =
178174
ins_rx.rxAccept(ti->tx_deadline_usec, ti->frame, 0, ti->specifier, transfer, &subscription);
179-
REQUIRE(0 == ins_rx.rxAccept(ti->tx_deadline_usec,
175+
REQUIRE(((-UDPARD_ERROR_OUT_OF_ORDER == ins_rx.rxAccept(ti->tx_deadline_usec,
180176
ti->frame,
181177
1,
182178
ti->specifier,
183179
transfer,
184-
&subscription)); // Redundant interface will never be used here.
180+
&subscription)) || (0 == ins_rx.rxAccept(ti->tx_deadline_usec,
181+
ti->frame,
182+
1,
183+
ti->specifier,
184+
transfer,
185+
&subscription)))); // Redundant interface will never be used here.
185186
if (result == 1)
186187
{
187188
Pending reference{}; // Fetch the reference transfer from the list of pending.
@@ -216,7 +217,7 @@ TEST_CASE("RoundtripSimple")
216217
}
217218
else
218219
{
219-
REQUIRE(result == 0);
220+
REQUIRE((result == 0 || result == -UDPARD_ERROR_OUT_OF_ORDER));
220221
}
221222
}
222223
else

tests/test_public_rx.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ TEST_CASE("RxBasic0")
2323
using exposed::RxSession;
2424

2525
Instance ins;
26+
Instance ins_new;
2627
UdpardRxTransfer transfer{};
2728
UdpardSessionSpecifier specifier{};
2829
UdpardFrameHeader header{};

0 commit comments

Comments
 (0)