eva_sdk/
types.rs

1use eva_common::events::{
2    LocalStateEvent, RawStateEventOwned, RemoteStateEvent, ReplicationStateEvent,
3};
4use eva_common::prelude::*;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use std::fmt;
7use std::str::FromStr;
8
9/// basic state object, usually collected from topics, ignores extra fields
10#[derive(Serialize, Deserialize, Clone, Debug)]
11pub struct State {
12    #[serde(alias = "s")]
13    pub status: ItemStatus,
14    #[serde(alias = "v")]
15    pub value: Option<Value>,
16    #[serde(rename = "t")]
17    pub set_time: f64,
18}
19
20impl From<LocalStateEvent> for State {
21    #[inline]
22    fn from(ev: LocalStateEvent) -> Self {
23        Self {
24            status: ev.status,
25            value: Some(ev.value),
26            set_time: ev.t,
27        }
28    }
29}
30
31impl From<RemoteStateEvent> for State {
32    #[inline]
33    fn from(ev: RemoteStateEvent) -> Self {
34        Self {
35            status: ev.status,
36            value: Some(ev.value),
37            set_time: ev.t,
38        }
39    }
40}
41
42/// basic state with OID specified inside the object, collected from RPC, useful to keep/transfer
43/// via internal channels
44#[derive(Serialize, Deserialize, Clone, Debug)]
45pub struct ItemState {
46    #[serde(alias = "i")]
47    pub oid: OID,
48    #[serde(alias = "s")]
49    pub status: ItemStatus,
50    #[serde(alias = "v")]
51    pub value: Option<Value>,
52    #[serde(rename = "t")]
53    pub set_time: f64,
54}
55
56impl ItemState {
57    #[inline]
58    pub fn from_state(state: State, oid: OID) -> Self {
59        Self {
60            oid,
61            status: state.status,
62            value: state.value,
63            set_time: state.set_time,
64        }
65    }
66    #[inline]
67    pub fn into_state(self) -> (OID, State) {
68        (
69            self.oid,
70            State {
71                status: self.status,
72                value: self.value,
73                set_time: self.set_time,
74            },
75        )
76    }
77}
78
79/// short item state, without set-time, collected from RPC, useful to keep/transfer via internal
80/// channels
81#[derive(Serialize, Deserialize, Clone, Debug)]
82pub struct ShortItemState {
83    #[serde(alias = "i")]
84    pub oid: OID,
85    #[serde(alias = "s")]
86    pub status: ItemStatus,
87    #[serde(alias = "v")]
88    pub value: Option<Value>,
89}
90
91impl ShortItemState {
92    #[inline]
93    pub fn into_raw_state_event_owned(self) -> (OID, RawStateEventOwned) {
94        (
95            self.oid,
96            if let Some(value) = self.value {
97                RawStateEventOwned::new(self.status, value)
98            } else {
99                RawStateEventOwned::new0(self.status)
100            },
101        )
102    }
103}
104
105/// short item state, without set-time but with connected field, useful to check are collected
106/// items connected or keep the most recent known state
107#[derive(Serialize, Deserialize, Clone, Debug)]
108pub struct ShortItemStateConnected {
109    #[serde(alias = "i")]
110    pub oid: OID,
111    #[serde(alias = "s")]
112    pub status: ItemStatus,
113    #[serde(alias = "v")]
114    pub value: Option<Value>,
115    pub connected: bool,
116}
117
118/// helper object for database services - status can be null/absent
119#[derive(Serialize, Deserialize, Clone, Debug)]
120pub struct HistoricalState {
121    #[serde(alias = "s", skip_serializing_if = "Option::is_none")]
122    pub status: Option<Value>,
123    #[serde(alias = "v", skip_serializing_if = "Option::is_none")]
124    pub value: Option<Value>,
125    #[serde(rename = "t")]
126    pub set_time: f64,
127}
128
129impl HistoricalState {
130    pub fn na(timestamp: f64, need_status: bool, need_value: bool) -> Self {
131        Self {
132            status: if need_status { Some(Value::Unit) } else { None },
133            value: if need_value { Some(Value::Unit) } else { None },
134            set_time: timestamp,
135        }
136    }
137}
138
139/// result object for database services
140#[derive(Serialize, Deserialize, Clone, Debug)]
141pub struct CompactStateHistory {
142    #[serde(alias = "s", skip_serializing_if = "Option::is_none")]
143    pub status: Option<Vec<Value>>,
144    #[serde(alias = "v", skip_serializing_if = "Option::is_none")]
145    pub value: Option<Vec<Value>>,
146    #[serde(rename = "t")]
147    pub set_time: Vec<f64>,
148}
149
150impl From<CompactStateHistory> for Vec<HistoricalState> {
151    fn from(mut data: CompactStateHistory) -> Self {
152        let mut result = Vec::new();
153        for set_time in data.set_time.into_iter().rev() {
154            let status = if let Some(ref mut s) = data.status {
155                s.pop()
156            } else {
157                None
158            };
159            let value = if let Some(ref mut v) = data.value {
160                v.pop()
161            } else {
162                None
163            };
164            result.push(HistoricalState {
165                status,
166                value,
167                set_time,
168            });
169        }
170        result.reverse();
171        result
172    }
173}
174
175/// a special state object, returned by state_history
176#[derive(Serialize, Deserialize, Clone, Debug)]
177#[serde(untagged)]
178pub enum StateHistoryData {
179    Regular(Vec<HistoricalState>),
180    Compact(CompactStateHistory),
181}
182
183impl StateHistoryData {
184    #[inline]
185    pub fn new_regular(data: Vec<HistoricalState>) -> Self {
186        Self::Regular(data)
187    }
188    pub fn new_compact(data: Vec<HistoricalState>, need_status: bool, need_value: bool) -> Self {
189        let mut status = Vec::new();
190        let mut value = Vec::new();
191        let mut set_time = Vec::new();
192        for d in data {
193            if need_status {
194                status.push(d.status.unwrap_or_default());
195            }
196            if need_value {
197                value.push(d.value.unwrap_or_default());
198            }
199            set_time.push(d.set_time);
200        }
201        Self::Compact(CompactStateHistory {
202            status: if need_status { Some(status) } else { None },
203            value: if need_value { Some(value) } else { None },
204            set_time,
205        })
206    }
207}
208
209/// Standard format for time-series data frames filling
210#[derive(Debug, Copy, Clone)]
211pub enum Fill {
212    Seconds(u32),
213    Minutes(u32),
214    Hours(u32),
215    Days(u32),
216    Weeks(u32),
217}
218
219impl Fill {
220    pub fn as_secs_f64(&self) -> f64 {
221        match self {
222            Fill::Seconds(v) => f64::from(*v),
223            Fill::Minutes(v) => f64::from(*v) * 60.0,
224            Fill::Hours(v) => f64::from(*v) * 3_600.0,
225            Fill::Days(v) => f64::from(*v) * 86_400.0,
226            Fill::Weeks(v) => f64::from(*v) * 604_800.0,
227        }
228    }
229    pub fn as_secs(&self) -> u64 {
230        match self {
231            Fill::Seconds(v) => u64::from(*v),
232            Fill::Minutes(v) => u64::from(*v) * 60,
233            Fill::Hours(v) => u64::from(*v) * 3_600,
234            Fill::Days(v) => u64::from(*v) * 86_400,
235            Fill::Weeks(v) => u64::from(*v) * 604_800,
236        }
237    }
238    pub fn time_series(&self, t_start: f64, t_end: f64, limit: Option<usize>) -> Vec<f64> {
239        let mut data = Vec::new();
240        let period = self.as_secs_f64();
241        if let Some(l) = limit {
242            let x = t_end % period;
243            let mut ts = if x == 0.0 { t_end } else { t_end - x };
244            let y = t_start % period;
245            let start = if y == 0.0 { t_start } else { t_start - x };
246            while ts >= start && data.len() < l {
247                data.push(ts);
248                ts -= period;
249            }
250            data.reverse();
251        } else {
252            let x = t_start % period;
253            let mut ts = if x == 0.0 { t_start } else { t_start - x };
254            while ts <= t_end {
255                data.push(ts);
256                ts += period;
257            }
258        }
259        data
260    }
261    pub fn fill_na(
262        &self,
263        t_start: f64,
264        t_end: f64,
265        limit: Option<usize>,
266        need_status: bool,
267        need_value: bool,
268    ) -> Vec<HistoricalState> {
269        let mut data = Vec::new();
270        let period = self.as_secs_f64();
271        if let Some(l) = limit {
272            let x = t_end % period;
273            let mut ts = if x == 0.0 { t_end } else { t_end - x };
274            let y = t_start % period;
275            let start = if y == 0.0 { t_start } else { t_start - x };
276            while ts >= start && data.len() < l {
277                data.push(HistoricalState::na(ts, need_status, need_value));
278                ts -= period;
279            }
280            data.reverse();
281        } else {
282            let x = t_start % period;
283            let mut ts = if x == 0.0 { t_start } else { t_start - x };
284            while ts <= t_end {
285                data.push(HistoricalState::na(ts, need_status, need_value));
286                ts += period;
287            }
288        }
289        data
290    }
291}
292
293impl fmt::Display for Fill {
294    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295        match self {
296            Fill::Seconds(v) => write!(f, "{}S", v),
297            Fill::Minutes(v) => write!(f, "{}T", v),
298            Fill::Hours(v) => write!(f, "{}H", v),
299            Fill::Days(v) => write!(f, "{}D", v),
300            Fill::Weeks(v) => write!(f, "{}W", v),
301        }
302    }
303}
304
305impl<'de> Deserialize<'de> for Fill {
306    #[inline]
307    fn deserialize<D>(deserializer: D) -> Result<Fill, D::Error>
308    where
309        D: Deserializer<'de>,
310    {
311        let s: String = Deserialize::deserialize(deserializer)?;
312        s.parse().map_err(serde::de::Error::custom)
313    }
314}
315
316impl Serialize for Fill {
317    #[inline]
318    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
319    where
320        S: Serializer,
321    {
322        serializer.serialize_str(&self.to_string())
323    }
324}
325
326impl FromStr for Fill {
327    type Err = Error;
328    #[inline]
329    fn from_str(s: &str) -> Result<Self, Self::Err> {
330        if s.len() < 2 {
331            Err(Error::invalid_params("invalid filling"))
332        } else {
333            macro_rules! ep {
334                ($res: expr) => {{
335                    let res = $res.map_err(|e| {
336                        Error::invalid_params(format!("unable to parse filling: {}", e))
337                    })?;
338                    if res == 0 {
339                        return Err(Error::invalid_params("fill numbers can not be zero"));
340                    }
341                    res
342                }};
343            }
344            Ok(match &s[s.len() - 1..] {
345                "S" => Fill::Seconds(ep!(s[..s.len() - 1].parse())),
346                "T" => Fill::Minutes(ep!(s[..s.len() - 1].parse())),
347                "H" => Fill::Hours(ep!(s[..s.len() - 1].parse())),
348                "D" => Fill::Days(ep!(s[..s.len() - 1].parse())),
349                "W" => Fill::Weeks(ep!(s[..s.len() - 1].parse())),
350                v => {
351                    return Err(Error::invalid_params(format!(
352                        "invalid filling type: {}",
353                        v
354                    )));
355                }
356            })
357        }
358    }
359}
360
361/// State property chooser
362#[derive(Copy, Clone, Eq, PartialEq, Hash)]
363pub enum StateProp {
364    Status,
365    Value,
366}
367
368impl StateProp {
369    pub fn as_str(&self) -> &str {
370        match self {
371            StateProp::Status => "status",
372            StateProp::Value => "value",
373        }
374    }
375}
376
377impl fmt::Display for StateProp {
378    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379        write!(f, "{}", self.as_str())
380    }
381}
382
383impl<'de> Deserialize<'de> for StateProp {
384    #[inline]
385    fn deserialize<D>(deserializer: D) -> Result<StateProp, D::Error>
386    where
387        D: Deserializer<'de>,
388    {
389        let s: String = Deserialize::deserialize(deserializer)?;
390        s.parse().map_err(serde::de::Error::custom)
391    }
392}
393
394impl Serialize for StateProp {
395    #[inline]
396    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
397    where
398        S: Serializer,
399    {
400        serializer.serialize_str(self.as_str())
401    }
402}
403
404impl FromStr for StateProp {
405    type Err = Error;
406    #[inline]
407    fn from_str(s: &str) -> Result<Self, Self::Err> {
408        match s {
409            "status" | "S" => Ok(StateProp::Status),
410            "value" | "V" => Ok(StateProp::Value),
411            v => Err(Error::invalid_params(format!("invalid prop: {}", v))),
412        }
413    }
414}
415
416/// State property extended chooser
417#[derive(Copy, Clone, Eq, PartialEq, Hash)]
418pub enum StatePropExt {
419    Status,
420    Value,
421    Act,
422}
423
424impl StatePropExt {
425    pub fn as_str(&self) -> &str {
426        match self {
427            StatePropExt::Status => "status",
428            StatePropExt::Value => "value",
429            StatePropExt::Act => "act",
430        }
431    }
432}
433
434impl<'de> Deserialize<'de> for StatePropExt {
435    #[inline]
436    fn deserialize<D>(deserializer: D) -> Result<StatePropExt, D::Error>
437    where
438        D: Deserializer<'de>,
439    {
440        let s: String = Deserialize::deserialize(deserializer)?;
441        s.parse().map_err(serde::de::Error::custom)
442    }
443}
444
445impl Serialize for StatePropExt {
446    #[inline]
447    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
448    where
449        S: Serializer,
450    {
451        serializer.serialize_str(self.as_str())
452    }
453}
454
455impl FromStr for StatePropExt {
456    type Err = Error;
457    #[inline]
458    fn from_str(s: &str) -> Result<Self, Self::Err> {
459        match s {
460            "status" | "S" => Ok(StatePropExt::Status),
461            "value" | "V" => Ok(StatePropExt::Value),
462            "act" | "A" => Ok(StatePropExt::Act),
463            v => Err(Error::invalid_params(format!("invalid prop: {}", v))),
464        }
465    }
466}
467
468/// Full item state, used by replication services for bulk topics
469#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct FullItemState {
471    #[serde(alias = "i")]
472    pub oid: OID,
473    #[serde(alias = "s")]
474    pub status: ItemStatus,
475    #[serde(alias = "v")]
476    pub value: Value,
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub act: Option<usize>,
479    pub ieid: IEID,
480    pub t: f64,
481}
482
483impl FullItemState {
484    pub fn from_local_state_event(state: LocalStateEvent, oid: OID) -> Self {
485        Self {
486            oid,
487            status: state.status,
488            value: state.value,
489            act: state.act,
490            ieid: state.ieid,
491            t: state.t,
492        }
493    }
494    pub fn from_remote_state_event(state: RemoteStateEvent, oid: OID) -> Self {
495        Self {
496            oid,
497            status: state.status,
498            value: state.value,
499            act: state.act,
500            ieid: state.ieid,
501            t: state.t,
502        }
503    }
504    pub fn into_replication_state_event(self, node_name: &str) -> ReplicationStateEvent {
505        ReplicationStateEvent {
506            status: self.status,
507            value: self.value,
508            act: self.act,
509            ieid: self.ieid,
510            t: self.t,
511            node: node_name.to_owned(),
512            force_accept: false,
513        }
514    }
515}
516
517impl From<FullItemState> for LocalStateEvent {
518    fn from(state: FullItemState) -> LocalStateEvent {
519        Self {
520            status: state.status,
521            value: state.value,
522            act: state.act,
523            ieid: state.ieid,
524            t: state.t,
525        }
526    }
527}
528
529/// Full item state with connected field
530#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct FullItemStateConnected {
532    #[serde(alias = "i")]
533    pub oid: OID,
534    #[serde(alias = "s")]
535    pub status: ItemStatus,
536    #[serde(alias = "v")]
537    pub value: Value,
538    #[serde(skip_serializing_if = "Option::is_none")]
539    pub act: Option<usize>,
540    pub ieid: IEID,
541    pub t: f64,
542    pub connected: bool,
543}
544
545impl FullItemStateConnected {
546    pub fn from_local_state_event(state: LocalStateEvent, oid: OID) -> Self {
547        Self {
548            oid,
549            status: state.status,
550            value: state.value,
551            act: state.act,
552            ieid: state.ieid,
553            t: state.t,
554            connected: true,
555        }
556    }
557    pub fn from_remote_state_event(state: RemoteStateEvent, oid: OID) -> Self {
558        Self {
559            oid,
560            status: state.status,
561            value: state.value,
562            act: state.act,
563            ieid: state.ieid,
564            t: state.t,
565            connected: state.connected,
566        }
567    }
568}
569
570/// Full item state for remote items, used by HMI services
571#[derive(Debug, Clone, Serialize, Deserialize)]
572#[serde(deny_unknown_fields)]
573pub struct FullRemoteItemState {
574    #[serde(alias = "i")]
575    pub oid: OID,
576    #[serde(alias = "s")]
577    pub status: ItemStatus,
578    #[serde(alias = "v")]
579    pub value: Value,
580    #[serde(skip_serializing_if = "Option::is_none")]
581    pub act: Option<usize>,
582    pub ieid: IEID,
583    pub t: f64,
584    pub node: String,
585    pub connected: bool,
586}
587
588impl FullRemoteItemState {
589    pub fn from_local_state_event(state: LocalStateEvent, oid: OID, system_name: &str) -> Self {
590        Self {
591            oid,
592            status: state.status,
593            value: state.value,
594            act: state.act,
595            ieid: state.ieid,
596            t: state.t,
597            node: system_name.to_owned(),
598            connected: true,
599        }
600    }
601    pub fn from_remote_state_event(state: RemoteStateEvent, oid: OID) -> Self {
602        Self {
603            oid,
604            status: state.status,
605            value: state.value,
606            act: state.act,
607            ieid: state.ieid,
608            t: state.t,
609            node: state.node,
610            connected: state.connected,
611        }
612    }
613}