730a0a89fd78a85131fe221d943d5f5425ef0426
[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 storage::Storage;
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 read_char() -> Option<u8> {
83     unsafe {
84         let mut c = b'\0';
85
86         if usb_serial_read(&mut c) {
87             Some(c)
88         } else {
89             None
90         }
91     }
92 }
93
94 fn read_char_delay_ms(limit_ms: u32) -> Option<u8> {
95     let mut total_delay_ms = 0u32;
96
97     while total_delay_ms < limit_ms {
98         if let Some(c) = read_char() {
99             return Some(c);
100         }
101
102         let ms = 50;
103
104         systick::delay_ms(ms);
105         total_delay_ms += ms;
106     }
107
108     None
109 }
110
111 struct Context<'a> {
112     storage: &'a mut dyn Storage,
113 }
114
115 impl<'a> Shell<'a> {
116     pub fn new(tx_buf: &mut Buffer) -> Shell {
117         Shell {
118             tx_buf: tx_buf,
119             command_offset: 0,
120             command_buffer: [0; 32],
121         }
122     }
123
124     pub fn update(&mut self, storage: &mut dyn Storage) {
125         let mut context = Context {
126             storage: storage,
127         };
128
129         while let Some(c) = read_char() {
130             if c != b'\n' {
131                 if self.command_offset != self.command_buffer.len() {
132                     self.command_buffer[self.command_offset] = c;
133                     self.command_offset += 1;
134                 }
135             } else {
136                 let command_length = self.command_offset;
137                 self.command_offset = 0;
138
139                 if command_length != self.command_buffer.len() {
140                     self.command_buffer[command_length] = b'\0';
141
142                     if command_length != 0 {
143                         self.dispatch(command_length, &mut context);
144                     }
145                 } else {
146                     self.command_buffer[self.command_offset] = b'\0';
147
148                     self.tx_buf.write(b"maximum command length exceeded.\n");
149                 }
150
151                 self.tx_buf.write(b"$ ");
152                 self.tx_buf.flush();
153             }
154         }
155     }
156
157     fn dispatch(&mut self, command_length: usize, mut context: &mut Context) {
158         let command : [u8; 32] = self.command_buffer;
159
160         let mut args_iter = ArgumentIter {
161             command: &command[0..command_length],
162             offset: 0,
163         };
164
165         match args_iter.next() {
166             Some(b"help") => {
167                 let usage = b"\
168 Supported commands:
169     help           Show this help message.
170     clear_storage  Fully erase the flash's contents.
171     dump_storage   Dump the flash's contents.
172 ";
173
174                 self.tx_buf.write(usage);
175             },
176
177             Some(b"clear_storage") => self.run_clear_storage(&mut context),
178             Some(b"dump_storage") => self.run_dump_storage(&mut context),
179
180             Some(ref other) => {
181                 self.tx_buf.write(b"unknown_command: ");
182                 self.tx_buf.write(other);
183                 self.tx_buf.write(b"\n");
184             },
185
186             None => {
187             }
188         }
189     }
190
191     fn run_clear_storage(&self, context: &mut Context) {
192         context.storage.clear();
193     }
194
195     fn run_dump_storage(&mut self, context: &mut Context) {
196         self.tx_buf.write(b"waiting for receiver to start...\n");
197         self.tx_buf.flush();
198
199         let have_receiver = read_char_delay_ms(5000).map(|c| {
200             c == b'R'
201         }).map_or_else(|| {
202             false
203         }, |_| {
204             let mut yenc = Yencode::new(&mut self.tx_buf);
205
206             yenc.start(b"gps-watch-storage.bin");
207
208             const CHUNK_SIZE: usize = 1024;
209             let num_chunks = context.storage.size() / CHUNK_SIZE;
210
211             for i in 0..num_chunks {
212                 let mut buf = [0u8; CHUNK_SIZE];
213
214                 context.storage.read(i * CHUNK_SIZE, &mut buf);
215
216                 yenc.data(&buf);
217             }
218
219             yenc.finish();
220
221             true
222         });
223
224         if !have_receiver {
225             self.tx_buf.write(b"no signal from receiver\n");
226         }
227
228         self.tx_buf.flush();
229     }
230 }