pub struct EmbeddedPublisher<M> { /* private fields */ }Expand description
Typed publisher handle.
Two methods, both byte-oriented at the wire:
publish/publish_with_buffer— accept&M: RosMessage, CDR-encode into a stack buffer, then callPublisher::publish_raw.publish_serialized— accepts pre-encoded CDR bytes for callers that already produced the wire payload.
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,
impl<M> EmbeddedPublisher<M>where
M: RosMessage,
Sourcepub fn publish(&self, msg: impl Borrow<M>) -> Result<(), NodeError>
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.
Sourcepub fn publish_with_buffer<const BUF: usize>(
&self,
msg: &M,
) -> Result<(), NodeError>
pub fn publish_with_buffer<const BUF: usize>( &self, msg: &M, ) -> Result<(), NodeError>
Publish a message with a custom buffer size.
Sourcepub fn publish_serialized(&self, data: &[u8]) -> Result<(), NodeError>
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).
Sourcepub fn publish_raw(&self, data: &[u8]) -> Result<(), NodeError>
👎Deprecated since 0.6.0: renamed to publish_serialized (phase-515 C8)
pub fn publish_raw(&self, data: &[u8]) -> Result<(), NodeError>
renamed to publish_serialized (phase-515 C8)
Renamed to publish_serialized (phase-515
C8), the transmit word of the receive side’s take_serialized.
Sourcepub fn publish_streamed<F>(
&self,
total_len: usize,
writer: F,
) -> Result<(), NodeError>
pub fn publish_streamed<F>( &self, total_len: usize, writer: F, ) -> Result<(), NodeError>
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 with StreamWriterwrite / extend /
before.)reserve_total
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.
Sourcepub fn assert_liveliness(&self) -> Result<(), NodeError>
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.
Sourcepub fn get_gid(&self) -> Result<[u8; 24], NodeError>
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_gidis 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 gidros2 topic info --verboseprints. 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.
Sourcepub fn supports_event(&self, kind: EventKind) -> bool
pub fn supports_event(&self, kind: EventKind) -> bool
true if the active backend can fire the named event for this
publisher.
Sourcepub fn on_liveliness_lost<F>(&mut self, cb: F) -> Result<(), NodeError>
pub fn on_liveliness_lost<F>(&mut self, cb: F) -> Result<(), NodeError>
Register a callback for LivelinessLost. Fires when this
publisher misses its own liveliness assertion deadline.
Sourcepub fn on_offered_deadline_missed<F>(
&mut self,
deadline: Duration,
cb: F,
) -> Result<(), NodeError>
pub fn on_offered_deadline_missed<F>( &mut self, deadline: Duration, cb: F, ) -> Result<(), NodeError>
Register a callback for OfferedDeadlineMissed. Fires when
this publisher promised deadline and falls behind.
Sourcepub fn on_matched<F>(&mut self, cb: F) -> Result<(), NodeError>
pub fn on_matched<F>(&mut self, cb: F) -> Result<(), NodeError>
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.
Sourcepub fn on_incompatible_type<F>(&mut self, cb: F) -> Result<(), NodeError>
pub fn on_incompatible_type<F>(&mut self, cb: F) -> Result<(), NodeError>
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.
Sourcepub fn topic_name(&self) -> &str
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.
Sourcepub fn get_subscription_count(
&self,
executor: &mut Executor<'_>,
) -> Result<usize, NodeError>
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.