Skip to main content

redispatch_xml/documents/
unavailability.rs

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