Add an (untested) way to persist measurements to disk

This commit is contained in:
2021-06-15 00:15:04 -07:00
parent aa5d38b0fb
commit 1a6f13e271
3 changed files with 83 additions and 4 deletions

View File

@@ -10,6 +10,7 @@ edition = "2018"
bincode = "1.3.1" bincode = "1.3.1"
common_macros = "0.1" common_macros = "0.1"
crossterm = "0.18" crossterm = "0.18"
csv = "1.1"
decorum = "0.3" decorum = "0.3"
dyn-clone = "1.0" dyn-clone = "1.0"
enum_dispatch = "0.3" enum_dispatch = "0.3"

View File

@@ -15,6 +15,15 @@ pub trait AbstractMeasurement: Send + Sync + DynClone {
} }
dyn_clone::clone_trait_object!(AbstractMeasurement); dyn_clone::clone_trait_object!(AbstractMeasurement);
pub fn eval_multiple_kv(state: &dyn GenericSim, meas: &[Box<dyn AbstractMeasurement>]) -> BTreeMap<String, String> {
let mut r = BTreeMap::new();
for m in meas {
let mut other = m.key_value(state);
r.append(&mut other);
}
r
}
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct Time; pub struct Time;

View File

@@ -1,7 +1,7 @@
use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u}; use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u};
use crate::real::ToFloat as _; use crate::real::ToFloat as _;
use crate::sim::{GenericSim, Sample, StaticSim}; use crate::sim::{GenericSim, Sample, StaticSim};
use crate::meas::AbstractMeasurement; use crate::meas::{self, AbstractMeasurement};
use crossterm::{cursor, QueueableCommand as _}; use crossterm::{cursor, QueueableCommand as _};
use crossterm::style::{style, Color, PrintStyledContent}; use crossterm::style::{style, Color, PrintStyledContent};
use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _};
@@ -12,9 +12,9 @@ use image::{RgbImage, Rgb};
use imageproc::{pixelops, drawing}; use imageproc::{pixelops, drawing};
use rayon::prelude::*; use rayon::prelude::*;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::fs::File; use std::fs::{File, OpenOptions};
use std::io::{BufReader, BufWriter, Write as _}; use std::io::{BufReader, BufWriter, Seek as _, SeekFrom, Write as _};
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::sync::{Mutex, RwLock}; use std::sync::{Mutex, RwLock};
use y4m; use y4m;
@@ -718,3 +718,72 @@ impl<S: GenericSim + Serialize> Renderer<S> for SerializerRenderer {
} }
} }
} }
enum CsvState {
Reading(csv::Reader<BufReader<File>>),
Writing(csv::Writer<BufWriter<File>>),
}
pub struct CsvRenderer {
state: Mutex<Option<CsvState>>,
}
impl CsvRenderer {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
let f = OpenOptions::new().read(true).write(true).create(true).open(path).unwrap();
let reader = csv::Reader::from_reader(BufReader::new(f));
Self {
state: Mutex::new(Some(CsvState::Reading(reader))),
}
}
}
impl<S: GenericSim> Renderer<S> for CsvRenderer {
fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box<dyn AbstractMeasurement>], config: RenderConfig) {
default_render_z_slice(self, state, z, measurements, config)
}
fn render(&self, state: &S, measurements: &[Box<dyn AbstractMeasurement>], _config: RenderConfig) {
let row = meas::eval_multiple_kv(state, measurements);
let step = state.step_no();
let mut lock = self.state.lock().unwrap();
let mut writer = match lock.take().unwrap() {
CsvState::Reading(mut reader) => {
let headers = reader.headers().unwrap();
let has_header = headers.get(0) == Some("step");
if has_header {
// read until we get a row whose step is >= this one
let mut seek_pos = None;
for record in reader.records() {
let record = record.unwrap();
if let Some(step_str) = record.get(0) {
if let Ok(step_num) = step_str.parse::<u64>() {
if step_num >= step {
// truncate csv here
seek_pos = record.position().map(|p| p.byte());
break;
}
}
}
}
let mut file = reader.into_inner().into_inner();
if let Some(pos) = seek_pos {
file.seek(SeekFrom::Start(pos)).unwrap();
file.set_len(pos).unwrap();
}
csv::Writer::from_writer(BufWriter::new(file))
} else { // no header
let mut file = reader.into_inner().into_inner();
file.seek(SeekFrom::Start(0)).unwrap();
file.set_len(0).unwrap();
let mut writer = csv::Writer::from_writer(BufWriter::new(file));
// write the header
writer.write_record(row.keys()).unwrap();
writer
}
},
CsvState::Writing(writer) => writer,
};
writer.write_record(row.values()).unwrap();
*lock = Some(CsvState::Writing(writer));
}
}