ichen_openprotocol/
controller.rs

1use super::{Address, GeoLocation, JobMode, OpMode, Operator, TextID, TextName, ID, R32};
2use chrono::{DateTime, FixedOffset};
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5use std::borrow::Cow;
6use std::convert::TryInto;
7
8/// A data structure containing the current known status of a controller.
9///
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct Controller<'a> {
13    /// Unique ID of the controller, which cannot be zero.
14    pub controller_id: ID,
15    //
16    /// User-specified human-friendly name for the machine.
17    pub display_name: TextName<'a>,
18    //
19    /// Controller type.
20    ///
21    /// # Examples
22    ///
23    /// * `Ai01`
24    /// * `Ai12`
25    /// * `CDC2000WIN`
26    /// * `MPC7`
27    pub controller_type: TextID<'a>,
28    //
29    /// Version of the controller's firmware.
30    pub version: TextID<'a>,
31    //
32    /// Machine model.
33    pub model: TextID<'a>,
34    //
35    /// Address of the controller.
36    ///
37    /// For a network-connected controller, this is usually the IP address and port, in the format `x.x.x.x:port`.
38    ///
39    /// For a serial-connected controller, this is usually the serial port device name, such as `COM1`, `ttyS0`.
40    #[serde(rename = "IP")]
41    pub address: Address<'a>,
42    //
43    /// Physical geo-location of the controller (if any).
44    #[serde(skip_serializing_if = "Option::is_none")]
45    #[serde(flatten)]
46    pub geo_location: Option<GeoLocation>,
47    //
48    /// Current operating mode of the controller.
49    pub op_mode: OpMode,
50    //
51    /// Current job mode of the controller.
52    pub job_mode: JobMode,
53    //
54    /// Last set of cycle data (if any) received from the controller.
55    #[serde(skip_serializing_if = "IndexMap::is_empty")]
56    #[serde(default)]
57    pub last_cycle_data: IndexMap<TextID<'a>, R32>,
58    //
59    /// Last-known states (if any) of controller variables.
60    #[serde(skip_serializing_if = "IndexMap::is_empty")]
61    #[serde(default)]
62    pub variables: IndexMap<TextID<'a>, R32>,
63    //
64    /// Time of last connection.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub last_connection_time: Option<DateTime<FixedOffset>>,
67    //
68    /// Current logged-in user (if any) on the controller
69    #[serde(flatten)]
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub operator: Option<Operator<'a>>,
72    //
73    /// Active job ID (if any) on the controller.
74    #[serde(skip_serializing_if = "Option::is_none")]
75    #[serde(borrow)]
76    pub job_card_id: Option<Box<Cow<'a, str>>>,
77    //
78    /// ID of the set of mold data currently loaded (if any) on the controller.
79    #[serde(skip_serializing_if = "Option::is_none")]
80    #[serde(borrow)]
81    pub mold_id: Option<Box<Cow<'a, str>>>,
82}
83
84impl Default for Controller<'_> {
85    /// Default value for `Controller`.
86    ///
87    /// `controller_id` is set to 1 because zero is not allowed.  
88    /// All other fields are set to `Unknown` or empty.
89    ///
90    fn default() -> Self {
91        Controller {
92            controller_id: ID::from_u32(1),
93            display_name: "Unknown".try_into().unwrap(),
94            controller_type: "Unknown".try_into().unwrap(),
95            version: "Unknown".try_into().unwrap(),
96            model: "Unknown".try_into().unwrap(),
97            address: Address::Unknown,
98            geo_location: None,
99            op_mode: OpMode::Unknown,
100            job_mode: JobMode::Unknown,
101            job_card_id: None,
102            last_cycle_data: Default::default(),
103            variables: Default::default(),
104            last_connection_time: None,
105            operator: None,
106            mold_id: None,
107        }
108    }
109}
110
111// Tests
112
113#[cfg(test)]
114mod test {
115    use super::*;
116    use std::result::Result;
117
118    #[test]
119    fn test_controller_to_json() -> Result<(), String> {
120        let c = Controller {
121            op_mode: OpMode::Automatic,
122            job_mode: JobMode::ID02,
123            operator: Some(Operator::try_new_with_name(ID::from_u32(123), "John")?),
124            geo_location: Some(GeoLocation::new(88.0, 123.0)?),
125            ..Default::default()
126        };
127        let serialized = serde_json::to_string(&c).map_err(|x| x.to_string())?;
128
129        assert_eq!(
130            r#"{"controllerId":1,"displayName":"Unknown","controllerType":"Unknown","version":"Unknown","model":"Unknown","IP":"0.0.0.0:0","geoLatitude":88.0,"geoLongitude":123.0,"opMode":"Automatic","jobMode":"ID02","operatorId":123,"operatorName":"John"}"#,
131            serialized
132        );
133
134        Ok(())
135    }
136
137    #[test]
138    fn test_controller_from_json() -> Result<(), String> {
139        let c: Controller = serde_json::from_str(r#"{"controllerId":1,"geoLatitude":88,"geoLongitude":-123,"displayName":"Hello","controllerType":"Unknown","version":"Unknown","model":"Unknown","IP":"127.0.0.1:123","opMode":"Automatic","jobMode":"ID02","operatorId":123,"operatorName":"John"}"#).map_err(|x| x.to_string())?;
140
141        assert_eq!(
142            r#"Controller { controller_id: 1, display_name: "Hello", controller_type: "Unknown", version: "Unknown", model: "Unknown", address: IPv4(127.0.0.1, 123), geo_location: Some((88,-123)), op_mode: Automatic, job_mode: ID02, last_cycle_data: {}, variables: {}, last_connection_time: None, operator: Some(Operator { operator_id: 123, operator_name: Some("John") }), job_card_id: None, mold_id: None }"#,
143            format!("{:?}", &c)
144        );
145
146        Ok(())
147    }
148}