1use crate::records::RecordSummary;
2use crate::records::Records;
3use crate::records::records::PTR;
4use crate::records::records::Record;
5use crate::records::records::TSR;
6use polars::frame::DataFrame;
7use polars::prelude::Column;
8use pyo3::Bound;
9use pyo3::IntoPyObject;
10use pyo3::Python;
11use pyo3::types::PyString;
12use serde::Serialize;
13use std::collections::HashMap;
14use std::convert::Infallible;
15use std::fmt;
16
17#[derive(Debug, IntoPyObject)]
23pub struct TestInformation {
24 pub test_num: u32,
25 pub head_num: u8,
26 pub site_num: u8,
27 pub test_type: TestType,
28 pub execution_count: u32,
29 pub test_name: String,
30 pub sequence_name: String,
31 pub test_label: String,
32 pub test_time: f32,
33 pub test_text: String,
34 pub low_limit: f32,
35 pub high_limit: f32,
36 pub units: String,
37 pub complete: Complete,
38}
39
40#[derive(Debug)]
47pub enum Complete {
48 PTR,
50 TSR,
52 Complete,
54}
55
56impl<'py> IntoPyObject<'py> for Complete {
61 type Target = PyString;
62 type Output = Bound<'py, Self::Target>;
63 type Error = Infallible;
64
65 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
66 let s = match self {
67 Self::TSR => "TSR".to_string().into_pyobject(py),
68 Self::PTR => "PTR".to_string().into_pyobject(py),
69 Self::Complete => "Complete".to_string().into_pyobject(py),
70 };
71 Ok(s?)
72 }
73}
74
75impl TestInformation {
76 pub fn new_from_ptr(ptr: &PTR) -> Self {
78 let test_num = ptr.test_num;
79 let head_num = ptr.head_num;
80 let site_num = ptr.site_num;
81 let test_type = TestType::Unknown;
82 let execution_count = 0;
83 let test_name = String::new();
84 let sequence_name = String::new();
85 let test_label = String::new();
86 let test_time = f32::NAN;
87 let test_text = ptr.test_txt.clone();
88 let low_limit = ptr.lo_limit;
89 let high_limit = ptr.hi_limit;
90 let units = ptr.units.clone();
91 let complete = Complete::PTR;
92
93 Self {
94 test_num,
95 head_num,
96 site_num,
97 test_type,
98 execution_count,
99 test_name,
100 sequence_name,
101 test_label,
102 test_time,
103 test_text,
104 low_limit,
105 high_limit,
106 units,
107 complete,
108 }
109 }
110
111 pub fn add_from_tsr(&mut self, tsr: &TSR) {
113 if (self.head_num != tsr.head_num)
114 || (self.site_num != tsr.site_num)
115 || (self.test_num != tsr.test_num)
116 {
117 panic!("head_num/site_num/test_num from TSR does not match!");
118 }
119 if let Complete::PTR = self.complete {
120 self.test_type = match tsr.test_typ {
121 'P' => TestType::P,
122 'F' => TestType::F,
123 'M' => TestType::M,
124 'S' => TestType::S,
125 _ => TestType::Unknown,
126 };
127 self.execution_count = tsr.exec_cnt;
128 self.test_name = tsr.test_nam.clone();
129 self.sequence_name = tsr.seq_name.clone();
130 self.test_label = tsr.test_lbl.clone();
131 self.test_time = tsr.test_tim;
132 self.complete = Complete::Complete;
133 }
134 }
135
136 pub fn new_from_tsr(tsr: &TSR) -> Self {
138 let test_num = tsr.test_num;
139 let head_num = tsr.head_num;
140 let site_num = tsr.site_num;
141 let test_type = match tsr.test_typ {
142 'P' => TestType::P,
143 'F' => TestType::F,
144 'M' => TestType::M,
145 'S' => TestType::S,
146 _ => TestType::Unknown,
147 };
148 let execution_count = tsr.exec_cnt;
149 let test_name = tsr.test_nam.clone();
150 let sequence_name = tsr.seq_name.clone();
151 let test_label = tsr.test_lbl.clone();
152 let test_time = tsr.test_tim;
153 let test_text = String::new();
154 let low_limit = f32::NAN;
155 let high_limit = f32::NAN;
156 let units = String::new();
157 let complete = Complete::TSR;
158
159 Self {
160 test_num,
161 head_num,
162 site_num,
163 test_type,
164 execution_count,
165 test_name,
166 sequence_name,
167 test_label,
168 test_time,
169 test_text,
170 low_limit,
171 high_limit,
172 units,
173 complete,
174 }
175 }
176
177 pub fn add_from_ptr(&mut self, ptr: &PTR) {
179 if (self.head_num != ptr.head_num)
180 || (self.site_num != ptr.site_num)
181 || (self.test_num != ptr.test_num)
182 {
183 panic!("head_num/site_num/test_num from PTR does not match!");
184 }
185 if let Complete::TSR = self.complete {
186 self.test_text = ptr.test_txt.clone();
187 self.low_limit = ptr.lo_limit;
188 self.high_limit = ptr.hi_limit;
189 self.units = ptr.units.clone();
190 self.complete = Complete::Complete;
191 }
192 }
193}
194
195#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize)]
197pub enum TestType {
198 P,
200 F,
202 M,
204 S,
206 Unknown,
208}
209
210impl fmt::Display for TestType {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 write!(f, "{:?}", self)
213 }
214}
215
216impl<'py> IntoPyObject<'py> for TestType {
221 type Target = PyString;
222 type Output = Bound<'py, Self::Target>;
223 type Error = Infallible;
224
225 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
226 let s = match self {
227 Self::P => "P".to_string().into_pyobject(py),
228 Self::F => "F".to_string().into_pyobject(py),
229 Self::M => "M".to_string().into_pyobject(py),
230 Self::S => "S".to_string().into_pyobject(py),
231 Self::Unknown => "Unknown".to_string().into_pyobject(py),
232 };
233 Ok(s?)
234 }
235}
236
237#[derive(Debug, IntoPyObject)]
241pub struct FullTestInformation {
242 pub test_infos: HashMap<(u32, u8, u8), TestInformation>,
243}
244
245impl FullTestInformation {
246 pub fn new() -> Self {
248 let test_infos = HashMap::new();
249 Self { test_infos }
250 }
251
252 pub fn add_from_ptr(&mut self, ptr: &PTR) {
257 let key = (ptr.test_num, ptr.site_num, ptr.head_num);
258 self.test_infos
259 .entry(key)
260 .and_modify(|e| e.add_from_ptr(ptr))
261 .or_insert(TestInformation::new_from_ptr(ptr));
262 }
263
264 pub fn add_from_tsr(&mut self, tsr: &TSR) {
269 if tsr.head_num == 255 {
270 return;
271 }
272 let key = (tsr.test_num, tsr.site_num, tsr.head_num);
273 self.test_infos
274 .entry(key)
275 .and_modify(|e| e.add_from_tsr(tsr))
276 .or_insert(TestInformation::new_from_tsr(tsr));
277 }
278
279 pub fn merge(&self) -> FullMergedTestInformation {
286 let mut merged_test_info = FullMergedTestInformation::new();
287 for ti in self.test_infos.values() {
288 merged_test_info.add_from_test_information(ti);
289 }
290 merged_test_info
291 }
292
293 pub fn from_fname(fname: &str, verbose: bool) -> std::io::Result<Self> {
305 let records = Records::new(&fname)?;
306 let mut test_info = Self::new();
307
308 for record in records {
309 if let Some(resolved) = record.resolve() {
310 let header = &record.header;
311
312 if verbose {
313 println!(
314 "{}.{} (0x{:x} @ 0x{:x}): {:?}",
315 header.rec_typ, header.rec_sub, header.rec_len, record.offset, record.rtype
316 );
317 }
318 if let Record::TSR(ref tsr) = resolved {
319 test_info.add_from_tsr(&tsr);
320 }
321 if let Record::PIR(_) = resolved {
322 continue;
323 }
324 if let Record::FTR(_) = resolved {
325 continue;
326 }
327 if let Record::PTR(ref ptr) = resolved {
328 test_info.add_from_ptr(&ptr);
329 }
330 if verbose {
334 println!("{resolved:#?}");
335 }
336 }
337 }
338 Ok(test_info)
339 }
340
341 pub fn from_fname_and_summarize(
342 fname: &str,
343 verbose: bool,
344 ) -> std::io::Result<(Self, RecordSummary)> {
345 let records = Records::new(&fname)?;
346 let mut summary = RecordSummary::new();
347 let mut test_info = Self::new();
348
349 for record in records {
350 summary.add(&record);
351 if let Some(resolved) = record.resolve() {
352 let header = &record.header;
353
354 if verbose {
355 println!(
356 "{}.{} (0x{:x} @ 0x{:x}): {:?}",
357 header.rec_typ, header.rec_sub, header.rec_len, record.offset, record.rtype
358 );
359 }
360 if let Record::TSR(ref tsr) = resolved {
361 test_info.add_from_tsr(&tsr);
362 }
363 if let Record::PIR(_) = resolved {
364 continue;
365 }
366 if let Record::FTR(_) = resolved {
367 continue;
368 }
369 if let Record::PTR(ref ptr) = resolved {
370 test_info.add_from_ptr(&ptr);
371 }
372 if verbose {
376 println!("{resolved:#?}");
377 }
378 }
379 }
380 Ok((test_info, summary))
381 }
382}
383
384impl IntoIterator for FullTestInformation {
385 type Item = ((u32, u8, u8), TestInformation);
386 type IntoIter = <HashMap<(u32, u8, u8), TestInformation> as IntoIterator>::IntoIter;
387 fn into_iter(self) -> Self::IntoIter {
388 self.test_infos.into_iter()
389 }
390}
391
392#[derive(Debug, IntoPyObject)]
406pub struct MergedTestInformation {
407 pub test_num: u32,
408 pub test_type: TestType,
409 pub execution_count: u32,
410 pub test_name: String,
411 pub sequence_name: String,
412 pub test_label: String,
413 pub test_time: f32,
414 pub test_text: String,
415 pub low_limit: f32,
416 pub high_limit: f32,
417 pub units: String,
418}
419impl MergedTestInformation {
420 pub fn new_from_test_information(test_information: &TestInformation) -> Self {
422 let test_num = test_information.test_num;
423 let test_type = test_information.test_type.clone();
424 let execution_count = test_information.execution_count;
425 let test_name = test_information.test_name.clone();
426 let sequence_name = test_information.sequence_name.clone();
427 let test_label = test_information.test_label.clone();
428 let test_time = test_information.test_time;
429 let test_text = test_information.test_text.clone();
430 let low_limit = test_information.low_limit;
431 let high_limit = test_information.high_limit;
432 let units = test_information.units.clone();
433 Self {
434 test_num,
435 test_type,
436 execution_count,
437 test_name,
438 sequence_name,
439 test_label,
440 test_time,
441 test_text,
442 low_limit,
443 high_limit,
444 units,
445 }
446 }
447
448 pub fn add(&mut self, test_information: &TestInformation) {
450 if self.test_num != test_information.test_num {
451 panic!("TestInformation.test_num does not match that of MergedTestInformation!")
452 }
453 self.execution_count += test_information.execution_count;
454 }
455}
456
457#[derive(Debug, IntoPyObject)]
461pub struct FullMergedTestInformation {
462 pub test_infos: HashMap<u32, MergedTestInformation>,
463}
464impl FullMergedTestInformation {
465 pub fn new() -> Self {
467 let test_infos = HashMap::new();
468 Self { test_infos }
469 }
470
471 pub fn add_from_test_information(&mut self, test_information: &TestInformation) {
477 let key = test_information.test_num;
478 self.test_infos
479 .entry(key)
480 .and_modify(|e| e.add(test_information))
481 .or_insert(MergedTestInformation::new_from_test_information(
482 test_information,
483 ));
484 }
485
486 pub fn get_num(&self, test_type: TestType) -> usize {
488 self.test_infos
489 .values()
490 .filter(|&mti| mti.test_type == test_type)
491 .collect::<Vec<_>>()
492 .len()
493 }
494}
495
496impl Into<DataFrame> for &FullMergedTestInformation {
498 fn into(self) -> DataFrame {
499 let mut test_nums: Vec<u32> = Vec::new();
500 let mut test_types: Vec<String> = Vec::new();
501 let mut execution_counts: Vec<u32> = Vec::new();
502 let mut test_names: Vec<String> = Vec::new();
503 let mut sequence_names: Vec<String> = Vec::new();
504 let mut test_labels: Vec<String> = Vec::new();
505 let mut test_times: Vec<f32> = Vec::new();
506 let mut test_texts: Vec<String> = Vec::new();
507 let mut low_limits: Vec<f32> = Vec::new();
508 let mut high_limits: Vec<f32> = Vec::new();
509 let mut unitss: Vec<String> = Vec::new();
510
511 for (tnum, mti) in &self.test_infos {
512 test_nums.push(*tnum);
513 test_types.push(mti.test_type.to_string());
514 execution_counts.push(mti.execution_count);
515 test_names.push(mti.test_name.clone());
516 sequence_names.push(mti.sequence_name.clone());
517 test_labels.push(mti.test_label.clone());
518 test_times.push(mti.test_time);
519 test_texts.push(mti.test_text.clone());
520 low_limits.push(mti.low_limit);
521 high_limits.push(mti.high_limit);
522 unitss.push(mti.units.clone());
523 }
524
525 let mut columns: Vec<Column> = Vec::new();
526 columns.push(Column::new("test_num".into(), test_nums));
527 columns.push(Column::new("test_type".into(), test_types));
528 columns.push(Column::new("execution_count".into(), execution_counts));
529 columns.push(Column::new("test_name".into(), test_names));
530 columns.push(Column::new("sequence_name".into(), sequence_names));
531 columns.push(Column::new("test_label".into(), test_labels));
532 columns.push(Column::new("test_time".into(), test_times));
533 columns.push(Column::new("test_text".into(), test_texts));
534 columns.push(Column::new("low_limit".into(), low_limits));
535 columns.push(Column::new("high_limit".into(), high_limits));
536 columns.push(Column::new("units".into(), unitss));
537
538 DataFrame::new(columns).unwrap()
539 }
540}