You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.rs 8.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. use std::time::Duration;
  2. use dora_node_api::{
  3. self,
  4. dora_core::config::DataId,
  5. merged::{MergeExternal, MergedEvent},
  6. DoraNode, Event,
  7. };
  8. use dora_ros2_bridge::{
  9. messages::{
  10. example_interfaces::service::{AddTwoInts, AddTwoIntsRequest},
  11. geometry_msgs::msg::{Twist, Vector3},
  12. turtlesim::msg::Pose,
  13. },
  14. ros2_client::{self, ros2, NodeOptions},
  15. rustdds::{self, policy},
  16. };
  17. use eyre::{eyre, Context};
  18. use futures::task::SpawnExt;
  19. fn main() -> eyre::Result<()> {
  20. let mut ros_node = init_ros_node()?;
  21. let turtle_vel_publisher = create_vel_publisher(&mut ros_node)?;
  22. let turtle_pose_reader = create_pose_reader(&mut ros_node)?;
  23. // spawn a background spinner task that is handles service discovery (and other things)
  24. let pool = futures::executor::ThreadPool::new()?;
  25. let spinner = ros_node
  26. .spinner()
  27. .map_err(|e| eyre::eyre!("failed to create spinner: {e:?}"))?;
  28. pool.spawn(async {
  29. if let Err(err) = spinner.spin().await {
  30. eprintln!("ros2 spinner failed: {err:?}");
  31. }
  32. })
  33. .context("failed to spawn ros2 spinner")?;
  34. // create an example service client
  35. let service_qos = {
  36. rustdds::QosPolicyBuilder::new()
  37. .reliability(policy::Reliability::Reliable {
  38. max_blocking_time: rustdds::Duration::from_millis(100),
  39. })
  40. .history(policy::History::KeepLast { depth: 1 })
  41. .build()
  42. };
  43. let add_client = ros_node.create_client::<AddTwoInts>(
  44. ros2_client::ServiceMapping::Enhanced,
  45. &ros2_client::Name::new("/", "add_two_ints").unwrap(),
  46. &ros2_client::ServiceTypeName::new("example_interfaces", "AddTwoInts"),
  47. service_qos.clone(),
  48. service_qos.clone(),
  49. )?;
  50. // wait until the service server is ready
  51. println!("wait for add_two_ints service");
  52. let service_ready = async {
  53. for _ in 0..10 {
  54. let ready = add_client.wait_for_service(&ros_node);
  55. futures::pin_mut!(ready);
  56. let timeout = futures_timer::Delay::new(Duration::from_secs(2));
  57. match futures::future::select(ready, timeout).await {
  58. futures::future::Either::Left(((), _)) => {
  59. println!("add_two_ints service is ready");
  60. return Ok(());
  61. }
  62. futures::future::Either::Right(_) => {
  63. println!("timeout while waiting for add_two_ints service, retrying");
  64. }
  65. }
  66. }
  67. eyre::bail!("add_two_ints service not available");
  68. };
  69. futures::executor::block_on(service_ready)?;
  70. let output = DataId::from("pose".to_owned());
  71. let (mut node, dora_events) = DoraNode::init_from_env()?;
  72. let merged = dora_events.merge_external(Box::pin(turtle_pose_reader.async_stream()));
  73. let mut events = futures::executor::block_on_stream(merged);
  74. for i in 0..1000 {
  75. let event = match events.next() {
  76. Some(input) => input,
  77. None => break,
  78. };
  79. match event {
  80. MergedEvent::Dora(event) => match event {
  81. Event::Input {
  82. id,
  83. metadata: _,
  84. data: _,
  85. } => match id.as_str() {
  86. "tick" => {
  87. let direction = Twist {
  88. linear: Vector3 {
  89. x: rand::random::<f64>() + 1.0,
  90. ..Default::default()
  91. },
  92. angular: Vector3 {
  93. z: (rand::random::<f64>() - 0.5) * 5.0,
  94. ..Default::default()
  95. },
  96. };
  97. println!("tick {i}, sending {direction:?}");
  98. turtle_vel_publisher.publish(direction).unwrap();
  99. }
  100. "service_timer" => {
  101. let a = rand::random();
  102. let b = rand::random();
  103. let service_result = add_two_ints_request(&add_client, a, b);
  104. let sum = futures::executor::block_on(service_result)
  105. .context("failed to send service request")?;
  106. if sum != a.wrapping_add(b) {
  107. eyre::bail!("unexpected addition result: expected {}, got {sum}", a + b)
  108. }
  109. }
  110. other => eprintln!("Ignoring unexpected input `{other}`"),
  111. },
  112. Event::Stop => println!("Received manual stop"),
  113. other => eprintln!("Received unexpected input: {other:?}"),
  114. },
  115. MergedEvent::External(pose) => {
  116. println!("received pose event: {pose:?}");
  117. if let Ok((pose, _)) = pose {
  118. let serialized = serde_json::to_string(&pose)?;
  119. node.send_output_bytes(
  120. output.clone(),
  121. Default::default(),
  122. serialized.len(),
  123. serialized.as_bytes(),
  124. )?;
  125. }
  126. }
  127. }
  128. }
  129. Ok(())
  130. }
  131. async fn add_two_ints_request(
  132. add_client: &ros2_client::Client<AddTwoInts>,
  133. a: i64,
  134. b: i64,
  135. ) -> eyre::Result<i64> {
  136. let request = AddTwoIntsRequest { a, b };
  137. println!("sending add request {request:?}");
  138. let request_id = add_client.async_send_request(request.clone()).await?;
  139. println!("{request_id:?}");
  140. let response = add_client.async_receive_response(request_id);
  141. futures::pin_mut!(response);
  142. let timeout = futures_timer::Delay::new(Duration::from_secs(15));
  143. match futures::future::select(response, timeout).await {
  144. futures::future::Either::Left((Ok(response), _)) => {
  145. println!("received response: {response:?}");
  146. Ok(response.sum)
  147. }
  148. futures::future::Either::Left((Err(err), _)) => eyre::bail!(err),
  149. futures::future::Either::Right(_) => {
  150. eyre::bail!("timeout while waiting for response");
  151. }
  152. }
  153. }
  154. fn init_ros_node() -> eyre::Result<ros2_client::Node> {
  155. let ros_context = ros2_client::Context::new().unwrap();
  156. ros_context
  157. .new_node(
  158. ros2_client::NodeName::new("/ros2_demo", "turtle_teleop")
  159. .map_err(|e| eyre!("failed to create ROS2 node name: {e}"))?,
  160. NodeOptions::new().enable_rosout(true),
  161. )
  162. .map_err(|e| eyre::eyre!("failed to create ros2 node: {e:?}"))
  163. }
  164. fn create_vel_publisher(
  165. ros_node: &mut ros2_client::Node,
  166. ) -> eyre::Result<ros2_client::Publisher<Twist>> {
  167. let topic_qos: rustdds::QosPolicies = {
  168. rustdds::QosPolicyBuilder::new()
  169. .durability(policy::Durability::Volatile)
  170. .liveliness(policy::Liveliness::Automatic {
  171. lease_duration: ros2::Duration::INFINITE,
  172. })
  173. .reliability(policy::Reliability::Reliable {
  174. max_blocking_time: ros2::Duration::from_millis(100),
  175. })
  176. .history(policy::History::KeepLast { depth: 1 })
  177. .build()
  178. };
  179. let turtle_cmd_vel_topic = ros_node
  180. .create_topic(
  181. &ros2_client::Name::new("/turtle1", "cmd_vel")
  182. .map_err(|e| eyre!("failed to create ROS2 name: {e}"))?,
  183. ros2_client::MessageTypeName::new("geometry_msgs", "Twist"),
  184. &topic_qos,
  185. )
  186. .context("failed to create topic")?;
  187. // The point here is to publish Twist for the turtle
  188. let turtle_cmd_vel_writer = ros_node
  189. .create_publisher::<Twist>(&turtle_cmd_vel_topic, None)
  190. .context("failed to create publisher")?;
  191. Ok(turtle_cmd_vel_writer)
  192. }
  193. fn create_pose_reader(
  194. ros_node: &mut ros2_client::Node,
  195. ) -> eyre::Result<ros2_client::Subscription<Pose>> {
  196. let turtle_pose_topic = ros_node
  197. .create_topic(
  198. &ros2_client::Name::new("/turtle1", "pose")
  199. .map_err(|e| eyre!("failed to create ROS2 name: {e}"))?,
  200. ros2_client::MessageTypeName::new("turtlesim", "Pose"),
  201. &Default::default(),
  202. )
  203. .context("failed to create topic")?;
  204. let turtle_pose_reader = ros_node
  205. .create_subscription::<Pose>(&turtle_pose_topic, None)
  206. .context("failed to create subscription")?;
  207. Ok(turtle_pose_reader)
  208. }