Skip to main content

sunspec/models/
model601.rs

1//! Tracker Controller DRAFT 2
2/// Type alias for [`TrackerController`].
3pub type Model601 = TrackerController;
4/// Tracker Controller DRAFT 2
5///
6/// Monitors and controls multiple trackers
7///
8/// Detail: Trackers may include GPS model 305 for location information
9#[derive(Debug)]
10#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
11pub struct TrackerController {
12    /// Controller
13    ///
14    /// Descriptive name for this control unit
15    pub nam: Option<String>,
16    /// Type
17    ///
18    /// Type of tracker
19    pub typ: Typ,
20    /// Date
21    ///
22    /// Local date in YYYYMMDD format
23    pub dt_loc: Option<String>,
24    /// Time
25    ///
26    /// 24 hour local time stamp to second
27    pub tm_loc: Option<String>,
28    /// Day
29    ///
30    /// Number of the day in the year (1-366)
31    pub day: Option<u16>,
32    /// Manual Elevation
33    ///
34    /// Global manual override target position of elevation in degrees from horizontal.  Unimplemented for single axis azimuth tracker type
35    pub glbl_el_ctl: Option<i32>,
36    /// Manual Azimuth
37    ///
38    /// Global manual override target position of azimuth in degrees from true north towards east.  Unimplemented for single axis azimuth tracker type
39    pub glbl_az_ctl: Option<i32>,
40    /// Global Mode
41    ///
42    /// Global Control register operates on all trackers. Normal operation is automatic.  Operator can override the position by setting the ElCtl, AzCtl and enabling Manual operation. Entering calibration mode will revert to automatic operation after calibration is complete.
43    ///
44    /// Detail: The global controls all trackers
45    pub glbl_ctl: Option<GlblCtl>,
46    /// Global Alarm
47    ///
48    /// Global tracker alarm conditions
49    ///
50    /// Detail: Combined tracker alarm conditions.  See individual trackers for alarms
51    pub glbl_alm: Option<GlblAlm>,
52    /// SF
53    ///
54    /// Scale Factor for targets and position measurements in degrees
55    pub dgr_sf: i16,
56    /// Trackers
57    ///
58    /// Number of trackers being controlled.  Size of repeating block.
59    pub n: u16,
60    #[allow(missing_docs)]
61    pub tracker: Vec<Tracker>,
62}
63#[allow(missing_docs)]
64impl TrackerController {
65    pub const NAM: crate::Point<Self, Option<String>> = crate::Point::new(0, 8, false);
66    pub const TYP: crate::Point<Self, Typ> = crate::Point::new(8, 1, false);
67    pub const DT_LOC: crate::Point<Self, Option<String>> = crate::Point::new(9, 5, false);
68    pub const TM_LOC: crate::Point<Self, Option<String>> = crate::Point::new(14, 3, false);
69    pub const DAY: crate::Point<Self, Option<u16>> = crate::Point::new(17, 1, false);
70    pub const GLBL_EL_CTL: crate::Point<Self, Option<i32>> = crate::Point::new(18, 2, true);
71    pub const GLBL_AZ_CTL: crate::Point<Self, Option<i32>> = crate::Point::new(20, 2, true);
72    pub const GLBL_CTL: crate::Point<Self, Option<GlblCtl>> = crate::Point::new(22, 1, true);
73    pub const GLBL_ALM: crate::Point<Self, Option<GlblAlm>> = crate::Point::new(23, 1, false);
74    pub const DGR_SF: crate::Point<Self, i16> = crate::Point::new(24, 1, false);
75    pub const N: crate::Point<Self, u16> = crate::Point::new(25, 1, false);
76}
77impl crate::Group for TrackerController {
78    const LEN: u16 = 26;
79}
80impl TrackerController {
81    fn parse_group(data: &[u16]) -> Result<(&[u16], Self), crate::DecodeError> {
82        let nested_data = data
83            .get(usize::from(<Self as crate::Group>::LEN)..)
84            .unwrap_or(&[]);
85        let (nested_data, tracker) = Tracker::parse_multiple(nested_data)?;
86        Ok((
87            nested_data,
88            Self {
89                nam: Self::NAM.from_data(data)?,
90                typ: Self::TYP.from_data(data)?,
91                dt_loc: Self::DT_LOC.from_data(data)?,
92                tm_loc: Self::TM_LOC.from_data(data)?,
93                day: Self::DAY.from_data(data)?,
94                glbl_el_ctl: Self::GLBL_EL_CTL.from_data(data)?,
95                glbl_az_ctl: Self::GLBL_AZ_CTL.from_data(data)?,
96                glbl_ctl: Self::GLBL_CTL.from_data(data)?,
97                glbl_alm: Self::GLBL_ALM.from_data(data)?,
98                dgr_sf: Self::DGR_SF.from_data(data)?,
99                n: Self::N.from_data(data)?,
100                tracker,
101            },
102        ))
103    }
104}
105/// Type
106///
107/// Type of tracker
108#[derive(Copy, Clone, Debug, Eq, PartialEq)]
109#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
110pub enum Typ {
111    #[allow(missing_docs)]
112    Unknown,
113    #[allow(missing_docs)]
114    Fixed,
115    #[allow(missing_docs)]
116    Horizontal,
117    #[allow(missing_docs)]
118    Tilted,
119    #[allow(missing_docs)]
120    Azimuth,
121    #[allow(missing_docs)]
122    Dual,
123    #[allow(missing_docs)]
124    Other,
125    /// Raw enum value not defined by the SunSpec model.
126    Invalid(u16),
127}
128impl crate::EnumValue for Typ {
129    type Repr = u16;
130    const INVALID: Self::Repr = 65535;
131    fn from_repr(value: Self::Repr) -> Self {
132        match value {
133            0 => Self::Unknown,
134            1 => Self::Fixed,
135            2 => Self::Horizontal,
136            3 => Self::Tilted,
137            4 => Self::Azimuth,
138            5 => Self::Dual,
139            99 => Self::Other,
140            value => Self::Invalid(value),
141        }
142    }
143    fn to_repr(self) -> Self::Repr {
144        match self {
145            Self::Unknown => 0,
146            Self::Fixed => 1,
147            Self::Horizontal => 2,
148            Self::Tilted => 3,
149            Self::Azimuth => 4,
150            Self::Dual => 5,
151            Self::Other => 99,
152            Self::Invalid(value) => value,
153        }
154    }
155}
156impl crate::FixedSize for Typ {
157    const SIZE: u16 = 1u16;
158    const INVALID: Self = Self::Invalid(65535);
159    fn is_invalid(&self) -> bool {
160        matches!(self, Self::Invalid(_))
161    }
162}
163/// Global Mode
164///
165/// Global Control register operates on all trackers. Normal operation is automatic.  Operator can override the position by setting the ElCtl, AzCtl and enabling Manual operation. Entering calibration mode will revert to automatic operation after calibration is complete.
166///
167/// Detail: The global controls all trackers
168#[derive(Copy, Clone, Debug, Eq, PartialEq)]
169#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
170pub enum GlblCtl {
171    #[allow(missing_docs)]
172    Automatic,
173    #[allow(missing_docs)]
174    Manual,
175    #[allow(missing_docs)]
176    Calibrate,
177    /// Raw enum value not defined by the SunSpec model.
178    Invalid(u16),
179}
180impl crate::EnumValue for GlblCtl {
181    type Repr = u16;
182    const INVALID: Self::Repr = 65535;
183    fn from_repr(value: Self::Repr) -> Self {
184        match value {
185            0 => Self::Automatic,
186            1 => Self::Manual,
187            2 => Self::Calibrate,
188            value => Self::Invalid(value),
189        }
190    }
191    fn to_repr(self) -> Self::Repr {
192        match self {
193            Self::Automatic => 0,
194            Self::Manual => 1,
195            Self::Calibrate => 2,
196            Self::Invalid(value) => value,
197        }
198    }
199}
200impl crate::FixedSize for GlblCtl {
201    const SIZE: u16 = 1u16;
202    const INVALID: Self = Self::Invalid(65535);
203    fn is_invalid(&self) -> bool {
204        matches!(self, Self::Invalid(_))
205    }
206}
207bitflags::bitflags! {
208    #[doc = " Global Alarm"] #[doc = " "] #[doc = " Global tracker alarm conditions"]
209    #[doc = " "] #[doc =
210    " Detail: Combined tracker alarm conditions.  See individual trackers for alarms"]
211    #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde",
212    derive(::serde::Serialize, ::serde::Deserialize))] pub struct GlblAlm : u16 {
213    #[allow(missing_docs)] const SetPoint = 1; #[allow(missing_docs)] const ObsEl = 2;
214    #[allow(missing_docs)] const ObsAz = 4; }
215}
216impl crate::Value for GlblAlm {
217    fn decode(data: &[u16]) -> Result<Self, crate::DecodeError> {
218        let value = u16::decode(data)?;
219        Ok(Self::from_bits_retain(value))
220    }
221    fn encode(self) -> Box<[u16]> {
222        self.bits().encode()
223    }
224}
225impl crate::FixedSize for GlblAlm {
226    const SIZE: u16 = 1u16;
227    const INVALID: Self = Self::from_bits_retain(65535u16);
228    fn is_invalid(&self) -> bool {
229        self.bits() == 65535u16
230    }
231}
232#[allow(missing_docs)]
233#[derive(Debug)]
234#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
235pub struct Tracker {
236    /// Tracker
237    ///
238    /// Descriptive name for this tracker unit
239    pub id: Option<String>,
240    /// Target Elevation
241    ///
242    /// Auto target elevation in degrees from horizontal.  Unimplemented for single axis azimuth tracker type
243    pub el_trgt: Option<i32>,
244    /// Target Azimuth
245    ///
246    /// Auto target azimuth  in degrees from true north towards east.  Unimplemented for single axis horizontal tracker type
247    pub az_trgt: Option<i32>,
248    /// Elevation
249    ///
250    /// Actual elevation position  in degrees from horizontal.  Unimplemented for single axis azimuth tracker type
251    pub el_pos: Option<i32>,
252    /// Azimuth
253    ///
254    /// Actual azimuth position  in degrees from true north towards east.  Unimplemented for single axis horizontal tracker type
255    pub az_pos: Option<i32>,
256    /// Manual Elevation
257    ///
258    /// Manual override target position of elevation in degrees from horizontal.  Unimplemented for single axis azimuth tracker type
259    pub el_ctl: Option<i32>,
260    /// Manual Azimuth
261    ///
262    /// Manual override target position of azimuth in degrees from true north towards east.  Unimplemented for single axis azimuth tracker type
263    pub az_ctl: Option<i32>,
264    /// Mode
265    ///
266    /// Control register. Normal operation is automatic.  Operator can override the position by setting the ElCtl, AzCtl and enabling Manual operation. Entering calibration mode will revert to automatic operation after calibration is complete.
267    pub ctl: Option<TrackerCtl>,
268    /// Alarm
269    ///
270    /// Tracker alarm conditions
271    pub alm: Option<TrackerAlm>,
272}
273#[allow(missing_docs)]
274impl Tracker {
275    pub const ID: crate::Point<Self, Option<String>> = crate::Point::new(0, 8, false);
276    pub const EL_TRGT: crate::Point<Self, Option<i32>> = crate::Point::new(8, 2, false);
277    pub const AZ_TRGT: crate::Point<Self, Option<i32>> = crate::Point::new(10, 2, false);
278    pub const EL_POS: crate::Point<Self, Option<i32>> = crate::Point::new(12, 2, false);
279    pub const AZ_POS: crate::Point<Self, Option<i32>> = crate::Point::new(14, 2, false);
280    pub const EL_CTL: crate::Point<Self, Option<i32>> = crate::Point::new(16, 2, true);
281    pub const AZ_CTL: crate::Point<Self, Option<i32>> = crate::Point::new(18, 2, true);
282    pub const CTL: crate::Point<Self, Option<TrackerCtl>> = crate::Point::new(20, 1, true);
283    pub const ALM: crate::Point<Self, Option<TrackerAlm>> = crate::Point::new(21, 1, false);
284}
285impl crate::Group for Tracker {
286    const LEN: u16 = 22;
287}
288impl Tracker {
289    fn parse_group(data: &[u16]) -> Result<(&[u16], Self), crate::DecodeError> {
290        let nested_data = data
291            .get(usize::from(<Self as crate::Group>::LEN)..)
292            .unwrap_or(&[]);
293        Ok((
294            nested_data,
295            Self {
296                id: Self::ID.from_data(data)?,
297                el_trgt: Self::EL_TRGT.from_data(data)?,
298                az_trgt: Self::AZ_TRGT.from_data(data)?,
299                el_pos: Self::EL_POS.from_data(data)?,
300                az_pos: Self::AZ_POS.from_data(data)?,
301                el_ctl: Self::EL_CTL.from_data(data)?,
302                az_ctl: Self::AZ_CTL.from_data(data)?,
303                ctl: Self::CTL.from_data(data)?,
304                alm: Self::ALM.from_data(data)?,
305            },
306        ))
307    }
308    fn parse_multiple(data: &[u16]) -> Result<(&[u16], Vec<Self>), crate::DecodeError> {
309        let group_len = usize::from(<Tracker as crate::Group>::LEN);
310        if group_len == 0 {
311            return Ok((data, Vec::new()));
312        }
313        if data.len() % group_len != 0 {
314            return Err(crate::DecodeError::OutOfBounds);
315        }
316        let group_count = data.len() / group_len;
317        let (data, groups) =
318            (0..group_count).try_fold((data, Vec::new()), |(data, mut groups), _| {
319                let (data, group) = Tracker::parse_group(data)?;
320                groups.push(group);
321                Ok::<_, crate::DecodeError>((data, groups))
322            })?;
323        Ok((data, groups))
324    }
325}
326/// Mode
327///
328/// Control register. Normal operation is automatic.  Operator can override the position by setting the ElCtl, AzCtl and enabling Manual operation. Entering calibration mode will revert to automatic operation after calibration is complete.
329#[derive(Copy, Clone, Debug, Eq, PartialEq)]
330#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
331pub enum TrackerCtl {
332    #[allow(missing_docs)]
333    Automatic,
334    #[allow(missing_docs)]
335    Manual,
336    #[allow(missing_docs)]
337    Calibrate,
338    /// Raw enum value not defined by the SunSpec model.
339    Invalid(u16),
340}
341impl crate::EnumValue for TrackerCtl {
342    type Repr = u16;
343    const INVALID: Self::Repr = 65535;
344    fn from_repr(value: Self::Repr) -> Self {
345        match value {
346            0 => Self::Automatic,
347            1 => Self::Manual,
348            2 => Self::Calibrate,
349            value => Self::Invalid(value),
350        }
351    }
352    fn to_repr(self) -> Self::Repr {
353        match self {
354            Self::Automatic => 0,
355            Self::Manual => 1,
356            Self::Calibrate => 2,
357            Self::Invalid(value) => value,
358        }
359    }
360}
361impl crate::FixedSize for TrackerCtl {
362    const SIZE: u16 = 1u16;
363    const INVALID: Self = Self::Invalid(65535);
364    fn is_invalid(&self) -> bool {
365        matches!(self, Self::Invalid(_))
366    }
367}
368bitflags::bitflags! {
369    #[doc = " Alarm"] #[doc = " "] #[doc = " Tracker alarm conditions"] #[derive(Copy,
370    Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde",
371    derive(::serde::Serialize, ::serde::Deserialize))] pub struct TrackerAlm : u16 {
372    #[allow(missing_docs)] const SetPoint = 1; #[allow(missing_docs)] const ObsEl = 2;
373    #[allow(missing_docs)] const ObsAz = 4; }
374}
375impl crate::Value for TrackerAlm {
376    fn decode(data: &[u16]) -> Result<Self, crate::DecodeError> {
377        let value = u16::decode(data)?;
378        Ok(Self::from_bits_retain(value))
379    }
380    fn encode(self) -> Box<[u16]> {
381        self.bits().encode()
382    }
383}
384impl crate::FixedSize for TrackerAlm {
385    const SIZE: u16 = 1u16;
386    const INVALID: Self = Self::from_bits_retain(65535u16);
387    fn is_invalid(&self) -> bool {
388        self.bits() == 65535u16
389    }
390}
391impl crate::Model for TrackerController {
392    const ID: u16 = 601;
393    fn addr(models: &crate::Models) -> crate::ModelAddr<Self> {
394        models.m601
395    }
396    fn parse(data: &[u16]) -> Result<Self, crate::ParseError<Self>> {
397        let (_, model) = Self::parse_group(data)?;
398        Ok(model)
399    }
400}