File astutedds.h

File List > astutedds > c > astutedds.h

Go to the documentation of this file

//
// Copyright (c) 2026, Astute Systems PTY LTD
//
// This file is part of the Astute DDS developed by Astute Systems.
//
// See the commercial LICENSE file in the project root for full license details.
//
// @file astutedds.h
// @brief AstuteDDS C API — opaque-handle interface for FFI consumers.
//
// All DDS entities are exposed as opaque pointers.  The caller must never
// dereference them directly.  Lifetime rules mirror the DDS specification:
//
//   DomainParticipant  owns  Topic | Publisher | Subscriber
//   Publisher          owns  DataWriter
//   Subscriber         owns  DataReader
//
// Every create_* call must be matched by the corresponding delete_* call.
// Deleting a parent before its children is undefined behaviour.
//
#ifndef ASTUTEDDS_C_ASTUTEDDS_H
#define ASTUTEDDS_C_ASTUTEDDS_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

// ── Opaque handle types ───────────────────────────────────────────────────────

typedef struct AstuteDDS_Participant_s  *AstuteDDS_Participant;
typedef struct AstuteDDS_Topic_s        *AstuteDDS_Topic;
typedef struct AstuteDDS_Publisher_s    *AstuteDDS_Publisher;
typedef struct AstuteDDS_Subscriber_s   *AstuteDDS_Subscriber;
typedef struct AstuteDDS_DataWriter_s   *AstuteDDS_DataWriter;
typedef struct AstuteDDS_DataReader_s   *AstuteDDS_DataReader;

// ── Return codes ──────────────────────────────────────────────────────────────

typedef enum
{
    ASTUTEDDS_OK              = 0,
    ASTUTEDDS_ERROR           = 1,
    ASTUTEDDS_UNSUPPORTED     = 2,
    ASTUTEDDS_BAD_PARAM       = 3,
    ASTUTEDDS_PRECONDITION    = 4,
    ASTUTEDDS_OUT_OF_RESOURCES = 5,
    ASTUTEDDS_NOT_ENABLED     = 6,
    ASTUTEDDS_TIMEOUT         = 10,
    ASTUTEDDS_NO_DATA         = 11,
} AstuteDDS_ReturnCode;

// ── Reliability / History kind enums ─────────────────────────────────────────

typedef enum { ASTUTEDDS_BEST_EFFORT = 0, ASTUTEDDS_RELIABLE = 1 } AstuteDDS_Reliability;
typedef enum { ASTUTEDDS_KEEP_LAST = 0, ASTUTEDDS_KEEP_ALL = 1 } AstuteDDS_HistoryKind;
typedef enum { ASTUTEDDS_VOLATILE = 0, ASTUTEDDS_TRANSIENT_LOCAL = 1 } AstuteDDS_Durability;

typedef enum
{
    ASTUTEDDS_AUTOMATIC_LIVELINESS            = 0,
    ASTUTEDDS_MANUAL_BY_PARTICIPANT_LIVELINESS = 1,
    ASTUTEDDS_MANUAL_BY_TOPIC_LIVELINESS      = 2
} AstuteDDS_LivelinessKind;

#define ASTUTEDDS_DURATION_INFINITE (INT64_C(0x7fffffffffffffff))

// ── QoS structs ───────────────────────────────────────────────────────────────

typedef struct
{
    AstuteDDS_Reliability     reliability;
    AstuteDDS_HistoryKind     history_kind;
    int32_t                   history_depth;
    AstuteDDS_Durability      durability;
    AstuteDDS_LivelinessKind  liveliness_kind;
    int64_t                   liveliness_lease_duration_ns;
    int64_t                   deadline_period_ns;
    int64_t                   lifespan_duration_ns;
    const char *const        *partitions;
    size_t                    partition_count;
} AstuteDDS_DataWriterQos;

typedef struct
{
    AstuteDDS_Reliability     reliability;
    AstuteDDS_HistoryKind     history_kind;
    int32_t                   history_depth;
    AstuteDDS_Durability      durability;
    int                       ignore_local_publications;
    AstuteDDS_LivelinessKind  liveliness_kind;
    int64_t                   liveliness_lease_duration_ns;
    int64_t                   deadline_period_ns;
    int64_t                   lifespan_duration_ns;
    const char *const        *partitions;
    size_t                    partition_count;
} AstuteDDS_DataReaderQos;

// ── Sample received by DataReader ─────────────────────────────────────────────

typedef struct
{
    const uint8_t *data;    
    size_t         length;
    int            valid;   
} AstuteDDS_Sample;

// ── Default QoS helpers ───────────────────────────────────────────────────────

