Skip to content

Commit 5ae474c

Browse files
improved subscription API
1 parent e7f7733 commit 5ae474c

9 files changed

Lines changed: 379 additions & 273 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ int main(void)
8989

9090
// Subscribe for messages.
9191
canard_subscription_t sub;
92-
if (!canard_subscribe_13b(&node, &sub, 7509U, 63U, CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, &sub_vtable)) {
92+
const canard_subscription_t* const installed =
93+
canard_subscribe_13b(&node, &sub, 7509U, 63U, CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, &sub_vtable);
94+
if (installed != &sub) { // NULL on invalid args, otherwise points to the incumbent subscription.
9395
canard_destroy(&node);
9496
return -1;
9597
}

demos/heartbeat_monitor.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -305,12 +305,12 @@ int main(const int argc, const char* const argv[])
305305

306306
// Subscribe to Cyphal heartbeat (subject 7509, 13-bit subject-ID space).
307307
canard_subscription_t sub_cyphal;
308-
if (!canard_subscribe_13b(&ins,
309-
&sub_cyphal,
310-
CYPHAL_HEARTBEAT_SUBJECT_ID,
311-
CYPHAL_HEARTBEAT_EXTENT,
312-
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us,
313-
&g_sub_vtable_cyphal)) {
308+
if (canard_subscribe_13b(&ins,
309+
&sub_cyphal,
310+
CYPHAL_HEARTBEAT_SUBJECT_ID,
311+
CYPHAL_HEARTBEAT_EXTENT,
312+
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us,
313+
&g_sub_vtable_cyphal) != &sub_cyphal) {
314314
(void)fputs("canard_subscribe_13b failed\n", stderr);
315315
canard_destroy(&ins);
316316
(void)close(sock);
@@ -320,13 +320,13 @@ int main(const int argc, const char* const argv[])
320320
// Subscribe to DroneCAN NodeStatus (DTID 341).
321321
canard_subscription_t sub_dronecan;
322322
const uint16_t dronecan_crc_seed = canard_v0_crc_seed_from_data_type_signature(DRONECAN_NODE_STATUS_SIGNATURE);
323-
if (!canard_v0_subscribe(&ins,
324-
&sub_dronecan,
325-
DRONECAN_NODE_STATUS_DTID,
326-
dronecan_crc_seed,
327-
DRONECAN_NODE_STATUS_EXTENT,
328-
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us,
329-
&g_sub_vtable_dronecan)) {
323+
if (canard_v0_subscribe(&ins,
324+
&sub_dronecan,
325+
DRONECAN_NODE_STATUS_DTID,
326+
dronecan_crc_seed,
327+
DRONECAN_NODE_STATUS_EXTENT,
328+
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us,
329+
&g_sub_vtable_dronecan) != &sub_dronecan) {
330330
(void)fputs("canard_v0_subscribe failed\n", stderr);
331331
canard_unsubscribe(&ins, &sub_cyphal);
332332
canard_destroy(&ins);

libcanard/canard.c

Lines changed: 102 additions & 80 deletions
Large diffs are not rendered by default.

libcanard/canard.h

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -487,41 +487,50 @@ bool canard_respond(canard_t* const self,
487487
/// truncated per the implicit truncation rule (see the Spec).
488488
/// Subscription updates are log-time; per-remote RX session state is allocated lazily on demand.
489489
///
490-
/// Returns true on success, false if any of the arguments are invalid or if there is already a subscription for the
491-
/// given subject-ID.
492-
bool canard_subscribe_16b(canard_t* const self,
493-
canard_subscription_t* const subscription,
494-
const uint16_t subject_id,
495-
const size_t extent,
496-
const canard_us_t transfer_id_timeout,
497-
const canard_subscription_vtable_t* const vtable);
498-
bool canard_subscribe_13b(canard_t* const self,
499-
canard_subscription_t* const subscription,
500-
const uint16_t subject_id, // [0,8191]
501-
const size_t extent,
502-
const canard_us_t transfer_id_timeout,
503-
const canard_subscription_vtable_t* const vtable);
490+
/// Returns the passed subscription on success, the incumbent if there is already a subscription for the same subject,
491+
/// or NULL if any of the arguments are invalid.
492+
canard_subscription_t* canard_subscribe_16b(canard_t* const self,
493+
canard_subscription_t* const subscription,
494+
const uint16_t subject_id,
495+
const size_t extent,
496+
const canard_us_t transfer_id_timeout,
497+
const canard_subscription_vtable_t* const vtable);
498+
canard_subscription_t* canard_subscribe_13b(canard_t* const self,
499+
canard_subscription_t* const subscription,
500+
const uint16_t subject_id, // [0,8191]
501+
const size_t extent,
502+
const canard_us_t transfer_id_timeout,
503+
const canard_subscription_vtable_t* const vtable);
504504

505505
/// Unicast transfers in Cyphal/CAN v1.1 are supposed to be modeled as requests to service-ID 511, with a 128-element
506506
/// array of transfer-ID counters, one per remote. Some large nonzero transfer-ID timeout is required to satisfy the
507507
/// deduplication requirement. This is outside of the scope of this library so it's not implemented here.
508508
/// There may be at most one request subscription per service-ID.
509509
/// Subscription updates are log-time; per-remote RX session state is allocated lazily on demand.
510-
bool canard_subscribe_request(canard_t* const self,
511-
canard_subscription_t* const subscription,
512-
const uint16_t service_id,
513-
const size_t extent,
514-
const canard_us_t transfer_id_timeout,
515-
const canard_subscription_vtable_t* const vtable);
510+
/// Return semantics match canard_subscribe_16b().
511+
canard_subscription_t* canard_subscribe_request(canard_t* const self,
512+
canard_subscription_t* const subscription,
513+
const uint16_t service_id,
514+
const size_t extent,
515+
const canard_us_t transfer_id_timeout,
516+
const canard_subscription_vtable_t* const vtable);
516517

517518
/// There may be at most one response subscription per service-ID.
518519
/// Response transfers necessarily have a zero transfer-ID timeout: https://github.com/OpenCyphal/libcanard/issues/247.
519520
/// Subscription updates are log-time; per-remote RX session state is allocated lazily on demand.
520-
bool canard_subscribe_response(canard_t* const self,
521-
canard_subscription_t* const subscription,
522-
const uint16_t service_id,
523-
const size_t extent,
524-
const canard_subscription_vtable_t* const vtable);
521+
/// Return semantics match canard_subscribe_16b().
522+
canard_subscription_t* canard_subscribe_response(canard_t* const self,
523+
canard_subscription_t* const subscription,
524+
const uint16_t service_id,
525+
const size_t extent,
526+
const canard_subscription_vtable_t* const vtable);
527+
528+
/// Find an installed subscription by transfer kind and port-ID.
529+
/// Returns the installed subscription if found, otherwise NULL. Invalid kind values also return NULL.
530+
/// Complexity is log-time in the subscription set of the requested kind.
531+
canard_subscription_t* canard_find_subscription(const canard_t* const self,
532+
const canard_kind_t kind,
533+
const uint16_t port_id);
525534

526535
/// This can be used to undo all kinds of subscriptions, incl. v0.
527536
/// Complexity is log-time in the subscription set plus linear in the number of remote sessions owned by it.
@@ -599,33 +608,36 @@ bool canard_v0_respond(canard_t* const self,
599608

600609
/// Register a legacy v0 message subscription.
601610
/// Subscription updates are log-time; per-remote RX session state is allocated lazily on demand.
602-
bool canard_v0_subscribe(canard_t* const self,
603-
canard_subscription_t* const subscription,
604-
const uint16_t data_type_id,
605-
const uint16_t crc_seed,
606-
const size_t extent,
607-
const canard_us_t transfer_id_timeout,
608-
const canard_subscription_vtable_t* const vtable);
611+
/// Return semantics match canard_subscribe_16b().
612+
canard_subscription_t* canard_v0_subscribe(canard_t* const self,
613+
canard_subscription_t* const subscription,
614+
const uint16_t data_type_id,
615+
const uint16_t crc_seed,
616+
const size_t extent,
617+
const canard_us_t transfer_id_timeout,
618+
const canard_subscription_vtable_t* const vtable);
609619

610620
/// Register a legacy v0 request subscription.
611621
/// Subscription updates are log-time; per-remote RX session state is allocated lazily on demand.
612-
bool canard_v0_subscribe_request(canard_t* const self,
613-
canard_subscription_t* const subscription,
614-
const uint_least8_t data_type_id,
615-
const uint16_t crc_seed,
616-
const size_t extent,
617-
const canard_us_t transfer_id_timeout,
618-
const canard_subscription_vtable_t* const vtable);
622+
/// Return semantics match canard_subscribe_16b().
623+
canard_subscription_t* canard_v0_subscribe_request(canard_t* const self,
624+
canard_subscription_t* const subscription,
625+
const uint_least8_t data_type_id,
626+
const uint16_t crc_seed,
627+
const size_t extent,
628+
const canard_us_t transfer_id_timeout,
629+
const canard_subscription_vtable_t* const vtable);
619630

620631
/// Register a legacy v0 response subscription.
621632
/// Response transfers necessarily have a zero transfer-ID timeout.
622633
/// Subscription updates are log-time; per-remote RX session state is allocated lazily on demand.
623-
bool canard_v0_subscribe_response(canard_t* const self,
624-
canard_subscription_t* const subscription,
625-
const uint_least8_t data_type_id,
626-
const uint16_t crc_seed,
627-
const size_t extent,
628-
const canard_subscription_vtable_t* const vtable);
634+
/// Return semantics match canard_subscribe_16b().
635+
canard_subscription_t* canard_v0_subscribe_response(canard_t* const self,
636+
canard_subscription_t* const subscription,
637+
const uint_least8_t data_type_id,
638+
const uint16_t crc_seed,
639+
const size_t extent,
640+
const canard_subscription_vtable_t* const vtable);
629641

630642
/// Computes the CRC-16/CCITT-FALSE checksum of the data type signature in the little-endian byte order.
631643
/// This value is then used to seed the transfer CRC for UAVCAN v0 and DroneCAN transfers.

tests/src/test_api_lifecycle.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ static void test_collision_on_ingest()
358358
// Must have a subscription so that the frame can be matched and processed.
359359
rx_capture_t rx_cap = {};
360360
canard_subscription_t sub = {};
361-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 1000U, 256U, 2000000, &capture_sub_vtable));
361+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 1000U, 256U, 2000000, &capture_sub_vtable));
362362
sub.user_context = &rx_cap;
363363

364364
// Ingest a single-frame (SOT=1, EOT=1) from source_node_id=42 (our own).
@@ -389,7 +389,7 @@ static void test_collision_filters_dirty()
389389

390390
rx_capture_t rx_cap = {};
391391
canard_subscription_t sub = {};
392-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 2000U, 256U, 2000000, &capture_sub_vtable));
392+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 2000U, 256U, 2000000, &capture_sub_vtable));
393393
sub.user_context = &rx_cap;
394394

