1use tokio::{spawn, task};
2use tokio::sync::mpsc;
3use tokio::net::UdpSocket;
4
5use crate::errors::{Result, TelloError};
6
7const STATE_UDP_PORT:u32 = 8890;
8
9pub type TelloStateSender = mpsc::UnboundedSender<TelloState>;
10pub type TelloStateReceiver = mpsc::UnboundedReceiver<TelloState>;
11
12pub fn make_tello_state_channel() -> (TelloStateSender, TelloStateReceiver) {
13 mpsc::unbounded_channel()
14}
15
16#[derive(Debug, Default)]
18pub struct TelloState {
19 pub roll: i16,
21
22 pub pitch: i16,
24
25 pub yaw: i16,
27
28 pub height: i16,
30
31 pub barometer: f32,
33
34 pub battery: u8,
36
37 pub time_of_flight: u16,
39
40 pub motor_time: u16,
42
43 pub temperature_low: i16,
45
46 pub temperature_high: i16,
48
49 pub velocity: Vector3<i16>,
51
52 pub acceleration: Vector3<f32>
54}
55
56#[derive(Debug, Default)]
57pub struct Vector3<T> {
58 x: T,
59 y: T,
60 z: T
61}
62
63impl TelloState {
64 pub fn from_message(s: &str) -> Result<TelloState> {
70 let mut state = TelloState::default();
71
72 for f in s.split(";") {
73 if f.is_empty() { continue; }
74
75 let (k,v) = split_key_value(f)?;
76
77 match k.as_str() {
78 "roll" => state.roll = value_as(&v)?,
79 "pitch" => state.pitch = value_as(&v)?,
80 "yaw" => state.yaw = value_as(&v)?,
81 "h" => state.height = value_as(&v)?,
82 "baro" => state.barometer = value_as(&v)?,
83 "bat" => state.battery = value_as(&v)?,
84 "tof" => state.time_of_flight = value_as(&v)?,
85 "time" => state.motor_time = value_as(&v)?,
86 "templ" => state.temperature_low = value_as(&v)?,
87 "temph" => state.temperature_high = value_as(&v)?,
88 "vgx" => state.velocity.x = value_as(&v)?,
89 "vgy" => state.velocity.y = value_as(&v)?,
90 "vgz" => state.velocity.z = value_as(&v)?,
91 "agx" => state.acceleration.x = value_as(&v)?,
92 "agy" => state.acceleration.y = value_as(&v)?,
93 "agz" => state.acceleration.z = value_as(&v)?,
94 _ => {}
95 }
96 }
97
98 Ok(state)
99 }
100}
101
102fn split_key_value(kv: &str) -> Result<(String, String)> {
103 let mut i = kv.split(":");
104 let k = i.next().ok_or_else(|| TelloError::ParseError { msg: kv.to_string() })?;
105 let v = i.next().ok_or_else(|| TelloError::ParseError { msg: kv.to_string() })?;
106 Ok((k.to_string(),v.to_string()))
107}
108
109fn value_as<T: std::str::FromStr>(s: &str) -> Result<T> {
110 s.parse::<T>().map_err(|_| TelloError::ParseError { msg: s.to_string() })
111}
112
113#[derive(Debug)]
119pub(crate) struct StateListener {
120 task: task::JoinHandle<()>
121}
122
123impl StateListener {
124 pub(crate) async fn start_listening(sender:TelloStateSender) -> Result<Self> {
125 let local_address = format!("0.0.0.0:{STATE_UDP_PORT}");
126 println!("[State] START LISTENING at {local_address}");
127
128 let sock = UdpSocket::bind(&local_address).await?;
129
130 let task = spawn(async move {
131 loop {
132 let s = &sock;
133 let mut buf = vec![0; 1024];
134 let n = s.recv(&mut buf).await.unwrap();
135
136 buf.truncate(n);
137 let r = String::from_utf8(buf).unwrap();
138 let raw_state = r.trim().to_string();
139
140 let state = TelloState::from_message(&raw_state).unwrap();
141 sender.send(state).unwrap();
142 }
143 });
144
145 Ok(Self { task })
146 }
147
148 pub(crate) async fn stop_listening(&self) -> Result<()> {
149 println!("[State] STOP LISTENING");
150 self.task.abort();
151 Ok(())
154 }
155 }