022b155f7d1b90b9667d5291dd4b8da9115b5dbb
[gps-watch.git] / src / common / logger.rs
1 /*
2  * Copyright (c) 2017-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 gps::TimeAndPos;
25 use storage::Storage;
26 use varint;
27 use systick::elapsed_ms;
28 use buffer::Buffer;
29 use yencode::Yencode;
30 use fmt::*;
31 use time::Time;
32
33 pub const MEMORY_SIZE: usize = 2 << 20;
34 const SECTOR_SIZE: usize = 4 << 10;
35
36 const NUM_SECTORS: usize = MEMORY_SIZE / SECTOR_SIZE;
37
38 #[derive(Clone, Copy, PartialEq, Debug)]
39 pub enum Error {
40     NoSuchRecording,
41 }
42
43 enum SectorFlag {
44     InUse      = 1 << 0,
45     DataRecord = 1 << 1,
46
47     // Overrides InUse. The idea is to have erased flash sectors
48     // (0xff..ff) be detected as not in use.
49     NotInUse   = 1 << 7,
50 }
51
52 #[repr(C)]
53 #[derive(Clone, Copy)]
54 struct SectorHeader {
55     flags: u16, // Combination of SectorFlag items.
56     recording_id: u16, // Zero is considered invalid.
57     start_time: u32, // UNIX time. Only present if flag DataRecord isn't set.
58 }
59
60 impl SectorHeader {
61     fn new() -> SectorHeader {
62         SectorHeader {
63             flags: 0,
64             recording_id: 0,
65             start_time: 0,
66         }
67     }
68
69     fn is_in_use(&self) -> bool {
70         let mask = (SectorFlag::InUse as u16) | (SectorFlag::NotInUse as u16);
71         let value = SectorFlag::InUse as u16;
72
73         (self.flags & mask) == value
74     }
75
76     fn starts_recording(&self) -> bool {
77         let mask = (SectorFlag::InUse as u16)
78                  | (SectorFlag::DataRecord as u16)
79                  | (SectorFlag::NotInUse as u16);
80         let value = SectorFlag::InUse as u16;
81
82         (self.flags & mask) == value
83     }
84
85     fn belongs_to(&self, recording_id: u16) -> bool {
86         self.is_in_use() && self.recording_id == recording_id
87     }
88 }
89
90 #[derive(Clone, Copy)]
91 struct InFlight {
92     d_time_s: u32,
93     d_lat: i32,
94     d_lon: i32,
95 }
96
97 impl InFlight {
98     fn new() -> InFlight {
99         InFlight {
100             d_time_s: 0,
101             d_lat: 0,
102             d_lon: 0,
103         }
104     }
105 }
106
107 pub struct Logger<'a> {
108     pub storage: &'a mut dyn Storage,
109
110     recording_id: u16, // Zero is considered invalid.
111
112     // The index of the first sector of the currently running recording.
113     // Only written in logger_start_recording.
114     first_sector: u16,
115
116     recording_started: u32,
117
118     // The number of slots filled in num_flight.
119     num_in_flight: usize,
120
121     // Deltas not yet written out to write_buffer.
122     //
123     // Limiting ourselves to 7 items here means we can use
124     // 0xff as a padding byte.
125     in_flight: [InFlight; 7],
126
127     sector_header: [SectorHeader; NUM_SECTORS],
128
129     sectors_written: u16,
130
131     write_buffer_offset: usize,
132     write_buffer: [u8; SECTOR_SIZE],
133 }
134
135 struct SectorHeaderIter<'a> {
136     sector_header: &'a [SectorHeader; NUM_SECTORS],
137     it_front: usize,
138     it_back: usize,
139     indices: [u16; NUM_SECTORS],
140 }
141
142 fn cmp_sector_header_indices(a: u16, b: u16,
143                              sector_header: &[SectorHeader]) -> i32 {
144     let header_a = &sector_header[a as usize];
145     let header_b = &sector_header[b as usize];
146
147     // Latest entries come first.
148     if header_a.start_time > header_b.start_time {
149         -1
150     } else if header_a.start_time < header_b.start_time {
151         1
152     } else {
153         0
154     }
155 }
156
157 fn downheap(heap: &mut [u16], mut index: usize, num_elts: usize,
158             sector_header: &[SectorHeader]) {
159     let orig = heap[index];
160
161     loop {
162         let mut worker = index * 2;
163
164         if worker < num_elts &&
165            cmp_sector_header_indices(heap[worker], heap[worker + 1], sector_header) < 0 {
166             worker += 1;
167         }
168
169         if worker > num_elts ||
170            cmp_sector_header_indices(orig, heap[worker], sector_header) >= 0 {
171             break;
172         }
173
174         heap[index] = heap[worker];
175         index = worker;
176     }
177
178     heap[index] = orig;
179 }
180
181 impl<'a> SectorHeaderIter<'a> {
182     fn new(logger: &'a Logger) -> SectorHeaderIter<'a> {
183         let mut iter = SectorHeaderIter {
184             sector_header: &logger.sector_header,
185             it_front: 0,
186             it_back: NUM_SECTORS,
187             indices: [0; NUM_SECTORS]
188         };
189
190         let mut num_used = 0;
191
192         // Put the indices of the used directory entries at the beginning
193         // of the array. Ignore the unused ones since we are not going
194         // to sort them anyway.
195         for i in 0..NUM_SECTORS {
196             let sector_header = &iter.sector_header[i];
197
198             if sector_header.starts_recording() {
199                 iter.indices[num_used] = i as u16;
200                 num_used += 1;
201             }
202         }
203
204         let num_elts_to_sort = num_used;
205
206         if num_elts_to_sort != 0 {
207             // Sort the used directory entries.
208             iter.sort(num_elts_to_sort);
209         }
210
211         // Now put the indices of the unused directory entries in the array.
212         if num_used == 0 {
213             for i in 0..NUM_SECTORS {
214                 iter.indices[i] = i as u16;
215             }
216         } else {
217             let latest_used = iter.indices[0] as usize;
218             let mut offset_unused = num_used;
219
220             // First put the entries that come after the latest one in use...
221             for i in (latest_used + 1)..NUM_SECTORS {
222                 let sector_header = &iter.sector_header[i];
223
224                 if !sector_header.is_in_use() {
225                     iter.indices[offset_unused] = i as u16;
226                     offset_unused += 1;
227                 }
228             }
229
230             // ... then wrap around if necessary.
231             for i in 0..latest_used {
232                 let sector_header = &iter.sector_header[i];
233
234                 if !sector_header.is_in_use() {
235                     iter.indices[offset_unused] = i as u16;
236                     offset_unused += 1;
237                 }
238             }
239         }
240
241         // XXX:
242         // Need to handle those sectors that don't start recordings
243         // but that are still used.
244
245         iter
246     }
247
248     fn sort(&mut self, num_elts_to_sort: usize) {
249         for i in (1..((num_elts_to_sort + 1) / 2) + 1).rev() {
250             downheap(&mut self.indices, i - 1, num_elts_to_sort - 1,
251                      self.sector_header);
252         }
253
254         for i in (1..num_elts_to_sort).rev() {
255             let t = self.indices[0];
256             self.indices[0] = self.indices[i];
257             self.indices[i] = t;
258
259             downheap(&mut self.indices, 0, i - 1, self.sector_header);
260         }
261     }
262 }
263
264 impl<'a> Iterator for SectorHeaderIter<'a> {
265     type Item = usize;
266
267     fn next(&mut self) -> Option<usize> {
268         if self.it_front == self.it_back {
269             None
270         } else {
271             let next_index = self.indices[self.it_front] as usize;
272
273             self.it_front += 1;
274
275             Some(next_index)
276         }
277     }
278 }
279
280 impl<'a> DoubleEndedIterator for SectorHeaderIter<'a> {
281     fn next_back(&mut self) -> Option<usize> {
282         if self.it_back == self.it_front {
283             None
284         } else {
285             self.it_back -= 1;
286
287             let next_index = self.indices[self.it_back] as usize;
288
289             Some(next_index)
290         }
291     }
292 }
293
294 fn normalize_angle(mut angle: i32) -> i32 {
295     let deg90 = 90 * 60 * 10000;
296     let deg180 = deg90 << 1;
297     let deg360 = deg180 << 1;
298
299     while angle >= deg180 {
300         angle -= deg360;
301     }
302
303     while angle <= -deg180 {
304         angle += deg360;
305     }
306
307     angle
308 }
309
310 fn max<T>(a: T, b: T) -> T
311           where T: PartialOrd {
312     if a > b {
313         a
314     } else {
315         b
316     }
317 }
318
319 impl<'a> Logger<'a> {
320     pub fn new(storage: &'a mut dyn Storage) -> Logger {
321         Logger {
322             storage: storage,
323
324             recording_id: 0,
325             first_sector: 0,
326             recording_started: 0,
327             num_in_flight: 0,
328
329             in_flight: [InFlight::new(); 7],
330             sector_header: [SectorHeader::new(); NUM_SECTORS],
331
332             sectors_written: 0,
333
334             write_buffer_offset: 0,
335             write_buffer: [0xff; SECTOR_SIZE],
336         }
337     }
338
339     pub fn init(&mut self) {
340         // Reading the directory entries one by one means
341         // we won't need an as large buffer on the stack.
342         for i in 0..NUM_SECTORS {
343             self.read_sector_header(i);
344         }
345     }
346
347     fn read_sector_header(&mut self, sector_index: usize) {
348         let address = sector_index * SECTOR_SIZE;
349         let mut chunk = [0u8; 8];
350
351         self.storage.read(address, &mut chunk);
352
353         let sector_header_ptr: *mut SectorHeader =
354             &mut self.sector_header[sector_index];
355
356         unsafe {
357             core::ptr::copy(chunk.as_ptr(),
358                             sector_header_ptr as *mut u8,
359                             chunk.len());
360         }
361     }
362
363     fn prepare_write_buffer(&mut self, is_initial: bool) {
364         self.write_buffer = [0xff; SECTOR_SIZE];
365
366         let flags = if is_initial {
367             (SectorFlag::InUse as u16)
368         } else {
369             (SectorFlag::InUse as u16) | (SectorFlag::DataRecord as u16)
370         };
371
372         // Write sector header.
373         self.write_buffer[0..2].copy_from_slice(&flags.to_le_bytes());
374         self.write_buffer[2..4].copy_from_slice(&self.recording_id.to_le_bytes());
375
376         self.write_buffer_offset = 4;
377
378         if is_initial {
379             let start = self.write_buffer_offset;
380             let end = start + 4;
381
382             self.write_buffer[start..end].copy_from_slice(
383                 &self.recording_started.to_le_bytes());
384
385             self.write_buffer_offset += 4;
386         }
387     }
388
389     pub fn start_recording(&mut self, tap: &TimeAndPos) -> u16 {
390         self.find_next_record_slot();
391
392         self.sectors_written = 0;
393         self.recording_started = tap.unix_time;
394         self.num_in_flight = 0;
395
396         self.prepare_write_buffer(true);
397
398         self.write_packet(0, tap.latitude, tap.longitude);
399
400         self.recording_id
401     }
402
403     pub fn log(&mut self, prev_tap: &TimeAndPos, tap: &TimeAndPos) {
404         let d_time_ms = elapsed_ms(tap.system_time, prev_tap.system_time);
405
406         // We know that our hardware cannot deliver updates more often
407         // than once a second. However when there's a delay in evaluating
408         // the hardware's messages, we will end up with intervals like
409         // 1050ms and 950ms (the latter will "make up" for the slowness
410         // in the former). To avoid logging deltas of 0 seconds, we round
411         // the intervals to full seconds.
412         let d_time_s = (d_time_ms + 500) / 1000;
413
414         let d_lat = tap.latitude - prev_tap.latitude;
415         let d_lon = tap.longitude - prev_tap.longitude;
416
417         if self.write_packet(d_time_s, d_lat, d_lon) {
418             self.flush_in_flight(false);
419         }
420     }
421
422     pub fn stop_recording(&mut self, tap: &TimeAndPos) -> u16 {
423         // Mark the end of the points stream.
424         self.write_packet(0xffffffff, 0, 0);
425         self.flush_in_flight(true);
426
427         // Write footer.
428         let duration = (tap.unix_time - self.recording_started) as u16;
429
430         let start = self.write_buffer_offset;
431         let end = start + 2;
432         let dst = &mut self.write_buffer[start..end];
433
434         dst.copy_from_slice(&duration.to_le_bytes());
435
436         let this_sector = self.first_sector + self.sectors_written;
437
438         if self.storage.write(this_sector as usize * SECTOR_SIZE,
439                               &self.write_buffer).is_err() {
440             // XXX
441         }
442
443         self.sectors_written += 1;
444
445         for i in 0..self.sectors_written {
446             self.read_sector_header((self.first_sector + i) as usize);
447         }
448
449         self.sectors_written
450     }
451
452     fn sector_header_iter(&self) -> SectorHeaderIter {
453         SectorHeaderIter::new(self)
454     }
455
456     fn find_next_record_slot(&mut self) {
457         let mut candidate_index = 0;
458         let mut max_recording_id = 0;
459
460         for index in self.sector_header_iter() {
461             candidate_index = index;
462
463             let sector_header = &self.sector_header[index];
464
465             if !sector_header.is_in_use() {
466                 // Due to our sorting we know that there will be no more
467                 // used directory entries following. At this point
468                 // we aren't interested in unused ones, so break the loop.
469                 break;
470             }
471
472             max_recording_id =
473                 max(max_recording_id, sector_header.recording_id);
474         }
475
476         self.first_sector = candidate_index as u16;
477         self.recording_id = max_recording_id.wrapping_add(1);
478     }
479
480     fn write_packet(&mut self, d_time_s: u32, d_lat: i32, d_lon: i32) -> bool {
481         {
482             let in_flight = &mut self.in_flight[self.num_in_flight];
483
484             in_flight.d_time_s = d_time_s;
485             in_flight.d_lat = normalize_angle(d_lat);
486             in_flight.d_lon = normalize_angle(d_lon);
487         }
488
489         self.num_in_flight += 1;
490
491         self.num_in_flight == self.in_flight.len()
492     }
493
494     // Flushes the "in flight" items to the write buffer.
495     //
496     // @param is_final @c true iff this is the final flush in this recording.
497     //
498     // @note May only be called if logger.num_in_flight is greater than zero.
499     fn flush_in_flight(&mut self, is_final: bool) {
500         let mut flags = 0u8;
501
502         // Normally our items will have a time delta of one second.
503         // Mark the ones that differ from that.
504         for i in 0..self.num_in_flight {
505             if self.in_flight[i].d_time_s != 1 {
506                 flags |= 1 << i;
507             }
508         }
509
510         let mut buffer = [0u8; 128];
511         let mut offset = 0;
512
513         buffer[offset] = flags;
514         offset += 1;
515
516         for i in 0..(self.num_in_flight - 1) {
517             let in_flight = &self.in_flight[i];
518
519             // Only write the time delta for the atypical cases.
520             if (flags & (1 << i)) != 0 {
521                 offset +=
522                     varint::write_u32(&mut buffer[offset..], in_flight.d_time_s);
523             }
524
525             offset +=
526                 varint::write_s32(&mut buffer[offset..], in_flight.d_lat);
527
528             offset +=
529                 varint::write_s32(&mut buffer[offset..], in_flight.d_lon);
530         }
531
532         let i = self.num_in_flight - 1;
533         let in_flight = &self.in_flight[i];
534
535         // Only write the time delta for the atypical cases.
536         if (flags & (1 << i)) != 0 {
537             offset +=
538                 varint::write_u32(&mut buffer[offset..], in_flight.d_time_s);
539         }
540
541         // The final point is an end-of-stream marker and doesn't store
542         // d_lat or d_lon.
543         if !is_final {
544             offset +=
545                 varint::write_s32(&mut buffer[offset..], in_flight.d_lat);
546
547             offset +=
548                 varint::write_s32(&mut buffer[offset..], in_flight.d_lon);
549         }
550
551         self.num_in_flight = 0;
552
553         let num_bytes_written = offset;
554
555         let remaining = self.write_buffer.len() - self.write_buffer_offset;
556
557         if remaining < num_bytes_written {
558             // We may use 0xff as padding bytes, since 0xff isn't a valid
559             // first byte in a points batch. prepare_write_buffer() fills
560             // our buffer with 0xff, so we don't need to do anything here.
561             let this_sector = self.first_sector + self.sectors_written;
562
563             if self.storage.write(this_sector as usize * SECTOR_SIZE,
564                                   &self.write_buffer).is_err() {
565                 // XXX
566             }
567
568             self.sectors_written += 1;
569
570             self.prepare_write_buffer(false);
571         }
572
573         let start = self.write_buffer_offset;
574         let end = start + num_bytes_written;
575         let dst = &mut self.write_buffer[start..end];
576
577         dst.copy_from_slice(&buffer[0..num_bytes_written]);
578
579         self.write_buffer_offset += num_bytes_written;
580     }
581
582     ///
583     /// Write a listing of the stored recordings to @p tx_buf.
584     pub fn list_recordings(&mut self, tx_buf: &mut Buffer) {
585         for index in self.sector_header_iter().rev() {
586             let sector_header = &self.sector_header[index as usize];
587
588             if !sector_header.starts_recording() {
589                 continue;
590             }
591
592             let mut date_time_s = [b' '; 19];
593
594             if let Some(tm) = Time::from_unix_time(sector_header.start_time) {
595                 tm.fmt_date(&mut date_time_s[0..]);
596                 tm.fmt_time(&mut date_time_s[11..]);
597             }
598
599             let mut recording_id_s = [b'0'; 9];
600             let recording_id_s_len =
601                 fmt_u32_pad(&mut recording_id_s,
602                             sector_header.recording_id as u32,
603                             8, b' ');
604
605             tx_buf.write(&date_time_s);
606             tx_buf.write(b"       ");
607             tx_buf.write(&recording_id_s[0..recording_id_s_len]);
608             tx_buf.write(b"\n");
609
610             tx_buf.flush();
611         }
612     }
613
614     ///
615     /// Check whether or not recording @p recording_id exists.
616     pub fn has_recording(&mut self, recording_id: u16) -> bool {
617         if recording_id == 0 {
618             return false;
619         }
620
621         self.sector_header_iter().find(|&index| {
622             let sector_header = &self.sector_header[index as usize];
623
624             sector_header.recording_id == recording_id &&
625             sector_header.starts_recording()
626         }).is_some()
627     }
628
629     ///
630     /// Retrieve recording @p recording_id and
631     /// write it to @p tx_buf in yencoded form.
632     pub fn get_recording(&mut self, recording_id: u16,
633                          tx_buf: &mut Buffer) -> Result<(), Error> {
634         if recording_id == 0 {
635             return Err(Error::NoSuchRecording);
636         }
637
638         if let Some(found_index) = self.sector_header_iter().find(|&index| {
639             let sector_header = &self.sector_header[index as usize];
640
641             sector_header.recording_id == recording_id &&
642             sector_header.starts_recording()
643         }) {
644             let mut filename = [b' '; 29];
645
646             filename[0..].copy_from_slice(b"gps-watch-recording-XXXXX.bin");
647
648             fmt_u32_pad(&mut filename[20..], recording_id as u32, 5, b'0');
649
650             let mut yenc = Yencode::new(tx_buf);
651
652             yenc.start(&filename);
653
654             let format_version = 1u8;
655             yenc.data(&[format_version]);
656
657             let mut next_sector = found_index as usize;
658
659             for _ in 0..NUM_SECTORS {
660                 let address = next_sector * SECTOR_SIZE;
661                 let mut buf = [0u8; SECTOR_SIZE];
662
663                 self.storage.read(address, &mut buf);
664
665                 // Skip flags and recording ID.
666                 yenc.data(&buf[4..]);
667
668                 next_sector += 1;
669                 next_sector &= NUM_SECTORS - 1;
670
671                 if !self.sector_header[next_sector].belongs_to(recording_id) {
672                     break;
673                 }
674             }
675
676             yenc.finish();
677
678             tx_buf.flush();
679
680             Ok(())
681         } else {
682             Err(Error::NoSuchRecording)
683         }
684     }
685 }