From: Tilman Sauerbeck Date: Thu, 9 Jan 2020 10:39:54 +0000 (+0100) Subject: common: Implement Logger::get_recording(). X-Git-Url: http://git.code-monkey.de/?a=commitdiff_plain;h=f68ab85d65e9053868ce022e997b3834d6812f59;p=gps-watch.git common: Implement Logger::get_recording(). --- diff --git a/src/common/logger.rs b/src/common/logger.rs index d83d239..23c40a1 100644 --- a/src/common/logger.rs +++ b/src/common/logger.rs @@ -25,12 +25,20 @@ use gps::TimeAndPos; use storage::Storage; use varint; use systick::elapsed_ms; +use buffer::Buffer; +use yencode::Yencode; +use fmt::*; pub const MEMORY_SIZE: usize = 2 << 20; const SECTOR_SIZE: usize = 4 << 10; const NUM_SECTORS: usize = MEMORY_SIZE / SECTOR_SIZE; +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum Error { + NoSuchRecording, +} + enum SectorFlag { InUse = 1 << 0, DataRecord = 1 << 1, @@ -72,6 +80,10 @@ impl SectorHeader { (self.flags & mask) == value } + + fn belongs_to(&self, recording_id: u16) -> bool { + self.is_in_use() && self.recording_id == recording_id + } } #[derive(Clone, Copy)] @@ -557,4 +569,61 @@ impl<'a> Logger<'a> { self.write_buffer_offset += num_bytes_written; } + + /// + /// Retrieve recording @p recording_id and + /// write it to @p tx_buf in yencoded form. + pub fn get_recording(&mut self, recording_id: u16, + tx_buf: &mut Buffer) -> Result<(), Error> { + if recording_id == 0 { + return Err(Error::NoSuchRecording); + } + + if let Some(found_index) = self.sector_header_iter().find(|&index| { + let sector_header = &self.sector_header[index as usize]; + + sector_header.recording_id == recording_id && + sector_header.starts_recording() + }) { + let mut filename = [b' '; 29]; + + filename[0..].copy_from_slice(b"gps-watch-recording-XXXXX.bin"); + + fmt_u32_pad(&mut filename[20..], recording_id as u32, 5, b'0'); + + let mut yenc = Yencode::new(tx_buf); + + yenc.start(&filename); + + let format_version = 1u8; + yenc.data(&[format_version]); + + let mut next_sector = found_index as usize; + + for _ in 0..NUM_SECTORS { + let address = next_sector * SECTOR_SIZE; + let mut buf = [0u8; SECTOR_SIZE]; + + self.storage.read(address, &mut buf); + + // Skip flags and recording ID. + yenc.data(&buf[4..]); + + next_sector += 1; + next_sector &= NUM_SECTORS - 1; + + if !self.sector_header[next_sector].belongs_to(recording_id) { + break; + } + } + + yenc.finish(); + + tx_buf.flush(); + + Ok(()) + } else { + Err(Error::NoSuchRecording) + } + } } diff --git a/test/logger_test.rs b/test/logger_test.rs index ee0b30b..efdfcf2 100644 --- a/test/logger_test.rs +++ b/test/logger_test.rs @@ -21,9 +21,10 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +use common::buffer::{Buffer, BufferUserData}; use common::gps; use common::storage::{Storage, Error}; -use common::logger::*; +use common::logger::{MEMORY_SIZE, Logger, Error as LoggerError}; struct FakeStorage { expected: Box<[u8]>, @@ -213,3 +214,99 @@ fn multi_sector_recording() { assert_eq!(header0, fake_storage.actual[0..(0 + header0.len())]); assert_eq!(header1, fake_storage.actual[4096..(4096 + header1.len())]); } + +extern "C" fn flush_write_buffer(user_data: *mut BufferUserData, + buf: *const u8, + _bufsiz: usize, + count: usize) -> isize { + let _final_buffer = user_data as *mut [u8; 8192]; + let final_buffer = unsafe { &mut * _final_buffer }; + + unsafe { + core::ptr::copy_nonoverlapping(buf, final_buffer.as_mut_ptr(), count); + } + + count as isize +} + +// Verifies that Logger::get_recording() detects unknown recording IDs. +#[test] +fn get_recording_invalid() { + let mut final_buffer = [0u8; 8192]; + + let mut yenc_buffer_space = [0u8; 8192]; + let mut yenc_buffer = Buffer::alloc(); + + let user_data = (&mut final_buffer as *mut [u8; 8192]) as *mut BufferUserData; + + yenc_buffer.init(yenc_buffer_space.as_mut_ptr(), + yenc_buffer_space.len(), + flush_write_buffer, + user_data); + let mut fake_storage = FakeStorage::new(); + + let mut logger = Logger::new(&mut fake_storage); + logger.init(); + + // Zero is never a valid recording id. + let result = logger.get_recording(0, &mut yenc_buffer); + assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err()); + + let result = logger.get_recording(1, &mut yenc_buffer); + assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err()); +} + +// Verifies that Logger::get_recording() can retrieve finished recordings. +#[test] +fn get_recording_valid() { + let mut final_buffer = [0u8; 8192]; + + let mut yenc_buffer_space = [0u8; 8192]; + let mut yenc_buffer = Buffer::alloc(); + + let user_data = (&mut final_buffer as *mut [u8; 8192]) as *mut BufferUserData; + + yenc_buffer.init(yenc_buffer_space.as_mut_ptr(), + yenc_buffer_space.len(), + flush_write_buffer, + user_data); + + let mut fake_storage = FakeStorage::new(); + + let mut logger = Logger::new(&mut fake_storage); + logger.init(); + + let result = logger.get_recording(1, &mut yenc_buffer); + assert_eq!(LoggerError::NoSuchRecording, result.unwrap_err()); + + let tap0 = gps::TimeAndPos { + system_time: 0, + unix_time: 1478026311, + latitude: 0x73234e, + longitude: 0x73234f, + }; + + logger.start_recording(&tap0); + + let tap1 = gps::TimeAndPos { + system_time: 0, + unix_time: 1478026311 + 1, + latitude: 0x73234e + 5, + longitude: 0x73234f + 5, + }; + + logger.log(&tap0, &tap1); + + let tap2 = gps::TimeAndPos { + system_time: 0, + unix_time: 1478026311 + 2, + latitude: 0x73234e + 10, + longitude: 0x73234f + 10, + }; + + logger.log(&tap1, &tap2); + + logger.stop_recording(&tap2); + + assert!(logger.get_recording(1, &mut yenc_buffer).is_ok()); +}