static inline AstuteDDS_DataWriterQos astutedds_default_datawriter_qos(void)
{
    AstuteDDS_DataWriterQos q;
    q.reliability                  = ASTUTEDDS_RELIABLE;
    q.history_kind                 = ASTUTEDDS_KEEP_LAST;
    q.history_depth                = 1;
    q.durability                   = ASTUTEDDS_VOLATILE;
    q.liveliness_kind              = ASTUTEDDS_AUTOMATIC_LIVELINESS;
    q.liveliness_lease_duration_ns = ASTUTEDDS_DURATION_INFINITE;
    q.deadline_period_ns           = ASTUTEDDS_DURATION_INFINITE;
    q.lifespan_duration_ns         = ASTUTEDDS_DURATION_INFINITE;
    q.partitions                   = NULL;
    q.partition_count              = 0;
    return q;
}

static inline AstuteDDS_DataReaderQos astutedds_default_datareader_qos(void)
{
    AstuteDDS_DataReaderQos q;
    q.reliability                  = ASTUTEDDS_RELIABLE;
    q.history_kind                 = ASTUTEDDS_KEEP_LAST;
    q.history_depth                = 1;
    q.durability                   = ASTUTEDDS_VOLATILE;
    q.ignore_local_publications    = 0;
    q.liveliness_kind              = ASTUTEDDS_AUTOMATIC_LIVELINESS;
    q.liveliness_lease_duration_ns = ASTUTEDDS_DURATION_INFINITE;
    q.deadline_period_ns           = ASTUTEDDS_DURATION_INFINITE;
    q.lifespan_duration_ns         = ASTUTEDDS_DURATION_INFINITE;
    q.partitions                   = NULL;
    q.partition_count              = 0;
    return q;
}

// ── DomainParticipant ─────────────────────────────────────────────────────────

AstuteDDS_Participant astutedds_create_participant(uint32_t domain_id);

void astutedds_delete_participant(AstuteDDS_Participant p);

AstuteDDS_ReturnCode astutedds_participant_set_user_data(
    AstuteDDS_Participant p,
    const uint8_t        *data,
    size_t                length);

// ── Topic ────────────────────────────────────────────────────────────────────

AstuteDDS_Topic astutedds_create_topic(
    AstuteDDS_Participant p,
    const char           *topic_name,
    const char           *type_name);

void astutedds_delete_topic(AstuteDDS_Participant p, AstuteDDS_Topic t);

const char *astutedds_topic_name(AstuteDDS_Topic t);
const char *astutedds_topic_type_name(AstuteDDS_Topic t);

// ── Publisher ─────────────────────────────────────────────────────────────────

AstuteDDS_Publisher astutedds_create_publisher(AstuteDDS_Participant p);
void                astutedds_delete_publisher(AstuteDDS_Participant p, AstuteDDS_Publisher pub);

// ── DataWriter ────────────────────────────────────────────────────────────────

AstuteDDS_DataWriter astutedds_create_datawriter(
    AstuteDDS_Publisher         pub,
    AstuteDDS_Topic             topic,
    const AstuteDDS_DataWriterQos *qos);  
void astutedds_delete_datawriter(AstuteDDS_Publisher pub, AstuteDDS_DataWriter w);

AstuteDDS_ReturnCode astutedds_write(
    AstuteDDS_DataWriter w,
    const uint8_t        *data,
    size_t                len);

// ── Subscriber ───────────────────────────────────────────────────────────────

AstuteDDS_Subscriber astutedds_create_subscriber(AstuteDDS_Participant p);
void                 astutedds_delete_subscriber(AstuteDDS_Participant p, AstuteDDS_Subscriber sub);

// ── DataReader ────────────────────────────────────────────────────────────────

AstuteDDS_DataReader astutedds_create_datareader(
    AstuteDDS_Subscriber          sub,
    AstuteDDS_Topic               topic,
    const AstuteDDS_DataReaderQos *qos);  
void astutedds_delete_datareader(AstuteDDS_Subscriber sub, AstuteDDS_DataReader r);

AstuteDDS_ReturnCode astutedds_take_next(AstuteDDS_DataReader r, AstuteDDS_Sample *out);

size_t astutedds_unread_count(AstuteDDS_DataReader r);

// ── Graph / discovery ─────────────────────────────────────────────────────────

typedef void (*AstuteDDS_TopicCallback)(
    void *       user_data,
    const char * topic_name,
    const char * type_name);

void astutedds_get_topic_names_and_types(
    AstuteDDS_Participant   p,
    AstuteDDS_TopicCallback callback,
    void *                  user_data);

// ── Entity identity (§1.2, §1.3) ──────────────────────────────────────────────

