1use std::{thread, sync::{mpsc, Arc}, time::Duration};
2use zmq;
3use serde_json;
4use tokio::sync::Mutex;
5
6pub mod errors {
7 #[derive(Debug, Clone)]
8 pub enum RskError {
9 OtherError(String),
10 GameControllerRefusal(String)
11 }
12 impl<T: RskErrorable> From<T> for RskError {
13 fn from(t: T) -> Self {
14 t.to_rsk_err()
15 }
16 }
17
18 pub trait RskErrorable {
19 fn to_rsk_err(&self) -> RskError;
20 }
21 impl<T: ToString> RskErrorable for T {
22 fn to_rsk_err(&self) -> RskError {
23 RskError::OtherError(self.to_string())
24 }
25 }
26
27 pub type RskResult<T> = Result<T, RskError>;
28}
29use errors::*;
30
31fn angle_wrap(angle: f32) -> f32 {
32 let mut angle = angle;
33 while angle > std::f32::consts::PI {
34 angle -= 2.0 * std::f32::consts::PI;
35 }
36 while angle < -std::f32::consts::PI {
37 angle += 2.0 * std::f32::consts::PI;
38 }
39 angle
40}
41
42#[derive(Debug, Clone, Copy)]
43pub enum Team {
44 Green,
45 Blue
46}
47impl ToString for Team {
48 fn to_string(&self) -> String {
49 match self {
50 Self::Green => "green".to_string(),
51 Self::Blue => "blue".to_string()
52 }
53 }
54}
55
56#[derive(Debug, Clone, Copy)]
57pub enum RobotNumber {
58 One,
59 Two
60}
61impl ToString for RobotNumber {
62 fn to_string(&self) -> String {
63 match self {
64 Self::One => "1".to_string(),
65 Self::Two => "2".to_string()
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy)]
71pub enum Command {
72 Kick(f32),
73 Control(AbsPose),
74 Teleport(AbsPose),
75 Leds(GameDataLedsValue),
76 Beep(u16, u16)
77}
78impl ToString for Command {
79 fn to_string(&self) -> String {
80 match self {
81 Self::Kick(a) => format!(r#"["kick", {a}]"#),
82 Self::Control(p) => format!(r#"["control", {}, {}, {}]"#, p.0, p.1, p.get_theta()),
83 Self::Teleport(p) => format!(r#"["teleport", {}, {}, {}]"#, p.0, p.1, p.get_theta()),
84 Self::Leds(a) => format!(r#"["leds", {}, {}, {}]"#, a.0, a.1, a.2),
85 Self::Beep(a, b) => format!(r#"["beep", {a}, {b}]"#)
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy)]
91pub struct Destination {
92 pub pose: AbsPose,
93 pub speed: f32,
94 pub rotation_speed: f32
95}
96impl Destination {
97 pub fn new(pose: AbsPose, speed: f32, rotation_speed: f32) -> Self {
98 Self {
99 pose,
100 speed,
101 rotation_speed
102 }
103 }
104 pub fn from_pose(pose: AbsPose) -> Self {
105 Self::new(pose, 3.0, 10.0)
106 }
107 pub fn to_control_command(&self, actual_pose:AbsPose) -> Command {
108 let mut vector = MoveVector::new(self.pose.0, self.pose.1);
109
110 vector.0 -= actual_pose.0;
112 vector.1 -= actual_pose.1;
113
114 let cos = angle_wrap(-actual_pose.2).cos();
116 let sin = angle_wrap(-actual_pose.2).sin();
117
118 vector = MoveVector::new(
119 vector.0 * cos - vector.1 * sin,
120 vector.0 * sin + vector.1 * cos
121 );
122
123 let distance = (actual_pose - self.pose).norm();
125 let mut speed = self.speed;
126
127 let t = distance;
128
129 speed = (2.0 * t * (1.0 - t) + t.powi(2)) * speed;
130
131 vector = MoveVector::new(
132 (vector.0 * speed) / vector.norm(),
133 (vector.1 * speed) / vector.norm()
134 );
135
136 let speed = self.rotation_speed;
138
139 let mut angle = angle_wrap(self.pose.2 - actual_pose.2);
141 angle *= speed;
142
143 Command::Control(AbsPose(
144 vector.0 + actual_pose.0,
145 vector.1 + actual_pose.1,
146 angle
147 ))
148 }
149}
150
151pub struct Robot<'a> {
152 pub position: Position,
153 pub orientation: Orientation,
154 pub leds: GameDataLedsValue,
155 pub penalized: bool,
156 pub penalized_remaining: Option<u32>,
157 pub penalized_reason: Option<String>,
158 pub preempted: bool,
159 pub preemption_reasons: Vec<String>,
160 pub number: RobotNumber,
161 pub team: Team,
162 pub client: &'a Client
163}
164impl std::fmt::Debug for Robot<'_> {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("Robot")
167 .field("position", &self.position)
168 .field("orientation", &self.orientation)
169 .field("leds", &self.leds)
170 .field("penalized", &self.penalized)
171 .field("penalized_remaining", &self.penalized_remaining)
172 .field("penalized_reason", &self.penalized_reason)
173 .field("preempted", &self.preempted)
174 .field("preemption_reasons", &self.preemption_reasons)
175 .field("number", &self.number)
176 .field("team", &self.team)
177 .finish()
178 }
179}
180impl Robot<'_> {
181 pub async fn send_command(&self, command: Command) -> RskResult<()> {
182 self.client.send_command(self.team, self.number, command).await
183 }
184 pub fn refresh(&mut self) -> RskResult<()> {
185 let robot = self.client.get_robot(self.team, self.number)?;
186 self.position = robot.position;
187 self.orientation = robot.orientation;
188 self.leds = robot.leds;
189 self.penalized = robot.penalized;
190 self.penalized_remaining = robot.penalized_remaining;
191 self.penalized_reason = robot.penalized_reason;
192 self.preempted = robot.preempted;
193 self.preemption_reasons = robot.preemption_reasons;
194 Ok(())
195 }
196 pub fn get_pose(&mut self) -> RskResult<AbsPose> {
197 self.refresh()?;
198 Ok(self.get_cache_pose())
199 }
200 pub fn get_cache_pose(&self) -> AbsPose {
201 AbsPose::new(
202 self.position.0,
203 self.position.1,
204 self.orientation.0
205 )
206 }
207 pub async fn get_destination(&self) -> RskResult<Option<Destination>> {
208 self.client.get_destination(self.team, self.number).await
209 }
210 pub async fn is_arrived(&self) -> RskResult<bool> {
211 self.client.is_arrived(self.team, self.number).await
212 }
213 pub async fn goto(&self, destination: Destination) -> RskResult<()> {
214 self.client.goto(self.team, self.number, destination).await
215 }
216 pub async fn stop(&self) -> RskResult<()> {
217 self.client.stop(self.team, self.number).await
218 }
219 pub async fn goto_blocking(&self, destination: Destination) -> RskResult<()> {
220 self.client.goto_blocking(self.team, self.number, destination).await
221 }
222 pub async fn kick(&self, power: f32) -> RskResult<()> {
223 self.client.send_command(self.team, self.number, Command::Kick(power)).await
224 }
225}
226
227pub mod game_data_structs {
228 use serde::{Serialize, Deserialize};
229
230 use super::{Team, Client, errors::RskResult, angle_wrap};
231 pub trait Pose: Serialize + for<'a> Deserialize<'a> + std::fmt::Debug + Clone + Copy + PartialEq + std::ops::Sub {
302 fn new(x: f32, y: f32, theta: f32) -> Self;
303 fn get_x(&self) -> f32;
304 fn get_y(&self) -> f32;
305 fn get_theta(&self) -> f32;
306 fn set_x(&mut self, x: f32);
307 fn set_y(&mut self, y: f32);
308 fn set_theta(&mut self, theta: f32);
309 }
310
311 #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
312 pub struct AbsPose(
313 pub f32,
314 pub f32,
315 pub f32
316 );
317 impl Pose for AbsPose {
318 fn new(x: f32, y: f32, theta: f32) -> Self {AbsPose(x, y, angle_wrap(theta))}
319 fn get_x(&self) -> f32 {self.0}
320 fn get_y(&self) -> f32 {self.1}
321 fn get_theta(&self) -> f32 {self.2}
322 fn set_x(&mut self, x: f32) {self.0 = x;}
323 fn set_y(&mut self, y: f32) {self.1 = y;}
324 fn set_theta(&mut self, theta: f32) {
325 self.2 = angle_wrap(theta);
326 }
327 }
328 impl std::ops::Sub for AbsPose {
329 type Output = MoveVector;
330 fn sub(self, other: AbsPose) -> MoveVector {
331 MoveVector (
332 other.get_x() - self.get_x(),
333 other.get_y() - self.get_y()
334 )
335 }
336 }
337 impl std::ops::Add<MoveVector> for AbsPose {
338 type Output = AbsPose;
339 fn add(self, other: MoveVector) -> AbsPose {
340 AbsPose (
341 self.get_x() + other.0,
342 self.get_y() + other.1,
343 self.get_theta()
344 )
345 }
346 }
347 impl AbsPose {
348 pub fn get_rel(&self, team:Team, client:&Client) -> RskResult<RelPose> {
349 let gd = client.get_game_data()?;
350 let x_positive = match team {
351 Team::Blue => gd.referee.teams.blue.x_positive,
352 Team::Green => gd.referee.teams.green.x_positive
353 };
354 let mut rel_pose = RelPose::new(
355 self.get_x(),
356 self.get_y(),
357 self.get_theta()
358 );
359 if x_positive {
360 rel_pose.set_x(rel_pose.get_x() * -1.0);
361 rel_pose.set_theta(rel_pose.get_theta() + std::f32::consts::PI);
362 }
363 Ok(rel_pose)
364 }
365 }
366
367 #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
368 pub struct RelPose (
369 pub f32,
370 pub f32,
371 pub f32
372 );
373 impl Pose for RelPose {
374 fn new(x: f32, y: f32, theta: f32) -> Self {
375 RelPose(x, y, angle_wrap(theta))
376 }
377 fn get_x(&self) -> f32 {self.0}
378 fn get_y(&self) -> f32 {self.1}
379 fn get_theta(&self) -> f32 {self.2}
380 fn set_x(&mut self, x: f32) {self.0 = x;}
381 fn set_y(&mut self, y: f32) {self.1 = y;}
382 fn set_theta(&mut self, theta: f32) {
383 self.2 = angle_wrap(theta);
384 }
385 }
386 impl std::ops::Sub for RelPose {
387 type Output = MoveVector;
388 fn sub(self, other: RelPose) -> MoveVector {
389 MoveVector (
390 other.get_x() - self.get_x(),
391 other.get_y() - self.get_y()
392 )
393 }
394 }
395 impl RelPose {
396 pub fn get_abs(&self, team:Team, client:&Client) -> RskResult<AbsPose> {
397 let gd = client.get_game_data()?;
398 let x_positive = match team {
399 Team::Blue => gd.referee.teams.blue.x_positive,
400 Team::Green => gd.referee.teams.green.x_positive
401 };
402 let mut abs_pose = AbsPose::new(
403 self.get_x(),
404 self.get_y(),
405 self.get_theta()
406 );
407 if x_positive {
408 abs_pose.set_x(abs_pose.get_x() * -1.0);
409 abs_pose.set_theta(abs_pose.get_theta() + std::f32::consts::PI);
410 }
411 Ok(abs_pose)
412 }
413 }
414
415 #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
416 pub struct Position(
417 pub f32,
418 pub f32
419 );
420 impl std::ops::Sub for Position {
421 type Output = MoveVector;
422 fn sub(self, other: Position) -> MoveVector {
423 MoveVector (
424 other.0 - self.0,
425 other.1 - self.1
426 )
427 }
428 }
429
430 #[derive(Serialize, Deserialize, Debug, Clone)]
431 pub struct Orientation(
432 pub f32
433 );
434 impl Orientation {
435 pub fn new(theta: f32) -> Self {
436 Orientation(angle_wrap(theta))
437 }
438 }
439
440 #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
441 pub struct MoveVector (
442 pub f32,
443 pub f32
444 );
445 impl std::ops::Mul<f32> for MoveVector {
446 type Output = MoveVector;
447 fn mul(self, other: f32) -> MoveVector {
448 MoveVector (
449 self.0 * other,
450 self.1 * other
451 )
452 }
453 }
454 impl std::ops::Div<f32> for MoveVector {
455 type Output = MoveVector;
456 fn div(self, other: f32) -> MoveVector {
457 MoveVector (
458 self.0 / other,
459 self.1 / other
460 )
461 }
462 }
463 impl MoveVector {
464 pub fn new(x: f32, y: f32) -> Self {
465 MoveVector(x, y)
466 }
467 pub fn norm(&self) -> f32 {
468 (self.0.powi(2) + self.1.powi(2)).sqrt()
469 }
470 }
471
472 #[derive(Serialize, Deserialize, Debug, Clone)]
473 pub struct GameDataMarkersRobot {
474 pub position: Position,
475 pub orientation: Orientation
476 }
477
478 #[derive(Serialize, Deserialize, Debug, Clone)]
479 pub struct GameDataMarkers {
480 pub green1: GameDataMarkersRobot,
481 pub green2: GameDataMarkersRobot,
482 pub blue1: GameDataMarkersRobot,
483 pub blue2: GameDataMarkersRobot
484 }
485
486 #[derive(Serialize, Deserialize, Debug, Clone)]
487 pub struct GameDataRefereeTeamRobot {
488 pub penalized: bool,
489 pub penalized_remaining: Option<u32>,
490 pub penalized_reason: Option<String>,
491 pub preempted: bool,
492 pub preemption_reasons: Vec<String>
493 }
494
495 #[derive(Serialize, Deserialize, Debug, Clone)]
496 pub struct GameDataRefereeTeamRobots {
497 pub one: GameDataRefereeTeamRobot,
498 pub two: GameDataRefereeTeamRobot
499 }
500
501 #[derive(Serialize, Deserialize, Debug, Clone)]
502 pub struct GameDataRefereeTeam {
503 pub name: String,
504 pub score: u8,
505 pub x_positive: bool,
506 pub robots: GameDataRefereeTeamRobots
507 }
508
509 #[derive(Serialize, Deserialize, Debug, Clone)]
510 pub struct GameDataRefereeTeams {
511 pub green: GameDataRefereeTeam,
512 pub blue: GameDataRefereeTeam
513 }
514
515 #[derive(Serialize, Deserialize, Debug, Clone)]
516 pub struct GameDataReferee {
517 pub game_is_running: bool,
518 pub game_paused: bool,
519 pub halftime_is_running: bool,
520 pub timer: i32,
521 pub game_state_msg: String,
522 pub teams: GameDataRefereeTeams,
523 pub referee_history_sliced: Vec<Vec<serde_json::Value>>
524 }
525
526 #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
527 pub struct GameDataLedsValue(
528 pub u8,
529 pub u8,
530 pub u8
531 );
532
533 #[derive(Serialize, Deserialize, Debug, Clone)]
534 pub struct GameDataLeds {
535 pub green1: GameDataLedsValue,
536 pub green2: GameDataLedsValue,
537 pub blue1: GameDataLedsValue,
538 pub blue2: GameDataLedsValue
539 }
540
541 #[derive(Serialize, Deserialize, Debug, Clone)]
542 pub struct GameData {
543 pub markers: GameDataMarkers,
544 pub ball: Position,
545 pub referee: GameDataReferee,
546 pub leds: GameDataLeds,
547 pub simulated: bool
548 }
549}
550use game_data_structs::*;
551
552#[derive(Debug, Clone, Copy)]
553pub struct Destinations {
554 pub blue1: Option<Destination>,
555 pub blue2: Option<Destination>,
556 pub green1: Option<Destination>,
557 pub green2: Option<Destination>
558}
559
560#[derive(Clone)]
561pub struct Client {
562 pub key: String,
563 pub host: String,
564 pub team: Team,
565 cmd_sender: mpsc::SyncSender<(Team, RobotNumber, Command)>,
566 game_data: Arc<std::sync::Mutex<Option<GameData>>>,
567 pub destinations: Arc<Mutex<Destinations>>
568}
569impl Client {
570 pub fn new(key:String, host:String, team:Team) -> RskResult<Client> {
571 let (
572 cmd_sender,
573 rx
574 ) = mpsc::sync_channel::<(Team, RobotNumber, Command)>(10);
575 let key_clone = key.clone();
576 let host_clone = host.clone();
577 thread::spawn(move || {
578 loop {
579 match Client::req_process(&key_clone, &host_clone, &rx) {
580 Ok(_) => (),
581 Err(e) => println!("Client::req_process() :\n{}", match e {
582 RskError::OtherError(s) => s,
583 RskError::GameControllerRefusal(s) => s
584 })
585 }
586 }
587 });
588
589 let game_data = Arc::new(std::sync::Mutex::new(None));
590 let game_data_clone = game_data.clone();
591 let host_clone = host.clone();
592 thread::spawn(move || {
593 loop {
594 match Client::sub_process(&host_clone, game_data_clone.clone()) {
595 Ok(_) => (),
596 Err(e) => println!("Client::sub_process() :\n{}", match e {
597 RskError::OtherError(s) => s,
598 RskError::GameControllerRefusal(s) => s
599 })
600 }
601 }
602 });
603 let client = Client {
604 key,
605 host,
606 team,
607 cmd_sender,
608 game_data,
609 destinations: Arc::new(Mutex::new(Destinations {
610 blue1: None,
611 blue2: None,
612 green1: None,
613 green2: None
614 }))
615 };
616
617 let client_clone = client.clone();
618 tokio::spawn(async move {
619 loop {
620 if let Err(e) = Self::goto_process(client_clone.clone(), team, RobotNumber::One).await {
621 println!("Error in goto process 1: {:?}", e);
622 }
623 }
624 });
625
626 let client_clone = client.clone();
627 tokio::spawn(async move {
628 loop {
629 if let Err(e) = Self::goto_process(client_clone.clone(), team, RobotNumber::Two).await {
630 println!("Error in goto process 2: {:?}", e);
631 }
632 }
633 });
634
635 let mut gd = None;
637 while let None = gd {
638 gd = (*client.game_data.lock()?).clone();
639 }
640
641 Ok(client)
642 }
643 pub async fn send_command(&self, team:Team, number:RobotNumber, command:Command) -> RskResult<()> {
644 self.cmd_sender.send((team, number, command))?;
645 Ok(())
646 }
647 pub fn req_process(key: &str, host: &str, rx: &mpsc::Receiver<(Team, RobotNumber, Command)>) -> RskResult<()> {
648 let ctx = zmq::Context::new();
649 let req = ctx.socket(zmq::REQ)?;
650 req.connect(&("tcp://".to_string() + host + ":7558"))?;
651
652 loop {
653 let (
654 team,
655 number,
656 command
657 ) = rx.recv()?;
658 req.send(
659 zmq::Message::from(
660 &format!(r#"["{0}", "{1}", {2}, {3}]"#, key, team.to_string(), number.to_string(), command.to_string())
661 ),
662 0
663 )?;
664
665 let response = match req.recv_string(0)? {
666 Err(e) => return Err(format!("Client.send_command() : the received message is not a string. raw data : {:?}", e).to_rsk_err()),
667 Ok(v) => v
668 };
669
670 let response: Vec<serde_json::Value> = serde_json::from_str(&response)?;
671
672 match response.get(0).unwrap_or(&serde_json::Value::Null) {
673 serde_json::Value::Bool(b) => {
674 if !b {
675 let message = match response.get(1).unwrap_or(&serde_json::Value::Null) {
676 serde_json::Value::String(s) => s,
677 _ => return Err("Client.send_command() : the second index of the received array is not a string".to_rsk_err())
678 };
679
680 return Err(("Client.send_command() : the server returned an error : ".to_string() + message).to_rsk_err());
681 }
682 },
683 serde_json::Value::Number(_) => {
684 println!("Client.reqprocess() : {}", response.get(1)
685 .unwrap_or(
686 &serde_json::Value::String("Client.send_command() : the second index of the received array is not a string".to_string())
687 )
688 );
689 },
690 _ => return Err("Client.send_command() : the first index of the received array is not a boolean".to_rsk_err())
691 };
692 }
693 }
694 pub fn sub_process(host: &str, game_data: Arc<std::sync::Mutex<Option<GameData>>>) -> RskResult<()> {
695 let ctx = zmq::Context::new();
696 let sub = ctx.socket(zmq::SUB)?;
697 sub.connect(&("tcp://".to_string() + host + ":7557"))?;
698 sub.set_subscribe(b"")?;
699
700 loop {
701 let mut response = match sub.recv_string(0)? {
702 Err(e) => return Err(format!("the received message is not a string.\n\nraw data : {:?}", e).to_rsk_err()),
703 Ok(v) => v
704 };
705
706 response = response.replace(r#""1":"#, r#""one":"#);
707 response = response.replace(r#""2":"#, r#""two":"#);
708
709 let received_game_data: GameData = match serde_json::from_str(&response) {
710 Err(e) => return Err(format!("the received message is not a valid json.\n\ninitial error : {:?}\n\nraw game data :\n{}", e, response).to_rsk_err()),
711 Ok(v) => v
712 };
713
714 let mut game_data = game_data.lock()?;
715 *game_data = Some(received_game_data);
716 }
717 }
718 pub async fn goto_process(client: Client, team:Team, number:RobotNumber) -> RskResult<()> {
719 loop {
720 let destination = client.get_destination(team, number).await?;
721 let actual_pose = client.get_robot(team, number)?.get_pose()?;
722 if let Some(destination) = destination {
723 if let Err(e) = client.cmd_sender.send((team, number, destination.to_control_command(actual_pose))) {
724 println!("Client.goto_process() : error while sending command : {:?}", e);
725 }
726 }
727 tokio::time::sleep(Duration::from_millis(50)).await;
728 }
729 }
730 pub async fn get_destinations(&self) -> RskResult<Destinations> {
731 let destinations = (*self.destinations.lock().await).clone();
732 Ok(destinations)
733 }
734 pub fn get_game_data(&self) -> RskResult<GameData> {
735 let gd = (*self.game_data.lock()?).clone();
736 match gd {
737 Some(gd) => Ok(gd),
738 None => Err("Client.get_game_data() : no game data available".to_rsk_err())
739 }
740 }
741 pub fn get_robot(&self, team:Team, number:RobotNumber) -> RskResult<Robot> {
742 let gd = self.get_game_data()?;
743 let markers_robot = match team {
744 Team::Blue => {
745 match number {
746 RobotNumber::One => gd.markers.blue1,
747 RobotNumber::Two => gd.markers.blue2
748 }
749 },
750 Team::Green => {
751 match number {
752 RobotNumber::One => gd.markers.green1,
753 RobotNumber::Two => gd.markers.green2
754 }
755 }
756 };
757 let referee_robot = match team {
758 Team::Blue => {
759 match number {
760 RobotNumber::One => gd.referee.teams.blue.robots.one,
761 RobotNumber::Two => gd.referee.teams.blue.robots.two
762 }
763 },
764 Team::Green => {
765 match number {
766 RobotNumber::One => gd.referee.teams.green.robots.one,
767 RobotNumber::Two => gd.referee.teams.green.robots.two
768 }
769 }
770 };
771 let leds = match team {
772 Team::Blue => {
773 match number {
774 RobotNumber::One => gd.leds.blue1,
775 RobotNumber::Two => gd.leds.blue2
776 }
777 },
778 Team::Green => {
779 match number {
780 RobotNumber::One => gd.leds.green1,
781 RobotNumber::Two => gd.leds.green2
782 }
783 }
784 };
785 Ok(Robot {
786 team,
787 number,
788 position: markers_robot.position,
789 orientation: markers_robot.orientation,
790 leds,
791 penalized: referee_robot.penalized,
792 penalized_remaining: referee_robot.penalized_remaining,
793 penalized_reason: referee_robot.penalized_reason,
794 preempted: referee_robot.preempted,
795 preemption_reasons: referee_robot.preemption_reasons,
796 client: &self
797 })
798 }
799 pub async fn is_arrived(&self, team:Team, number:RobotNumber) -> RskResult<bool> {
800 let robot = self.get_robot(team, number)?;
801 let destination = self.get_destination(team, number).await?;
802 if let Some(destination) = destination {
803 return Ok((robot.get_cache_pose() - destination.pose).norm() < 0.03 && (robot.get_cache_pose().get_theta() - destination.pose.get_theta()).abs() < 0.05);
804 }
805 Ok(true)
806 }
807 pub async fn get_destination(&self, team:Team, number:RobotNumber) -> RskResult<Option<Destination>> {
808 let destinations = self.destinations.lock().await;
809 let destination = match team {
810 Team::Blue => {
811 match number {
812 RobotNumber::One => destinations.blue1,
813 RobotNumber::Two => destinations.blue2
814 }
815 },
816 Team::Green => {
817 match number {
818 RobotNumber::One => destinations.green1,
819 RobotNumber::Two => destinations.green2
820 }
821 }
822 };
823 Ok(destination)
824 }
825 pub async fn set_destination(&self, team:Team, number:RobotNumber, destination:Option<Destination>) -> RskResult<()> {
826 let mut destinations = self.destinations.lock().await;
827 match team {
828 Team::Blue => {
829 match number {
830 RobotNumber::One => destinations.blue1 = destination,
831 RobotNumber::Two => destinations.blue2 = destination
832 }
833 },
834 Team::Green => {
835 match number {
836 RobotNumber::One => destinations.green1 = destination,
837 RobotNumber::Two => destinations.green2 = destination
838 }
839 }
840 };
841 Ok(())
842 }
843 pub async fn goto(&self, team:Team, number:RobotNumber, destination:Destination) -> RskResult<()> {
844 self.set_destination(team, number, Some(destination)).await?;
845 Ok(())
846 }
847 pub async fn stop(&self, team:Team, number:RobotNumber) -> RskResult<()> {
848 self.set_destination(team, number, None).await?;
849 Ok(())
850 }
851 pub async fn goto_blocking(&self, team:Team, number:RobotNumber, destination:Destination) -> RskResult<()> {
852 loop {
853 self.goto(team, number, destination).await?;
854 if self.is_arrived(team, number).await? {
855 break;
856 }
857 tokio::time::sleep(Duration::from_millis(10)).await;
858 }
859 Ok(())
860 }
861 pub fn get_ball(&self) -> RskResult<Position> {
862 Ok(self.get_game_data()?.ball)
863 }
864 pub async fn get_allies(&self) -> RskResult<(Robot, Robot)> {
865 Ok((self.get_robot(self.team, RobotNumber::One)?, self.get_robot(self.team, RobotNumber::Two)?))
866 }
867}