rsiot/components/cmp_plc/plc/function_block_base.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
//! Функциональный блок
use std::time::Duration;
use serde::{Deserialize, Serialize};
/// Функциональный блок
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct FunctionBlockBase<I, Q, S>
where
I: Clone + Default + Serialize,
Q: Clone + Default + Serialize,
S: Clone + Default + Serialize,
Self: IFunctionBlock<I, Q, S>,
{
/// Входные данные
pub input: I,
/// Выходные данные
pub output: Q,
/// Статичные данные - сохраняются между вызовами
pub stat: S,
/// Системные данные функционального блока
fb_system_data: FbSystemData,
}
impl<I, Q, S> FunctionBlockBase<I, Q, S>
where
I: Clone + Default + Serialize,
Q: Clone + Default + Serialize,
S: Clone + Default + Serialize,
Self: IFunctionBlock<I, Q, S>,
{
/// Создание экземпляра функционального блока со значениями по-умолчанию
pub fn new(period: Duration) -> Self {
Self {
fb_system_data: FbSystemData {
first_call: true,
period,
},
..Default::default()
}
}
/// Создание экземпляра функционального блока с восстановленными значениями области stat
pub(crate) fn new_with_restore_stat(self, stat: S, period: Duration) -> Self {
Self {
stat,
fb_system_data: FbSystemData {
first_call: true,
period,
},
..Default::default()
}
}
/// Вызов функционального блока
pub fn call(&mut self, input: &mut I, period: Duration) -> Q {
self.fb_system_data.period = period;
self.output = FunctionBlockBase::logic(input, &mut self.stat, &self.fb_system_data);
self.input = input.clone();
self.fb_system_data.first_call = false;
self.output.clone()
}
/// Период вызова блока
pub fn get_period(&self) -> Duration {
self.fb_system_data.period
}
}
/// Трейт для логики выполнения блока
pub trait IFunctionBlock<I, Q, S> {
/// Основная логика выполнения блока
///
/// Нужно переопределить для своего функционального блока.
/// Вызывать самому не нужно, вызывается функцией `call`
///
/// TODO: рассмотреть возможность добавления аргумента fn_output, чтобы блок самостоятельно
/// мог генерировать исходящие сообщения
fn logic(input: &mut I, stat: &mut S, fb_system_data: &FbSystemData) -> Q;
}
/// Системные данные функционального блока
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct FbSystemData {
/// true - первый вызов функционального блока
pub first_call: bool,
/// Период вызова блока
pub period: Duration,
}