typedef struct
{
    uint8_t bytes[16];
} AstuteDDS_Gid;

AstuteDDS_ReturnCode astutedds_participant_get_gid(
    AstuteDDS_Participant p,
    AstuteDDS_Gid        *out);

AstuteDDS_ReturnCode astutedds_datawriter_get_gid(
    AstuteDDS_DataWriter w,
    AstuteDDS_Gid       *out);

AstuteDDS_ReturnCode astutedds_datareader_get_gid(
    AstuteDDS_DataReader r,
    AstuteDDS_Gid       *out);

// ── QoS read-back (§1.5) ─────────────────────────────────────────────────────

AstuteDDS_ReturnCode astutedds_datawriter_get_qos(
    AstuteDDS_DataWriter     w,
    AstuteDDS_DataWriterQos *out);

AstuteDDS_ReturnCode astutedds_datareader_get_qos(
    AstuteDDS_DataReader     r,
    AstuteDDS_DataReaderQos *out);

// ── Endpoint enumeration (§1.2) ──────────────────────────────────────────────

typedef struct
{
    int                   kind;                    
    AstuteDDS_Gid         endpoint_gid;
    AstuteDDS_Gid         participant_gid;
    const char           *topic_name;
    const char           *type_name;
    const uint8_t        *participant_user_data;   
    size_t                participant_user_data_len;
    const char           *participant_name;        
    /* Effective RxO QoS snapshot */
    AstuteDDS_Reliability reliability;
    AstuteDDS_Durability  durability;
} AstuteDDS_EndpointInfo;

typedef void (*AstuteDDS_EndpointCallback)(
    void                         *user_data,
    const AstuteDDS_EndpointInfo *info);

void astutedds_enumerate_publications(
    AstuteDDS_Participant      p,
    AstuteDDS_EndpointCallback callback,
    void                      *user_data);

void astutedds_enumerate_subscriptions(
    AstuteDDS_Participant      p,
    AstuteDDS_EndpointCallback callback,
    void                      *user_data);

/* ── Entity status + listener callbacks (§1.1) ─────────────────────────── */

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
    int32_t current_count;
    int32_t current_count_change;
    int32_t last_subscription_handle;
} AstuteDDS_PublicationMatchedStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
    int32_t current_count;
    int32_t current_count_change;
    int32_t last_publication_handle;
} AstuteDDS_SubscriptionMatchedStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
    int32_t last_policy_id;
} AstuteDDS_OfferedIncompatibleQosStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
    int32_t last_policy_id;
} AstuteDDS_RequestedIncompatibleQosStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
} AstuteDDS_LivelinessLostStatus;

typedef struct
{
    int32_t alive_count;
    int32_t not_alive_count;
    int32_t alive_count_change;
    int32_t not_alive_count_change;
    int32_t last_publication_handle;
} AstuteDDS_LivelinessChangedStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
    int32_t last_instance_handle;
} AstuteDDS_OfferedDeadlineMissedStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
    int32_t last_instance_handle;
} AstuteDDS_RequestedDeadlineMissedStatus;

typedef struct
{
    int32_t total_count;
    int32_t total_count_change;
} AstuteDDS_SampleLostStatus;

AstuteDDS_ReturnCode astutedds_datawriter_get_publication_matched_status(
    AstuteDDS_DataWriter                w,
    AstuteDDS_PublicationMatchedStatus *out);

AstuteDDS_ReturnCode astutedds_datawriter_get_offered_incompatible_qos_status(
    AstuteDDS_DataWriter                    w,
    AstuteDDS_OfferedIncompatibleQosStatus *out);

AstuteDDS_ReturnCode astutedds_datareader_get_subscription_matched_status(
    AstuteDDS_DataReader                 r,
    AstuteDDS_SubscriptionMatchedStatus *out);

AstuteDDS_ReturnCode astutedds_datareader_get_requested_incompatible_qos_status(
    AstuteDDS_DataReader                      r,
    AstuteDDS_RequestedIncompatibleQosStatus *out);

AstuteDDS_ReturnCode astutedds_datawriter_get_liveliness_lost_status(
    AstuteDDS_DataWriter            w,
    AstuteDDS_LivelinessLostStatus *out);

AstuteDDS_ReturnCode astutedds_datawriter_assert_liveliness(
    AstuteDDS_DataWriter w);

AstuteDDS_ReturnCode astutedds_datareader_get_liveliness_changed_status(
    AstuteDDS_DataReader               r,
    AstuteDDS_LivelinessChangedStatus *out);

