Skip to content

Commit 213978f

Browse files
Cleanup and add some docs / tests
Signed-off-by: Luca Della Vedova <[email protected]>
1 parent eda7103 commit 213978f

File tree

5 files changed

+125
-21
lines changed

5 files changed

+125
-21
lines changed

rclrs/src/clock.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ use std::sync::{Arc, Mutex};
66
/// from the `rcl_clock_type_t` enum in the binding.
77
#[derive(Clone, Debug, Copy)]
88
pub enum ClockType {
9+
/// Time with behavior dependent on the `set_ros_time(bool)` function. If called with `true`
10+
/// it will be driven by a manual value override, otherwise it will be System Time
911
RosTime = 1,
12+
/// Wall time depending on the current system
1013
SystemTime = 2,
14+
/// Steady time, monotonically increasing but not necessarily equal to wall time.
1115
SteadyTime = 3,
1216
}
1317

@@ -21,13 +25,15 @@ impl From<ClockType> for rcl_clock_type_t {
2125
}
2226
}
2327

28+
/// Struct that implements a Clock and wraps `rcl_clock_t`.
2429
pub struct Clock {
2530
_type: ClockType,
2631
_rcl_clock: Arc<Mutex<rcl_clock_t>>,
2732
// TODO(luca) Implement jump callbacks
2833
}
2934

3035
impl Clock {
36+
/// Creates a new clock of the given `ClockType`.
3137
// TODO(luca) proper error handling
3238
pub fn new(type_: ClockType) -> Result<Self, RclrsError> {
3339
let mut rcl_clock;
@@ -43,11 +49,14 @@ impl Clock {
4349
})
4450
}
4551

52+
/// Returns the clock's `ClockType`.
4653
pub fn clock_type(&self) -> ClockType {
4754
self._type
4855
}
4956

