use automapper_validation::{
ConditionEvaluator, ConditionExprEvaluator, ConditionResult, EvaluationContext,
MapExternalProvider,
};
use edifact_mapper::element_scopes::entity_element_scopes;
use edifact_mapper::{
validate_with_conditions, validate_with_conditions_in_scopes, DataDir, Mapper,
};
use mig_assembly::assembler::{AssembledGroup, AssembledTree};
use mig_assembly::navigator::AssembledTreeNavigator;
use mig_types::segment::OwnedSegment;
use serde_json::Value;
use std::path::PathBuf;
fn setup(fv: &str) -> Option<(Mapper, Value)> {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
if !root.join(format!("dist/edifact-data-{fv}.bin")).exists() {
eprintln!("skipping: build the {fv} bundle first");
return None;
}
let mapper =
Mapper::from_data_dir(DataDir::path(root.join("dist")).eager(&[fv])).expect("bundle");
let dump: Value = serde_json::from_str(
&std::fs::read_to_string(
root.join("crates/edifact-mapper/tests/data/mako_twin_55043_zaehler_ohne_smgw.json"),
)
.expect("dump"),
)
.expect("json");
Some((mapper, dump[fv].clone()))
}
#[test]
fn every_element_is_scoped_to_its_own_instance() {
for fv in ["FV2604", "FV2610"] {
let Some((mapper, json)) = setup(fv) else {
continue;
};
let engine = mapper.engine(fv, "UTILMD_Strom", "55043").expect("engine");
let tree = engine.map_all_reverse(&json, None);
let scopes = entity_element_scopes(&engine, &json, &tree);
let scope = |key: &str, index: usize| {
scopes
.get(&(key.to_string(), Some(index)))
.cloned()
.unwrap_or_else(|| panic!("{fv}: no scope for {key}[{index}]"))
};
let obis = [
scope("obisZaehleinrichtung", 0),
scope("obisZaehleinrichtung", 1),
];
let meter_with_smgw = scope("zaehlerDaten", 2);
for path in obis.iter().chain([&meter_with_smgw]) {
let groups: Vec<&str> = path.iter().map(|(g, _)| g.as_str()).collect();
assert_eq!(groups, ["SG4", "SG8"], "{fv}: {path:?}");
}
assert_ne!(obis[0], obis[1], "{fv}");
assert!(!obis.contains(&meter_with_smgw), "{fv}");
let sg8 = &tree
.groups
.iter()
.find(|g| g.group_id == "SG4")
.unwrap()
.repetitions[meter_with_smgw[0].1]
.child_groups
.iter()
.find(|g| g.group_id == "SG8")
.unwrap()
.repetitions[meter_with_smgw[1].1];
assert!(
sg8.segments
.iter()
.any(|s| s.tag == "RFF" && s.elements[0][0] == "Z14"),
"{fv}"
);
assert!(
json["prozessdaten"].is_object()
== scopes.contains_key(&("prozessdaten".to_string(), None)),
"{fv}"
);
}
}
struct Only420Scoped;
impl ConditionEvaluator for Only420Scoped {
fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
if condition != 420 {
return ConditionResult::True;
}
ConditionResult::from(
ctx.scoped_find_segments_in("SG8", "RFF")
.iter()
.any(|s| s.get_element(0) == "Z14"),
)
}
fn is_external(&self, _: u32) -> bool {
false
}
fn message_type(&self) -> &str {
"UTILMD"
}
fn format_version(&self) -> &str {
"FV2604"
}
}
fn flat(tree: &AssembledTree) -> Vec<OwnedSegment> {
fn walk(groups: &[AssembledGroup], out: &mut Vec<OwnedSegment>) {
for g in groups {
for r in &g.repetitions {
for s in &r.segments {
out.push(OwnedSegment {
id: s.tag.clone(),
elements: s.elements.clone(),
segment_number: out.len() as u32,
});
}
walk(&r.child_groups, out);
}
}
}
let mut out = Vec::new();
walk(&tree.groups, &mut out);
out
}
#[test]
fn an_obis_group_is_judged_by_its_own_sg8() {
for fv in ["FV2604", "FV2610"] {
let Some((mapper, json)) = setup(fv) else {
continue;
};
let engine = mapper.engine(fv, "UTILMD_Strom", "55043").expect("engine");
let requirements = mapper
.pid_requirements(fv, "UTILMD_Strom", "55043")
.expect("requirements");
let tree = engine.map_all_reverse(&json, None);
let segments = flat(&tree);
let navigator = AssembledTreeNavigator::new(&tree);
let external = MapExternalProvider::new(Default::default());
let ctx = EvaluationContext::with_navigator("55043", &external, &segments, &navigator);
let evaluator = Only420Scoped;
let expr_eval = ConditionExprEvaluator::new(&evaluator);
let konfiguration = |errors: Vec<edifact_mapper::PidValidationError>| -> usize {
errors
.iter()
.filter(|e| {
e.to_string()
.contains("ObisZaehleinrichtung.konfigurationsId")
})
.count()
};
assert_eq!(
konfiguration(validate_with_conditions(
&json,
&requirements,
&expr_eval,
&ctx
)),
2,
"{fv}"
);
let scopes = entity_element_scopes(&engine, &json, &tree);
assert_eq!(
konfiguration(validate_with_conditions_in_scopes(
&json,
&requirements,
&expr_eval,
&ctx,
&scopes
)),
0,
"{fv}"
);
}
}