AstuteDDS_ReturnCode astutedds_datawriter_get_offered_deadline_missed_status(
    AstuteDDS_DataWriter                   w,
    AstuteDDS_OfferedDeadlineMissedStatus *out);

AstuteDDS_ReturnCode astutedds_datareader_get_requested_deadline_missed_status(
    AstuteDDS_DataReader                     r,
    AstuteDDS_RequestedDeadlineMissedStatus *out);

AstuteDDS_ReturnCode astutedds_datareader_get_sample_lost_status(
    AstuteDDS_DataReader        r,
    AstuteDDS_SampleLostStatus *out);

/* Listener callback typedefs.  Every callback receives the entity handle,
 * a pointer to the newly-updated status snapshot (owned by the DDS core
 * for the duration of the callback), and the caller-supplied user_data. */

typedef void (*AstuteDDS_PublicationMatchedCallback)(
    void                                      *user_data,
    AstuteDDS_DataWriter                       writer,
    const AstuteDDS_PublicationMatchedStatus  *status);

typedef void (*AstuteDDS_OfferedIncompatibleQosCallback)(
    void                                          *user_data,
    AstuteDDS_DataWriter                           writer,
    const AstuteDDS_OfferedIncompatibleQosStatus  *status);

typedef void (*AstuteDDS_SubscriptionMatchedCallback)(
    void                                       *user_data,
    AstuteDDS_DataReader                        reader,
    const AstuteDDS_SubscriptionMatchedStatus  *status);

typedef void (*AstuteDDS_RequestedIncompatibleQosCallback)(
    void                                           *user_data,
    AstuteDDS_DataReader                            reader,
    const AstuteDDS_RequestedIncompatibleQosStatus *status);

typedef void (*AstuteDDS_DataAvailableCallback)(
    void                *user_data,
    AstuteDDS_DataReader reader);

typedef void (*AstuteDDS_LivelinessLostCallback)(
    void                                *user_data,
    AstuteDDS_DataWriter                 writer,
    const AstuteDDS_LivelinessLostStatus *status);

typedef void (*AstuteDDS_LivelinessChangedCallback)(
    void                                    *user_data,
    AstuteDDS_DataReader                     reader,
    const AstuteDDS_LivelinessChangedStatus *status);

typedef void (*AstuteDDS_OfferedDeadlineMissedCallback)(
    void                                        *user_data,
    AstuteDDS_DataWriter                         writer,
    const AstuteDDS_OfferedDeadlineMissedStatus *status);

typedef void (*AstuteDDS_RequestedDeadlineMissedCallback)(
    void                                          *user_data,
    AstuteDDS_DataReader                           reader,
    const AstuteDDS_RequestedDeadlineMissedStatus *status);

typedef void (*AstuteDDS_SampleLostCallback)(
    void                             *user_data,
    AstuteDDS_DataReader              reader,
    const AstuteDDS_SampleLostStatus *status);

typedef struct
{
    AstuteDDS_PublicationMatchedCallback     on_publication_matched;
    AstuteDDS_OfferedIncompatibleQosCallback on_offered_incompatible_qos;
    AstuteDDS_LivelinessLostCallback         on_liveliness_lost;
    AstuteDDS_OfferedDeadlineMissedCallback  on_offered_deadline_missed;
    void                                    *user_data;
} AstuteDDS_DataWriterListener;

typedef struct
{
    AstuteDDS_SubscriptionMatchedCallback      on_subscription_matched;
    AstuteDDS_RequestedIncompatibleQosCallback on_requested_incompatible_qos;
    AstuteDDS_DataAvailableCallback            on_data_available;
    AstuteDDS_LivelinessChangedCallback        on_liveliness_changed;
    AstuteDDS_RequestedDeadlineMissedCallback  on_requested_deadline_missed;
    AstuteDDS_SampleLostCallback               on_sample_lost;
    void                                      *user_data;
} AstuteDDS_DataReaderListener;

AstuteDDS_ReturnCode astutedds_datawriter_set_listener(
    AstuteDDS_DataWriter                w,
    const AstuteDDS_DataWriterListener *listener);

AstuteDDS_ReturnCode astutedds_datareader_set_listener(
    AstuteDDS_DataReader                r,
    const AstuteDDS_DataReaderListener *listener);

/* ── §1.4 Request / Reply (RPC) primitives ─────────────────────────────
 *
 * These wrap dcps::Requester / dcps::Replier.  They implement the
 * DDS-RPC pattern: two topics per service (rq/<svc>Request and
 * rr/<svc>Reply), a 16-byte requester GID + 8-byte little-endian
 * sequence number prepended to every payload, and reply filtering by
 * GID on the requester side.  The RMW layer's hand-rolled correlation
 * header (rmw_astutedds_cpp/jazzy/src/rpc.cpp) can be collapsed onto
 * these calls.
 */

