use cimmodel::CimDataset;
use super::Fields;
use crate::Violation;
pub fn validate(dataset: &CimDataset) -> Vec<Violation> {
check_mutual_coupling_terminals_assignment(dataset)
}
fn check_mutual_coupling_terminals_assignment(dataset: &CimDataset) -> Vec<Violation> {
let mut v = Vec::new();
let conducting_equipment_of = |term_ref: &str| -> Option<(String, Option<String>)> {
let term_id = term_ref.trim_start_matches('#');
let term = Fields::get(dataset, term_id, "Terminal")?;
let ce_ref = term.reference("Terminal.ConductingEquipment")?;
let eq_id = ce_ref.trim_start_matches('#').to_string();
let type_name = dataset.entries.get(&eq_id).map(|e| e.type_name().to_string());
Some((eq_id, type_name))
};
for mrid in dataset.by_type.get("MutualCoupling").into_iter().flatten() {
let entry = &dataset.entries[mrid];
let mc = match Fields::of_class(entry, "MutualCoupling") {
Some(o) => o, None => continue,
};
let (eq1_id, eq1_type) = match mc.reference("MutualCoupling.First_Terminal").and_then(conducting_equipment_of) {
Some(x) => x, None => continue,
};
let (eq2_id, eq2_type) = match mc.reference("MutualCoupling.Second_Terminal").and_then(conducting_equipment_of) {
Some(x) => x, None => continue,
};
let is_line_like = |t: &Option<String>| -> bool {
matches!(t.as_deref(), Some("ACLineSegment") | Some("Equipment"))
};
if !is_line_like(&eq1_type) || !is_line_like(&eq2_type) || eq1_id == eq2_id {
let t1 = eq1_type.as_deref().unwrap_or("unknown");
let t2 = eq2_type.as_deref().unwrap_or("unknown");
v.push(Violation {
object_id: mrid.clone(),
rule_id: "scn301:MutualCoupling-terminalsAssignment".into(),
name: "C:301:SC:MutualCoupling:terminalsAssignment".into(),
class: "MutualCoupling".into(),
property: "MutualCoupling.First_Terminal".into(),
message: format!("The terminals are either not related to ACLineSegment or the first and the second terminal associations are not pointing to different ACLineSegments. Type line 1: {t1}. Type line 2: {t2}."),
severity: "sh:Violation".into(),
description: String::new(),
});
}
}
v
}