safe_drive/
node.rs

1//! Node of ROS2.
2//! Nodes can be create by `Context::create_node`.
3//!
4//! # Example
5//!
6//! ```
7//! use safe_drive::context::Context;
8//!
9//! // Create a context.
10//! let ctx = Context::new().unwrap();
11//!
12//! // Create a node.
13//! let node = ctx
14//!     .create_node("node_rs", Some("namespace"), Default::default())
15//!     .unwrap();
16//! ```
17
18use libc::atexit;
19
20use crate::{
21    context::{remove_context, Context},
22    error::{DynError, RCLResult},
23    helper::InitOnce,
24    msg::{ServiceMsg, TypeSupport},
25    parameter::ParameterServer,
26    qos, rcl,
27    service::{client::Client, server::Server},
28    topic::publisher::Publisher,
29    topic::subscriber::Subscriber,
30};
31use std::{ffi::CString, sync::Arc};
32
33static SET_ATEXIT: InitOnce = InitOnce::new();
34
35/// Node of ROS2.
36pub struct Node {
37    node: rcl::rcl_node_t,
38    init_param_server: InitOnce,
39    pub(crate) context: Arc<Context>,
40}
41
42impl Node {
43    pub(crate) fn new(
44        context: Arc<Context>,
45        name: &str,
46        namespace: Option<&str>,
47        options: NodeOptions,
48    ) -> RCLResult<Arc<Self>> {
49        let mut node = rcl::MTSafeFn::rcl_get_zero_initialized_node();
50
51        let name_c = CString::new(name).unwrap();
52        let namespace_c = CString::new(namespace.unwrap_or_default()).unwrap();
53
54        {
55            let guard = rcl::MT_UNSAFE_FN.lock();
56            guard.rcl_node_init(
57                &mut node,
58                name_c.as_ptr(),
59                namespace_c.as_ptr(),
60                unsafe { context.as_ptr_mut() },
61                options.as_ptr(),
62            )?;
63        }
64
65        // FastDDS uses atexit(3) to destroy resources when creating a node.
66        // Because of functions registed to atexit(3) will be invoked reverse order,
67        // remove_context() must be set here.
68        SET_ATEXIT.init(|| unsafe { atexit(remove_context) }, 0);
69
70        Ok(Arc::new(Node {
71            node,
72            init_param_server: InitOnce::new(),
73            context,
74        }))
75    }
76
77    pub(crate) fn as_ptr(&self) -> *const rcl::rcl_node_t {
78        &self.node
79    }
80
81    pub(crate) unsafe fn as_ptr_mut(&self) -> *mut rcl::rcl_node_t {
82        &self.node as *const _ as *mut _
83    }
84
85    pub fn get_name(&self) -> RCLResult<String> {
86        rcl::MTSafeFn::rcl_node_get_name(&self.node)
87    }
88
89    pub fn get_fully_qualified_name(&self) -> RCLResult<String> {
90        rcl::MTSafeFn::rcl_node_get_fully_qualified_name(&self.node)
91    }
92
93    pub fn get_namespace(&self) -> RCLResult<String> {
94        rcl::MTSafeFn::rcl_node_get_namespace(&self.node)
95    }
96
97    pub fn create_parameter_server(self: &Arc<Self>) -> Result<ParameterServer, DynError> {
98        self.init_param_server.init(
99            || ParameterServer::new(self.clone()),
100            Err("a parameter server has been already created".into()),
101        )
102    }
103
104    /// Create a publisher.
105    /// If `qos` is specified `None`,
106    /// the default profile is used.
107    ///
108    /// `T` is the type of messages the created publisher send.
109    ///
110    /// # Example
111    ///
112    /// ```
113    /// use safe_drive::{msg::common_interfaces::std_msgs, node::Node, topic::publisher::Publisher};
114    /// use std::sync::Arc;
115    ///
116    /// fn create_new_publisher(node: Arc<Node>) -> Publisher<std_msgs::msg::Bool> {
117    ///     node.create_publisher("topic_name", None).unwrap()
118    /// }
119    /// ```
120    pub fn create_publisher<T: TypeSupport>(
121        self: &Arc<Self>,
122        topic_name: &str,
123        qos: Option<qos::Profile>,
124    ) -> RCLResult<Publisher<T>> {
125        Publisher::new(self.clone(), topic_name, qos)
126    }
127
128    /// Create a publisher.
129    /// If `qos` is specified `None`,
130    /// the default profile is used.
131    ///
132    /// `T` is the type of messages the created publisher send.
133    ///
134    /// This function is the same as `create_publisher` but it disables loaned message.
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// use safe_drive::{msg::common_interfaces::std_msgs, node::Node, topic::publisher::Publisher};
140    /// use std::sync::Arc;
141    ///
142    /// fn create_publisher_disable_loaned_message(node: Arc<Node>) -> Publisher<std_msgs::msg::Bool> {
143    ///     node.create_publisher_disable_loaned_message("topic_name", None).unwrap()
144    /// }
145    /// ```
146    pub fn create_publisher_disable_loaned_message<T: TypeSupport>(
147        self: &Arc<Self>,
148        topic_name: &str,
149        qos: Option<qos::Profile>,
150    ) -> RCLResult<Publisher<T>> {
151        Publisher::new_disable_loaned_message(self.clone(), topic_name, qos)
152    }
153
154    /// Create a subscriber.
155    /// If `qos` is specified `None`,
156    /// the default profile is used.
157    ///
158    /// `T` is the type of messages the created subscriber receive.
159    ///
160    /// # Example
161    ///
162    /// ```
163    /// use safe_drive::{msg::common_interfaces::std_msgs, node::Node, topic::subscriber::Subscriber};
164    /// use std::sync::Arc;
165    ///
166    /// fn create_new_subscriber(node: Arc<Node>) -> Subscriber<std_msgs::msg::Bool> {
167    ///     node.create_subscriber("topic_name", None).unwrap()
168    /// }
169    /// ```
170    pub fn create_subscriber<T: TypeSupport>(
171        self: &Arc<Self>,
172        topic_name: &str,
173        qos: Option<qos::Profile>,
174    ) -> RCLResult<Subscriber<T>> {
175        Subscriber::new(self.clone(), topic_name, qos)
176    }
177
178    /// Create a subscriber.
179    /// If `qos` is specified `None`,
180    /// the default profile is used.
181    ///
182    /// `T` is the type of messages the created subscriber receive.
183    ///
184    /// # Example
185    ///
186    /// ```
187    /// use safe_drive::{msg::common_interfaces::std_msgs, node::Node, topic::subscriber::Subscriber};
188    /// use std::sync::Arc;
189    ///
190    /// fn create_subscriber_disable_loaned_message(node: Arc<Node>) -> Subscriber<std_msgs::msg::Bool> {
191    ///     node.create_subscriber_disable_loaned_message("topic_name", None).unwrap()
192    /// }
193    /// ```
194    pub fn create_subscriber_disable_loaned_message<T: TypeSupport>(
195        self: &Arc<Self>,
196        topic_name: &str,
197        qos: Option<qos::Profile>,
198    ) -> RCLResult<Subscriber<T>> {
199        Subscriber::new_disable_loaned_message(self.clone(), topic_name, qos)
200    }
201
202    /// Create a server.
203    /// If `qos` is specified `None`,
204    /// the default profile is used.
205    ///
206    /// A server must receive `ServiceMsg::Request` and send `ServiceMsg::Response`.
207    ///
208    /// # Example
209    ///
210    /// ```
211    /// use safe_drive::{msg::common_interfaces::std_srvs, node::Node, service::server::Server};
212    /// use std::sync::Arc;
213    ///
214    /// fn create_new_server(node: Arc<Node>) -> Server<std_srvs::srv::Empty> {
215    ///     node.create_server("service_name", None).unwrap()
216    /// }
217    /// ```
218    pub fn create_server<T: ServiceMsg>(
219        self: &Arc<Self>,
220        service_name: &str,
221        qos: Option<qos::Profile>,
222    ) -> RCLResult<Server<T>> {
223        Server::new(self.clone(), service_name, qos)
224    }
225
226    /// Create a client.
227    /// If `qos` is specified `None`,
228    /// the default profile is used.
229    ///
230    /// A client must send `ServiceMsg::Request` and receive `ServiceMsg::Response`.
231    ///
232    /// # Example
233    ///
234    /// ```
235    /// use safe_drive::{msg::common_interfaces::std_srvs, node::Node, service::client::Client};
236    /// use std::sync::Arc;
237    ///
238    /// fn create_new_client(node: Arc<Node>) -> Client<std_srvs::srv::Empty> {
239    ///     node.create_client("service_name", None).unwrap()
240    /// }
241    /// ```
242    pub fn create_client<T: ServiceMsg>(
243        self: &Arc<Self>,
244        service_name: &str,
245        qos: Option<qos::Profile>,
246    ) -> RCLResult<Client<T>> {
247        Client::new(self.clone(), service_name, qos)
248    }
249}
250
251impl Drop for Node {
252    fn drop(&mut self) {
253        let guard = rcl::MT_UNSAFE_FN.lock();
254        let _ = guard.rcl_node_fini(&mut self.node);
255    }
256}
257
258/// Options for nodes.
259pub struct NodeOptions {
260    options: rcl::rcl_node_options_t,
261}
262
263impl Default for NodeOptions {
264    fn default() -> Self {
265        let options = rcl::MTSafeFn::rcl_node_get_default_options();
266        NodeOptions { options }
267    }
268}
269
270impl NodeOptions {
271    /// Create options to create a node
272    pub fn new() -> Self {
273        // TODO: allow setting options
274        Default::default()
275    }
276
277    pub(crate) fn as_ptr(&self) -> *const rcl::rcl_node_options_t {
278        &self.options
279    }
280}
281
282impl Drop for NodeOptions {
283    fn drop(&mut self) {
284        let guard = rcl::MT_UNSAFE_FN.lock();
285        let _ = guard.rcl_node_options_fini(&mut self.options);
286    }
287}
288
289unsafe impl Sync for Node {}
290unsafe impl Send for Node {}