Skip to main content

redispatch_xml/documents/
kaskade.rs

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