typedef struct AstuteDDS_Requester_s *AstuteDDS_Requester;
typedef struct AstuteDDS_Replier_s   *AstuteDDS_Replier;

typedef struct
{
    AstuteDDS_Reliability reliability;
    AstuteDDS_HistoryKind history_kind;
    int32_t               history_depth;
    AstuteDDS_Durability  durability;
} AstuteDDS_RequesterQos;

typedef AstuteDDS_RequesterQos AstuteDDS_ReplierQos;

static inline AstuteDDS_RequesterQos astutedds_default_requester_qos(void)
{
    AstuteDDS_RequesterQos q;
    q.reliability   = ASTUTEDDS_RELIABLE;
    q.history_kind  = ASTUTEDDS_KEEP_LAST;
    q.history_depth = 10;
    q.durability    = ASTUTEDDS_VOLATILE;
    return q;
}

static inline AstuteDDS_ReplierQos astutedds_default_replier_qos(void)
{
    return astutedds_default_requester_qos();
}

AstuteDDS_Requester astutedds_create_requester(
    AstuteDDS_Participant         participant,
    const char                   *service_name,
    const char                   *request_type_name,
    const char                   *reply_type_name,
    const AstuteDDS_RequesterQos *qos);

void astutedds_delete_requester(AstuteDDS_Participant participant,
                                AstuteDDS_Requester   requester);

AstuteDDS_ReturnCode astutedds_requester_get_gid(
    AstuteDDS_Requester requester,
    uint8_t            *out_gid);

AstuteDDS_ReturnCode astutedds_requester_send_request(
    AstuteDDS_Requester requester,
    const uint8_t      *payload,
    size_t              payload_length,
    int64_t            *out_sequence_number);

AstuteDDS_ReturnCode astutedds_requester_take_reply(
    AstuteDDS_Requester requester,
    const uint8_t     **out_payload,
    size_t             *out_payload_length,
    int64_t            *out_sequence_number);

AstuteDDS_DataReader astutedds_requester_get_reply_reader(
    AstuteDDS_Requester requester);

AstuteDDS_DataWriter astutedds_requester_get_request_writer(
    AstuteDDS_Requester requester);

AstuteDDS_Replier astutedds_create_replier(
    AstuteDDS_Participant       participant,
    const char                 *service_name,
    const char                 *request_type_name,
    const char                 *reply_type_name,
    const AstuteDDS_ReplierQos *qos);

void astutedds_delete_replier(AstuteDDS_Participant participant,
                              AstuteDDS_Replier     replier);

AstuteDDS_ReturnCode astutedds_replier_take_request(
    AstuteDDS_Replier   replier,
    const uint8_t     **out_payload,
    size_t             *out_payload_length,
    uint8_t            *out_client_gid,
    int64_t            *out_sequence_number);

AstuteDDS_ReturnCode astutedds_replier_send_reply(
    AstuteDDS_Replier  replier,
    const uint8_t     *client_gid,
    int64_t            sequence_number,
    const uint8_t     *payload,
    size_t             payload_length);

AstuteDDS_DataReader astutedds_replier_get_request_reader(
    AstuteDDS_Replier replier);

AstuteDDS_DataWriter astutedds_replier_get_reply_writer(
    AstuteDDS_Replier replier);

/* ── §1.6 Data-arrival file descriptor (WaitSet / rmw_wait integration) ─
 *
 * Returns an eventfd(2) that becomes readable when new samples arrive on
 * this DataReader.  Suitable for select()/poll()/epoll() integration —
 * lets an executor block on data arrival instead of polling
 * astutedds_unread_count().  The fd is created lazily on the first
 * call and reused for the lifetime of the DataReader (subsequent calls
 * return the same value).  Ownership stays with AstuteDDS; the fd is
 * closed automatically when the DataReader is deleted.
 *
 * The fd is in semaphore mode so each read(fd, buf, 8) consumes exactly
 * one arrival credit.  Callers that want edge-triggered semantics
 * (drain all pending credits and then treat as a single wake-up) can
 * use astutedds_datareader_drain_read_fd() below.
 *
 * @return  A non-negative eventfd on success, or -1 if the reader is
 *          NULL or the eventfd() syscall fails.
 */
int astutedds_datareader_get_read_fd(AstuteDDS_DataReader r);

AstuteDDS_ReturnCode astutedds_datareader_drain_read_fd(int fd);

#ifdef __cplusplus
}  // extern "C"
#endif

#endif  // ASTUTEDDS_C_ASTUTEDDS_H