Skip to main content

EmbeddedPublisher

Struct EmbeddedPublisher 

Source
pub struct EmbeddedPublisher<M> { /* private fields */ }
Expand description

Typed publisher handle.

Two methods, both byte-oriented at the wire:

No typed loan() exists. Loan/borrow live exclusively on EmbeddedRawPublisher / RawSubscription. try_loan(len) requires the byte length up front, which CDR ser/de can only discover after encoding — the two APIs are incompatible by construction. See docs/design/0010-zero-copy-raw-api.md decision D7.

Implementations§

Source§

impl<M> EmbeddedPublisher<M>
where M: RosMessage,

Source

pub fn publish(&self, msg: &M) -> Result<(), NodeError>

Publish a message using the default buffer size.

Source

pub fn publish_with_buffer<const BUF: usize>( &self, msg: &M, ) -> Result<(), NodeError>

Publish a message with a custom buffer size.

Source

pub fn publish_raw(&self, data: &[u8]) -> Result<(), NodeError>

Publish raw CDR-encoded data (must include CDR header).

Source

pub fn publish_streamed<F>( &self, total_len: usize, writer: F, ) -> Result<(), NodeError>
where F: FnMut(&mut [u8]) -> usize,

Phase 124.E.1 — streamed publish. Use a closure-driven writer to serialise straight into the backend’s outbound buffer without a per-publisher staging copy. Saves on RAM-constrained nodes that publish multi-KB payloads.

The writer closure receives a StreamWriter mutable reference and uses StreamWriter::write / extend / reserved_len to fill the slot in chunks. The total payload length must be declared up-front via StreamWriter::reserve_total; the backend allocates that many bytes in its outbound buffer before any chunks land.

Backends without a native stream slot fall through to a stack-allocated staging buffer (capped at 4 KiB) + a single publish_raw — same observable result, just no zero-staging win for the big-message case.

Source

pub fn assert_liveliness(&self) -> Result<(), NodeError>

Phase 108.B — manually assert this publisher’s liveliness. Required for publishers configured with QoSLivelinessPolicy::ManualByTopic / QoSLivelinessPolicy::ManualByNode. No-op for AUTOMATIC / NONE kinds. Returns Err(Unsupported) if the backend doesn’t implement manual liveliness — which, since the phase-467 RMW gap-closure design study’s Row 7, includes zenoh under a manual kind: its liveliness is a session keepalive, and a Ok(()) from a call that put nothing on the wire was the wrong answer. Cyclone performs the assertion for real.

Source

pub fn get_gid(&self) -> Result<[u8; 24], NodeError>

This publisher’s own global identifier — upstream rmw_get_gid_for_publisher / rclcpp::PublisherBase::get_gid.

The phase-467 RMW gap-closure design study’s Q1. 24 bytes, nros_rmw::PUBLISHER_GID_SIZE, the width upstream’s RMW_GID_STORAGE_SIZE uses.

Three bounds, each of which a caller will otherwise assume away:

  • How many of the 24 bytes mean anything is a BACKEND property. zenoh fills 16 and zero-extends the rest; Cyclone fills a 16-byte DDS writer GUID the same way. The padding has one spelling (nros_rmw::pad_publisher_gid) so two gids naming one entity compare equal.
  • Comparable with a gid from a TAKE on zenoh and Cyclone. A received sample’s MessageInfo::publisher_gid is produced from the same source as this (issue 1495): zenoh’s XXH3-128 of the publisher’s liveliness keyexpr, Cyclone’s writer GUID — the same gid ros2 topic info --verbose prints.
  • Err(Unsupported) is a real answer, and it is not zero. A backend with no identity for this publisher (XRCE, uORB) says so; nothing here ever hands back an all-zero gid, because that is what an unwritten buffer holds.
Source

pub fn supports_event(&self, kind: EventKind) -> bool

true if the active backend can fire the named event for this publisher.

Source

