Skip to main content

automapper_validation/generated/fv2604/
utilmd_strom_conditions_fv2604.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2604/UTILMD_AHB_Strom_2_1_Fehlerkorrektur_20260629.xml
4// Generated: 2026-09-23T18:15:06Z
5// Delegates unchanged conditions to UtilmdStromConditionEvaluatorFV2510 (FV2510).
6// </auto-generated>
7
8#![allow(clippy::style, clippy::complexity)]
9
10#[allow(unused_imports)]
11use crate::eval::format_validators::*;
12// HAND-EDITED: instances of one SG4 compared with each other.
13use crate::eval::sg4_instances;
14use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
15use crate::generated::fv2510::UtilmdStromConditionEvaluatorFV2510;
16use mig_types::segment::OwnedSegment;
17
18/// Condition evaluator for UTILMD_Strom FV2604.
19///
20/// Delegates to [`UtilmdStromConditionEvaluatorFV2510`] for conditions whose AHB description is
21/// unchanged from that version. Local overrides: [160], [182], [183], [203], [271].
22pub struct UtilmdStromConditionEvaluatorFV2604 {
23    fallback: UtilmdStromConditionEvaluatorFV2510,
24}
25
26impl Default for UtilmdStromConditionEvaluatorFV2604 {
27    fn default() -> Self {
28        Self {
29            fallback: UtilmdStromConditionEvaluatorFV2510::default(),
30        }
31    }
32}
33
34/// Condition IDs whose implementation differs from the fallback.
35const FV2604_OVERRIDES: &[u32] = &[160, 182, 183, 203, 271];
36
37impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2604 {
38    fn message_type(&self) -> &str {
39        "UTILMD_Strom"
40    }
41
42    fn format_version(&self) -> &str {
43        "FV2604"
44    }
45
46    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
47        match condition {
48            160 => self.evaluate_160(ctx),
49            182 => self.evaluate_182(ctx),
50            183 => self.evaluate_183(ctx),
51            203 => self.evaluate_203(ctx),
52            271 => self.evaluate_271(ctx),
53            other => self.fallback.evaluate(other, ctx),
54        }
55    }
56
57    fn is_external(&self, condition: u32) -> bool {
58        !FV2604_OVERRIDES.contains(&condition) && self.fallback.is_external(condition)
59    }
60
61    fn is_known(&self, condition: u32) -> bool {
62        FV2604_OVERRIDES.contains(&condition) || self.fallback.is_known(condition)
63    }
64}
65
66/// Whether some SG8 has an SG10 whose CCI and CAV segments satisfy `pred`.
67fn sg10_has(
68    ctx: &EvaluationContext,
69    pred: impl Fn(&[OwnedSegment], &[OwnedSegment]) -> bool,
70) -> ConditionResult {
71    // HAND-EDITED: "in dieser SG8" — only the SG10s of the enclosing SG8 when the
72    // scope reaches one; every SG8's otherwise.
73    if let (Some(sg8), Some(nav)) = (ctx.enclosing_scope("SG8"), ctx.navigator()) {
74        let found = (0..nav.child_instance_count(sg8, "SG10")).any(|j| {
75            let mut sg10 = sg8.to_vec();
76            sg10.push(("SG10", j));
77            pred(
78                &nav.find_segments_in_instance("CCI", &sg10),
79                &nav.find_segments_in_instance("CAV", &sg10),
80            )
81        });
82        return ConditionResult::from(found);
83    }
84    let Some(nav) = ctx.navigator() else {
85        let ccis: Vec<OwnedSegment> = ctx.find_segments("CCI").into_iter().cloned().collect();
86        let cavs: Vec<OwnedSegment> = ctx.find_segments("CAV").into_iter().cloned().collect();
87        return ConditionResult::from(pred(&ccis, &cavs));
88    };
89    for i in 0..nav.group_instance_count(&["SG4", "SG8"]) {
90        for j in 0..nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10") {
91            let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
92            let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
93            if pred(&ccis, &cavs) {
94                return ConditionResult::True;
95            }
96        }
97    }
98    ConditionResult::False
99}
100
101impl UtilmdStromConditionEvaluatorFV2604 {
102    /// [160] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+...
103    // Message-wide approximation: some two SG8 SEQ+Z58/ZC9/ZD0/ZD6 share Zeitraum-ID
104    // (SEQ C286 1050), DE1229 code and the RFF+Z33 Objektcode.
105    fn evaluate_160(&self, ctx: &EvaluationContext) -> ConditionResult {
106        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
107        // reaches an SG8; the message as before otherwise.
108        if let Some(scope) = ctx.enclosing_scope("SG8") {
109            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
110                ctx,
111                scope,
112                &["Z58", "ZC9", "ZD0", "ZD6"],
113                false,
114                true,
115            ));
116        }
117        let nav = match ctx.navigator() {
118            Some(n) => n,
119            None => return ConditionResult::Unknown,
120        };
121        let mut seen = std::collections::BTreeSet::new();
122        for i in 0..nav.group_instance_count(&["SG4", "SG8"]) {
123            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
124            let Some(seq) = seqs.first() else { continue };
125            let code = seq
126                .elements
127                .first()
128                .and_then(|e| e.first())
129                .cloned()
130                .unwrap_or_default();
131            if !["Z58", "ZC9", "ZD0", "ZD6"].contains(&code.as_str()) {
132                continue;
133            }
134            let zeitraum = seq
135                .elements
136                .get(1)
137                .and_then(|e| e.first())
138                .cloned()
139                .unwrap_or_default();
140            for rff in
141                nav.find_segments_with_qualifier_in_group("RFF", 0, "Z33", &["SG4", "SG8"], i)
142            {
143                let objekt = rff
144                    .elements
145                    .first()
146                    .and_then(|e| e.get(1))
147                    .cloned()
148                    .unwrap_or_default();
149                if !seen.insert((zeitraum.clone(), code.clone(), objekt)) {
150                    return ConditionResult::True;
151                }
152            }
153        }
154        ConditionResult::False
155    }
156
157    /// [182] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14 (TLP/TEP) in dieser S...
158    fn evaluate_182(&self, ctx: &EvaluationContext) -> ConditionResult {
159        sg10_has(ctx, |ccis, cavs| {
160            ccis.iter().any(|s| {
161                s.elements
162                    .get(2)
163                    .and_then(|e| e.first())
164                    .is_some_and(|v| v == "ZA6")
165            }) && cavs.iter().any(|s| {
166                s.elements
167                    .first()
168                    .and_then(|e| e.first())
169                    .is_some_and(|v| v == "E14")
170            })
171        })
172    }
173
174    /// [183] Wenn SG10 CCI+Z30++Z07 (Lieferrichtung: Verbrauch) in dieser SG8 vorhanden
175    fn evaluate_183(&self, ctx: &EvaluationContext) -> ConditionResult {
176        sg10_has(ctx, |ccis, _| {
177            ccis.iter().any(|s| {
178                s.elements
179                    .first()
180                    .and_then(|e| e.first())
181                    .is_some_and(|v| v == "Z30")
182                    && s.elements
183                        .get(2)
184                        .and_then(|e| e.first())
185                        .is_some_and(|v| v == "Z07")
186            })
187        })
188    }
189
190    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 / Z02 / ZZD vorhanden
191    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
192        ctx.has_qualified_value(
193            "STS",
194            0,
195            "7",
196            2,
197            0,
198            &["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7", "Z02", "ZZD"],
199        )
200    }
201
202    /// [271] Wenn das RFF+Z50 (Termine der Marktlokation) aus dieser SG6 (Termine der Marktlokation) auf das gleiche SG5 LOC+Z16 (Mar...
203    // As FV2504's [86] (RFF+Z50 of an SG6 references the LOC+Z16 an SG8 RFF+Z18 does),
204    // and some SG10 carries CCI+++Z83 with CAV+Z53 (kME/mME).
205    fn evaluate_271(&self, ctx: &EvaluationContext) -> ConditionResult {
206        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" for this SG6's RFF+Z50 — compared within the enclosing SG4 when the scope
207        // reaches an SG6; the message as before otherwise.
208        if let Some(scope) = ctx.enclosing_scope("SG6") {
209            let refs = sg4_instances::rff_values_at(ctx, scope, "Z50");
210            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
211                sg4_instances::has_cci(s, "", "Z83") && sg4_instances::has_cav(s, "Z53")
212            }));
213        }
214        let shared = ctx.groups_share_qualified_value(
215            "RFF",
216            0,
217            "Z50",
218            0,
219            2,
220            &["SG4", "SG6"],
221            "RFF",
222            0,
223            2,
224            &["SG4", "SG8"],
225        );
226        let messtechnik = sg10_has(ctx, |ccis, cavs| {
227            ccis.iter().any(|s| {
228                s.elements
229                    .get(2)
230                    .and_then(|e| e.first())
231                    .is_some_and(|v| v == "Z83")
232            }) && cavs.iter().any(|s| {
233                s.elements
234                    .first()
235                    .and_then(|e| e.first())
236                    .is_some_and(|v| v == "Z53")
237            })
238        });
239        shared.and(messtechnik)
240    }
241}