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: impl Borrow<M>) -> Result<(), NodeError>

Publish a message using the default buffer size.

phase-515 R5 — takes the message borrowed (publish(&msg)) or owned (publish(msg)), as rclrs’s publish(impl MessageCow) does. Both serialise immediately into the stack buffer, so owning buys no deferred path; it only lets an rclrs call site that hands its message over compile unchanged. core::borrow::Borrow is the one trait, so no MessageCow type is added.

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_serialized(&self, data: &[u8]) -> Result<(), NodeError>

Publish CDR-encoded data (must include the CDR header) — rcl’s rcl_publish_serialized_message, rclcpp’s publish(SerializedMessage).

Source

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

👎Deprecated since 0.6.0:

renamed to publish_serialized (phase-515 C8)

Renamed to publish_serialized (phase-515 C8), the transmit word of the receive side’s take_serialized.

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.

total_len declares the payload length up front, and the backend reserves that many bytes in its outbound buffer before any chunk lands. The writer closure is then handed the slot one chunk (&mut [u8]) at a time and returns how many bytes it wrote. (It was handed a StreamWriter with write / extend / reserve_total before.)

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, and XRCE the 16-byte DDS GUID its graph derives for the Agent-side writer (issue 2110). 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 (uORB; XRCE when its GUID probe went unanswered) 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 on_matched<F>(&mut self, cb: F) -> Result<(), NodeError>
where F: FnMut(MatchedStatus) + Send + 'static,

Register a callback for PublicationMatched — issue 1972, upstream RMW_EVENT_PUBLICATION_MATCHED / rclcpp’s PublisherEventCallbacks::matched_callback. Fires when a subscription starts or stops matching this publisher; current_count is how many match now.

When it is observed is a backend property. Both backends that carry it (Cyclone DDS, zenoh) poll it from the publisher’s own data path — publish and assert_liveliness — so a publisher that waits for a listener before its first publish calls assert_liveliness() in its wait loop to give it a poll point.

Source

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

Register a callback for PublisherIncompatibleType — issue 1972, upstream RMW_EVENT_PUBLISHER_INCOMPATIBLE_TYPE / rclcpp’s PublisherEventCallbacks::incompatible_type_callback. Fires when a subscription on this topic NAME with a different type is seen.

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.