rust_sk/
lib.rs

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        // Translate the vector to the robot's frame
111        vector.0 -= actual_pose.0;
112        vector.1 -= actual_pose.1;
113
114        // Rotate the vector to the robot's frame
115        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        // Smooth the vector and do magic
124        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        // Compute the angle
137        let speed = self.rotation_speed;
138
139        // Apply speed
140        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    /* Sample game data:
232    {
233        "markers": {
234          "green1": { "position": [-0.5, 0.5], "orientation": 0.0 },
235          "green2": { "position": [-0.5, -0.5], "orientation": 0.0 },
236          "blue1": { "position": [0.5, 0.5], "orientation": 0.0 },
237          "blue2": { "position": [0.5, -0.5], "orientation": 0.0 }
238        },
239        "ball": [0.0, 0.0],
240        "referee": {
241          "game_is_running": false,
242          "game_paused": true,
243          "halftime_is_running": false,
244          "timer": 0,
245          "game_state_msg": "Game is ready to start",
246          "teams": {
247            "green": {
248              "name": "",
249              "score": 0,
250              "x_positive": true,
251              "robots": {
252                "1": {
253                  "penalized": false,
254                  "penalized_remaining": null,
255                  "penalized_reason": null,
256                  "preempted": false,
257                  "preemption_reasons": []
258                },
259                "2": {
260                  "penalized": false,
261                  "penalized_remaining": null,
262                  "penalized_reason": null,
263                  "preempted": false,
264                  "preemption_reasons": []
265                }
266              }
267            },
268            "blue": {
269              "name": "",
270              "score": 0,
271              "x_positive": false,
272              "robots": {
273                "1": {
274                  "penalized": false,
275                  "penalized_remaining": null,
276                  "penalized_reason": null,
277                  "preempted": false,
278                  "preemption_reasons": []
279                },
280                "2": {
281                  "penalized": false,
282                  "penalized_remaining": null,
283                  "penalized_reason": null,
284                  "preempted": false,
285                  "preemption_reasons": []
286                }
287              }
288            }
289          },
290          "referee_history_sliced": [0, -9263, "neutral", "Sideline crossed"]
291        },
292        "leds": {
293          "green1": [0, 50, 0],
294          "green2": [0, 50, 0],
295          "blue1": [0, 0, 50],
296          "blue2": [0, 0, 50]
297        },
298        "simulated": true
299      }
300    */
301    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        // Wait for the first game data
636        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}