rosrust/
singleton.rs

1use crate::api::raii::{Publisher, Service, Subscriber};
2use crate::api::resolve::get_unused_args;
3use crate::api::{Delay, Parameter, Rate, Ros, SystemState, Topic};
4use crate::error::{ErrorKind, Result};
5use crate::rosxmlrpc::Response;
6use crate::tcpros::{Client, Message, ServicePair, ServiceResult};
7use crate::util::FAILED_TO_LOCK;
8use crate::{RawMessageDescription, SubscriptionHandler};
9use crossbeam::sync::ShardedLock;
10use ctrlc;
11use error_chain::bail;
12use lazy_static::lazy_static;
13use ros_message::{Duration, Time};
14use std::collections::HashMap;
15use std::thread;
16use std::time;
17
18lazy_static! {
19    static ref ROS: ShardedLock<Option<Ros>> = ShardedLock::new(None);
20}
21
22#[inline]
23pub fn init(name: &str) {
24    try_init(name).expect("ROS init failed!");
25}
26
27#[inline]
28pub fn loop_init(name: &str, wait_millis: u64) {
29    loop {
30        if try_init(name).is_ok() {
31            break;
32        }
33        log::info!("roscore not found. Will retry until it becomes available...");
34        thread::sleep(std::time::Duration::from_millis(wait_millis));
35    }
36    log::info!("Connected to roscore");
37}
38
39#[inline]
40pub fn try_init(name: &str) -> Result<()> {
41    try_init_with_options(name, true)
42}
43
44pub fn try_init_with_options(name: &str, capture_sigint: bool) -> Result<()> {
45    let mut ros = ROS.write().expect(FAILED_TO_LOCK);
46    if ros.is_some() {
47        bail!(ErrorKind::MultipleInitialization);
48    }
49    let client = Ros::new(name)?;
50    if capture_sigint {
51        let shutdown_sender = client.shutdown_sender();
52        ctrlc::set_handler(move || {
53            shutdown_sender.shutdown();
54        })?;
55    }
56    *ros = Some(client);
57    Ok(())
58}
59
60pub fn is_initialized() -> bool {
61    ROS.read().expect(FAILED_TO_LOCK).is_some()
62}
63
64macro_rules! ros {
65    () => {
66        ROS.read()
67            .expect(FAILED_TO_LOCK)
68            .as_ref()
69            .expect(UNINITIALIZED)
70    };
71}
72
73#[inline]
74pub fn args() -> Vec<String> {
75    get_unused_args()
76}
77
78#[inline]
79pub fn uri() -> String {
80    ros!().uri().into()
81}
82
83#[inline]
84pub fn name() -> String {
85    ros!().name().into()
86}
87
88#[inline]
89pub fn hostname() -> String {
90    ros!().hostname().into()
91}
92
93#[inline]
94pub fn now() -> Time {
95    ros!().now()
96}
97
98#[inline]
99pub fn delay(d: Duration) -> Delay {
100    ros!().delay(d)
101}
102
103#[inline]
104pub fn sleep(d: Duration) {
105    delay(d).sleep();
106}
107
108#[inline]
109pub fn rate(rate: f64) -> Rate {
110    ros!().rate(rate)
111}
112
113#[inline]
114pub fn is_ok() -> bool {
115    ros!().is_ok()
116}
117
118#[inline]
119pub fn spin() {
120    // Spinner drop locks the thread until a kill request is received
121    let _spinner = { ros!().spin() };
122}
123
124#[inline]
125pub fn shutdown() {
126    ros!().shutdown_sender().shutdown()
127}
128
129#[inline]
130pub fn param(name: &str) -> Option<Parameter> {
131    ros!().param(name)
132}
133
134#[inline]
135pub fn parameters() -> Response<Vec<String>> {
136    ros!().parameters()
137}
138
139#[inline]
140pub fn state() -> Response<SystemState> {
141    ros!().state()
142}
143
144#[inline]
145pub fn topics() -> Response<Vec<Topic>> {
146    ros!().topics()
147}
148
149#[inline]
150pub fn client<T: ServicePair>(service: &str) -> Result<Client<T>> {
151    ros!().client::<T>(service)
152}
153
154#[inline]
155pub fn wait_for_service(service: &str, timeout: Option<time::Duration>) -> Result<()> {
156    ros!().wait_for_service(service, timeout)
157}
158
159#[inline]
160pub fn service<T, F>(service: &str, handler: F) -> Result<Service>
161where
162    T: ServicePair,
163    F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
164{
165    ros!().service::<T, F>(service, handler)
166}
167
168#[inline]
169pub fn subscribe<T, F>(topic: &str, queue_size: usize, callback: F) -> Result<Subscriber>
170where
171    T: Message,
172    F: Fn(T) + Send + 'static,
173{
174    ros!().subscribe::<T, F>(topic, queue_size, callback)
175}
176
177#[inline]
178pub fn subscribe_with_ids<T, F>(topic: &str, queue_size: usize, callback: F) -> Result<Subscriber>
179where
180    T: Message,
181    F: Fn(T, &str) + Send + 'static,
182{
183    ros!().subscribe_with_ids::<T, F>(topic, queue_size, callback)
184}
185
186#[inline]
187pub fn subscribe_with_ids_and_headers<T, F, G>(
188    topic: &str,
189    queue_size: usize,
190    on_message: F,
191    on_connect: G,
192) -> Result<Subscriber>
193where
194    T: Message,
195    F: Fn(T, &str) + Send + 'static,
196    G: Fn(HashMap<String, String>) + Send + 'static,
197{
198    ros!().subscribe_with_ids_and_headers::<T, F, G>(topic, queue_size, on_message, on_connect)
199}
200
201#[inline]
202pub fn subscribe_with<T, H>(topic: &str, queue_size: usize, handler: H) -> Result<Subscriber>
203where
204    T: Message,
205    H: SubscriptionHandler<T>,
206{
207    ros!().subscribe_with::<T, H>(topic, queue_size, handler)
208}
209
210#[inline]
211pub fn publish<T>(topic: &str, queue_size: usize) -> Result<Publisher<T>>
212where
213    T: Message,
214{
215    ros!().publish::<T>(topic, queue_size)
216}
217
218#[inline]
219pub fn publish_with_description<T>(
220    topic: &str,
221    queue_size: usize,
222    message_description: RawMessageDescription,
223) -> Result<Publisher<T>>
224where
225    T: Message,
226{
227    ros!().publish_with_description::<T>(topic, queue_size, message_description)
228}
229
230#[inline]
231pub fn log(level: i8, msg: String, file: &str, line: u32) {
232    ros!().log(level, msg, file, line)
233}
234
235#[inline]
236pub fn log_once(level: i8, msg: String, file: &str, line: u32) {
237    ros!().log_once(level, msg, file, line)
238}
239
240#[inline]
241pub fn log_throttle(period: f64, level: i8, msg: String, file: &str, line: u32) {
242    ros!().log_throttle(period, level, msg, file, line)
243}
244
245#[inline]
246pub fn log_throttle_identical(period: f64, level: i8, msg: String, file: &str, line: u32) {
247    ros!().log_throttle_identical(period, level, msg, file, line)
248}
249
250static UNINITIALIZED: &str = "ROS uninitialized. Please run ros::init(name) first!";