395395
// Clear the dirty flag by polling once (subscription triggers dirty).
@@ -426,7 +426,7 @@ static void test_poll_filter_reconfiguration()
426426
// Subscribe sets filters_dirty.
427427
rx_capture_t rx_cap = {};
428428
canard_subscription_t sub = {};
429-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 3000U, 256U, 2000000, &capture_sub_vtable));
429+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 3000U, 256U, 2000000, &capture_sub_vtable));
430430
sub.user_context = &rx_cap;
431431

432432
// Poll invokes filter callback.
@@ -447,7 +447,7 @@ static void test_poll_filter_after_unsubscribe()
447447

448448
rx_capture_t rx_cap = {};
449449
canard_subscription_t sub = {};
450-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 4000U, 256U, 2000000, &capture_sub_vtable));
450+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 4000U, 256U, 2000000, &capture_sub_vtable));
451451
sub.user_context = &rx_cap;
452452

453453
// First poll configures filters.
@@ -475,7 +475,7 @@ static void test_poll_session_cleanup()
475475

476476
rx_capture_t rx_cap = {};
477477
canard_subscription_t sub = {};
478-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 5000U, 256U, tid_timeout, &capture_sub_vtable));
478+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 5000U, 256U, tid_timeout, &capture_sub_vtable));
479479
sub.user_context = &rx_cap;
480480

