Skip to main content

cimvalidation/
detect.rs

1use cimdecoder::CimDataset;
2use crate::Config;
3
4const PROF_EQ:   &str = "http://iec.ch/TC57/ns/CIM/CoreEquipment-EU/3.0";
5const PROF_EQBD: &str = "http://iec.ch/TC57/ns/CIM/EquipmentBoundary-EU/3.0";
6const PROF_DY:   &str = "http://iec.ch/TC57/ns/CIM/Dynamics-EU/1.0";
7const PROF_DL:   &str = "http://iec.ch/TC57/ns/CIM/DiagramLayout-EU/3.0";
8const PROF_SC:   &str = "http://iec.ch/TC57/ns/CIM/ShortCircuit-EU/3.0";
9const PROF_OP:   &str = "http://iec.ch/TC57/ns/CIM/Operation-EU/3.0";
10const PROF_GL:   &str = "http://iec.ch/TC57/ns/CIM/GeographicalLocation-EU/3.0";
11const PROF_SV:   &str = "http://iec.ch/TC57/ns/CIM/StateVariables-EU/3.0";
12const PROF_TP:   &str = "http://iec.ch/TC57/ns/CIM/Topology-EU/3.0";
13const PROF_SSH:  &str = "http://iec.ch/TC57/ns/CIM/SteadyStateHypothesis-EU/3.0";
14
15fn uri_to_short_name(uri: &str) -> Option<&'static str> {
16    match uri {
17        PROF_EQ   => Some("EQ"),
18        PROF_SSH  => Some("SSH"),
19        PROF_TP   => Some("TP"),
20        PROF_SV   => Some("SV"),
21        PROF_DY   => Some("DY"),
22        PROF_SC   => Some("SC"),
23        PROF_DL   => Some("DL"),
24        PROF_GL   => Some("GL"),
25        PROF_OP   => Some("OP"),
26        PROF_EQBD => Some("EQBD"),
27        _         => None,
28    }
29}
30
31fn collect_profiles_from_type(dataset: &CimDataset, type_name: &str, seen: &mut std::collections::HashSet<&'static str>) {
32    for mrid in dataset.by_type.get(type_name).into_iter().flatten() {
33        let entry = match dataset.entries.get(mrid) { Some(e) => e, None => continue };
34        let profiles: &[String] = if let Some(fm) = entry.element.as_any().downcast_ref::<cimstructs::FullModel>() {
35            &fm.base.profile
36        } else if let Some(dm) = entry.element.as_any().downcast_ref::<cimstructs::DifferenceModel>() {
37            &dm.base.profile
38        } else {
39            continue;
40        };
41        for p in profiles {
42            let p = p.trim();
43            if !p.is_empty() {
44                if let Some(short) = uri_to_short_name(p) {
45                    seen.insert(short);
46                }
47            }
48        }
49    }
50}
51
52/// Inspects dataset model headers and returns a Config with profiles, solved, and not_solved populated.
53pub fn detect_config(dataset: &CimDataset) -> Config {
54    let mut seen: std::collections::HashSet<&'static str> = std::collections::HashSet::new();
55    collect_profiles_from_type(dataset, "FullModel", &mut seen);
56    collect_profiles_from_type(dataset, "DifferenceModel", &mut seen);
57
58    let mut profiles: Vec<String> = seen.iter().map(|s| s.to_string()).collect();
59    profiles.sort();
60
61    let is_solved = seen.contains("SV");
62    Config {
63        profiles,
64        solved: is_solved,
65        not_solved: !is_solved,
66        ..Config::default()
67    }
68}