Skip to main content

nros_node/
lib.rs

1//! Node abstraction for nros
2//!
3//! This crate provides the high-level Node API for creating ROS 2 compatible
4//! publishers and subscribers on embedded systems.
5//!
6//! # Executor-Based API
7//!
8//! The executor-based API provides a unified interface that works on both
9//! std (desktop) and no_std (embedded) targets.
10//!
11//! ## Desktop Example
12//!
13//! ```ignore
14//! use nros::prelude::*;
15//! use std_msgs::msg::Int32;
16//!
17//! let config = ExecutorConfig::from_env().node_name("my_node");
18//! let mut executor: Executor = Executor::open(&config)?;
19//!
20//! // Register subscription callback
21//! let node = executor.node_builder("my_node").build()?;
22//! executor.node_mut(node).create_subscription::<Int32, _>("/topic", |msg: &Int32| {
23//!     println!("Received: {}", msg.data);
24//! })?;
25//!
26//! // Spin (processes callbacks)
27//! executor.spin_blocking(SpinOptions::default());
28//! ```
29//!
30//! ## Embedded Example
31//!
32//! ```ignore
33//! use nros::prelude::*;
34//! use std_msgs::msg::Int32;
35//!
36//! let config = ExecutorConfig { locator: "tcp/192.168.1.1:7447", ..Default::default() };
37//! let mut executor: Executor = Executor::open(&config)?;
38//!
39//! // Register subscription callback
40//! let node = executor.node_builder("my_node").build()?;
41//! executor.node_mut(node).create_subscription::<Int32, _>("/cmd", |msg: &Int32| {
42//!     // process message...
43//! })?;
44//!
45//! // In your main loop:
46//! loop {
47//!     executor.spin_once(core::time::Duration::from_millis(10));
48//!     // platform delay...
49//! }
50//! ```
51//!
52//! # Features
53//!
54//! - `std` - Enable standard library support (spin_blocking)
55//! - `alloc` - Enable heap allocation (parameter service boxed replies)
56
57#![no_std]
58
59// Phase 248 (C2) — `nros-rmw-cyclonedds[-sys]` deps removed (issue #60,
60// Tier 1). Per-type descriptor registration is now the generic
61// `nros_rmw::register_type_descriptor` seam (see `rmw_type_registry`);
62// the Cyclone backend installs its registrar from its own crate. The
63// `__cyclonedds-link` marker feature (no dep edge) still emits
64// `cfg(rmw_needs_type_descriptors)` to compile the schema-passing body +
65// `M: Message` bound for builds where a descriptor-needing backend is
66// linked by the umbrella.
67
68#[cfg(feature = "std")]
69extern crate std;
70
71#[cfg(feature = "alloc")]
72extern crate alloc;
73
74pub mod c_waker;
75pub mod config;
76/// Phase 212.K.7.6.b — runtime cyclonedds type-descriptor registry hook.
77pub mod rmw_type_registry;
78
79/// Server-discovery probe cadence (issue #224 — one shared constant; was
80/// independently defined at four call sites across nros-node and nros-c).
81/// One probe per second balances "see freshly-declared tokens quickly"
82/// against "burn fewer FFI round-trips on a healthy network"; the outer
83/// wall-clock budget is the caller's.
84pub const SERVER_DISCOVERY_PROBE_TIMEOUT_MS: u32 = 1000;
85pub mod executor;
86pub mod lifecycle;
87pub mod limits;
88mod node;
89mod publisher;
90#[cfg(any(has_rmw, test))]
91pub mod session;
92mod subscriber;
93pub mod timer;
94
95// MockSession only matters when neither a real RMW backend feature
96// nor lifecycle-services is enabled — the same gate as
97// `session::ConcreteSession = MockSession` and the executor tests in
98// `executor/mod.rs:42`. Compiling mock.rs unconditionally under
99// `cfg(test)` produced "never constructed / never used" warnings on
100// `cargo build --tests` when feature-unification activated a real
101// RMW backend (e.g. workspace builds with `rmw-uorb` on).
102#[cfg(all(test, not(feature = "rmw-cffi")))]
103pub(crate) mod mock;
104
105// Issue 0092 — the service servers these modules build (`executor::
106// EmbeddedServiceServer`) only exist when an RMW backend is present
107// (`#[cfg(any(has_rmw, test))]` on `executor::handles`). Gate the modules on
108// `has_rmw` too — `--features {lifecycle,param}-services` with no RMW otherwise
109// fails to resolve `EmbeddedServiceServer`. Service servers are meaningless
110// without a backend; every shipping app/entry selects an RMW (→ has_rmw). The
111// `test` arm keeps the modules in test builds.
112#[cfg(all(feature = "param-services", any(has_rmw, test)))]
113pub mod parameter_services;
114
115// Re-export parameter types when param-services is enabled
116#[cfg(feature = "param-services")]
117pub use nros_params::{
118    ParameterDescriptor, ParameterServer, ParameterType, ParameterValue, SetParameterResult,
119};
120
121#[cfg(all(feature = "lifecycle-services", any(has_rmw, test)))]
122pub mod lifecycle_services;
123
124// Export standalone node (without transport)
125pub use node::{Node as StandaloneNode, NodeConfig, NodeError as StandaloneNodeError};
126
127pub use publisher::PublisherHandle;
128pub use subscriber::SubscriberHandle;
129
130// Re-export transport types for convenience
131pub use nros_rmw::{
132    ActionInfo, QosDurabilityPolicy, QosHistoryPolicy, QosLivelinessPolicy, QosPolicyMask,
133    QosReliabilityPolicy, QosSettings, ServiceInfo, TopicInfo, TransportConfig, TransportError,
134};
135
136// Re-export RMW protocol traits so thin wrappers (nros-c, nros-cpp) can
137// pull them through nros-node instead of going around it. Phase 91.B.
138pub use nros_rmw::{Publisher, ServiceClientTrait, ServiceServerTrait, Session, Subscriber};
139
140// Re-export action protocol types from nros-core. Same motivation as the
141// RMW trait re-exports above — keeps thin wrappers off the
142// nros-core::* path. Phase 91.B5.
143pub use nros_core::{CancelResponse, GoalId, GoalResponse, GoalStatus};
144
145// Re-export lifecycle protocol types. Phase 91.B2.
146pub use nros_core::lifecycle::{LifecycleState, LifecycleTransition, TransitionResult};
147
148// Re-export CDR ser/de types so the C-side serialization helpers in
149// nros-c/src/cdr.rs don't have to reach past nros-node either. These
150// are themselves re-exports from nros-serdes via nros-core; collecting
151// them here keeps the import boundary uniform. Phase 91.B6.
152pub use nros_core::{CdrReader, CdrWriter, DeserError, SerError};
153
154// Re-export safety types when feature is enabled
155#[cfg(feature = "safety-e2e")]
156pub use nros_rmw::{IntegrityStatus, SafetyValidator};
157
158// Re-export publisher/subscriber options (topic + QoS; backend-agnostic).
159pub use node::{PublisherOptions, SubscriberOptions};
160
161// Re-export session mode (used by ExecutorConfig)
162pub use nros_rmw::SessionMode;
163
164// Re-export timer types
165pub use timer::{TimerCallbackFn, TimerDuration, TimerHandle, TimerMode, TimerState};
166
167// Re-export lifecycle types
168pub use lifecycle::{LifecycleCallbackFn, LifecycleError, LifecyclePollingNode};
169
170// Re-export types that don't depend on RMW (always available)
171pub use executor::{
172    BOOT_SET_DOMAIN, BOOT_SET_LOCATOR, BOOT_SET_NAMESPACE, BOOT_SET_NODE_NAME, BakedBootConfig,
173    BootConfig, BootConfigError, DOMAIN_ID_EXPLICIT_ZERO_C_ABI, DOMAIN_ID_MAX, ExecutorConfig,
174    ExecutorSemantics, GuardConditionHandle, HandleId, HandleSet, InvocationMode,
175    NROS_BOOT_CONFIG_MAGIC, NROS_BOOT_CONFIG_VERSION, NodeError, RawAcceptedCallback,
176    RawCancelCallback, RawGoalCallback, RawResponseCallback, RawServiceCallback,
177    RawSubscriptionCallback, ReadinessSnapshot, SpinOnceResult, SpinOptions,
178    SpinPeriodPollingResult, Trigger, baked_domain_from_c_abi,
179};
180
181// Re-export RMW-dependent executor types
182#[cfg(any(has_rmw, test))]
183pub use executor::{
184    ActionClient, ActionClientCore, ActionServer, ActionServerCore, ActionServerHandle,
185    ActionServerRawHandle, ActiveGoal, CallbackGroup, CompletedGoal, EmbeddedPublisher,
186    EmbeddedRawPublisher, EmbeddedServiceClient, EmbeddedServiceServer, Executor, FeedbackStream,
187    GoalFeedbackStream, LoanError, NodeHandle, Promise, PublishLoan, RawActionClientSpec,
188    RawActionServerSpec, RawActiveGoal, RawServiceClient, RawServiceServer, RawSubscription,
189    RecvView, SessionHandle, Subscription, executor_storage_layout, executor_storage_u64_len,
190};
191#[cfg(any(has_rmw, test))]
192pub use executor::{ExecutorInlineStorage, ExecutorSizing};
193
194// Phase 173.5 — bridge multi-session spec (consumed by the generated
195// orchestration package's `Executor::open_multi`). Gated to match
196// `executor::SessionSpec` (needs the cffi vtable surface).
197#[cfg(all(any(has_rmw, test), feature = "rmw-cffi"))]
198pub use executor::SessionSpec;
199
200#[cfg(all(feature = "std", any(has_rmw, test)))]
201pub use executor::SpinPeriodResult;