Skip to main content

redispatch_xml/documents/
stammdaten.rs

1//! `Stammdaten` -- technical master data exchange (ANB/DV to VNB, VNB to UNB) for registered Redispatch resources.
2use serde::{Deserialize, Serialize};
3
4use crate::types::{Decimal3, DocumentId, MarketParticipantId, Mrid, UtcDateTime};
5
6// ── German-localised coding scheme for Stammdaten ────────────────────────────
7
8/// `Codierung` coding scheme used in `Stammdaten` (German-localised variant).
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum Codierung {
11    /// GS1 (GLN/GSRN).
12    #[serde(rename = "A10")]
13    Gs1,
14    /// Germany National coding scheme (BDEW-Code).
15    #[serde(rename = "NDE")]
16    Nde,
17}
18
19/// Sender / receiver reference for `Stammdaten` documents.
20///
21/// The `Code` and `Codierung` attributes are German-language equivalents of
22/// `v` and `codingScheme` used in ENTSO-E documents.
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct StammdatenParticipantRef {
25    /// 13-digit market participant identifier.
26    #[serde(rename = "@Code")]
27    pub code: MarketParticipantId,
28    /// Coding scheme for the identifier.
29    #[serde(rename = "@Codierung")]
30    pub codierung: Codierung,
31}
32
33// ── DocumentType ──────────────────────────────────────────────────────────────
34
35/// `DocumentType` codes for `Stammdaten`.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37pub enum StammdatenDocType {
38    /// Reduced master data (reduzierte Stammdaten).
39    #[serde(rename = "Z02")]
40    Reduced,
41    /// Enriched master data (angereicherte Stammdaten).
42    #[serde(rename = "Z03")]
43    Enriched,
44    /// Grid operator aggregate master data.
45    #[serde(rename = "Z04")]
46    NbAggregate,
47    /// Balance responsible party master data.
48    #[serde(rename = "Z14")]
49    Bilanzkreis,
50}
51
52/// Sender market role (`Senderrolle`) in `Stammdaten`.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub enum StammdatenSenderRole {
55    /// Grid operator (Netzbetreiber, A18).
56    #[serde(rename = "A18")]
57    GridOperator,
58    /// Resource provider (Anlagenbetreiber / Direktvermarkter, A27).
59    #[serde(rename = "A27")]
60    ResourceProvider,
61    /// Data provider (Datenprovider, A39).
62    #[serde(rename = "A39")]
63    DataProvider,
64    /// Supplier (Lieferant, Z01).
65    #[serde(rename = "Z01")]
66    Supplier,
67}
68
69/// Receiver market role (`Empfaengerrolle`) in `Stammdaten`.
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71pub enum StammdatenReceiverRole {
72    /// Balance responsible party (Bilanzkreisverantwortlicher, A08).
73    #[serde(rename = "A08")]
74    BalanceResponsibleParty,
75    /// Grid operator (Netzbetreiber, A18).
76    #[serde(rename = "A18")]
77    GridOperator,
78    /// Data provider (Datenprovider, A39).
79    #[serde(rename = "A39")]
80    DataProvider,
81    /// Supplier (Lieferant, Z01).
82    #[serde(rename = "Z01")]
83    Supplier,
84}
85
86/// Message status: indicates whether this is a creation, update, or deactivation.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub enum Meldungsstatus {
89    /// Initial creation of master data.
90    #[serde(rename = "A14")]
91    Creation,
92    /// Update to existing master data.
93    #[serde(rename = "A15")]
94    Update,
95    /// Deactivation of master data.
96    #[serde(rename = "A16")]
97    Deactivation,
98}
99
100/// German control zone (`Regelzone`) codes used in `Stammdaten`.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
102pub enum Regelzone {
103    /// TransnetBW control zone (EIC 10YDE-ENBW-----N).
104    #[serde(rename = "10YDE-ENBW-----N")]
105    TransnetBw,
106    /// TenneT DE control zone (EIC 10YDE-EON------1).
107    #[serde(rename = "10YDE-EON------1")]
108    TennetDe,
109    /// Amprion control zone (EIC 10YDE-RWENET---I).
110    #[serde(rename = "10YDE-RWENET---I")]
111    Amprion,
112    /// 50Hertz control zone (EIC 10YDE-VE-------2).
113    #[serde(rename = "10YDE-VE-------2")]
114    FiftyHertz,
115    /// Stadtwerke Flensburg control zone (EIC 10YFLENSBURG---3).
116    #[serde(rename = "10YFLENSBURG---3")]
117    Flensburg,
118    /// DB Energie (Bahnstrom) control zone (EIC 11YRBAHNSTROM--P).
119    #[serde(rename = "11YRBAHNSTROM--P")]
120    Bahnstrom,
121}
122
123/// Energy carrier (`Energietraeger`) codes used in `SR_Objekt`.
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
125#[non_exhaustive]
126pub enum Energietraeger {
127    /// Natural gas (B01).
128    #[serde(rename = "B01")]
129    NaturalGas,
130    /// Lignite (brown coal, B02).
131    #[serde(rename = "B02")]
132    LigniteCoal,
133    /// Hard coal (B03).
134    #[serde(rename = "B03")]
135    HardCoal,
136    /// Oil (B04).
137    #[serde(rename = "B04")]
138    Oil,
139    /// Uranium / nuclear (B05).
140    #[serde(rename = "B05")]
141    Uranium,
142    /// Biomass (B06).
143    #[serde(rename = "B06")]
144    Biomass,
145    /// Wind energy (B07).
146    #[serde(rename = "B07")]
147    Wind,
148    /// Solar / photovoltaic (B08).
149    #[serde(rename = "B08")]
150    Solar,
151    /// Run-of-river hydro (B09).
152    #[serde(rename = "B09")]
153    RunOfRiver,
154    /// Pumped-storage hydro (B10).
155    #[serde(rename = "B10")]
156    PumpedStorage,
157    /// Geothermal (B11).
158    #[serde(rename = "B11")]
159    Geothermal,
160    /// Waste-to-energy (B12).
161    #[serde(rename = "B12")]
162    WasteToEnergy,
163    /// Other renewable energy source (B13).
164    #[serde(rename = "B13")]
165    OtherRenewable,
166    /// Mixed energy carrier (B14).
167    #[serde(rename = "B14")]
168    Mixed,
169    /// Pumped-storage hydro with natural inflow (B15).
170    #[serde(rename = "B15")]
171    PumpedStorageWithNaturalInflow,
172    /// Other non-renewable energy source (B16).
173    #[serde(rename = "B16")]
174    OtherNonRenewable,
175    /// Other storage technology (B17).
176    #[serde(rename = "B17")]
177    OtherStorage,
178    /// Hydrogen (B18).
179    #[serde(rename = "B18")]
180    Hydrogen,
181    /// Offshore wind energy (B19).
182    #[serde(rename = "B19")]
183    Offshore,
184    /// Battery storage (B20).
185    #[serde(rename = "B20")]
186    Battery,
187    /// EEG-remunerated renewable energy (Z01).
188    #[serde(rename = "Z01")]
189    Eeg,
190    /// KWKG-remunerated combined heat and power (Z02).
191    #[serde(rename = "Z02")]
192    Kwkg,
193}
194
195/// Billing model (`Bilanzierungsmodell`) for a controllable resource.
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
197pub enum Bilanzierungsmodell {
198    /// Plan value.
199    #[serde(rename = "Z01")]
200    Planwert,
201    /// Forecast.
202    #[serde(rename = "Z02")]
203    Prognose,
204    /// Forecast with planning data delivery.
205    #[serde(rename = "Z03")]
206    PrognoseWithPlanningData,
207}
208
209/// Call type (`Abrufart_Aufforderungsfall`) for a controllable resource.
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
211pub enum AbrufartAufforderungsfall {
212    /// Delta instruction (Deltaanweisung).
213    #[serde(rename = "Z01")]
214    Delta,
215    /// Setpoint (Sollwert).
216    #[serde(rename = "Z02")]
217    Sollwert,
218}
219
220/// Tolerance case (`Status_Duldungsfall`) for a controllable resource.
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
222pub enum StatusDuldungsfall {
223    /// Tolerance case applies (A01).
224    #[serde(rename = "A01")]
225    Yes,
226    /// Tolerance case does not apply (A02).
227    #[serde(rename = "A02")]
228    No,
229}
230
231/// Compensation type (`Verguetungsart`).
232#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
233pub enum Verguetungsart {
234    /// EEG (Renewable Energy Act).
235    #[serde(rename = "Z01")]
236    Eeg,
237    /// KWKG (CHP Act).
238    #[serde(rename = "Z02")]
239    Kwkg,
240    /// Other.
241    #[serde(rename = "Z03")]
242    Other,
243}
244
245// ── Grid operator reference ───────────────────────────────────────────────────
246
247/// Network operator reference used in `SR_Objekt`.
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
249pub struct NbRef {
250    /// 13-digit market participant identifier.
251    #[serde(rename = "@Code")]
252    pub code: MarketParticipantId,
253    /// Coding scheme.
254    #[serde(rename = "@Codierung")]
255    pub codierung: Codierung,
256}
257
258/// Affected grid operator reference (includes cascade position 1–6).
259#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
260pub struct BetroffenerNb {
261    /// 13-digit market participant identifier.
262    #[serde(rename = "@Code")]
263    pub code: MarketParticipantId,
264    /// Coding scheme.
265    #[serde(rename = "@Codierung")]
266    pub codierung: Codierung,
267    /// Position in the cascade (1–6).
268    #[serde(rename = "@Pos")]
269    pub pos: u8,
270}
271
272// ── Steuerbarkeit ─────────────────────────────────────────────────────────────
273
274/// Measure unit for `Steuerbarkeit` steps / increments.
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
276pub enum SteuerbarkeitEinheit {
277    /// Megawatt.
278    #[serde(rename = "MAW")]
279    Megawatt,
280    /// Percent.
281    #[serde(rename = "P1")]
282    Percent,
283}
284
285/// Step-based controllability definition (`Stufen`).
286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
287pub struct Stufen {
288    /// Individual step values (percentage of installed capacity).
289    #[serde(rename = "Einzelstufe", default)]
290    pub einzelstufen: Vec<Decimal3>,
291    /// Unit (always `P1` — percent).
292    #[serde(rename = "@Einheit", default, skip_serializing_if = "Option::is_none")]
293    pub einheit: Option<String>,
294}
295
296/// Increment-based controllability definition (`Schritte`).
297#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
298pub struct Schritte {
299    /// Unit of the increment (`MAW` or `P1`).
300    #[serde(rename = "Einheit")]
301    pub einheit: SteuerbarkeitEinheit,
302    /// Step size.
303    #[serde(rename = "Schrittweite")]
304    pub schrittweite: Decimal3,
305    /// Minimum value.
306    #[serde(rename = "Min")]
307    pub min: Decimal3,
308    /// Maximum value.
309    #[serde(rename = "Max")]
310    pub max: Decimal3,
311}
312
313/// Controllability definition of a steuerbare Ressource.
314/// Either step-based (`Stufen`) or increment-based (`Schritte`).
315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
316pub struct Steuerbarkeit {
317    /// Step-based controllability (optional — exclusive with `Schritte`).
318    #[serde(rename = "Stufen", default, skip_serializing_if = "Option::is_none")]
319    pub stufen: Option<Stufen>,
320    /// Increment-based controllability (optional — exclusive with `Stufen`).
321    #[serde(rename = "Schritte", default, skip_serializing_if = "Option::is_none")]
322    pub schritte: Option<Schritte>,
323    /// Whether the controllability values are fixed (optional attribute).
324    #[serde(
325        rename = "@Fixierung",
326        default,
327        skip_serializing_if = "Option::is_none"
328    )]
329    pub fixierung: Option<String>,
330}
331
332// ── Technische_Parameter ──────────────────────────────────────────────────────
333
334/// Technical parameters of a controllable resource.
335///
336/// All fields are optional; only those relevant to the resource type are
337/// populated.
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
339pub struct TechnischeParameter {
340    /// Minimum dispatchable generation (MW).
341    #[serde(
342        rename = "Fahrbare_Mindesterzeugungsleistung",
343        default,
344        skip_serializing_if = "Option::is_none"
345    )]
346    pub fahrbare_mindesterzeugungsleistung: Option<Decimal3>,
347    /// Minimum run time (minutes).
348    #[serde(
349        rename = "Mindestbetriebszeit",
350        default,
351        skip_serializing_if = "Option::is_none"
352    )]
353    pub mindestbetriebszeit: Option<u32>,
354    /// Minimum downtime (minutes).
355    #[serde(
356        rename = "Mindeststillstandszeit",
357        default,
358        skip_serializing_if = "Option::is_none"
359    )]
360    pub mindeststillstandszeit: Option<u32>,
361    /// Cold start time, i.e. after > 48h downtime (minutes).
362    #[serde(
363        rename = "Anfahrzeit_kalt",
364        default,
365        skip_serializing_if = "Option::is_none"
366    )]
367    pub anfahrzeit_kalt: Option<u32>,
368    /// Warm start time, i.e. after ≤ 48h downtime (minutes).
369    #[serde(
370        rename = "Anfahrzeit_warm",
371        default,
372        skip_serializing_if = "Option::is_none"
373    )]
374    pub anfahrzeit_warm: Option<u32>,
375    /// Ramp-up time from cold start to synchronisation (minutes).
376    #[serde(
377        rename = "Hochfahrzeit_kalt",
378        default,
379        skip_serializing_if = "Option::is_none"
380    )]
381    pub hochfahrzeit_kalt: Option<u32>,
382    /// Ramp-up time from warm start to synchronisation (minutes).
383    #[serde(
384        rename = "Hochfahrzeit_warm",
385        default,
386        skip_serializing_if = "Option::is_none"
387    )]
388    pub hochfahrzeit_warm: Option<u32>,
389    /// Ramp-down time to grid disconnection (minutes).
390    #[serde(
391        rename = "Abfahrzeit",
392        default,
393        skip_serializing_if = "Option::is_none"
394    )]
395    pub abfahrzeit: Option<u32>,
396    /// Load gradient — upward ramp rate.
397    #[serde(
398        rename = "Lastgradient_Erhoehung",
399        default,
400        skip_serializing_if = "Option::is_none"
401    )]
402    pub lastgradient_erhoehung: Option<Decimal3>,
403    /// Load gradient — downward ramp rate.
404    #[serde(
405        rename = "Lastgradient_Reduzierung",
406        default,
407        skip_serializing_if = "Option::is_none"
408    )]
409    pub lastgradient_reduzierung: Option<Decimal3>,
410}
411
412// ── Enthaltene_TR (contained technical resources) ────────────────────────────
413
414/// A technical resource (Technische Ressource) contained within an
415/// `SR_Objekt` (cluster resource or control group).
416#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
417pub struct EnthaltenesTr {
418    /// MaStR number (Marktstammdatenregister).
419    #[serde(rename = "MaStR_Nr", default, skip_serializing_if = "Option::is_none")]
420    pub ma_str_nr: Option<String>,
421    /// Human-readable name.
422    #[serde(rename = "Klarname", default, skip_serializing_if = "Option::is_none")]
423    pub klarname: Option<String>,
424    /// Resource type code.
425    #[serde(rename = "Typ", default, skip_serializing_if = "Option::is_none")]
426    pub typ: Option<String>,
427    /// Plant code (`Code_Kraftwerk`).
428    #[serde(
429        rename = "Code_Kraftwerk",
430        default,
431        skip_serializing_if = "Option::is_none"
432    )]
433    pub code_kraftwerk: Option<String>,
434    /// Market location (Marktlokation).
435    #[serde(
436        rename = "Marktlokation",
437        default,
438        skip_serializing_if = "Option::is_none"
439    )]
440    pub marktlokation: Option<String>,
441}
442
443// ── SR_Objekt ─────────────────────────────────────────────────────────────────
444
445/// A steuerbare Ressource (controllable resource) object.
446///
447/// Each `SR_Objekt` describes one resource or cluster that participates in
448/// the Redispatch 2.0 process.
449#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
450pub struct SrObjekt {
451    /// Human-readable name (optional, max 35 chars, `[A-Z0-9\-+_]*`).
452    #[serde(rename = "Klarname", default, skip_serializing_if = "Option::is_none")]
453    pub klarname: Option<String>,
454    /// Network connection point operator.
455    #[serde(rename = "Anschluss_Netzbetreiber")]
456    pub anschluss_netzbetreiber: NbRef,
457    /// Ordering grid operator (optional — absent when same as connection NB).
458    #[serde(
459        rename = "Anweisender_Netzbetreiber",
460        default,
461        skip_serializing_if = "Option::is_none"
462    )]
463    pub anweisender_netzbetreiber: Option<NbRef>,
464    /// Affected grid operators in cascade order (up to 6).
465    #[serde(
466        rename = "Betroffene_Netzbetreiber",
467        default,
468        skip_serializing_if = "Vec::is_empty"
469    )]
470    pub betroffene_netzbetreiber: Vec<BetroffenerNb>,
471    /// Additional affected grid operators beyond the cascade of 6.
472    #[serde(
473        rename = "Weitere_betroffene_Netzbetreiber",
474        default,
475        skip_serializing_if = "Vec::is_empty"
476    )]
477    pub weitere_betroffene_netzbetreiber: Vec<NbRef>,
478    /// Energy carrier.
479    #[serde(
480        rename = "Energietraeger",
481        default,
482        skip_serializing_if = "Option::is_none"
483    )]
484    pub energietraeger: Option<Energietraeger>,
485    /// Compensation type.
486    #[serde(
487        rename = "Verguetungsart",
488        default,
489        skip_serializing_if = "Option::is_none"
490    )]
491    pub verguetungsart: Option<Verguetungsart>,
492    /// Tolerance case status.
493    #[serde(
494        rename = "Status_Duldungsfall",
495        default,
496        skip_serializing_if = "Option::is_none"
497    )]
498    pub status_duldungsfall: Option<StatusDuldungsfall>,
499    /// Controllability definition.
500    #[serde(
501        rename = "Steuerbarkeit",
502        default,
503        skip_serializing_if = "Option::is_none"
504    )]
505    pub steuerbarkeit: Option<Steuerbarkeit>,
506    /// Call type for demand requests.
507    #[serde(
508        rename = "Abrufart_Aufforderungsfall",
509        default,
510        skip_serializing_if = "Option::is_none"
511    )]
512    pub abrufart_aufforderungsfall: Option<AbrufartAufforderungsfall>,
513    /// Billing model.
514    #[serde(rename = "Bilanzierungsmodell")]
515    pub bilanzierungsmodell: Bilanzierungsmodell,
516    /// Individual allocation quota percentages.
517    #[serde(
518        rename = "Individuelle_Quote",
519        default,
520        skip_serializing_if = "Option::is_none"
521    )]
522    pub individuelle_quote: Option<IndividuelleQuote>,
523    /// Control zone (Regelzone / TSO EIC code).
524    #[serde(rename = "Regelzone")]
525    pub regelzone: Regelzone,
526    /// Technical parameters of the resource (optional).
527    #[serde(
528        rename = "Technische_Parameter",
529        default,
530        skip_serializing_if = "Option::is_none"
531    )]
532    pub technische_parameter: Option<TechnischeParameter>,
533    /// Contained technical resources (for cluster / control group resources).
534    #[serde(
535        rename = "Enthaltene_TR",
536        default,
537        skip_serializing_if = "Vec::is_empty"
538    )]
539    pub enthaltene_tr: Vec<EnthaltenesTr>,
540}
541
542/// Individual allocation quota definition.
543#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
544pub struct IndividuelleQuote {
545    /// List of percentage quota values.
546    #[serde(rename = "Quote", default)]
547    pub quoten: Vec<Decimal3>,
548}
549
550// ── Stammdaten ────────────────────────────────────────────────────────────────
551
552/// `Stammdaten` — master data for steuerbare Ressourcen in Redispatch 2.0.
553///
554/// XSD version: 1.4b (Fehlerkorrektur 2026-02-19)  
555/// Namespace: `urn:kwep_stammdaten:1:0`
556///
557/// Contains the static attributes of controllable resources (generation plants,
558/// storage, flexible loads) that participate in the Redispatch 2.0 process.
559/// Submitted by resource providers (EIV) and DSOs (VNB) to TSOs (ÜNB).
560#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
561#[serde(rename = "Stammdaten")]
562pub struct Stammdaten {
563    /// Unique document identifier (max 35 chars).
564    #[serde(rename = "DocumentIdentification")]
565    pub document_identification: DocumentId,
566    /// Document type.
567    #[serde(rename = "DocumentType")]
568    pub document_type: StammdatenDocType,
569    /// Document creation timestamp (UTC, second precision).
570    #[serde(rename = "Erstellungszeitpunkt")]
571    pub erstellungszeitpunkt: UtcDateTime,
572    /// Sender identification.
573    #[serde(rename = "Sender")]
574    pub sender: StammdatenParticipantRef,
575    /// Sender's market role.
576    #[serde(rename = "Senderrolle")]
577    pub senderrolle: StammdatenSenderRole,
578    /// Receiver identification.
579    #[serde(rename = "Empfaenger")]
580    pub empfaenger: StammdatenParticipantRef,
581    /// Receiver's market role.
582    #[serde(rename = "Empfaengerrolle")]
583    pub empfaengerrolle: StammdatenReceiverRole,
584    /// Reference document identification (optional; used for updates).
585    #[serde(
586        rename = "RefDokumentID",
587        default,
588        skip_serializing_if = "Option::is_none"
589    )]
590    pub ref_dokument_id: Option<Mrid>,
591    /// Original sender when forwarded via data provider (optional).
592    #[serde(
593        rename = "OriginalSender",
594        default,
595        skip_serializing_if = "Option::is_none"
596    )]
597    pub original_sender: Option<StammdatenParticipantRef>,
598    /// Original document identifier when forwarded (optional).
599    #[serde(
600        rename = "OriginalDokumentID",
601        default,
602        skip_serializing_if = "Option::is_none"
603    )]
604    pub original_dokument_id: Option<Mrid>,
605    /// Original creation timestamp when forwarded (optional).
606    #[serde(
607        rename = "OriginalErstellungszeitpunkt",
608        default,
609        skip_serializing_if = "Option::is_none"
610    )]
611    pub original_erstellungszeitpunkt: Option<UtcDateTime>,
612    /// Validity start timestamp (UTC; represents German local time midnight).
613    #[serde(rename = "Gueltig_ab")]
614    pub gueltig_ab: UtcDateTime,
615    /// Message status: creation, update, or deactivation.
616    #[serde(rename = "Meldungsstatus")]
617    pub meldungsstatus: Meldungsstatus,
618    /// Controllable resource objects described in this document.
619    #[serde(rename = "SR_Objekt", default)]
620    pub sr_objekte: Vec<SrObjekt>,
621}