Skip to content

Commit 8beba96

Browse files
Implement canard_subscribe, canard_subscribe_request, canard_subscribe_response, canard_unsubscribe
Extract common subscription logic into static rx_subscribe() helper. Add API-level tests covering validation, duplicate rejection, lifecycle, end-to-end single-frame reception for messages and services, unsubscribe behavior, and multi-subscription routing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d03aac3 commit 8beba96

File tree

3 files changed

+556
-0
lines changed

3 files changed

+556
-0
lines changed

libcanard/canard.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,42 @@ static bool rx_filter_configure(canard_t* const self)
16931693
return ok;
16941694
}
16951695

1696+
// Common subscribe logic: validate, initialize, insert into tree, mark filters dirty.
1697+
static bool rx_subscribe(canard_t* const self,
1698+
canard_subscription_t* const subscription,
1699+
const canard_kind_t kind,
1700+
const uint16_t port_id,
1701+
const uint16_t crc_seed,
1702+
const size_t extent,
1703+
const canard_us_t transfer_id_timeout,
1704+
const canard_subscription_vtable_t* const vtable)
1705+
{
1706+
bool ok = (self != NULL) && (subscription != NULL) && (vtable != NULL) && (vtable->on_message != NULL) &&
1707+
(transfer_id_timeout >= 0);
1708+
if (ok) {
1709+
(void)memset(subscription, 0, sizeof(*subscription));
1710+
subscription->transfer_id_timeout = transfer_id_timeout;
1711+
subscription->extent = extent;
1712+
subscription->port_id = port_id;
1713+
subscription->crc_seed = crc_seed;
1714+
subscription->kind = kind;
1715+
subscription->owner = self;
1716+
subscription->vtable = vtable;
1717+
const canard_tree_t* const existing = cavl2_find_or_insert(&self->rx.subscriptions[kind],
1718+
&subscription->port_id,
1719+
rx_subscription_cavl_compare,
1720+
&subscription->index_port_id,
1721+
cavl2_trivial_factory);
1722+
if (existing != &subscription->index_port_id) {
1723+
(void)memset(subscription, 0, sizeof(*subscription)); // Undo partial init on duplicate.
1724+
ok = false;
1725+
} else {
1726+
self->rx.filters_dirty = true;
1727+
}
1728+
}
1729+
return ok;
1730+
}
1731+
16961732
// --------------------------------------------- MISC ---------------------------------------------
16971733

16981734
static void node_id_occupancy_reset(canard_t* const self)
@@ -1896,3 +1932,54 @@ uint16_t canard_0v1_crc_seed_from_data_type_signature(const uint64_t data_type_s
18961932
}
18971933
return crc;
18981934
}
1935+
1936+
bool canard_subscribe(canard_t* const self,
1937+
canard_subscription_t* const subscription,
1938+
const uint16_t subject_id,
1939+
const bool rev_1v0,
1940+
const size_t extent,
1941+
const canard_us_t transfer_id_timeout,
1942+
const canard_subscription_vtable_t* const vtable)
1943+
{
1944+
return (!rev_1v0 || (subject_id <= CANARD_SUBJECT_ID_MAX_1v0)) &&
1945+
rx_subscribe(self,
1946+
subscription,
1947+
rev_1v0 ? canard_kind_1v0_message : canard_kind_1v1_message,
1948+
subject_id,
1949+
CRC_INITIAL,
1950+
extent,
1951+
transfer_id_timeout,
1952+
vtable);
1953+
}
1954+
1955+
bool canard_subscribe_request(canard_t* const self,
1956+
canard_subscription_t* const subscription,
1957+
const uint16_t service_id,
1958+
const size_t extent,
1959+
const canard_us_t transfer_id_timeout,
1960+
const canard_subscription_vtable_t* const vtable)
1961+
{
1962+
return (service_id <= CANARD_SERVICE_ID_MAX) &&
1963+
rx_subscribe(
1964+
self, subscription, canard_kind_1v0_request, service_id, CRC_INITIAL, extent, transfer_id_timeout, vtable);
1965+
}
1966+
1967+
bool canard_subscribe_response(canard_t* const self,
1968+
canard_subscription_t* const subscription,
1969+
const uint16_t service_id,
1970+
const size_t extent,
1971+
const canard_subscription_vtable_t* const vtable)
1972+
{
1973+
return (service_id <= CANARD_SERVICE_ID_MAX) &&
1974+
rx_subscribe(self, subscription, canard_kind_1v0_response, service_id, CRC_INITIAL, extent, 0, vtable);
1975+
}
1976+
1977+
void canard_unsubscribe(canard_t* const self, canard_subscription_t* const subscription)
1978+
{
1979+
CANARD_ASSERT((self != NULL) && (subscription != NULL) && (subscription->owner == self));
1980+
while (subscription->sessions != NULL) {
1981+
rx_session_destroy((rx_session_t*)(void*)cavl2_min(subscription->sessions));
1982+
}
1983+
cavl2_remove(&self->rx.subscriptions[subscription->kind], &subscription->index_port_id);
1984+
self->rx.filters_dirty = true;
1985+
}

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ gen_test_matrix(test_intrusive_rx_session "src/test_intrusive_rx_session.c")
106106
gen_test_matrix(test_intrusive_misc "src/test_intrusive_misc.c")
107107
# API tests.
108108
gen_test_single(test_api_tx "${library_dir}/canard.c;src/test_api_tx.cpp")
109+
gen_test_single(test_api_rx "${library_dir}/canard.c;src/test_api_rx.cpp")
109110

110111
# Coverage targets. Usage:
111112
# cmake -DENABLE_COVERAGE=ON -DNO_STATIC_ANALYSIS=ON ..

0 commit comments

Comments
 (0)