parse_monitors/monitors/reports/loader/
y2021_25.rs1use std::time::Instant;
2use std::{fs::File, io::Read, path::Path};
3
4use regex::Regex;
5
6use crate::{Exertion, Monitors, MonitorsError};
7
8use super::MonitorsLoader;
9
10type Result<T> = std::result::Result<T, MonitorsError>;
11
12impl<const Y: u32> MonitorsLoader<Y> {
13 #[cfg(feature = "bzip2")]
14 fn decompress(&self) -> Result<String> {
15 let mut contents = String::new();
16 let data_path = Path::new(&self.path).with_extension("csv.bz2");
17 let csv_file =
18 File::open(&data_path).map_err(|e| MonitorsError::Io(e, data_path.clone()))?;
19
20 log::info!("Loading {:?}...", csv_file);
21 let buf = std::io::BufReader::new(csv_file);
22 let mut bz2 = bzip2::bufread::BzDecoder::new(buf);
23 bz2.read_to_string(&mut contents)
24 .map_err(|e| MonitorsError::Io(e, data_path))?;
25 Ok(contents)
26 }
27 #[cfg(not(feature = "bzip2"))]
28 fn decompress(&self) -> Result<String> {
29 use flate2::read::GzDecoder;
30
31 use crate::MonitorsError;
32
33 let mut contents = String::new();
34 let data_path = Path::new(&self.path).with_extension("csv.z");
35 log::info!("Loading {:?}...", data_path);
36 let csv_file =
37 File::open(&data_path).map_err(|e| MonitorsError::Io(e, data_path.clone()))?;
38 let mut gz = GzDecoder::new(csv_file);
39 gz.read_to_string(&mut contents)
40 .map_err(|e| MonitorsError::Io(e, data_path))?;
41 Ok(contents)
42 }
43 #[cfg(feature = "object_store")]
44 pub async fn load_from_store(self, store: impl object_store::ObjectStore) -> Result<Monitors> {
45 use object_store::ObjectStoreExt;
46 use std::io::Cursor;
47 use std::path::PathBuf;
48 let data_path = Path::new(&self.path).with_extension("csv.z");
49 let location = object_store::path::Path::from(data_path.to_str().unwrap());
50 let bytes = store.get(&location).await?.bytes().await?;
51 let cursor = Cursor::new(bytes);
52 let mut gz = flate2::bufread::GzDecoder::new(cursor);
53 let mut contents = String::new();
54 gz.read_to_string(&mut contents)
55 .map_err(|e| MonitorsError::Io(e, PathBuf::from(location.to_string())))?;
56 self.load_from_bytes(contents.into_bytes())
57 }
58 pub fn load(self) -> Result<Monitors> {
59 let contents = self.decompress()?;
60 self.load_from_bytes(contents.into_bytes())
61 }
62 pub fn load_from_bytes(self, bytes: Vec<u8>) -> Result<Monitors> {
63 let now = Instant::now();
64 let mut rdr = csv::Reader::from_reader(bytes.as_slice());
65
66 let headers: Vec<_> = {
67 let headers = rdr.headers()?;
68 headers.into_iter().map(|h| h.to_string()).collect()
70 };
71 if Y == 2025 {
72 headers
73 .iter()
74 .find(|h| h.contains("M1c_"))
75 .ok_or(MonitorsError::YearMismatch(2021, Y))?;
76 }
77
78 let re_htc = Regex::new(
79 r"(\w+) Monitor: Surface Average of Heat Transfer Coefficient \(W/m\^2-K\)",
80 )?;
81 let re_force = Regex::new(r"(.+)_([XYZ]) Monitor(?:: Force)? \(N\)")?;
83 let re_moment = Regex::new(r"(.+)Mom_([XYZ]) Monitor(?:: Moment)? \(N-m\)")?;
84
85 let re_header = Regex::new(&self.header_regex)?;
86 let re_x_header = if let Some(re) = self.header_exclude_regex {
87 Some(Regex::new(&re)?)
88 } else {
89 None
90 };
91
92 let mut monitors = Monitors::default();
93
94 for result in rdr.records() {
95 let record = result?;
96 let time = record.iter().next().unwrap().parse::<f64>()?;
97 if time < self.time_range.0 - 1. / 40. || time > self.time_range.1 + 1. / 40. {
98 continue;
99 };
100 monitors.time.push(time);
101 for (data, header) in record.iter().skip(1).zip(headers.iter().skip(1)).filter(
102 |(_, h)| match &re_x_header {
103 Some(re_x_header) => re_header.is_match(h) && !re_x_header.is_match(h),
104 None => re_header.is_match(h),
105 },
106 ) {
107 if let Some(capts) = re_htc.captures(header) {
109 let key = capts.get(1).unwrap().as_str().to_owned();
110 let value = data.parse::<f64>()?;
111 monitors
112 .heat_transfer_coefficients
113 .entry(key)
114 .or_insert_with(Vec::new)
115 .push(value.abs());
116 }
117 if let Some(capts) = re_force.captures(header) {
119 let key = capts.get(1).unwrap().as_str().to_owned();
120 let value = data.parse::<f64>()?;
121 let exertions = monitors
122 .forces_and_moments
123 .entry(key)
124 .or_insert(vec![Exertion::default()]);
125 let exertion = exertions.last_mut().unwrap();
126 match capts.get(2).unwrap().as_str() {
127 "X" => match exertion.force.x {
128 Some(_) => exertions.push(Exertion::from_force_x(value)),
129 None => {
130 exertion.force.x = Some(value);
131 }
132 },
133 "Y" => match exertion.force.y {
134 Some(_) => exertions.push(Exertion::from_force_y(value)),
135 None => {
136 exertion.force.y = Some(value);
137 }
138 },
139 "Z" => match exertion.force.z {
140 Some(_) => exertions.push(Exertion::from_force_z(value)),
141 None => {
142 exertion.force.z = Some(value);
143 }
144 },
145 &_ => (),
146 };
147 }
148 if let Some(capts) = re_moment.captures(header) {
150 let key = capts
151 .get(1)
152 .unwrap()
153 .as_str()
154 .trim_end_matches('_')
155 .to_owned();
156 let value = data.parse::<f64>()?;
157 let exertions = monitors
158 .forces_and_moments
159 .entry(key)
160 .or_insert(vec![Exertion::default()]);
161 let exertion = exertions.last_mut().unwrap();
162 match capts.get(2).unwrap().as_str() {
163 "X" => match exertion.moment.x {
164 Some(_) => exertions.push(Exertion::from_moment_x(value)),
165 None => {
166 exertion.moment.x = Some(value);
167 }
168 },
169 "Y" => match exertion.moment.y {
170 Some(_) => exertions.push(Exertion::from_moment_y(value)),
171 None => {
172 exertion.moment.y = Some(value);
173 }
174 },
175 "Z" => match exertion.moment.z {
176 Some(_) => exertions.push(Exertion::from_moment_z(value)),
177 None => {
178 exertion.moment.z = Some(value);
179 }
180 },
181 &_ => (),
182 };
183 }
184 }
185 }
186 log::info!("... loaded in {:}s", now.elapsed().as_secs());
187 Ok(monitors)
188 }
189}