Link engine types to packaged side views
This commit is contained in:
parent
d45a84038e
commit
8387008728
3 changed files with 144 additions and 6 deletions
|
|
@ -1,9 +1,11 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::pk4::inspect_pk4_file;
|
||||
|
||||
const CAR_PRIMARY_DISPLAY_NAME_OFFSET: usize = 0x0c;
|
||||
const CAR_CONTENT_NAME_OFFSET: usize = 0x48;
|
||||
const CAR_INTERNAL_STEM_OFFSET: usize = 0x84;
|
||||
|
|
@ -124,8 +126,11 @@ pub struct EngineTypeFamilyEntry {
|
|||
pub internal_stem: Option<String>,
|
||||
pub auxiliary_stem: Option<String>,
|
||||
pub side_view_resource: Option<String>,
|
||||
pub side_view_resource_found_in_pk4: Option<bool>,
|
||||
pub companion_stem: Option<String>,
|
||||
pub body_type_label: Option<String>,
|
||||
pub internal_ne_profile_name: Option<String>,
|
||||
pub internal_ne_profile_found_in_pk4: Option<bool>,
|
||||
pub cct_identifier: Option<String>,
|
||||
pub cct_value: Option<i64>,
|
||||
pub has_matched_locomotive_pair: bool,
|
||||
|
|
@ -134,6 +139,7 @@ pub struct EngineTypeFamilyEntry {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct EngineTypesInspectionReport {
|
||||
pub source_root: String,
|
||||
pub side_view_imb_pk4_path: Option<String>,
|
||||
pub family_count: usize,
|
||||
pub car_file_count: usize,
|
||||
pub lco_file_count: usize,
|
||||
|
|
@ -145,8 +151,14 @@ pub struct EngineTypesInspectionReport {
|
|||
pub unmatched_cgo_file_count: usize,
|
||||
pub unmatched_cct_file_count: usize,
|
||||
pub car_side_view_resource_counts: BTreeMap<String, usize>,
|
||||
pub car_side_view_resource_pk4_match_count: usize,
|
||||
pub car_side_view_resource_pk4_missing_count: usize,
|
||||
pub car_auxiliary_stem_counts: BTreeMap<String, usize>,
|
||||
pub car_auxiliary_stem_relation_counts: BTreeMap<String, usize>,
|
||||
pub internal_ne_profile_pk4_match_count: usize,
|
||||
pub internal_ne_profile_pk4_missing_count: usize,
|
||||
pub locomotive_pair_internal_ne_profile_pk4_match_count: usize,
|
||||
pub locomotive_pair_internal_ne_profile_pk4_missing_count: usize,
|
||||
pub lco_companion_stem_counts: BTreeMap<String, usize>,
|
||||
pub lco_body_type_label_counts: BTreeMap<String, usize>,
|
||||
pub lco_low_cardinality_lane_counts: BTreeMap<String, BTreeMap<String, usize>>,
|
||||
|
|
@ -297,6 +309,7 @@ pub fn inspect_engine_types_dir(
|
|||
let mut lco_reports = BTreeMap::<String, EngineTypeLcoInspectionReport>::new();
|
||||
let mut cgo_reports = BTreeMap::<String, EngineTypeCgoInspectionReport>::new();
|
||||
let mut cct_reports = BTreeMap::<String, EngineTypeCctInspectionReport>::new();
|
||||
let (side_view_imb_pk4_path, side_view_imb_entry_names) = load_side_view_imb_pk4_lookup(path)?;
|
||||
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
|
|
@ -343,7 +356,15 @@ pub fn inspect_engine_types_dir(
|
|||
|
||||
let family_entries = families
|
||||
.values()
|
||||
.map(|family| build_family_entry(family, &car_reports, &lco_reports, &cct_reports))
|
||||
.map(|family| {
|
||||
build_family_entry(
|
||||
family,
|
||||
&car_reports,
|
||||
&lco_reports,
|
||||
&cct_reports,
|
||||
side_view_imb_entry_names.as_ref(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let matched_locomotive_pair_count = family_entries
|
||||
.iter()
|
||||
|
|
@ -354,6 +375,14 @@ pub fn inspect_engine_types_dir(
|
|||
.iter()
|
||||
.filter_map(|family| family.side_view_resource.as_deref()),
|
||||
);
|
||||
let car_side_view_resource_pk4_match_count = family_entries
|
||||
.iter()
|
||||
.filter(|family| family.side_view_resource_found_in_pk4 == Some(true))
|
||||
.count();
|
||||
let car_side_view_resource_pk4_missing_count = family_entries
|
||||
.iter()
|
||||
.filter(|family| family.side_view_resource_found_in_pk4 == Some(false))
|
||||
.count();
|
||||
let car_auxiliary_stem_counts = count_named_values(
|
||||
family_entries
|
||||
.iter()
|
||||
|
|
@ -369,6 +398,28 @@ pub fn inspect_engine_types_dir(
|
|||
.iter()
|
||||
.filter_map(|family| family.companion_stem.as_deref()),
|
||||
);
|
||||
let internal_ne_profile_pk4_match_count = family_entries
|
||||
.iter()
|
||||
.filter(|family| family.internal_ne_profile_found_in_pk4 == Some(true))
|
||||
.count();
|
||||
let internal_ne_profile_pk4_missing_count = family_entries
|
||||
.iter()
|
||||
.filter(|family| family.internal_ne_profile_found_in_pk4 == Some(false))
|
||||
.count();
|
||||
let locomotive_pair_internal_ne_profile_pk4_match_count = family_entries
|
||||
.iter()
|
||||
.filter(|family| {
|
||||
family.has_matched_locomotive_pair
|
||||
&& family.internal_ne_profile_found_in_pk4 == Some(true)
|
||||
})
|
||||
.count();
|
||||
let locomotive_pair_internal_ne_profile_pk4_missing_count = family_entries
|
||||
.iter()
|
||||
.filter(|family| {
|
||||
family.has_matched_locomotive_pair
|
||||
&& family.internal_ne_profile_found_in_pk4 == Some(false)
|
||||
})
|
||||
.count();
|
||||
let lco_body_type_label_counts = count_named_values(
|
||||
family_entries
|
||||
.iter()
|
||||
|
|
@ -400,6 +451,7 @@ pub fn inspect_engine_types_dir(
|
|||
|
||||
Ok(EngineTypesInspectionReport {
|
||||
source_root: path.display().to_string(),
|
||||
side_view_imb_pk4_path,
|
||||
family_count: family_entries.len(),
|
||||
car_file_count: family_entries
|
||||
.iter()
|
||||
|
|
@ -439,8 +491,14 @@ pub fn inspect_engine_types_dir(
|
|||
})
|
||||
.count(),
|
||||
car_side_view_resource_counts,
|
||||
car_side_view_resource_pk4_match_count,
|
||||
car_side_view_resource_pk4_missing_count,
|
||||
car_auxiliary_stem_counts,
|
||||
car_auxiliary_stem_relation_counts,
|
||||
internal_ne_profile_pk4_match_count,
|
||||
internal_ne_profile_pk4_missing_count,
|
||||
locomotive_pair_internal_ne_profile_pk4_match_count,
|
||||
locomotive_pair_internal_ne_profile_pk4_missing_count,
|
||||
lco_companion_stem_counts,
|
||||
lco_body_type_label_counts,
|
||||
lco_low_cardinality_lane_counts,
|
||||
|
|
@ -468,6 +526,7 @@ fn build_family_entry(
|
|||
car_reports: &BTreeMap<String, EngineTypeCarInspectionReport>,
|
||||
lco_reports: &BTreeMap<String, EngineTypeLcoInspectionReport>,
|
||||
cct_reports: &BTreeMap<String, EngineTypeCctInspectionReport>,
|
||||
side_view_imb_entry_names: Option<&BTreeSet<String>>,
|
||||
) -> EngineTypeFamilyEntry {
|
||||
let car_report = family
|
||||
.car_file
|
||||
|
|
@ -481,6 +540,11 @@ fn build_family_entry(
|
|||
.cct_file
|
||||
.as_ref()
|
||||
.and_then(|file_name| cct_reports.get(file_name));
|
||||
let side_view_resource = car_report.and_then(|report| report.side_view_resource.clone());
|
||||
let internal_stem = car_report.and_then(|report| report.internal_stem.clone());
|
||||
let internal_ne_profile_name = internal_stem
|
||||
.as_ref()
|
||||
.map(|internal_stem| format!("{internal_stem}_NE.imb"));
|
||||
EngineTypeFamilyEntry {
|
||||
canonical_stem: family.canonical_stem.clone(),
|
||||
car_file: family.car_file.clone(),
|
||||
|
|
@ -489,17 +553,59 @@ fn build_family_entry(
|
|||
cct_file: family.cct_file.clone(),
|
||||
primary_display_name: car_report.and_then(|report| report.primary_display_name.clone()),
|
||||
content_name: car_report.and_then(|report| report.content_name.clone()),
|
||||
internal_stem: car_report.and_then(|report| report.internal_stem.clone()),
|
||||
internal_stem,
|
||||
auxiliary_stem: car_report.and_then(|report| report.auxiliary_stem.clone()),
|
||||
side_view_resource: car_report.and_then(|report| report.side_view_resource.clone()),
|
||||
side_view_resource: side_view_resource.clone(),
|
||||
side_view_resource_found_in_pk4: side_view_resource.as_ref().and_then(|resource| {
|
||||
side_view_imb_entry_names.map(|entries| entries.contains(resource))
|
||||
}),
|
||||
companion_stem: lco_report.and_then(|report| report.companion_stem.clone()),
|
||||
body_type_label: lco_report.and_then(|report| report.body_type_label.clone()),
|
||||
internal_ne_profile_name: internal_ne_profile_name.clone(),
|
||||
internal_ne_profile_found_in_pk4: internal_ne_profile_name.as_ref().and_then(
|
||||
|entry_name| side_view_imb_entry_names.map(|entries| entries.contains(entry_name)),
|
||||
),
|
||||
cct_identifier: cct_report.and_then(|report| report.identifier.clone()),
|
||||
cct_value: cct_report.and_then(|report| report.value),
|
||||
has_matched_locomotive_pair: family.car_file.is_some() && family.lco_file.is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_side_view_imb_pk4_lookup(
|
||||
engine_types_dir: &Path,
|
||||
) -> Result<(Option<String>, Option<BTreeSet<String>>), Box<dyn std::error::Error>> {
|
||||
let Some(data_dir) = engine_types_dir.parent() else {
|
||||
return Ok((None, None));
|
||||
};
|
||||
let pk4_path = find_case_insensitive_file(&data_dir.join("2D"), "rt3_2imb.pk4");
|
||||
let Some(pk4_path) = pk4_path else {
|
||||
return Ok((None, None));
|
||||
};
|
||||
|
||||
let inspection = inspect_pk4_file(&pk4_path)?;
|
||||
let entry_names = inspection
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| entry.name)
|
||||
.collect::<BTreeSet<_>>();
|
||||
Ok((Some(pk4_path.display().to_string()), Some(entry_names)))
|
||||
}
|
||||
|
||||
fn find_case_insensitive_file(dir: &Path, expected_name: &str) -> Option<PathBuf> {
|
||||
let expected_lower = expected_name.to_ascii_lowercase();
|
||||
fs::read_dir(dir)
|
||||
.ok()?
|
||||
.filter_map(Result::ok)
|
||||
.find(|entry| {
|
||||
entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|name| name.to_ascii_lowercase() == expected_lower)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|entry| entry.path())
|
||||
}
|
||||
|
||||
fn build_locomotive_display_census(
|
||||
path: &Path,
|
||||
families: &[EngineTypeFamilyEntry],
|
||||
|
|
@ -894,14 +1000,28 @@ mod tests {
|
|||
},
|
||||
)]);
|
||||
|
||||
let entry = build_family_entry(&family, &car_reports, &lco_reports, &cct_reports);
|
||||
let pk4_entry_names =
|
||||
BTreeSet::from(["CarSideView_1.imb".to_string(), "GP7L_NE.imb".to_string()]);
|
||||
let entry = build_family_entry(
|
||||
&family,
|
||||
&car_reports,
|
||||
&lco_reports,
|
||||
&cct_reports,
|
||||
Some(&pk4_entry_names),
|
||||
);
|
||||
assert_eq!(entry.auxiliary_stem.as_deref(), Some("GP7L"));
|
||||
assert_eq!(
|
||||
entry.side_view_resource.as_deref(),
|
||||
Some("CarSideView_1.imb")
|
||||
);
|
||||
assert_eq!(entry.side_view_resource_found_in_pk4, Some(true));
|
||||
assert_eq!(entry.companion_stem.as_deref(), Some("VL80T"));
|
||||
assert_eq!(entry.body_type_label.as_deref(), Some("Loco"));
|
||||
assert_eq!(
|
||||
entry.internal_ne_profile_name.as_deref(),
|
||||
Some("GP7L_NE.imb")
|
||||
);
|
||||
assert_eq!(entry.internal_ne_profile_found_in_pk4, Some(true));
|
||||
assert_eq!(entry.cct_identifier.as_deref(), Some("GP7"));
|
||||
}
|
||||
|
||||
|
|
@ -963,8 +1083,11 @@ mod tests {
|
|||
internal_stem: Some("GP7L".to_string()),
|
||||
auxiliary_stem: Some("GP7L".to_string()),
|
||||
side_view_resource: None,
|
||||
side_view_resource_found_in_pk4: None,
|
||||
companion_stem: None,
|
||||
body_type_label: None,
|
||||
internal_ne_profile_name: None,
|
||||
internal_ne_profile_found_in_pk4: None,
|
||||
cct_identifier: None,
|
||||
cct_value: None,
|
||||
has_matched_locomotive_pair: false,
|
||||
|
|
@ -1129,8 +1252,11 @@ mod tests {
|
|||
internal_stem: Some("2D2L".to_string()),
|
||||
auxiliary_stem: Some("2D2L".to_string()),
|
||||
side_view_resource: Some("CarSideView_2.imb".to_string()),
|
||||
side_view_resource_found_in_pk4: Some(true),
|
||||
companion_stem: None,
|
||||
body_type_label: None,
|
||||
internal_ne_profile_name: Some("2D2L_NE.imb".to_string()),
|
||||
internal_ne_profile_found_in_pk4: Some(true),
|
||||
cct_identifier: None,
|
||||
cct_value: None,
|
||||
has_matched_locomotive_pair: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue