rsiot/components/cmp_plc/plc/
function_block_base.rs

1//! Функциональный блок
2
3use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7#[cfg(not(target_arch = "wasm32"))]
8use std::time::SystemTime;
9#[cfg(target_arch = "wasm32")]
10use web_time::SystemTime;
11
12/// Функциональный блок
13#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
14pub struct FunctionBlockBase<I, Q, S>
15where
16    I: Clone + Default + Serialize,
17    Q: Clone + Default + Serialize,
18    S: Clone + Default + Serialize,
19    Self: IFunctionBlock<I, Q, S>,
20{
21    /// Входные данные
22    pub i: I,
23    /// Выходные данные
24    pub q: Q,
25    /// Статичные данные - сохраняются между вызовами
26    pub s: S,
27
28    calc_period_errors: usize,
29
30    /// Время последнего вызова.
31    ///
32    /// TODO - если нет ошибок компиляции в разных таргетах, сделать на основе этого поля
33    /// определение периодичности вызовов и убрать ручное задание period
34    pub last_call_time: SystemTime,
35
36    /// true - первый вызов функционального блока
37    pub first_call: bool,
38}
39
40impl<I, Q, S> Default for FunctionBlockBase<I, Q, S>
41where
42    I: Clone + Default + Serialize,
43    Q: Clone + Default + Serialize,
44    S: Clone + Default + Serialize,
45    Self: IFunctionBlock<I, Q, S>,
46{
47    fn default() -> Self {
48        Self {
49            i: I::default(),
50            q: Q::default(),
51            s: S::default(),
52            calc_period_errors: 0,
53            last_call_time: SystemTime::now(),
54            first_call: true,
55        }
56    }
57}
58
59impl<I, Q, S> FunctionBlockBase<I, Q, S>
60where
61    I: Clone + Default + Serialize,
62    Q: Clone + Default + Serialize,
63    S: Clone + Default + Serialize,
64    Self: IFunctionBlock<I, Q, S>,
65{
66    /// Создание экземпляра функционального блока со значениями по-умолчанию
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    /// Создание экземпляра функционального блока с восстановленными значениями области stat
72    pub(crate) fn new_with_restore_stat(self, stat: S) -> Self {
73        Self {
74            s: stat,
75            ..Default::default()
76        }
77    }
78
79    /// Вызов функционального блока
80    pub fn call(&mut self, input: &mut I) -> Q {
81        let now = SystemTime::now();
82
83        let period = now.duration_since(self.last_call_time);
84        self.last_call_time = now;
85
86        let period = match period {
87            Ok(v) => {
88                if v >= Duration::from_millis(5000) {
89                    Duration::from_millis(5000)
90                } else {
91                    v
92                }
93            }
94            Err(_) => {
95                self.calc_period_errors += 1;
96                Duration::from_millis(0)
97            }
98        };
99
100        let fb_system_data = FbSystemData {
101            first_call: self.first_call,
102            period,
103        };
104        // TODO - замерять фактический период вызова функционального блока, а не передавать
105        // константу
106        self.q = FunctionBlockBase::logic(input, &mut self.s, &fb_system_data);
107        self.i = input.clone();
108        self.first_call = false;
109        self.q.clone()
110    }
111}
112
113/// Трейт для логики выполнения блока
114pub trait IFunctionBlock<I, Q, S> {
115    /// Основная логика выполнения блока
116    ///
117    /// Нужно переопределить для своего функционального блока.
118    /// Вызывать самому не нужно, вызывается функцией `call`
119    fn logic(input: &mut I, stat: &mut S, fb_system_data: &FbSystemData) -> Q;
120}
121
122/// Системные данные функционального блока
123#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
124pub struct FbSystemData {
125    /// true - первый вызов функционального блока
126    pub first_call: bool,
127
128    /// Период вызова блока
129    pub period: Duration,
130}
131
132impl Default for FbSystemData {
133    fn default() -> Self {
134        Self {
135            first_call: true,
136            period: Duration::from_millis(100),
137        }
138    }
139}