#![deny(clippy::all)]
#[cfg(not(unix))]
fn main() {
println!("This example only works on a unix based system");
}
use std::time::Duration;
#[cfg(unix)]
use termion::raw::*;
#[allow(unused_imports)]
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use mio_06::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel as mio_channel;
use rustdds::{
policy::*,
ros2::{NodeOptions, RosParticipant},
*,
};
#[cfg(unix)]
use ui::{RosCommand, UiController};
#[cfg(unix)]
mod ui;
const TURTLE_CMD_VEL_READER_TOKEN: Token = Token(1);
const ROS2_COMMAND_TOKEN: Token = Token(2);
const TURTLE_POSE_READER_TOKEN: Token = Token(3);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Twist {
pub linear: Vector3,
pub angular: Vector3,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pose {
pub x: f32,
pub y: f32,
pub theta: f32,
pub linear_velocity: f32,
pub angular_velocity: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Vector3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Vector3 {
pub const ZERO: Vector3 = Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
};
}
#[cfg(unix)]
fn main() {
log4rs::init_file("examples/turtle_teleop/log4rs.yaml", Default::default()).unwrap();
let (command_sender, command_receiver) = mio_channel::sync_channel::<RosCommand>(10);
let (readback_sender, readback_receiver) = mio_channel::sync_channel(10);
let (pose_sender, pose_receiver) = mio_channel::sync_channel(10);
let jhandle = std::thread::Builder::new()
.name("ros2_loop".into())
.spawn(move || ros2_loop(command_receiver, readback_sender, pose_sender))
.unwrap();
let _stdout_restorer = std::io::stdout().into_raw_mode().unwrap();
let mut main_control = UiController::new(
std::io::stdout(),
command_sender,
readback_receiver,
pose_receiver,
);
main_control.start();
jhandle.join().unwrap();
std::thread::sleep(Duration::from_millis(10));
}
#[cfg(unix)]
fn ros2_loop(
command_receiver: mio_channel::Receiver<RosCommand>,
readback_sender: mio_channel::SyncSender<Twist>,
pose_sender: mio_channel::SyncSender<Pose>,
) {
info!("ros2_loop");
let qos: QosPolicies = {
QosPolicyBuilder::new()
.durability(Durability::Volatile)
.liveliness(Liveliness::Automatic {
lease_duration: rustdds::Duration::INFINITE,
})
.reliability(Reliability::Reliable {
max_blocking_time: rustdds::Duration::from_millis(100),
})
.history(History::KeepLast { depth: 10 })
.build()
};
let mut ros_participant = RosParticipant::new().unwrap();
let mut ros_node = ros_participant
.new_ros_node(
"turtle_teleop", "/ros2_demo", NodeOptions::new(false), )
.unwrap();
let turtle_cmd_vel_topic = ros_node
.create_ros_topic(
"/turtle1/cmd_vel",
String::from("geometry_msgs::msg::dds_::Twist_"),
&qos,
TopicKind::NoKey,
)
.unwrap();
let turtle_cmd_vel_writer = ros_node
.create_ros_no_key_publisher::<Twist, CDRSerializerAdapter<Twist>>(&turtle_cmd_vel_topic, None)
.unwrap();
let mut turtle_cmd_vel_reader = ros_node
.create_ros_no_key_subscriber::<Twist, CDRDeserializerAdapter<_>>(&turtle_cmd_vel_topic, None)
.unwrap();
let turtle_pose_topic = ros_node
.create_ros_topic(
"/turtle1/pose",
String::from("turtlesim::msg::dds_::Pose_"),
&qos,
TopicKind::NoKey,
)
.unwrap();
let mut turtle_pose_reader = ros_node
.create_ros_no_key_subscriber::<Pose, CDRDeserializerAdapter<_>>(&turtle_pose_topic, None)
.unwrap();
let poll = Poll::new().unwrap();
poll
.register(
&command_receiver,
ROS2_COMMAND_TOKEN,
Ready::readable(),
PollOpt::edge(),
)
.unwrap();
poll
.register(
&turtle_cmd_vel_reader,
TURTLE_CMD_VEL_READER_TOKEN,
Ready::readable(),
PollOpt::edge(),
)
.unwrap();
poll
.register(
&turtle_pose_reader,
TURTLE_POSE_READER_TOKEN,
Ready::readable(),
PollOpt::edge(),
)
.unwrap();
info!("Entering event_loop");
'event_loop: loop {
let mut events = Events::with_capacity(100);
poll.poll(&mut events, None).unwrap();
for event in events.iter() {
match event.token() {
ROS2_COMMAND_TOKEN => {
while let Ok(command) = command_receiver.try_recv() {
match command {
RosCommand::StopEventLoop => {
info!("Stopping main event loop");
ros_participant.clear();
break 'event_loop;
}
RosCommand::TurtleCmdVel { twist } => {
match turtle_cmd_vel_writer.write(twist.clone(), None) {
Ok(_) => {
info!("Wrote to ROS2 {:?}", twist);
}
Err(e) => {
error!("Failed to write to turtle writer. {e:?}");
ros_node.clear_node();
return;
}
}
}
};
}
}
TURTLE_CMD_VEL_READER_TOKEN => {
while let Ok(Some(twist)) = turtle_cmd_vel_reader.take_next_sample() {
readback_sender.send(twist.value().clone()).unwrap();
}
}
TURTLE_POSE_READER_TOKEN => {
while let Ok(Some(pose)) = turtle_pose_reader.take_next_sample() {
pose_sender.send(pose.value().clone()).unwrap();
}
}
_ => {
error!("Unknown poll token {:?}", event.token())
}
} } }
}