Skip to main content

redispatch_xml/documents/
kaskade.rs

1//! `Kaskade` -- emergency cascade measure document for congestion relief between TSO and DSO.
2use serde::{Deserialize, Serialize};
3
4use crate::documents::activation::EicCodingScheme;
5use crate::types::{Decimal3, Mrid, RevisionNumber, SimpleContent, UtcDateTime, UtcMinuteDateTime};
6
7// ── Namespace ─────────────────────────────────────────────────────────────────
8
9/// Expected XML namespace for `Kaskade`.
10pub const NAMESPACE: &str = "urn:iec62325.351:tc57wg16:451-6:outagedocument:3:0";
11
12// ── Enumerations ──────────────────────────────────────────────────────────────
13
14/// Status value for the `Kaskade` document.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub enum KaskadeStatus {
17    /// Activated (information).
18    #[serde(rename = "A07")]
19    Activated,
20    /// Ordered (Anweisung).
21    #[serde(rename = "A10")]
22    Ordered,
23    /// Deactivation.
24    #[serde(rename = "A16")]
25    Deactivation,
26    /// Preliminary.
27    #[serde(rename = "A35")]
28    Preliminary,
29}
30
31/// Document type for `Kaskade`.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub enum KaskadeType {
34    /// Emergency measures per § 13(2) EnWG.
35    #[serde(rename = "Z16")]
36    EmergencyMeasures,
37    /// Test message.
38    #[serde(rename = "Z17")]
39    TestMessage,
40}
41
42/// Market role type used in `Kaskade` sender/receiver.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
44pub enum KaskadeRoleType {
45    /// Grid operator (NB).
46    #[serde(rename = "A18")]
47    GridOperator,
48}
49
50/// Business type for `Kaskade` time series.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
52pub enum KaskadeBusinessType {
53    /// Production.
54    #[serde(rename = "A01")]
55    Production,
56    /// Consumption.
57    #[serde(rename = "A04")]
58    Consumption,
59}
60
61/// Curve type for `Kaskade` time series (always `A03`).
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
63pub enum CurveType {
64    /// Variable sized block.
65    #[serde(rename = "A03")]
66    VariableSizedBlock,
67}
68
69/// Measure unit for `Kaskade` quantity (always `MAW`).
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71pub enum KaskadeMeasureUnit {
72    /// Megawatt.
73    #[serde(rename = "MAW")]
74    Megawatt,
75}
76
77/// Reason code for `Kaskade` time series.
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
79pub enum KaskadeReasonCode {
80    /// Local grid problem.
81    #[serde(rename = "Z19")]
82    LocalGridProblem,
83    /// System balance problem.
84    #[serde(rename = "Z20")]
85    SystemBalanceProblem,
86}
87
88// ── IEC 62325 Market Participant ──────────────────────────────────────────────
89
90/// `mRID` element with `codingScheme` attribute (IEC 62325 simpleContent).
91pub type ParticipantMrid = SimpleContent<String>;
92
93/// Market role element used in IEC 62325 `marketRole` sub-element.
94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95pub struct KaskadeMarketRole {
96    /// Market role type code.
97    #[serde(rename = "type")]
98    pub role_type: KaskadeRoleType,
99}
100
101/// Market participant reference in the IEC 62325 style.
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct KaskadeParticipant {
104    /// Market participant mRID (text + codingScheme attribute).
105    #[serde(rename = "mRID")]
106    pub m_rid: ParticipantMrid,
107    /// Market role.
108    #[serde(rename = "marketRole")]
109    pub market_role: KaskadeMarketRole,
110}
111
112// ── Status ────────────────────────────────────────────────────────────────────
113
114/// Status sub-element: `<status><value>A07</value></status>`.
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct StatusElement {
117    /// Status code.
118    pub value: KaskadeStatus,
119}
120
121// ── Time period ───────────────────────────────────────────────────────────────
122
123/// Time interval (separate start/end elements, minute precision).
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct KaskadeTimeInterval {
126    /// Interval start timestamp (optional — absent for non-time-restricted measures).
127    #[serde(rename = "start", default, skip_serializing_if = "Option::is_none")]
128    pub start: Option<UtcMinuteDateTime>,
129    /// Interval end timestamp (required — marks when the emergency measure expires).
130    pub end: UtcMinuteDateTime,
131}
132
133/// The `Available_Period` element wrapping the time interval and optional
134/// point data.
135#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136pub struct AvailablePeriod {
137    /// Time interval for this period.
138    #[serde(rename = "timeInterval")]
139    pub time_interval: KaskadeTimeInterval,
140    /// Resolution (optional; `PT1M` when present).
141    #[serde(
142        rename = "resolution",
143        default,
144        skip_serializing_if = "Option::is_none"
145    )]
146    pub resolution: Option<String>,
147    /// Point values within this period.
148    #[serde(rename = "Point", default, skip_serializing_if = "Vec::is_empty")]
149    pub points: Vec<KaskadePoint>,
150}
151
152/// A single point within `Available_Period`.
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
154pub struct KaskadePoint {
155    /// Position in the series (always 1 for emergency measures).
156    pub position: u32,
157    /// Quantity in MW (the curtailed / required power).
158    pub quantity: Decimal3,
159}
160
161// ── Reason ────────────────────────────────────────────────────────────────────
162
163/// Reason for the cascade outage.
164#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
165pub struct KaskadeReason {
166    /// Reason code: local grid problem (`Z19`) or system balance (`Z20`).
167    pub code: KaskadeReasonCode,
168    /// Optional free-text description (max 512 chars).
169    #[serde(
170        rename = "ReasonText",
171        default,
172        skip_serializing_if = "Option::is_none"
173    )]
174    pub reason_text: Option<String>,
175}
176
177// ── ResourceObject ────────────────────────────────────────────────────────────
178
179/// Coding scheme for resource object identifiers within `Kaskade`.
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
181pub enum ResourceObjScheme {
182    /// EIC (Energy Identification Code) coding scheme (A01).
183    #[serde(rename = "A01")]
184    Eic,
185    /// National resource coding scheme (A02).
186    #[serde(rename = "A02")]
187    NationalResource,
188    /// NDE (German national format) coding scheme.
189    #[serde(rename = "NDE")]
190    Nde,
191    /// Other or proprietary coding scheme (Z01).
192    #[serde(rename = "Z01")]
193    Other,
194}
195
196/// A network connection point or resource object reference (simpleContent).
197pub type ResourceObjectRef = SimpleContent<String, ResourceObjScheme>;
198
199// ── BiddingZoneDomain ─────────────────────────────────────────────────────────
200
201/// Bidding zone domain reference (control zone EIC, simpleContent + A01).
202pub type BiddingZoneMrid = SimpleContent<String, EicCodingScheme>;
203
204/// `biddingZone_Domain` element.
205#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206pub struct BiddingZoneDomain {
207    /// EIC code of the control zone.
208    #[serde(rename = "mRID")]
209    pub m_rid: BiddingZoneMrid,
210}
211
212// ── QuantityMeasureUnit ───────────────────────────────────────────────────────
213
214/// `quantity_Measure_Unit` element.
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216pub struct QuantityMeasureUnit {
217    /// Unit name (always `MAW`).
218    pub name: KaskadeMeasureUnit,
219}
220
221// ── KaskadeTimeSeries ─────────────────────────────────────────────────────────
222
223/// The time series within a `Kaskade` document.
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
225pub struct KaskadeTimeSeries {
226    /// Time-series identifier.
227    #[serde(rename = "mRID")]
228    pub m_rid: Mrid,
229    /// `mRID` of the original document this message relates to (optional).
230    #[serde(
231        rename = "senders_document_mRID",
232        default,
233        skip_serializing_if = "Option::is_none"
234    )]
235    pub senders_document_m_rid: Option<Mrid>,
236    /// Revision number of the original document (optional).
237    #[serde(
238        rename = "senders_revisionNumber",
239        default,
240        skip_serializing_if = "Option::is_none"
241    )]
242    pub senders_revision_number: Option<RevisionNumber>,
243    /// Creation timestamp of the original document (optional).
244    #[serde(
245        rename = "senders_createdDateTime",
246        default,
247        skip_serializing_if = "Option::is_none"
248    )]
249    pub senders_created_date_time: Option<UtcDateTime>,
250    /// Business type: production (`A01`) or consumption (`A04`).
251    #[serde(rename = "businessType")]
252    pub business_type: KaskadeBusinessType,
253    /// Network connection points / resource objects (0+).
254    #[serde(
255        rename = "ResourceObject",
256        default,
257        skip_serializing_if = "Vec::is_empty"
258    )]
259    pub resource_objects: Vec<ResourceObjectRef>,
260    /// Bidding zone domain (control zone).
261    #[serde(rename = "biddingZone_Domain")]
262    pub bidding_zone_domain: BiddingZoneDomain,
263    /// Power unit (always `MAW`).
264    #[serde(rename = "quantity_Measure_Unit")]
265    pub quantity_measure_unit: QuantityMeasureUnit,
266    /// Curve type (always `A03`).
267    #[serde(rename = "curveType")]
268    pub curve_type: CurveType,
269    /// Available period with time interval and optional point data.
270    #[serde(rename = "Available_Period")]
271    pub available_period: AvailablePeriod,
272    /// Reason for the cascade measure.
273    #[serde(rename = "Reason")]
274    pub reason: KaskadeReason,
275}
276
277// ── Kaskade ───────────────────────────────────────────────────────────────────
278
279/// `Kaskade` — cascade outage / emergency measure notification.
280///
281/// XSD version: 1.0 (Fehlerkorrektur 2026-02-19)  
282/// Namespace: `urn:iec62325.351:tc57wg16:451-6:outagedocument:3:0`
283///
284/// Sent by a grid operator to notify downstream operators of an emergency
285/// curtailment measure under § 13(2) EnWG. All fields are in IEC 62325 /
286/// ENTSO-E direct-text style (not attr-v).
287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
288#[serde(rename = "Kaskade")]
289pub struct Kaskade {
290    /// Document creation timestamp (UTC, second precision).
291    #[serde(rename = "createdDateTime")]
292    pub created_date_time: UtcDateTime,
293    /// Unique message identifier.
294    #[serde(rename = "mRID")]
295    pub m_rid: Mrid,
296    /// Revision number (1–999).
297    #[serde(rename = "revisionNumber")]
298    pub revision_number: RevisionNumber,
299    /// Status of this document revision.
300    pub status: StatusElement,
301    /// Document type: emergency measure (`Z16`) or test (`Z17`).
302    #[serde(rename = "type")]
303    pub doc_type: KaskadeType,
304    /// Sender market participant.
305    #[serde(rename = "sender_MarketParticipant")]
306    pub sender_market_participant: KaskadeParticipant,
307    /// Receiver market participant.
308    #[serde(rename = "receiver_MarketParticipant")]
309    pub receiver_market_participant: KaskadeParticipant,
310    /// The curtailment / emergency time series.
311    #[serde(rename = "TimeSeries")]
312    pub time_series: KaskadeTimeSeries,
313}