Skip to main content

redispatch_xml/documents/
stammdaten.rs

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