application: Set up a Logger instance.
[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 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 impl<'a> Shell<'a> {
112     pub fn new(tx_buf: &mut Buffer) -> Shell {
113         Shell {
114             tx_buf: tx_buf,
115             command_offset: 0,
116             command_buffer: [0; 32],
117         }
118     }
119
120     pub fn update(&mut self, logger: &mut Logger) {
121         while let Some(c) = read_char() {
122             if c != b'\n' {
123                 if self.command_offset != self.command_buffer.len() {
124                     self.command_buffer[self.command_offset] = c;
125                     self.command_offset += 1;
126                 }
127             } else {
128                 let command_length = self.command_offset;
129                 self.command_offset = 0;
130
131                 if command_length != self.command_buffer.len() {
132                     self.command_buffer[command_length] = b'\0';
133
134                     if command_length != 0 {
135                         self.dispatch(command_length, logger);
136                     }
137                 } else {
138                     self.command_buffer[self.command_offset] = b'\0';
139
140                     self.tx_buf.write(b"maximum command length exceeded.\n");
141                 }
142
143                 self.tx_buf.write(b"$ ");
144                 self.tx_buf.flush();
145             }
146         }
147     }
148
149     fn dispatch(&mut self, command_length: usize, logger: &mut Logger) {
150         let command : [u8; 32] = self.command_buffer;
151
152         let mut args_iter = ArgumentIter {
153             command: &command[0..command_length],
154             offset: 0,
155         };
156
157         match args_iter.next() {
158             Some(b"help") => {
159                 let usage = b"\
160 Supported commands:
161     help           Show this help message.
162     clear_storage  Fully erase the flash's contents.
163     dump_storage   Dump the flash's contents.
164 ";
165
166                 self.tx_buf.write(usage);
167             },
168
169             Some(b"clear_storage") => self.run_clear_storage(logger),
170             Some(b"dump_storage") => self.run_dump_storage(logger),
171
172             Some(ref other) => {
173                 self.tx_buf.write(b"unknown_command: ");
174                 self.tx_buf.write(other);
175                 self.tx_buf.write(b"\n");
176             },
177
178             None => {
179             }
180         }
181     }
182
183     fn run_clear_storage(&self, logger: &mut Logger) {
184         logger.storage.clear();
185     }
186
187     fn run_dump_storage(&mut self, logger: &mut Logger) {
188         self.tx_buf.write(b"waiting for receiver to start...\n");
189         self.tx_buf.flush();
190
191         let have_receiver = read_char_delay_ms(5000).map(|c| {
192             c == b'R'
193         }).map_or_else(|| {
194             false
195         }, |_| {
196             let mut yenc = Yencode::new(&mut self.tx_buf);
197
198             yenc.start(b"gps-watch-storage.bin");
199
200             const CHUNK_SIZE: usize = 1024;
201             let num_chunks = logger.storage.size() / CHUNK_SIZE;
202
203             for i in 0..num_chunks {
204                 let mut buf = [0u8; CHUNK_SIZE];
205
206                 logger.storage.read(i * CHUNK_SIZE, &mut buf);
207
208                 yenc.data(&buf);
209             }
210
211             yenc.finish();
212
213             true
214         });
215
216         if !have_receiver {
217             self.tx_buf.write(b"no signal from receiver\n");
218         }
219
220         self.tx_buf.flush();
221     }
222 }