common: Implement the "get" shell command.
[gps-watch.git] / src / common / shell.rs
1 /*
2  * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 use buffer::Buffer;
25 use logger::Logger;
26 use yencode::Yencode;
27 use systick;
28
29 pub struct Shell<'a> {
30     tx_buf: &'a mut Buffer,
31     command_offset: usize,
32     command_buffer: [u8; 32],
33 }
34
35 extern {
36     fn usb_serial_read(c: *mut u8) -> bool;
37 }
38
39 struct ArgumentIter<'a> {
40     command: &'a [u8],
41     offset: usize,
42 }
43
44 impl<'a> Iterator for ArgumentIter<'a> {
45     type Item = &'a [u8];
46
47     fn next(&mut self) -> Option<&'a [u8]> {
48         let delimiter = b' ';
49         let mut start : Option<usize> = None;
50
51         // Find the start of the substring.
52         for o in self.offset..self.command.len() {
53             if self.command[o] != delimiter {
54                 start = Some(o);
55                 break
56             }
57         }
58
59         start.and_then(|start2| {
60             let mut end : Option<usize> = None;
61
62             // Find the end of the substring.
63             for o in start2..self.command.len() {
64                 if self.command[o] == delimiter {
65                     break
66                 }
67
68                 end = Some(o);
69             }
70
71             end.and_then(|end2| {
72                 let end3 = end2 + 1;
73
74                 self.offset = end3;
75
76                 Some(&self.command[start2..end3])
77             })
78         })
79     }
80 }
81
82 fn atoi32(s: &[u8]) -> Option<u32> {
83     let mut u = 0u32;
84
85     for o in 0..s.len() {
86         let c = s[o];
87         let zero = b'0' as u8;
88         let nine = b'9' as u8;
89
90         if c < zero || c > nine {
91             return None;
92         }
93
94         let d = (c - zero) as u32;
95
96         if let Some(u2) = u.checked_mul(10) {
97             if let Some(u3) = u2.checked_add(d) {
98                 u = u3;
99             } else {
100                 return None;
101             }
102         } else {
103             return None;
104         }
105     }
106
107     Some(u)
108 }
109
110 fn atoi16(s: &[u8]) -> Option<u16> {
111     atoi32(s).and_then(|u| {
112         if u <= u16::max_value() as u32 {
113             Some(u as u16)
114         } else {
115             None
116         }
117     })
118 }
119
120 fn read_char() -> Option<u8> {
121     unsafe {
122         let mut c = b'\0';
123
124         if usb_serial_read(&mut c) {
125             Some(c)
126         } else {
127             None
128         }
129     }
130 }
131
132 fn read_char_delay_ms(limit_ms: u32) -> Option<u8> {
133     let mut total_delay_ms = 0u32;
134
135     while total_delay_ms < limit_ms {
136         if let Some(c) = read_char() {
137             return Some(c);
138         }
139
140         let ms = 50;
141
142         systick::delay_ms(ms);
143         total_delay_ms += ms;
144     }
145
146     None
147 }
148
149 impl<'a> Shell<'a> {
150     pub fn new(tx_buf: &mut Buffer) -> Shell {
151         Shell {
152             tx_buf: tx_buf,
153             command_offset: 0,
154             command_buffer: [0; 32],
155         }
156     }
157
158     pub fn update(&mut self, logger: &mut Logger) {
159         while let Some(c) = read_char() {
160             if c != b'\n' {
161                 if self.command_offset != self.command_buffer.len() {
162                     self.command_buffer[self.command_offset] = c;
163                     self.command_offset += 1;
164                 }
165             } else {
166                 let command_length = self.command_offset;
167                 self.command_offset = 0;
168
169                 if command_length != self.command_buffer.len() {
170                     self.command_buffer[command_length] = b'\0';
171
172                     if command_length != 0 {
173                         self.dispatch(command_length, logger);
174                     }
175                 } else {
176                     self.command_buffer[self.command_offset] = b'\0';
177
178                     self.tx_buf.write(b"maximum command length exceeded.\n");
179                 }
180
181                 self.tx_buf.write(b"$ ");
182                 self.tx_buf.flush();
183             }
184         }
185     }
186
187     fn dispatch(&mut self, command_length: usize, logger: &mut Logger) {
188         let command : [u8; 32] = self.command_buffer;
189
190         let mut args_iter = ArgumentIter {
191             command: &command[0..command_length],
192             offset: 0,
193         };
194
195         match args_iter.next() {
196             Some(b"help") => {
197                 let usage = b"\
198 Supported commands:
199     help           Show this help message.
200     get REC_ID     Retrieve recording.
201     clear_storage  Fully erase the flash's contents.
202     dump_storage   Dump the flash's contents.
203 ";
204
205                 self.tx_buf.write(usage);
206             },
207
208             Some(b"get") => self.run_get(logger, &mut args_iter),
209             Some(b"clear_storage") => self.run_clear_storage(logger),
210             Some(b"dump_storage") => self.run_dump_storage(logger),
211
212             Some(ref other) => {
213                 self.tx_buf.write(b"unknown_command: ");
214                 self.tx_buf.write(other);
215                 self.tx_buf.write(b"\n");
216             },
217
218             None => {
219             }
220         }
221     }
222
223     fn run_get(&mut self, logger: &mut Logger, args_iter: &mut ArgumentIter) {
224         if let Some(recording_id_s) = args_iter.next() {
225             if let Some(recording_id) = atoi16(recording_id_s) {
226                 if logger.has_recording(recording_id) {
227                     self.tx_buf.write(b"waiting for receiver to start...\n");
228                     self.tx_buf.flush();
229
230                     let have_receiver = read_char_delay_ms(5000).map(|c| {
231                         c == b'R'
232                     }).map_or_else(|| {
233                         false
234                     }, |_| {
235                         let result = logger.get_recording(recording_id,
236                                                           self.tx_buf);
237
238                         if result.is_err() {
239                             self.tx_buf.write(b"get: failed to retrieve recording\n");
240                         }
241
242                         true
243                     });
244
245                     if !have_receiver {
246                         self.tx_buf.write(b"no signal from receiver\n");
247                     }
248                 } else {
249                     self.tx_buf.write(b"get: no such recording\n");
250                 }
251             } else {
252                 self.tx_buf.write(b"get: invalid argument\n");
253             }
254         } else {
255             self.tx_buf.write(b"get: missing argument\n");
256         }
257
258         self.tx_buf.flush();
259     }
260
261     fn run_clear_storage(&self, logger: &mut Logger) {
262         logger.storage.clear();
263     }
264
265     fn run_dump_storage(&mut self, logger: &mut Logger) {
266         self.tx_buf.write(b"waiting for receiver to start...\n");
267         self.tx_buf.flush();
268
269         let have_receiver = read_char_delay_ms(5000).map(|c| {
270             c == b'R'
271         }).map_or_else(|| {
272             false
273         }, |_| {
274             let mut yenc = Yencode::new(&mut self.tx_buf);
275
276             yenc.start(b"gps-watch-storage.bin");
277
278             const CHUNK_SIZE: usize = 1024;
279             let num_chunks = logger.storage.size() / CHUNK_SIZE;
280
281             for i in 0..num_chunks {
282                 let mut buf = [0u8; CHUNK_SIZE];
283
284                 logger.storage.read(i * CHUNK_SIZE, &mut buf);
285
286                 yenc.data(&buf);
287             }
288
289             yenc.finish();
290
291             true
292         });
293
294         if !have_receiver {
295             self.tx_buf.write(b"no signal from receiver\n");
296         }
297
298         self.tx_buf.flush();
299     }
300 }