From 1a6f13e271e2cbad51b64acf6cf5e3b732a05d9f Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 15 Jun 2021 00:15:04 -0700 Subject: [PATCH] Add an (untested) way to persist measurements to disk --- Cargo.toml | 1 + src/meas.rs | 9 ++++++ src/render.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 472d3ab..19671da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" bincode = "1.3.1" common_macros = "0.1" crossterm = "0.18" +csv = "1.1" decorum = "0.3" dyn-clone = "1.0" enum_dispatch = "0.3" diff --git a/src/meas.rs b/src/meas.rs index 815846b..498bbe9 100644 --- a/src/meas.rs +++ b/src/meas.rs @@ -15,6 +15,15 @@ pub trait AbstractMeasurement: Send + Sync + DynClone { } dyn_clone::clone_trait_object!(AbstractMeasurement); +pub fn eval_multiple_kv(state: &dyn GenericSim, meas: &[Box]) -> BTreeMap { + 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)] pub struct Time; diff --git a/src/render.rs b/src/render.rs index 353c453..2786d82 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,7 +1,7 @@ use crate::geom::{Index, Meters, Vec2, Vec3, Vec3u}; use crate::real::ToFloat as _; use crate::sim::{GenericSim, Sample, StaticSim}; -use crate::meas::AbstractMeasurement; +use crate::meas::{self, AbstractMeasurement}; use crossterm::{cursor, QueueableCommand as _}; use crossterm::style::{style, Color, PrintStyledContent}; use font8x8::{BASIC_FONTS, GREEK_FONTS, UnicodeFonts as _}; @@ -12,9 +12,9 @@ use image::{RgbImage, Rgb}; use imageproc::{pixelops, drawing}; use rayon::prelude::*; use serde::{Serialize, Deserialize}; -use std::fs::File; -use std::io::{BufReader, BufWriter, Write as _}; -use std::path::PathBuf; +use std::fs::{File, OpenOptions}; +use std::io::{BufReader, BufWriter, Seek as _, SeekFrom, Write as _}; +use std::path::{Path, PathBuf}; use std::sync::{Mutex, RwLock}; use y4m; @@ -718,3 +718,72 @@ impl Renderer for SerializerRenderer { } } } + +enum CsvState { + Reading(csv::Reader>), + Writing(csv::Writer>), +} + +pub struct CsvRenderer { + state: Mutex>, +} + +impl CsvRenderer { + pub fn new>(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 Renderer for CsvRenderer { + fn render_z_slice(&self, state: &S, z: u32, measurements: &[Box], config: RenderConfig) { + default_render_z_slice(self, state, z, measurements, config) + } + fn render(&self, state: &S, measurements: &[Box], _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::() { + 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)); + } +}