Skip to main content

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// ANCHOR: IFunctionBlock
114/// Трейт для логики выполнения блока
115pub trait IFunctionBlock<I, Q, S> {
116    /// Основная логика выполнения блока
117    ///
118    /// Нужно переопределить для своего функционального блока.
119    /// Вызывать самому не нужно, вызывается функцией `call`
120    fn logic(input: &mut I, stat: &mut S, fb_system_data: &FbSystemData) -> Q;
121}
122// ANCHOR: IFunctionBlock
123
124// ANCHOR: FbSystemData
125/// Системные данные функционального блока
126#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
127pub struct FbSystemData {
128    /// true - первый вызов функционального блока
129    pub first_call: bool,
130
131    /// Период вызова блока
132    pub period: Duration,
133}
134// ANCHOR: FbSystemData
135
136impl Default for FbSystemData {
137    fn default() -> Self {
138        Self {
139            first_call: true,
140            period: Duration::from_millis(100),
141        }
142    }
143}