ichen_openprotocol/
types.rs

1use derive_more::*;
2use serde::{Deserialize, Serialize};
3use std::cmp::{Ordering, PartialEq, PartialOrd};
4use std::convert::TryFrom;
5use std::fmt::{Debug, Formatter};
6use std::num::NonZeroU32;
7use std::{borrow::Borrow, ops::Deref};
8
9/// Supported UI languages for the controller's HMI.
10///
11/// See [this document] for details.
12///
13/// [this document]: https://github.com/chenhsong/OpenProtocol/blob/master/doc/enums.md#languages
14///
15#[derive(
16    Debug, Display, Ord, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize, Copy, Clone,
17)]
18pub enum Language {
19    /// Unknown language.
20    #[display(fmt = "Unknown")]
21    Unknown,
22    /// English (en)
23    #[display(fmt = "English")]
24    EN,
25    /// Traditional Chinese (zh-tw)
26    #[display(fmt = "䌓體中文")]
27    B5,
28    /// Simplified Chinese (zh-cn)
29    #[display(fmt = "简体中文")]
30    GB,
31    /// French (fr)
32    #[display(fmt = "Français")]
33    FR,
34    /// German (de)
35    #[display(fmt = "Deutsch")]
36    DE,
37    /// Italian (it)
38    #[display(fmt = "Italiano")]
39    IT,
40    /// Spanish (es)
41    #[display(fmt = "Español")]
42    ES,
43    /// Portuguese (pt)
44    #[display(fmt = "Português")]
45    PT,
46    /// Japanese (ja)
47    #[display(fmt = "日本語")]
48    JA,
49}
50
51impl Language {
52    /// Returns true if `Unknown`.
53    ///
54    /// # Examples
55    ///
56    /// ~~~
57    /// # use ichen_openprotocol::*;
58    /// assert!(Language::Unknown.is_unknown());
59    /// assert!(!Language::FR.is_unknown());
60    /// ~~~
61    #[allow(clippy::trivially_copy_pass_by_ref)]
62    pub fn is_unknown(&self) -> bool {
63        *self == Language::Unknown
64    }
65}
66
67impl Default for Language {
68    /// Default value for `Language`.
69    fn default() -> Self {
70        Language::Unknown
71    }
72}
73
74/// Operating modes of the controller.
75///
76/// See [this document] for details.
77///
78/// [this document]: https://github.com/chenhsong/OpenProtocol/blob/master/doc/enums.md#opmodes
79///
80#[derive(
81    Debug, Display, Ord, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize, Copy, Clone,
82)]
83pub enum OpMode {
84    /// Unknown operation mode.
85    Unknown,
86    /// Manual mode.
87    Manual,
88    /// Semi-Automatic mode.
89    #[display(fmt = "Semi-Automatic")]
90    SemiAutomatic,
91    /// Automatic mode.
92    Automatic,
93    /// Other unspecified operation mode.
94    Others,
95    /// The controller is off-line.
96    ///
97    /// When the controller is off-line, both its operating mode and [job mode] should be `Offline`.
98    ///
99    /// [job mode]: enum.JobMode.html
100    ///
101    #[display(fmt = "Off-Line")]
102    Offline,
103}
104
105impl OpMode {
106    /// Returns true if `Unknown`.
107    ///
108    /// # Examples
109    ///
110    /// ~~~
111    /// # use ichen_openprotocol::*;
112    /// assert!(OpMode::Unknown.is_unknown());
113    /// assert!(!OpMode::Manual.is_unknown());
114    /// ~~~
115    #[allow(clippy::trivially_copy_pass_by_ref)]
116    pub fn is_unknown(&self) -> bool {
117        *self == OpMode::Unknown
118    }
119
120    /// Returns true if `Offline`.
121    ///
122    /// # Examples
123    ///
124    /// ~~~
125    /// # use ichen_openprotocol::*;
126    /// assert!(OpMode::Offline.is_offline());
127    /// assert!(!OpMode::Manual.is_offline());
128    /// ~~~
129    #[allow(clippy::trivially_copy_pass_by_ref)]
130    pub fn is_offline(&self) -> bool {
131        *self == OpMode::Offline
132    }
133
134    /// Returns true for all variants other than `Unknown` and `Offline`.
135    ///
136    /// # Examples
137    ///
138    /// ~~~
139    /// # use ichen_openprotocol::*;
140    /// assert!(!OpMode::Offline.is_online());
141    /// assert!(!OpMode::Unknown.is_online());
142    /// assert!(OpMode::Automatic.is_online());
143    /// assert!(OpMode::Manual.is_online());
144    /// ~~~
145    #[allow(clippy::trivially_copy_pass_by_ref)]
146    pub fn is_online(&self) -> bool {
147        match self {
148            OpMode::Unknown | OpMode::Offline => false,
149            _ => true,
150        }
151    }
152
153    /// A machine is producing if it is in either `Automatic` or `Semi-Automatic` mode.
154    ///
155    /// # Examples
156    ///
157    /// ~~~
158    /// # use ichen_openprotocol::*;
159    /// assert!(!OpMode::Offline.is_producing());
160    /// assert!(!OpMode::Unknown.is_producing());
161    /// assert!(OpMode::Automatic.is_producing());
162    /// assert!(!OpMode::Manual.is_producing());
163    /// ~~~
164    #[allow(clippy::trivially_copy_pass_by_ref)]
165    pub fn is_producing(&self) -> bool {
166        match self {
167            OpMode::SemiAutomatic | OpMode::Automatic => true,
168            _ => false,
169        }
170    }
171}
172
173impl Default for OpMode {
174    /// Default value for `OpMode`.
175    fn default() -> Self {
176        OpMode::Unknown
177    }
178}
179
180/// Job modes of the controller.
181///
182/// On some controller models, job modes 1-15 (`ID01` - `ID15`) can be user-defined.
183///
184/// See [this document] for details.
185///
186/// [this document]: https://github.com/chenhsong/OpenProtocol/blob/master/doc/enums.md#jobmodes
187///
188#[derive(
189    Debug, Display, Ord, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize, Copy, Clone,
190)]
191pub enum JobMode {
192    /// Unknown job mode.
193    Unknown,
194    ID01,
195    ID02,
196    ID03,
197    ID04,
198    ID05,
199    ID06,
200    ID07,
201    ID08,
202    ID09,
203    ID10,
204    ID11,
205    ID12,
206    ID13,
207    ID14,
208    ID15,
209    /// The controller is off-line.
210    ///
211    /// When the controller is off-line, both its [operating mode] and job mode should be `Offline`.
212    ///
213    /// [operating mode]: enum.OpMode.html
214    ///
215    #[display(fmt = "Off-Line")]
216    Offline,
217}
218
219impl JobMode {
220    /// Returns true if `Unknown`.
221    ///
222    /// # Examples
223    ///
224    /// ~~~
225    /// # use ichen_openprotocol::*;
226    /// assert!(JobMode::Unknown.is_unknown());
227    /// assert!(!JobMode::ID08.is_unknown());
228    /// ~~~
229    #[allow(clippy::trivially_copy_pass_by_ref)]
230    pub fn is_unknown(&self) -> bool {
231        *self == JobMode::Unknown
232    }
233
234    /// Returns true if `Offline`.
235    ///
236    /// # Examples
237    ///
238    /// ~~~
239    /// # use ichen_openprotocol::*;
240    /// assert!(JobMode::Offline.is_offline());
241    /// assert!(!JobMode::ID08.is_offline());
242    /// ~~~
243    #[allow(clippy::trivially_copy_pass_by_ref)]
244    pub fn is_offline(&self) -> bool {
245        *self == JobMode::Offline
246    }
247
248    /// Returns true for all variants other than `Unknown` and `Offline`.
249    ///
250    /// # Examples
251    ///
252    /// ~~~
253    /// # use ichen_openprotocol::*;
254    /// assert!(!JobMode::Offline.is_online());
255    /// assert!(!JobMode::Unknown.is_online());
256    /// assert!(JobMode::ID01.is_online());
257    /// assert!(JobMode::ID15.is_online());
258    /// ~~~
259    #[allow(clippy::trivially_copy_pass_by_ref)]
260    pub fn is_online(&self) -> bool {
261        match self {
262            JobMode::Unknown | JobMode::Offline => false,
263            _ => true,
264        }
265    }
266}
267
268impl Default for JobMode {
269    /// Default value for `JobMode`.
270    fn default() -> Self {
271        JobMode::Unknown
272    }
273}
274
275/// A 32-bit numeric ID that cannot be zero or negative.
276///
277/// This type is usually used for specifying a unique identification number.
278///
279#[derive(
280    AsRef,
281    AsMut,
282    Deref,
283    DerefMut,
284    Display,
285    Copy,
286    Clone,
287    Ord,
288    PartialOrd,
289    Eq,
290    PartialEq,
291    Hash,
292    From,
293    Into,
294    FromStr,
295    Serialize,
296    Deserialize,
297)]
298pub struct ID(NonZeroU32);
299
300impl ID {
301    /// Create a new `ID` from a `u32` value.
302    ///
303    /// # Errors
304    ///
305    /// Returns `None` if `value` is zero.
306    ///
307    /// # Examples
308    ///
309    /// ~~~
310    /// # use ichen_openprotocol::*;
311    /// let id = ID::new(42).unwrap();
312    /// assert_eq!(42, u32::from(id));
313    /// assert_eq!(None, ID::new(0));
314    /// ~~~
315    pub fn new(value: u32) -> Option<Self> {
316        Self::try_from(value).ok()
317    }
318
319    /// Create a new `ID` from a `u32` value.
320    ///
321    /// # Panics
322    ///
323    /// Panics if `value` is zero.
324    ///
325    /// ## Error Examples
326    ///
327    /// ~~~should_panic
328    /// # use ichen_openprotocol::*;
329    /// let id = ID::from_u32(0);    // This will panic.
330    /// ~~~
331    ///
332    /// # Examples
333    ///
334    /// ~~~
335    /// # use ichen_openprotocol::*;
336    /// let id = ID::from_u32(42);
337    /// assert_eq!(42, id.get());
338    /// ~~~
339    pub fn from_u32(value: u32) -> Self {
340        Self::try_from(value).unwrap()
341    }
342
343    /// Convert an ID into a `u32` value.
344    ///
345    /// # Examples
346    ///
347    /// ~~~
348    /// # use ichen_openprotocol::*;
349    /// # fn main() -> std::result::Result<(), &'static str> {
350    /// let id = ID::new(42).unwrap();
351    /// assert_eq!(42, id.get());
352    /// # Ok(())
353    /// # }
354    /// ~~~
355    pub fn get(self) -> u32 {
356        self.0.get()
357    }
358}
359
360impl Debug for ID {
361    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
362        write!(f, "{:?}", &self.0)
363    }
364}
365
366impl TryFrom<u32> for ID {
367    type Error = &'static str;
368
369    /// Create a new `ID` from an integer value;
370    ///
371    /// # Errors
372    ///
373    /// Return `Err(&'static str)` if `num` is zero.
374    ///
375    /// # Examples
376    ///
377    /// ~~~
378    /// # use std::convert::TryFrom;
379    /// # use ichen_openprotocol::*;
380    /// let id = ID::try_from(42).unwrap();
381    /// assert_eq!(42, u32::from(id));
382    /// assert_eq!(Err("ID value cannot be zero."), ID::try_from(0));
383    /// ~~~
384    fn try_from(value: u32) -> Result<Self, Self::Error> {
385        NonZeroU32::new(value).map(Self).ok_or("ID value cannot be zero.")
386    }
387}
388
389impl From<ID> for u32 {
390    fn from(id: ID) -> Self {
391        id.get()
392    }
393}
394
395impl PartialEq<u32> for ID {
396    fn eq(&self, other: &u32) -> bool {
397        self.get() == *other
398    }
399}
400
401impl PartialEq<ID> for u32 {
402    fn eq(&self, other: &ID) -> bool {
403        *self == other.get()
404    }
405}
406
407impl PartialOrd<u32> for ID {
408    fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
409        self.get().partial_cmp(other)
410    }
411}
412
413impl PartialOrd<ID> for u32 {
414    fn partial_cmp(&self, other: &ID) -> Option<Ordering> {
415        self.partial_cmp(&other.get())
416    }
417}
418
419/// A 32-bit ID that represents a controller action.
420///
421/// It `Deref`s into an `i32`.
422///
423#[derive(
424    AsRef,
425    AsMut,
426    DerefMut,
427    Display,
428    Constructor,
429    Copy,
430    Clone,
431    Ord,
432    PartialOrd,
433    Eq,
434    PartialEq,
435    Hash,
436    From,
437    Into,
438    FromStr,
439    Serialize,
440    Deserialize,
441)]
442pub struct ActionID(i32);
443
444impl Deref for ActionID {
445    type Target = i32;
446
447    fn deref(&self) -> &Self::Target {
448        &self.0
449    }
450}
451
452impl Borrow<i32> for ActionID {
453    fn borrow(&self) -> &i32 {
454        &self.0
455    }
456}
457
458impl Debug for ActionID {
459    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
460        write!(f, "{:?}", &self.0)
461    }
462}
463
464impl PartialEq<i32> for ActionID {
465    fn eq(&self, other: &i32) -> bool {
466        self.0 == *other
467    }
468}
469
470impl PartialEq<ActionID> for i32 {
471    fn eq(&self, other: &ActionID) -> bool {
472        *self == other.0
473    }
474}
475
476impl PartialOrd<i32> for ActionID {
477    fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
478        self.0.partial_cmp(other)
479    }
480}
481
482impl PartialOrd<ActionID> for i32 {
483    fn partial_cmp(&self, other: &ActionID) -> Option<Ordering> {
484        self.partial_cmp(&other.0)
485    }
486}