pub fn on_liveliness_lost<F>(&mut self, cb: F) -> Result<(), NodeError>
where F: FnMut(CountStatus) + Send + 'static,

Register a callback for LivelinessLost. Fires when this publisher misses its own liveliness assertion deadline.

Source

pub fn on_offered_deadline_missed<F>( &mut self, deadline: Duration, cb: F, ) -> Result<(), NodeError>
where F: FnMut(CountStatus) + Send + 'static,

Register a callback for OfferedDeadlineMissed. Fires when this publisher promised deadline and falls behind.

Source

pub fn topic_name(&self) -> &str

The topic this publisher was created on — rclrs’s PublisherState::topic_name, C’s rcl_publisher_get_topic_name, C++’s Publisher::get_topic_name. phase-444.

&self, which is upstream’s own signature. The ledger row this closes (rust:Publisher::topic_name) argued the accessor had to take the node or the executor as well, because “our Rust entity handle is a bare arena index with no back-reference — PublisherHandle { index, _marker }”. Measured, that is a DIFFERENT type: Node::create_publisher hands back an EmbeddedPublisher, which OWNS its backend handle, and that handle has retained the name since creation (CffiPublisher::topic_name, written from the caller’s string at publisher_create). Nothing is indexed and nothing is re-resolved.

BORROWED, never owned: rclrs returns String, which needs an allocator a 128 KiB target does not have. The bytes live in the backend handle for as long as the publisher does, so a caller that wants an owned copy can make one and a caller that does not pays nothing.

Not on the nros_rmw::Publisher trait, deliberately. The C-ABI shim above every backend already retains the name, so a trait method would make each backend keep a SECOND copy of a 256-byte string for a call that never reaches it. Contrast actual_qos, which IS on the trait because only the backend can answer it.

Source

pub fn get_subscription_count( &self, executor: &mut Executor<'_>, ) -> Result<usize, NodeError>

How many subscriptions this publisher reaches, RIGHT NOW — rclcpp’s Publisher::get_subscription_count, rcl’s rclc_publisher_get_subscription_count. phase-444, issue 1643.

Upstream’s question where the backend can answer it, a weaker one where it cannot — and the difference belongs here rather than in a surprise. Upstream counts the subscriptions MATCHED to THIS publisher — peers whose QoS is compatible with it — through rmw_publisher_count_matched_subscriptions. A backend that fills that slot (Cyclone) is asked first and its answer is upstream’s exactly. Only when it answers Unsupported does this fall back to the graph: every subscription DISCOVERED on the topic, matched or not, because that is what the graph can answer and a backend with no QoS negotiation cannot tell the two apart at all.

The fallback is non-inverting, and in the safe direction: a topic nobody subscribes to still reads 0, so the use this exists for — skip the work when nobody is listening — is answered exactly. An INCOMPATIBLE peer makes it read 1 where upstream reads 0, i.e. you do work nobody receives. Never the reverse.

Takes the executor, which upstream does not, and that is forced: we open ONE transport session per image and the Executor owns it, so the graph is the executor’s to read — the same receiver difference as every other graph verb (phase-381 W4). The compiler demands the argument, which is RFC-0089’s mechanical-edit test.

Reports what has been DISCOVERED and never blocks, so a small answer right after startup is “not seen yet”, never “not there”; and Err(Transport(Unsupported)) from a backend with no graph is a DIFFERENT answer from 0, which must not be collapsed into one.

Trait Implementations§

Source§

impl<M> Drop for EmbeddedPublisher<M>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<M> !Freeze for EmbeddedPublisher<M>

§

impl<M> !RefUnwindSafe for EmbeddedPublisher<M>

§

impl<M> !Send for EmbeddedPublisher<M>

§

impl<M> !Sync for EmbeddedPublisher<M>

§

impl<M> Unpin for EmbeddedPublisher<M>
where M: Unpin,

§

impl<M> UnsafeUnpin for EmbeddedPublisher<M>

§

impl<M> UnwindSafe for EmbeddedPublisher<M>
where M: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.