Skip to main content

sunspec/models/
model711.rs

1//! DER Frequency Droop
2/// Type alias for [`DerFreqDroop`].
3pub type Model711 = DerFreqDroop;
4struct Counts {
5    n_ctl: u16,
6}
7/// DER Frequency Droop
8///
9/// DER Frequency Droop model.
10#[derive(Debug)]
11#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
12pub struct DerFreqDroop {
13    /// DER Frequency Droop Module Enable
14    ///
15    /// DER Frequency-Watt (Frequency-Droop) control enable.
16    pub ena: Ena,
17    /// Set Active Control Request
18    ///
19    /// Set active control. 0 = No active control.
20    pub adpt_ctl_req: u16,
21    /// Set Active Control Result
22    ///
23    /// Result of last set active control operation.
24    pub adpt_ctl_rslt: AdptCtlRslt,
25    /// Stored Control Count
26    ///
27    /// Number of stored controls supported.
28    pub n_ctl: u16,
29    /// Reversion Timeout
30    ///
31    /// Reversion time in seconds.  0 = No reversion time.
32    pub rvrt_tms: Option<u32>,
33    /// Reversion Time Left
34    ///
35    /// Reversion time remaining in seconds.
36    pub rvrt_rem: Option<u32>,
37    /// Reversion Control
38    ///
39    /// Default control after reversion timeout.
40    pub rvrt_ctl: Option<u16>,
41    /// Deadband Scale Factor
42    ///
43    /// Deadband scale factor.
44    pub db_sf: i16,
45    /// Frequency Change Scale Factor
46    ///
47    /// Frequency change scale factor.
48    pub k_sf: i16,
49    /// Open-Loop Scale Factor
50    ///
51    /// Open loop response time scale factor.
52    pub rsp_tms_sf: i16,
53    /// Stored Controls
54    ///
55    /// Stored control sets.
56    ///
57    /// Comments: Stored control sets - Number of control sets contained in NCtl - The first set is read-only and indicates the current settings.
58    pub ctl: Vec<Ctl>,
59}
60#[allow(missing_docs)]
61impl DerFreqDroop {
62    pub const ENA: crate::Point<Self, Ena> = crate::Point::new(0, 1, true);
63    pub const ADPT_CTL_REQ: crate::Point<Self, u16> = crate::Point::new(1, 1, true);
64    pub const ADPT_CTL_RSLT: crate::Point<Self, AdptCtlRslt> = crate::Point::new(2, 1, false);
65    pub const N_CTL: crate::Point<Self, u16> = crate::Point::new(3, 1, false);
66    pub const RVRT_TMS: crate::Point<Self, Option<u32>> = crate::Point::new(4, 2, true);
67    pub const RVRT_REM: crate::Point<Self, Option<u32>> = crate::Point::new(6, 2, false);
68    pub const RVRT_CTL: crate::Point<Self, Option<u16>> = crate::Point::new(8, 1, true);
69    pub const DB_SF: crate::Point<Self, i16> = crate::Point::new(9, 1, false);
70    pub const K_SF: crate::Point<Self, i16> = crate::Point::new(10, 1, false);
71    pub const RSP_TMS_SF: crate::Point<Self, i16> = crate::Point::new(11, 1, false);
72}
73impl crate::Group for DerFreqDroop {
74    const LEN: u16 = 12;
75}
76impl DerFreqDroop {
77    fn parse_group(data: &[u16]) -> Result<(&[u16], Self), crate::DecodeError> {
78        let nested_data = data
79            .get(usize::from(<Self as crate::Group>::LEN)..)
80            .unwrap_or(&[]);
81        let counts = Counts {
82            n_ctl: Self::N_CTL.from_data(data)?,
83        };
84        let (nested_data, ctl) = Ctl::parse_multiple(nested_data, &counts)?;
85        Ok((
86            nested_data,
87            Self {
88                ena: Self::ENA.from_data(data)?,
89                adpt_ctl_req: Self::ADPT_CTL_REQ.from_data(data)?,
90                adpt_ctl_rslt: Self::ADPT_CTL_RSLT.from_data(data)?,
91                n_ctl: Self::N_CTL.from_data(data)?,
92                rvrt_tms: Self::RVRT_TMS.from_data(data)?,
93                rvrt_rem: Self::RVRT_REM.from_data(data)?,
94                rvrt_ctl: Self::RVRT_CTL.from_data(data)?,
95                db_sf: Self::DB_SF.from_data(data)?,
96                k_sf: Self::K_SF.from_data(data)?,
97                rsp_tms_sf: Self::RSP_TMS_SF.from_data(data)?,
98                ctl,
99            },
100        ))
101    }
102}
103/// DER Frequency Droop Module Enable
104///
105/// DER Frequency-Watt (Frequency-Droop) control enable.
106#[derive(Copy, Clone, Debug, Eq, PartialEq)]
107#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
108pub enum Ena {
109    /// Disabled
110    ///
111    /// Function is disabled.
112    Disabled,
113    /// Enabled
114    ///
115    /// Function is enabled.
116    Enabled,
117    /// Raw enum value not defined by the SunSpec model.
118    Invalid(u16),
119}
120impl crate::EnumValue for Ena {
121    type Repr = u16;
122    const INVALID: Self::Repr = 65535;
123    fn from_repr(value: Self::Repr) -> Self {
124        match value {
125            0 => Self::Disabled,
126            1 => Self::Enabled,
127            value => Self::Invalid(value),
128        }
129    }
130    fn to_repr(self) -> Self::Repr {
131        match self {
132            Self::Disabled => 0,
133            Self::Enabled => 1,
134            Self::Invalid(value) => value,
135        }
136    }
137}
138impl crate::FixedSize for Ena {
139    const SIZE: u16 = 1u16;
140    const INVALID: Self = Self::Invalid(65535);
141    fn is_invalid(&self) -> bool {
142        matches!(self, Self::Invalid(_))
143    }
144}
145/// Set Active Control Result
146///
147/// Result of last set active control operation.
148#[derive(Copy, Clone, Debug, Eq, PartialEq)]
149#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
150pub enum AdptCtlRslt {
151    /// Update In Progress
152    ///
153    /// Control update in progress.
154    InProgress,
155    /// Update Complete
156    ///
157    /// Control update completed successfully.
158    Completed,
159    /// Update Failed
160    ///
161    /// Control update failed.
162    Failed,
163    /// Raw enum value not defined by the SunSpec model.
164    Invalid(u16),
165}
166impl crate::EnumValue for AdptCtlRslt {
167    type Repr = u16;
168    const INVALID: Self::Repr = 65535;
169    fn from_repr(value: Self::Repr) -> Self {
170        match value {
171            0 => Self::InProgress,
172            1 => Self::Completed,
173            2 => Self::Failed,
174            value => Self::Invalid(value),
175        }
176    }
177    fn to_repr(self) -> Self::Repr {
178        match self {
179            Self::InProgress => 0,
180            Self::Completed => 1,
181            Self::Failed => 2,
182            Self::Invalid(value) => value,
183        }
184    }
185}
186impl crate::FixedSize for AdptCtlRslt {
187    const SIZE: u16 = 1u16;
188    const INVALID: Self = Self::Invalid(65535);
189    fn is_invalid(&self) -> bool {
190        matches!(self, Self::Invalid(_))
191    }
192}
193/// Stored Controls
194///
195/// Stored control sets.
196///
197/// Comments: Stored control sets - Number of control sets contained in NCtl - The first set is read-only and indicates the current settings.
198#[derive(Debug)]
199#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
200pub struct Ctl {
201    /// Over-Frequency Deadband
202    ///
203    /// The deadband value for over-frequency conditions in Hz.
204    pub db_of: u32,
205    /// Under-Frequency Deadband
206    ///
207    /// The deadband value for under-frequency conditions in Hz.
208    pub db_uf: u32,
209    /// Over-Frequency Change Ratio
210    ///
211    /// Frequency droop per-unit frequency change for over-frequency conditions corresponding to 1 per-unit power output change.
212    pub k_of: u16,
213    /// Under-Frequency Change Ratio
214    ///
215    /// Frequency droop per-unit frequency change for under-frequency conditions corresponding to 1 per-unit power output change.
216    pub k_uf: u16,
217    /// Open-Loop Response Time
218    ///
219    /// The open-loop response time in seconds.
220    pub rsp_tms: u32,
221    /// Minimum Active Power
222    ///
223    /// The minimum active power output due to DER prime mover constraints, in percent of the DER active power rating. The valid range is -100 to 100. This setting applies only to the frequency droop control.
224    pub p_min: Option<i16>,
225    /// Control Access
226    ///
227    /// Control read-write access.
228    pub read_only: CtlReadOnly,
229}
230#[allow(missing_docs)]
231impl Ctl {
232    pub const DB_OF: crate::Point<Self, u32> = crate::Point::new(0, 2, true);
233    pub const DB_UF: crate::Point<Self, u32> = crate::Point::new(2, 2, true);
234    pub const K_OF: crate::Point<Self, u16> = crate::Point::new(4, 1, true);
235    pub const K_UF: crate::Point<Self, u16> = crate::Point::new(5, 1, true);
236    pub const RSP_TMS: crate::Point<Self, u32> = crate::Point::new(6, 2, true);
237    pub const P_MIN: crate::Point<Self, Option<i16>> = crate::Point::new(8, 1, true);
238    pub const READ_ONLY: crate::Point<Self, CtlReadOnly> = crate::Point::new(9, 1, false);
239}
240impl crate::Group for Ctl {
241    const LEN: u16 = 10;
242}
243impl Ctl {
244    fn parse_group(data: &[u16]) -> Result<(&[u16], Self), crate::DecodeError> {
245        let nested_data = data
246            .get(usize::from(<Self as crate::Group>::LEN)..)
247            .unwrap_or(&[]);
248        Ok((
249            nested_data,
250            Self {
251                db_of: Self::DB_OF.from_data(data)?,
252                db_uf: Self::DB_UF.from_data(data)?,
253                k_of: Self::K_OF.from_data(data)?,
254                k_uf: Self::K_UF.from_data(data)?,
255                rsp_tms: Self::RSP_TMS.from_data(data)?,
256                p_min: Self::P_MIN.from_data(data)?,
257                read_only: Self::READ_ONLY.from_data(data)?,
258            },
259        ))
260    }
261    fn parse_multiple<'a>(
262        data: &'a [u16],
263        counts: &Counts,
264    ) -> Result<(&'a [u16], Vec<Self>), crate::DecodeError> {
265        let (data, groups) =
266            (0..counts.n_ctl).try_fold((data, Vec::new()), |(data, mut groups), _| {
267                let (data, group) = Ctl::parse_group(data)?;
268                groups.push(group);
269                Ok::<_, crate::DecodeError>((data, groups))
270            })?;
271        Ok((data, groups))
272    }
273}
274/// Control Access
275///
276/// Control read-write access.
277#[derive(Copy, Clone, Debug, Eq, PartialEq)]
278#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
279pub enum CtlReadOnly {
280    /// Read-Write Access
281    ///
282    /// Control has read-write access.
283    Rw,
284    /// Read-Only Access
285    ///
286    /// Control has read-only access.
287    R,
288    /// Raw enum value not defined by the SunSpec model.
289    Invalid(u16),
290}
291impl crate::EnumValue for CtlReadOnly {
292    type Repr = u16;
293    const INVALID: Self::Repr = 65535;
294    fn from_repr(value: Self::Repr) -> Self {
295        match value {
296            0 => Self::Rw,
297            1 => Self::R,
298            value => Self::Invalid(value),
299        }
300    }
301    fn to_repr(self) -> Self::Repr {
302        match self {
303            Self::Rw => 0,
304            Self::R => 1,
305            Self::Invalid(value) => value,
306        }
307    }
308}
309impl crate::FixedSize for CtlReadOnly {
310    const SIZE: u16 = 1u16;
311    const INVALID: Self = Self::Invalid(65535);
312    fn is_invalid(&self) -> bool {
313        matches!(self, Self::Invalid(_))
314    }
315}
316impl crate::Model for DerFreqDroop {
317    const ID: u16 = 711;
318    fn addr(models: &crate::Models) -> crate::ModelAddr<Self> {
319        models.m711
320    }
321    fn parse(data: &[u16]) -> Result<Self, crate::ParseError<Self>> {
322        let (_, model) = Self::parse_group(data)?;
323        Ok(model)
324    }
325}