50-
pub fn set_ros_time(&mut self, enable: bool) {
57+
/// Sets the clock to use ROS Time, if enabled the clock will report the last value set through
58+
/// `Clock::set_ros_time_override(nanoseconds: i64)`.
59+
pub fn set_ros_time(&self, enable: bool) {
5160
let mut clock = self._rcl_clock.lock().unwrap();
5261
if enable {
5362
// SAFETY: Safe if clock jump callbacks are not edited, which is guaranteed
@@ -64,6 +73,7 @@ impl Clock {
6473
}
6574
}
6675

76+
/// Returns the current clock's timestamp.
6777
pub fn now(&self) -> Time {
6878
let mut clock = self._rcl_clock.lock().unwrap();
6979
let mut time_point: i64 = 0;
@@ -77,6 +87,7 @@ impl Clock {
7787
}
7888
}
7989

90+
/// Sets the value of the current ROS time.
8091
pub fn set_ros_time_override(&self, nanoseconds: i64) {
8192
let mut clock = self._rcl_clock.lock().unwrap();
8293
// SAFETY: Safe if clock jump callbacks are not edited, which is guaranteed
@@ -118,3 +129,42 @@ impl Drop for Clock {
118129
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
119130
// they are running in. Therefore, this type can be safely sent to another thread.
120131
unsafe impl Send for rcl_clock_t {}
132+
133+
#[cfg(test)]
134+
mod tests {
135+
use super::*;
136+
137+
fn assert_send<T: Send>() {}
138+
fn assert_sync<T: Sync>() {}
139+
140+
#[test]
141+
fn clock_is_send_and_sync() {
142+
assert_send::<Clock>();
143+
assert_sync::<Clock>();
144+
}
145+
146+
#[test]
147+
fn clock_system_time_now() {
148+
let clock = Clock::new(ClockType::SystemTime).unwrap();
149+
assert!(clock.now().nsec > 0);
150+
}
151+
152+
#[test]
153+
fn clock_ros_time_with_override() {
154+
let clock = Clock::new(ClockType::RosTime).unwrap();
155+
let start = clock.now();
156+
// Ros time is not set, should return wall time
157+
assert!(start.nsec > 0);
158+
clock.set_ros_time(true);
159+
// No manual time set, it should default to 0
160+
assert!(clock.now().nsec == 0);
161+
let set_time = 1234i64;
162+
clock.set_ros_time_override(set_time);
163+
// Ros time is set, should return the value that was set
164+
assert_eq!(clock.now().nsec, set_time);
165+
// Back to normal time, should be greater than before
166+
clock.set_ros_time(false);
167+
assert!(clock.now().nsec != set_time);
168+
assert!(clock.now().nsec > start.nsec);
169+
}
170+
}

rclrs/src/node.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::rcl_bindings::*;
1515
use crate::{
1616
Client, ClientBase, Clock, Context, GuardCondition, ParameterOverrideMap, ParameterValue,
1717
Publisher, QoSProfile, RclrsError, Service, ServiceBase, Subscription, SubscriptionBase,
18-
SubscriptionCallback, TimeSource, TimeSourceBuilder, ToResult,
18+
SubscriptionCallback, TimeSource, ToResult,
1919
};
2020

2121
impl Drop for rcl_node_t {
@@ -102,6 +102,7 @@ impl Node {
102102
Self::builder(context, node_name).build()
103103
}
104104

105+
/// Gets the clock associated with this node.
105106
pub fn get_clock(&self) -> Arc<Mutex<Clock>> {
106107
self._clock.clone()
107108
}

rclrs/src/node/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
44
use crate::rcl_bindings::*;
55
use crate::{
66
node::call_string_getter_with_handle, resolve_parameter_overrides, Clock, ClockType, Context,
7-
Node, RclrsError, TimeSource, TimeSourceBuilder, ToResult,
7+
Node, RclrsError, ToResult,
88
};
99

1010
/// A builder for creating a [`Node`][1].

rclrs/src/time.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::clock::ClockType;
22
use crate::rcl_bindings::*;
33

4-
// TODO(luca) this is currently unused, maybe remove?
4+
/// Struct that represents time.
55
#[derive(Debug)]
66
pub struct Time {
7+
/// Timestamp in nanoseconds.
78
pub nsec: i64,
9+
/// Clock type.
810
pub clock_type: ClockType,
911
}
1012

rclrs/src/time_source.rs

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::rcl_bindings::*;
21
use crate::{
32
clock::{Clock, ClockType},
43
RclrsError,
@@ -8,7 +7,9 @@ use rosgraph_msgs::msg::Clock as ClockMsg;
87
use std::fmt;
98
use std::sync::{Arc, Mutex};
109

11-
/// Time source for a node that subscribes to the /clock node, if the use_sim_time parameter is set
10+
/// Time source for a node that drives the attached clocks.
11+
/// If the node's `use_sim_time` parameter is set to `true`, the `TimeSource` will subscribe
12+
/// to the `/clock` topic and drive the attached clocks
1213
pub struct TimeSource {
1314
_node: Arc<Node>,
1415
_clocks: Arc<Mutex<Vec<Arc<Mutex<Clock>>>>>,
@@ -21,6 +22,19 @@ pub struct TimeSource {
2122
_last_time_msg: Arc<Mutex<Option<ClockMsg>>>,
2223
}
2324

25+
/// A builder for creating a [`TimeSource`][1].
26+
///
27+
/// The builder pattern allows selectively setting some fields, and leaving all others at their default values.
28+
/// This struct instance can be created via [`TimeSource::builder()`][2].
29+
///
30+
/// The default values for optional fields are:
31+
/// - `clock_qos: QOS_PROFILE_CLOCK`[3]
32+
/// - `use_clock_thread: true`
33+
///
34+
///
35+
/// [1]: crate::TimeSource
36+
/// [2]: crate::TimeSource::builder
37+
/// [3]: crate::QOS_PROFILE_CLOCK
2438
pub struct TimeSourceBuilder {
2539
node: Arc<Node>,
2640
clock_qos: QoSProfile,
@@ -29,6 +43,9 @@ pub struct TimeSourceBuilder {
2943
}
3044

3145
impl TimeSourceBuilder {
46+
/// Creates a builder for a time source that drives the given clock for the given node.
47+
/// The clock's ClockType must be set to `RosTime` to allow updates based on the `/clock`
48+
/// topic.
3249
pub fn new(node: Arc<Node>, clock: Arc<Mutex<Clock>>) -> Self {
3350
Self {
3451
node,
@@ -38,16 +55,22 @@ impl TimeSourceBuilder {
3855
}
3956
}
4057

58+
/// Sets the QoS for the `/clock` topic.
4159
pub fn clock_qos(mut self, clock_qos: QoSProfile) -> Self {
4260
self.clock_qos = clock_qos;
4361
self
4462
}
4563

64+
/// Sets use_clock_thread.
65+
///
66+
/// If set to `true`, the clock callbacks will run in a separate thread.
67+
/// NOTE: Currently unimplemented
4668
pub fn use_clock_thread(mut self, use_clock_thread: bool) -> Self {
4769
self.use_clock_thread = use_clock_thread;
4870
self
4971
}
5072

73+
/// Builds the `TimeSource` and attaches the provided `Node` and `Clock`.
5174
pub fn build(self) -> TimeSource {
5275
let mut source = TimeSource {
5376
_node: self.node.clone(),
@@ -64,6 +87,7 @@ impl TimeSourceBuilder {
6487
}
6588
}
6689

90+
#[derive(Debug)]
6791
pub struct ClockMismatchError(ClockType);
6892

6993
impl fmt::Display for ClockMismatchError {
@@ -77,6 +101,12 @@ impl fmt::Display for ClockMismatchError {
77101
}
78102

79103
impl TimeSource {
104+
/// Creates a new time source with default parameters.
105+
pub fn new(node: Arc<Node>, clock: Arc<Mutex<Clock>>) -> Self {
106+
TimeSourceBuilder::new(node, clock).build()
107+
}
108+
109+
/// Attaches the given clock to the `TimeSource`, enabling the `TimeSource` to control it.
80110
pub fn attach_clock(&self, clock: Arc<Mutex<Clock>>) -> Result<(), ClockMismatchError> {
81111
let clock_type = clock.clone().lock().unwrap().clock_type();
82112
if !matches!(clock_type, ClockType::RosTime) && *self._ros_time_active.lock().unwrap() {
@@ -93,6 +123,7 @@ impl TimeSource {
93123
Ok(())
94124
}
95125

126+
/// Detaches the given clock from the `TimeSource`.
96127
// TODO(luca) should we return a result to denote whether the clock was removed?
97128
pub fn detach_clock(&self, clock: Arc<Mutex<Clock>>) {
98129
self._clocks
@@ -101,25 +132,19 @@ impl TimeSource {
101132
.retain(|c| !Arc::ptr_eq(c, &clock));
102133
}
103134

135+
/// Attaches the given node to to the `TimeSource`, using its interface to read the
136+
/// `use_sim_time` parameter and create the clock subscription.
104137
pub fn attach_node(&mut self, node: Arc<Node>) -> Result<(), RclrsError> {
105138
self._node = node;
106-
println!("Checking for use sim time");
107139
// TODO*luca) REMOVE THIS
108-
self._clock_subscription = Some(self.create_clock_sub()?);
109-
self.set_ros_time(true);
140+
self.set_ros_time(true)?;
110141
return Ok(());
111142

143+
// TODO(luca) register a parameter callback
112144
if let Some(sim_param) = node.get_parameter("use_sim_time") {
113145
match sim_param {
114146
ParameterValue::Bool(val) => {
115-
if val {
116-
println!("use sim time set");
117-
self._clock_subscription = Some(self.create_clock_sub()?);
118-
self.set_ros_time(true);
119-
} else {
120-
self._clock_subscription = None;
121-
self.set_ros_time(false);
122-
}
147+
self.set_ros_time(val)?;
123148
}
124149
// TODO(luca) more graceful error handling
125150
_ => panic!("use_sim_time parameter must be boolean"),
@@ -128,11 +153,20 @@ impl TimeSource {
128153
Ok(())
129154
}
130155

131-
fn set_ros_time(&self, enable: bool) {
132-
*self._ros_time_active.lock().unwrap() = enable;
133-
for clock in self._clocks.lock().unwrap().iter() {
134-
clock.lock().unwrap().set_ros_time(enable);
156+
fn set_ros_time(&mut self, enable: bool) -> Result<(), RclrsError> {
157+
let mut ros_time_active = self._ros_time_active.lock().unwrap();
158+
if enable != *ros_time_active {
159+
*ros_time_active = enable;
160+
for clock in self._clocks.lock().unwrap().iter() {
161+
clock.lock().unwrap().set_ros_time(enable);
162+
}
163+
if enable {
164+
self._clock_subscription = Some(self.create_clock_sub()?);
165+
} else {
166+
self._clock_subscription = None;
167+
}
135168
}
169+
Ok(())
136170
}
137171

138172
fn update_clock(clock: &Arc<Mutex<Clock>>, nanoseconds: i64) {
@@ -165,3 +199,20 @@ impl TimeSource {
165199
)
166200
}
167201
}
202+
203+
#[cfg(test)]
204+
mod tests {
205+
use super::*;
206+
use crate::create_node;
207+
use crate::Context;
208+
209+
#[test]
210+
fn time_source_attach_clock() {
211+
let clock = Arc::new(Mutex::new(Clock::new(ClockType::RosTime).unwrap()));
212+
let node = create_node(&Context::new([]).unwrap(), "test_node").unwrap();
213+
let time_source = TimeSource::new(node, clock);
214+
let clock = Arc::new(Mutex::new(Clock::new(ClockType::RosTime).unwrap()));
215+
// Attaching additional clocks should be OK
216+
time_source.attach_clock(clock).unwrap();
217+
}
218+
}

0 commit comments

Comments
 (0)