use crate::{
clock::{Clock, ClockSource, ClockType},
vendor::rosgraph_msgs::msg::Clock as ClockMsg,
IntoPrimitiveOptions, Node, NodeState, QoSProfile, ReadOnlyParameter, Subscription,
QOS_PROFILE_CLOCK,
};
use std::sync::{Arc, Mutex, RwLock, Weak};
pub(crate) struct TimeSource {
node: Mutex<Weak<NodeState>>,
clock: RwLock<Clock>,
clock_source: Arc<Mutex<Option<ClockSource>>>,
requested_clock_type: ClockType,
clock_qos: QoSProfile,
clock_subscription: Mutex<Option<Subscription<ClockMsg>>>,
last_received_time: Arc<Mutex<Option<i64>>>,
use_sim_time: Mutex<Option<ReadOnlyParameter<bool>>>,
}
pub(crate) struct TimeSourceBuilder {
clock_qos: QoSProfile,
clock_type: ClockType,
}
impl TimeSourceBuilder {
pub(crate) fn new(clock_type: ClockType) -> Self {
Self {
clock_qos: QOS_PROFILE_CLOCK,
clock_type,
}
}
pub(crate) fn clock_qos(mut self, clock_qos: QoSProfile) -> Self {
self.clock_qos = clock_qos;
self
}
pub(crate) fn build(self) -> TimeSource {
let clock = match self.clock_type {
ClockType::RosTime | ClockType::SystemTime => Clock::system(),
ClockType::SteadyTime => Clock::steady(),
};
TimeSource {
node: Mutex::new(Weak::new()),
clock: RwLock::new(clock),
clock_source: Arc::new(Mutex::new(None)),
requested_clock_type: self.clock_type,
clock_qos: self.clock_qos,
clock_subscription: Mutex::new(None),
last_received_time: Arc::new(Mutex::new(None)),
use_sim_time: Mutex::new(None),
}
}
}
impl TimeSource {
pub(crate) fn builder(clock_type: ClockType) -> TimeSourceBuilder {
TimeSourceBuilder::new(clock_type)
}
pub(crate) fn get_clock(&self) -> Clock {
self.clock.read().unwrap().clone()
}
pub(crate) fn attach_node(&self, node: &Node) {
let param = node
.declare_parameter("use_sim_time")
.default(false)
.read_only()
.unwrap();
*self.node.lock().unwrap() = Arc::downgrade(node);
self.set_ros_time_enable(param.get());
*self.use_sim_time.lock().unwrap() = Some(param);
}
fn set_ros_time_enable(&self, enable: bool) {
if matches!(self.requested_clock_type, ClockType::RosTime) {
let mut clock = self.clock.write().unwrap();
if enable && matches!(clock.clock_type(), ClockType::SystemTime) {
let (new_clock, mut clock_source) = Clock::with_source();
if let Some(last_received_time) = *self.last_received_time.lock().unwrap() {
Self::update_clock(&mut clock_source, last_received_time);
}
*clock = new_clock;
*self.clock_source.lock().unwrap() = Some(clock_source);
*self.clock_subscription.lock().unwrap() = Some(self.create_clock_sub());
}
if !enable && matches!(clock.clock_type(), ClockType::RosTime) {
*clock = Clock::system();
*self.clock_source.lock().unwrap() = None;
*self.clock_subscription.lock().unwrap() = None;
}
}
}
fn update_clock(clock: &mut ClockSource, nanoseconds: i64) {
clock.set_ros_time_override(nanoseconds);
}
fn create_clock_sub(&self) -> Subscription<ClockMsg> {
let clock = self.clock_source.clone();
let last_received_time = self.last_received_time.clone();
self.node
.lock()
.unwrap()
.upgrade()
.unwrap()
.create_subscription::<ClockMsg, _>(
"/clock".qos(self.clock_qos),
move |msg: ClockMsg| {
let nanoseconds: i64 =
(msg.clock.sec as i64 * 1_000_000_000) + msg.clock.nanosec as i64;
*last_received_time.lock().unwrap() = Some(nanoseconds);
if let Some(clock) = clock.lock().unwrap().as_mut() {
Self::update_clock(clock, nanoseconds);
}
},
)
.unwrap()
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn time_source_default_clock() {
let node = Context::default()
.create_basic_executor()
.create_node(&format!("time_source_test_node_{}", line!()))
.unwrap();
assert!(node.get_clock().now().nsec > 0);
}
#[test]
fn time_source_sim_time() {
let executor = Context::new(
[
String::from("--ros-args"),
String::from("-p"),
String::from("use_sim_time:=true"),
],
InitOptions::default(),
)
.unwrap()
.create_basic_executor();
let node = executor
.create_node(&format!("time_source_test_node_{}", line!()))
.unwrap();
assert_eq!(node.get_clock().now().nsec, 0);
}
}