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}