481481
// Ingest a single-frame transfer from node 10, TID=5.
@@ -642,7 +642,7 @@ static void test_err_rx_transfer_bad_crc()
642642

643643
rx_capture_t rx_cap = {};
644644
canard_subscription_t sub = {};
645-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 7000U, 256U, 2000000, &capture_sub_vtable));
645+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 7000U, 256U, 2000000, &capture_sub_vtable));
646646
sub.user_context = &rx_cap;
647647

648648
const uint32_t can_id = make_v1v1_msg_can_id(canard_prio_nominal, 7000U, 10U);
@@ -695,7 +695,7 @@ static void test_err_collision_counter()
695695

696696
rx_capture_t rx_cap = {};
697697
canard_subscription_t sub = {};
698-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 8000U, 256U, 2000000, &capture_sub_vtable));
698+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 8000U, 256U, 2000000, &capture_sub_vtable));
699699
sub.user_context = &rx_cap;
700700

701701
TEST_ASSERT_EQUAL_UINT64(0U, self.err.collision);
@@ -771,7 +771,7 @@ static void test_redundant_rx_dedup()
771771

772772
rx_capture_t rx_cap = {};
773773
canard_subscription_t sub = {};
774-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 11000U, 256U, 2000000, &capture_sub_vtable));
774+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 11000U, 256U, 2000000, &capture_sub_vtable));
775775
sub.user_context = &rx_cap;
776776

777777
const uint32_t can_id = make_v1v1_msg_can_id(canard_prio_nominal, 11000U, 10U);
@@ -800,7 +800,7 @@ static void test_redundant_rx_dedup_multiframe()
800800

801801
rx_capture_t rx_cap = {};
802802
canard_subscription_t sub = {};
803-
TEST_ASSERT_TRUE(canard_subscribe_16b(&self, &sub, 12000U, 256U, 2000000, &capture_sub_vtable));
803+
TEST_ASSERT_EQUAL_PTR(&sub, canard_subscribe_16b(&self, &sub, 12000U, 256U, 2000000, &capture_sub_vtable));
804804
sub.user_context = &rx_cap;
805805

806806
const uint32_t can_id = make_v1v1_msg_can_id(canard_prio_nominal, 12000U, 10U);

0 commit comments

Comments
 (0)