Skip to main content

redispatch_xml/documents/
unavailability.rs

1//! `Unavailability_MarketDocument` — planned and forced unavailability declarations for generation resources.
2use serde::{Deserialize, Serialize};
3
4use crate::documents::activation::EicCodingScheme;
5use crate::documents::kaskade::ParticipantMrid;
6use crate::types::{Mrid, RevisionNumber, SimpleContent, UtcDateTime, UtcMinuteDateTime};
7
8// ── Namespace ─────────────────────────────────────────────────────────────────
9
10/// Expected XML namespace for `Unavailability_MarketDocument`.
11pub const NAMESPACE: &str = "urn:iec62325.351:tc57wg16:451-6:outagedocument:3:0";
12
13// ── Enumerations ──────────────────────────────────────────────────────────────
14
15/// Document type codes for `Unavailability_MarketDocument`.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub enum UnavailabilityDocType {
18    /// Planned unavailability.
19    #[serde(rename = "A67")]
20    PlannedUnavailability,
21    /// Forced (unplanned) unavailability.
22    #[serde(rename = "A76")]
23    ForcedUnavailability,
24    /// Production unavailability.
25    #[serde(rename = "A80")]
26    ProductionUnavailability,
27}
28
29/// Process type for `Unavailability_MarketDocument`.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub enum UnavailabilityProcessType {
32    /// Day-ahead / intraday forecast.
33    #[serde(rename = "A14")]
34    Forecast,
35    /// Outage information.
36    #[serde(rename = "A26")]
37    OutageInfo,
38}
39
40/// Business type for `Unavailability_MarketDocument` time series.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42pub enum UnavailabilityBusinessType {
43    /// Production.
44    #[serde(rename = "A01")]
45    Production,
46    /// Planned maintenance.
47    #[serde(rename = "A53")]
48    PlannedMaintenance,
49    /// Unplanned outage.
50    #[serde(rename = "A54")]
51    UnplannedOutage,
52}
53
54/// Sender role for `Unavailability_MarketDocument`.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
56pub enum UnavailabilitySenderRole {
57    /// Resource provider.
58    #[serde(rename = "A27")]
59    ResourceProvider,
60    /// Data provider.
61    #[serde(rename = "A39")]
62    DataProvider,
63}
64
65/// Receiver role for `Unavailability_MarketDocument`.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
67pub enum UnavailabilityReceiverRole {
68    /// Grid operator.
69    #[serde(rename = "A18")]
70    GridOperator,
71    /// Data provider.
72    #[serde(rename = "A39")]
73    DataProvider,
74}
75
76// ── Market participant helpers ────────────────────────────────────────────────
77
78/// Market role type for `Unavailability_MarketDocument`.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
80pub enum UnavailabilityMarketRoleType {
81    /// Grid operator.
82    #[serde(rename = "A18")]
83    GridOperator,
84    /// Resource provider.
85    #[serde(rename = "A27")]
86    ResourceProvider,
87    /// Data provider.
88    #[serde(rename = "A39")]
89    DataProvider,
90}
91
92/// Market role sub-element.
93#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94pub struct UnavailabilityMarketRole {
95    /// Market role type code.
96    #[serde(rename = "type")]
97    pub role_type: UnavailabilityMarketRoleType,
98}
99
100/// Market participant reference.
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub struct UnavailabilityParticipant {
103    /// Market participant identifier (EIC or national code).
104    #[serde(rename = "mRID")]
105    pub m_rid: ParticipantMrid,
106    /// Market role of this participant.
107    #[serde(rename = "marketRole")]
108    pub market_role: UnavailabilityMarketRole,
109}
110
111// ── UnavailabilityTimeInterval ────────────────────────────────────────────────
112
113/// A UTC time interval expressed as separate `start` and `end` sub-elements
114/// (minute precision).
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct UnavailabilityTimeInterval {
117    /// Start of the unavailability period (UTC, minute precision).
118    pub start: UtcMinuteDateTime,
119    /// End of the unavailability period (UTC, minute precision).
120    pub end: UtcMinuteDateTime,
121}
122
123/// `unavailability_Time_Period` wrapper element.
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct UnavailabilityTimePeriod {
126    /// The time interval covered by this unavailability period.
127    #[serde(rename = "timeInterval")]
128    pub time_interval: UnavailabilityTimeInterval,
129}
130
131// ── docStatus ─────────────────────────────────────────────────────────────────
132
133/// Document withdrawal status (used instead of `TimeSeries` for withdrawals).
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub struct DocStatus {
136    /// Always `"A13"` (withdrawn).
137    pub value: String,
138}
139
140// ── TimeSeries ────────────────────────────────────────────────────────────────
141
142/// Bidding zone domain reference in `Unavailability_MarketDocument`.
143pub type UnavailabilityBiddingZone = SimpleContent<String, EicCodingScheme>;
144
145/// `biddingZone_Domain` element in `Unavailability_MarketDocument`.
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
147pub struct UnavailabilityBiddingZoneDomain {
148    /// EIC code of the bidding zone / control zone.
149    #[serde(rename = "mRID")]
150    pub m_rid: UnavailabilityBiddingZone,
151}
152
153/// A single unavailability time series.
154///
155/// Each `TimeSeries` covers one calendar day and one business type.
156/// Instead of quarter-hour `Period/Interval` data, this uses separate
157/// `start_DateAndOrTime.date` / `time` and `end_DateAndOrTime.date` / `time`
158/// fields per IEC 62325.
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160pub struct UnavailabilityTimeSeries {
161    /// Unique time-series identifier within this document.
162    #[serde(rename = "mRID")]
163    pub m_rid: Mrid,
164    /// Original sender mRID when forwarded via data provider (optional).
165    #[serde(
166        rename = "original_sender_MarketParticipant.mRID",
167        default,
168        skip_serializing_if = "Option::is_none"
169    )]
170    pub original_sender_m_rid: Option<ParticipantMrid>,
171    /// Original document mRID when forwarded (optional).
172    #[serde(
173        rename = "original_document_mRID",
174        default,
175        skip_serializing_if = "Option::is_none"
176    )]
177    pub original_document_m_rid: Option<Mrid>,
178    /// Original revision number when forwarded (optional).
179    #[serde(
180        rename = "original_revisionNumber",
181        default,
182        skip_serializing_if = "Option::is_none"
183    )]
184    pub original_revision_number: Option<RevisionNumber>,
185    /// Original creation timestamp when forwarded (optional).
186    #[serde(
187        rename = "original_createdDateTime",
188        default,
189        skip_serializing_if = "Option::is_none"
190    )]
191    pub original_created_date_time: Option<UtcDateTime>,
192    /// Original time-series mRID when forwarded (optional).
193    #[serde(
194        rename = "original_timeseries_mRID",
195        default,
196        skip_serializing_if = "Option::is_none"
197    )]
198    pub original_timeseries_m_rid: Option<Mrid>,
199    /// Business type: production, planned maintenance, or unplanned outage.
200    #[serde(rename = "businessType")]
201    pub business_type: UnavailabilityBusinessType,
202    /// Control zone of the resource.
203    #[serde(rename = "biddingZone_Domain")]
204    pub bidding_zone_domain: UnavailabilityBiddingZoneDomain,
205    /// Start date of the unavailability period (ISO date `yyyy-mm-dd`).
206    #[serde(rename = "start_DateAndOrTime.date")]
207    pub start_date: String,
208    /// Start time of the unavailability period (`hh:mm:ssZ`).
209    #[serde(rename = "start_DateAndOrTime.time")]
210    pub start_time: String,
211    /// End date of the unavailability period (ISO date `yyyy-mm-dd`).
212    #[serde(rename = "end_DateAndOrTime.date")]
213    pub end_date: String,
214    /// End time of the unavailability period (`hh:mm:ssZ`).
215    #[serde(rename = "end_DateAndOrTime.time")]
216    pub end_time: String,
217}
218
219// ── Unavailability_MarketDocument ─────────────────────────────────────────────
220
221/// `Unavailability_MarketDocument` — planned or forced unavailability of a
222/// generation resource.
223///
224/// XSD version: 1.1b (Fehlerkorrektur 2025-04-16)  
225/// Namespace: `urn:iec62325.351:tc57wg16:451-6:outagedocument:3:0`
226///
227/// Each time series covers one complete calendar day. If the document carries
228/// a `docStatus` (withdrawal), no `TimeSeries` elements are present.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230#[serde(rename = "Unavailability_MarketDocument")]
231pub struct UnavailabilityMarketDocument {
232    /// Unique message identifier (max 35 chars).
233    #[serde(rename = "mRID")]
234    pub m_rid: Mrid,
235    /// Revision number (1–999).
236    #[serde(rename = "revisionNumber")]
237    pub revision_number: RevisionNumber,
238    /// Document type.
239    #[serde(rename = "type")]
240    pub doc_type: UnavailabilityDocType,
241    /// Process type.
242    #[serde(rename = "process.processType")]
243    pub process_type: UnavailabilityProcessType,
244    /// Document creation timestamp (UTC, second precision).
245    #[serde(rename = "createdDateTime")]
246    pub created_date_time: UtcDateTime,
247    /// Sender market participant.
248    #[serde(rename = "sender_MarketParticipant")]
249    pub sender_market_participant: UnavailabilityParticipant,
250    /// Receiver market participant.
251    #[serde(rename = "receiver_MarketParticipant")]
252    pub receiver_market_participant: UnavailabilityParticipant,
253    /// The overall unavailability period (one calendar day).
254    #[serde(rename = "unavailability_Time_Period")]
255    pub unavailability_time_period: UnavailabilityTimePeriod,
256    /// Document withdrawal status (mutually exclusive with `time_series`).
257    #[serde(rename = "docStatus", default, skip_serializing_if = "Option::is_none")]
258    pub doc_status: Option<DocStatus>,
259    /// Unavailability time series (0–30; absent when `doc_status` is set).
260    #[serde(rename = "TimeSeries", default, skip_serializing_if = "Vec::is_empty")]
261    pub time_series: Vec<UnavailabilityTimeSeries>,
262}