Skip to content

Commit 417d92c

Browse files
Improve the README and the transmission API (#54)
Improve the TX API usability for the case of redundant interfaces: do not increment the transfer-ID automatically so that the caller does not have to restore the original value before sending a redundant copy of the transfer
1 parent 6e72fe5 commit 417d92c

File tree

5 files changed

+66
-89
lines changed

5 files changed

+66
-89
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ and the network layer below the library using a third-party UDP/IP stack impleme
5353
In the most straightforward case, the network layer can be based on the standard Berkeley socket API
5454
or a lightweight embedded stack such as LwIP.
5555

56+
```mermaid
57+
%%{init: {"fontFamily": "Ubuntu Mono, monospace", "flowchart": {"curve": "basis"}}}%%
58+
flowchart TD
59+
classDef OpenCyphal color:#00DAC6,fill:#1700b3,stroke:#00DAC6,stroke-width:2px,font-weight:600
60+
Application <-->|messages,\nrequests,\nresponses| LibUDPard[fa:fa-code LibUDPard]
61+
class LibUDPard OpenCyphal
62+
LibUDPard <-->|multicast datagrams| UDP
63+
subgraph domain_udpip["3rd-party UDP/IP+IGMP stack"]
64+
UDP <--> IP["IPv4, IGMPv1+"] <--> MAC
65+
end
66+
MAC <--> PHY
67+
```
68+
69+
To integrate the library into your application, simply copy the files under `libudpard/` into your project tree,
70+
or add this entire repository as a submodule.
71+
The library contains only one translation unit named `udpard.c`;
72+
no special compiler options are needed to build it.
73+
The library should be compatible with all conventional computer architectures where a standards-compliant C99 compiler
74+
is available.
75+
5676
**Read the API docs in [`libudpard/udpard.h`](libudpard/udpard.h).**
5777
For complete usage examples, please refer to <https://github.com/OpenCyphal-Garage/demos>.
5878

libudpard/udpard.c

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -548,14 +548,13 @@ int32_t udpardTxPublish(struct UdpardTx* const self,
548548
const UdpardMicrosecond deadline_usec,
549549
const enum UdpardPriority priority,
550550
const UdpardPortID subject_id,
551-
UdpardTransferID* const transfer_id,
551+
const UdpardTransferID transfer_id,
552552
const struct UdpardPayload payload,
553553
void* const user_transfer_reference)
554554
{
555555
int32_t out = -UDPARD_ERROR_ARGUMENT;
556556
const bool args_ok = (self != NULL) && (self->local_node_id != NULL) && (priority <= UDPARD_PRIORITY_MAX) &&
557-
(subject_id <= UDPARD_SUBJECT_ID_MAX) && (transfer_id != NULL) &&
558-
((payload.data != NULL) || (payload.size == 0U));
557+
(subject_id <= UDPARD_SUBJECT_ID_MAX) && ((payload.data != NULL) || (payload.size == 0U));
559558
if (args_ok)
560559
{
561560
out = txPush(self,
@@ -564,16 +563,12 @@ int32_t udpardTxPublish(struct UdpardTx* const self,
564563
.priority = priority,
565564
.src_node_id = *self->local_node_id,
566565
.dst_node_id = UDPARD_NODE_ID_UNSET,
567-
.transfer_id = *transfer_id,
566+
.transfer_id = transfer_id,
568567
.data_specifier = subject_id,
569568
},
570569
makeSubjectUDPIPEndpoint(subject_id),
571570
payload,
572571
user_transfer_reference);
573-
if (out > 0)
574-
{
575-
++(*transfer_id);
576-
}
577572
}
578573
return out;
579574
}
@@ -583,14 +578,14 @@ int32_t udpardTxRequest(struct UdpardTx* const self,
583578
const enum UdpardPriority priority,
584579
const UdpardPortID service_id,
585580
const UdpardNodeID server_node_id,
586-
UdpardTransferID* const transfer_id,
581+
const UdpardTransferID transfer_id,
587582
const struct UdpardPayload payload,
588583
void* const user_transfer_reference)
589584
{
590585
int32_t out = -UDPARD_ERROR_ARGUMENT;
591586
const bool args_ok = (self != NULL) && (self->local_node_id != NULL) && (priority <= UDPARD_PRIORITY_MAX) &&
592587
(service_id <= UDPARD_SERVICE_ID_MAX) && (server_node_id <= UDPARD_NODE_ID_MAX) &&
593-
(transfer_id != NULL) && ((payload.data != NULL) || (payload.size == 0U));
588+
((payload.data != NULL) || (payload.size == 0U));
594589
if (args_ok)
595590
{
596591
out = txPush(self,
@@ -599,17 +594,13 @@ int32_t udpardTxRequest(struct UdpardTx* const self,
599594
.priority = priority,
600595
.src_node_id = *self->local_node_id,
601596
.dst_node_id = server_node_id,
602-
.transfer_id = *transfer_id,
597+
.transfer_id = transfer_id,
603598
.data_specifier = DATA_SPECIFIER_SERVICE_NOT_MESSAGE_MASK |
604599
DATA_SPECIFIER_SERVICE_REQUEST_NOT_RESPONSE_MASK | service_id,
605600
},
606601
makeServiceUDPIPEndpoint(server_node_id),
607602
payload,
608603
user_transfer_reference);
609-
if (out > 0)
610-
{
611-
++(*transfer_id);
612-
}
613604
}
614605
return out;
615606
}

libudpard/udpard.h

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -506,12 +506,12 @@ int_fast8_t udpardTxInit(struct UdpardTx* const self,
506506
/// The MTU of the generated datagrams is dependent on the value of the MTU setting at the time when this function
507507
/// is invoked. The MTU setting can be changed arbitrarily between invocations.
508508
///
509-
/// The pointer to the transfer_id will be used to populate the transfer_id field of the generated datagrams and
510-
/// then to increment the pointed-to value to prepare it for the next publication.
509+
/// The transfer_id parameter will be used to populate the transfer_id field of the generated datagrams.
510+
/// The caller shall increment the transfer-ID counter after each successful invocation of this function
511+
/// per redundant interface; the same transfer published over redundant interfaces shall have the same transfer-ID.
511512
/// There shall be a separate transfer-ID counter per subject (topic).
512-
/// The lifetime of the pointed-to transfer-ID counter must exceed the lifetime of the intent to publish on this
513-
/// subject (topic); one common approach is to use a static variable.
514-
/// The transfer-ID counter is not modified if the function fails.
513+
/// The lifetime of the transfer-ID counter must exceed the lifetime of the intent to publish on this subject (topic);
514+
/// one common approach is to use a static variable or a field in a type that contains the state of the publisher.
515515
///
516516
/// The user_transfer_reference is an opaque pointer that will be assigned to the user_transfer_reference field of
517517
/// each enqueued item. The library itself does not use or check this value in any way, so it can be NULL if not needed.
@@ -558,19 +558,21 @@ int32_t udpardTxPublish(struct UdpardTx* const self,
558558
const UdpardMicrosecond deadline_usec,
559559
const enum UdpardPriority priority,
560560
const UdpardPortID subject_id,
561-
UdpardTransferID* const transfer_id,
561+
const UdpardTransferID transfer_id,
562562
const struct UdpardPayload payload,
563563
void* const user_transfer_reference);
564564

565565
/// This is similar to udpardTxPublish except that it is intended for service request transfers.
566566
/// It takes the node-ID of the server that is intended to receive the request.
567567
///
568-
/// The pointer to the transfer_id will be used to populate the transfer_id field of the generated datagrams and
569-
/// then to increment the pointed-to value to prepare it for the next request.
568+
/// The transfer_id parameter will be used to populate the transfer_id field of the generated datagrams.
569+
/// The caller shall increment the transfer-ID counter after each successful invocation of this function
570+
/// per redundant interface; the same transfer published over redundant interfaces shall have the same transfer-ID.
570571
/// There shall be a separate transfer-ID counter per pair of (service-ID, server node-ID).
571-
/// The lifetime of the pointed-to transfer-ID counter must exceed the lifetime of the intent to invoke this service
572-
/// on this server node; one common approach is to use a static array indexed by the server node-ID per service-ID
573-
/// (memory-constrained applications may choose a more compact container).
572+
/// The lifetime of the transfer-ID counter must exceed the lifetime of the intent to invoke this service
573+
/// on this server node; one common approach is to use a static array or a struct field indexed by
574+
/// the server node-ID per service-ID (memory-constrained applications may choose a more compact container;
575+
/// e.g., a list or an AVL tree).
574576
///
575577
/// Additional error conditions:
576578
/// - UDPARD_ERROR_ARGUMENT if the server node-ID value is invalid.
@@ -582,14 +584,13 @@ int32_t udpardTxRequest(struct UdpardTx* const self,
582584
const enum UdpardPriority priority,
583585
const UdpardPortID service_id,
584586
const UdpardNodeID server_node_id,
585-
UdpardTransferID* const transfer_id,
587+
const UdpardTransferID transfer_id,
586588
const struct UdpardPayload payload,
587589
void* const user_transfer_reference);
588590

589-
/// This is similar to udpardTxRequest except that it takes the node-ID of the client instead of server,
590-
/// and the transfer-ID is passed by value rather than by pointer.
591-
/// The transfer-ID is passed by value because when responding to an RPC-service request, the server must
592-
/// reuse the transfer-ID value of the request (this is to allow the client to match responses with their requests).
591+
/// This is similar to udpardTxRequest except that it takes the node-ID of the client instead of server.
592+
/// The transfer-ID must be the same as that of the corresponding RPC-request transfer;
593+
/// this is to allow the client to match responses with their requests.
593594
int32_t udpardTxRespond(struct UdpardTx* const self,
594595
const UdpardMicrosecond deadline_usec,
595596
const enum UdpardPriority priority,

tests/src/test_e2e.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ void testPubSub()
101101
10'000'000,
102102
UdpardPrioritySlow,
103103
5000,
104-
&transfer_id.at(0),
104+
transfer_id.at(0)++,
105105
makePayload("Last night, I had a dream."),
106106
nullptr));
107107
const std::string_view Eden =
@@ -114,7 +114,7 @@ void testPubSub()
114114
10'001'000,
115115
UdpardPriorityNominal,
116116
5000,
117-
&transfer_id.at(0),
117+
transfer_id.at(0),
118118
makePayload(Eden),
119119
nullptr));
120120
node_id = 42; // Change the node-ID to allow multi-frame transfers, then try again.
@@ -123,7 +123,7 @@ void testPubSub()
123123
10'002'000,
124124
UdpardPriorityOptional,
125125
5000,
126-
&transfer_id.at(0),
126+
transfer_id.at(0)++,
127127
makePayload(Eden),
128128
nullptr));
129129
TEST_ASSERT_EQUAL(5, tx.queue_size);
@@ -136,7 +136,7 @@ void testPubSub()
136136
10'003'000,
137137
UdpardPriorityNominal,
138138
5001,
139-
&transfer_id.at(1),
139+
transfer_id.at(1)++,
140140
makePayload(Later),
141141
nullptr));
142142
TEST_ASSERT_EQUAL(6, tx.queue_size);
@@ -148,7 +148,7 @@ void testPubSub()
148148
10'004'000,
149149
UdpardPriorityNominal,
150150
5002,
151-
&transfer_id.at(2),
151+
transfer_id.at(2)++,
152152
makePayload(Dark),
153153
nullptr));
154154
TEST_ASSERT_EQUAL(7, tx.queue_size);
@@ -446,7 +446,7 @@ void testRPC()
446446
UdpardPriorityFast,
447447
200,
448448
4321,
449-
&transfer_id_shared,
449+
transfer_id_shared++,
450450
makePayload(Entry),
451451
nullptr));
452452
TEST_ASSERT_EQUAL(1, tx.queue_size);
@@ -464,7 +464,6 @@ void testRPC()
464464
makePayload(Forest),
465465
nullptr));
466466
TEST_ASSERT_EQUAL(2, tx.queue_size);
467-
TEST_ASSERT_EQUAL(1, transfer_id_shared); // Not incremented.
468467

469468
// Transmit the enqueued frames by pushing them into the RPC dispatcher.
470469
UdpardRxRPCTransfer transfer{};

0 commit comments

Comments
 (0)