1pub mod client;
3pub mod data_types;
4pub mod server;
5
6use crate::server::add_data;
8use crate::Data::{RecordData, RecordDataOption};
9use data_types::DataType;
10use serde::{Deserialize, Serialize};
11use std::fmt::{Debug, Display};
12use std::fs::OpenOptions;
13use std::io::Write;
14use std::sync::atomic::AtomicBool;
15use std::sync::atomic::Ordering::SeqCst;
16use std::sync::Mutex;
17use std::time::{SystemTime, UNIX_EPOCH};
18use strum_macros::Display;
19use whoami::fallible;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
23pub enum Direction {
24 Left,
25 Right,
26}
27
28#[derive(Debug, Clone, Copy)]
30pub enum Command {
31 Turn(i16),
32 TurnRadius(i16, i16),
34 DriveDist(i16),
35 DriveLine(i16),
36 AlignDist(i16),
37 AlignLine(i16),
38 TurnOneWheel(i16, Direction),
39}
40
41impl Display for Command {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Command::Turn(a) => write!(f, "Turn({})", a),
46 Command::TurnRadius(r, a) => write!(f, "TurnRadius({}, {})", r, a),
47 Command::DriveDist(d) => write!(f, "DriveDist({})", d),
48 Command::DriveLine(d) => write!(f, "DriveLine({})", d),
49 Command::AlignDist(d) => write!(f, "AlignDist({})", d),
50 Command::AlignLine(d) => write!(f, "AlignLine({})", d),
51 Command::TurnOneWheel(a, d) => write!(f, "TurnOneWheel({}, {})", a, d),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Display)]
58pub enum Data {
59 Command(String),
60 RecordData(u128, Vec<DataType>),
61 RecordDataOption(u128, Vec<Option<DataType>>),
62}
63
64#[derive(Debug, Clone)]
66pub struct RecData {
67 pub data: Vec<Data>,
69 commands: Vec<Command>,
70 start_time: u128,
71 right_total_d: f32,
72 left_total_d: f32,
73 first_time: bool,
74}
75
76impl Default for RecData {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83impl RecData {
85 const fn new() -> Self {
86 Self {
87 data: vec![],
88 commands: vec![],
89 start_time: 0,
90 right_total_d: 0.0,
91 left_total_d: 0.0,
92 first_time: true,
93 }
94 }
95}
96
97static REC_DATA: Mutex<RecData> = Mutex::new(RecData::new());
99static SERVER: AtomicBool = AtomicBool::new(false);
100
101pub fn set_server(b: bool) {
103 SERVER.store(b, SeqCst);
104}
105
106macro_rules! lm {
108 ($mutex:expr) => {
109 $mutex.lock().expect("Mutex poisoned")
110 };
111}
112
113pub fn get_rec_data() -> RecData {
115 lm!(REC_DATA).clone()
116}
117
118pub fn get_rec_len() -> usize {
119 lm!(REC_DATA).data.len()
120}
121
122pub fn get_rec_index(index: usize) -> Data {
123 lm!(REC_DATA).data[index].clone()
124}
125
126pub fn get_rec_start_time() -> u128 {
127 lm!(REC_DATA).start_time
128}
129
130pub fn add_command(command: Command) {
131 lm!(REC_DATA).commands.push(command);
132 lm!(REC_DATA).data.push(Data::Command(command.to_string()));
134}
135
136pub fn add_comment(comment: String) {
137 lm!(REC_DATA).data.push(Data::Command(comment));
138}
139
140pub fn get_data_name() -> String {
141 println!("COMMANDS: {:?}", lm!(REC_DATA).commands);
142 "data_".to_string()
143 + &*lm!(REC_DATA)
144 .commands
145 .iter()
146 .map(|c| c.to_string())
147 .collect::<Vec<String>>()
148 .join("_")
149 .replace(' ', "")
150 .to_string()
151}
152
153pub fn get_time() -> Option<u128> {
154 if lm!(REC_DATA).start_time == 0 {
155 None
156 } else {
157 Some(
158 SystemTime::now()
159 .duration_since(UNIX_EPOCH)
160 .unwrap()
161 .as_millis()
162 - lm!(REC_DATA).start_time,
163 )
164 }
165}
166
167pub fn save_data(mut data: Vec<DataType>) {
168 if lm!(REC_DATA).start_time == 0 {
169 lm!(REC_DATA).start_time = SystemTime::now()
170 .duration_since(UNIX_EPOCH)
171 .unwrap()
172 .as_millis();
173 }
174 let mut had = vec![];
176 for d in &data {
177 if had.contains(d) {
178 panic!("data contains the same DataType multiple times");
179 }
180 had.push(*d);
181 }
182 let right_total_d = lm!(REC_DATA).right_total_d;
184 let left_total_d = lm!(REC_DATA).left_total_d;
185 for d in data.iter_mut() {
187 if let DataType::DrivenDistance(r, l) = d {
188 *d = DataType::DrivenDistance(right_total_d + *r, left_total_d + *l);
189 }
190 }
191 let start_time = lm!(REC_DATA).start_time;
192 let rec_data = RecordData(
193 SystemTime::now()
194 .duration_since(UNIX_EPOCH)
195 .unwrap()
196 .as_millis()
197 - start_time,
198 data.clone(),
199 );
200 if SERVER.load(SeqCst) {
201 add_data(rec_data.clone());
202 }
203 lm!(REC_DATA).data.push(rec_data);
204}
205
206pub fn save_record_data(data: Data) {
207 lm!(REC_DATA).data.push(data);
208}
209
210pub fn update_total_distance(right: f32, left: f32) {
211 if !lm!(REC_DATA).first_time {
212 lm!(REC_DATA).right_total_d += right;
213 lm!(REC_DATA).left_total_d += left;
214 } else {
215 lm!(REC_DATA).first_time = false;
216 }
217}
218
219pub fn get_right_total_distance() -> f32 {
220 lm!(REC_DATA).right_total_d
221}
222
223pub fn get_left_total_distance() -> f32 {
224 lm!(REC_DATA).left_total_d
225}
226
227pub fn write_data(file_name: String) {
228 get_data_name();
229 println!(
230 "Writing data to {}: len: {}",
231 file_name,
232 lm!(REC_DATA).data.len()
233 );
234 let mut file = OpenOptions::new()
235 .write(true)
236 .create(true)
237 .open(file_name)
238 .unwrap();
239 file.set_len(0).unwrap();
240 let mut data = vec![];
242 let mut used = vec![];
243 for dat in &*lm!(REC_DATA).data {
244 match dat {
245 RecordData(t, d) => {
246 let mut temp = vec![];
248 for i in d {
249 let index = i.to_u8();
251 while temp.len() <= index as usize {
252 temp.push(None);
253 }
254 if !used.contains(&index) {
255 used.push(index);
256 }
257 temp[index as usize] = Some(*i);
258 }
259 data.push(RecordDataOption(*t, temp));
260 }
261 Data::Command(s) => {
262 data.push(Data::Command(s.clone()));
263 }
264 _ => {
265 panic!("Data is not RecordData or Command");
266 }
267 }
268 }
269 used.sort();
270 file.write_all(
271 format!(
272 "time, {}\n",
273 used.iter()
274 .map(|i| DataType::from_repr(*i)
275 .expect("DataType not found")
276 .write_description())
277 .filter(|x| x != &"".to_string())
278 .collect::<Vec<String>>()
279 .join(", ")
280 )
281 .as_bytes(),
282 )
283 .unwrap();
284 file.write_all(b"# Phoenix data\n").unwrap();
285 let now = chrono::Local::now();
288 let user = whoami::username();
289 let machine_name = fallible::hostname().unwrap_or_else(|_| "unknown".to_string());
290 file.write_all(format!(
291 "# Created on {} at {} by {} on {}\n",
292 now.format("%d-%m-%Y"),
293 now.format("%H:%M:%S"),
294 user,
295 machine_name
296 ).as_bytes()).unwrap();
297 file.write_all(format!("# {}\n", get_data_name()).as_bytes())
298 .unwrap();
299 for data in data {
312 match data {
313 RecordDataOption(t, d) => {
314 file.write_all(
315 format!(
316 "{}, {}\n",
317 t,
318 d.iter()
319 .enumerate()
320 .map(|(i, x)| {
321 if let Some(x) = x {
322 x.write()
323 } else if used.contains(&(i as u8)) {
324 DataType::None(i as u8).write()
325 } else {
326 "".to_string()
328 }
329 })
330 .filter(|x| x != &"".to_string())
331 .collect::<Vec<String>>()
332 .join(", ")
333 )
334 .as_bytes(),
335 )
336 .unwrap();
337 }
338 Data::Command(s) => {
339 file.write_all(format!("# {}\n", s).as_bytes()).unwrap();
340 }
341 _ => {
342 panic!("Data::RecordDataOption or Data::Command expected");
343 }
344 }
345 }
346}
347
348pub fn clear_data() {
349 *lm!(REC_DATA) = RecData::default();
350}
351pub fn delete_data(n: usize) {
353 if n < lm!(REC_DATA).data.len() {
354 lm!(REC_DATA).data.drain(0..n);
355 }
356}