eva_common/
transform.rs

1#![allow(
2    clippy::cast_sign_loss,
3    clippy::cast_possible_truncation,
4    clippy::cast_lossless,
5    clippy::float_cmp,
6    clippy::cast_precision_loss
7)]
8
9use parking_lot::Mutex;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::ops::{Add, Sub};
13use std::sync::LazyLock;
14use std::time::{Duration, Instant};
15
16use crate::value::{Value, to_value};
17use crate::{EResult, Error, OID};
18
19#[derive(Deserialize, Debug)]
20#[serde(deny_unknown_fields)]
21pub struct Task {
22    func: Function,
23    #[serde(default)]
24    params: Vec<f64>,
25}
26
27pub fn transform<T>(tasks: &[Task], oid: &OID, value: T) -> EResult<f64>
28where
29    T: Transform,
30{
31    let mut n = value.to_num()?;
32    for task in tasks {
33        match task.func {
34            Function::Multiply => {
35                n = n.multiply(*task.params.first().unwrap_or(&1.0))?;
36            }
37            Function::Divide => {
38                n = n.divide(*task.params.first().unwrap_or(&1.0))?;
39            }
40            Function::Round => {
41                n = n.round_to(*task.params.first().unwrap_or(&0.0))?;
42            }
43            Function::CalcSpeed => {
44                n = n
45                    .calc_speed(oid, *task.params.first().unwrap_or(&0.0))?
46                    .unwrap_or_default();
47            }
48            Function::Invert => {
49                n = n.invert()?;
50            }
51        }
52    }
53    Ok(n)
54}
55
56#[derive(Debug)]
57struct ValSpeedInfo {
58    value: Value,
59    t: Instant,
60}
61
62static SPEED_INFO: LazyLock<Mutex<HashMap<OID, ValSpeedInfo>>> = LazyLock::new(<_>::default);
63
64fn calculate_growth_speed<T>(oid: &OID, value: T, maxval: T, interval: f64) -> EResult<Option<f64>>
65where
66    T: Serialize + TryFrom<Value> + Sub<Output = T> + Add<Output = T> + PartialOrd + Copy,
67{
68    let mut speed_info = SPEED_INFO.lock();
69    if let Some(v) = speed_info.get_mut(oid) {
70        let t_delta: Duration = v.t.elapsed();
71        if t_delta < Duration::from_secs_f64(interval) {
72            Ok(None)
73        } else {
74            let prev_val: T = v
75                .value
76                .clone()
77                .try_into()
78                .map_err(|_| Error::invalid_data(format!("value error for {}", oid)))?;
79            let v_delta: f64 = if value >= prev_val {
80                to_value(value - prev_val)?.try_into()?
81            } else {
82                to_value(maxval - prev_val + value)?.try_into()?
83            };
84            v.value = to_value(value)?;
85            v.t = Instant::now();
86            Ok(Some(v_delta / (t_delta.as_secs_f64())))
87        }
88    } else {
89        speed_info.insert(
90            oid.clone(),
91            ValSpeedInfo {
92                value: to_value(value)?,
93                t: Instant::now(),
94            },
95        );
96        Ok(Some(0.0))
97    }
98}
99
100pub trait Transform {
101    fn multiply(&self, multiplier: f64) -> EResult<f64>;
102    fn divide(&self, divisor: f64) -> EResult<f64>;
103    fn round_to(&self, digits: f64) -> EResult<f64>;
104    fn to_num(&self) -> EResult<f64>;
105    fn to_bool(&self) -> EResult<bool>;
106    fn invert(&self) -> EResult<f64>;
107    fn calc_speed(&self, oid: &OID, interval: f64) -> EResult<Option<f64>>;
108}
109
110// TODO check ranges and return correct errors
111macro_rules! impl_Transform_N {
112    ($t:ty, $max:path) => {
113        impl Transform for $t {
114            #[inline]
115            fn multiply(&self, multiplier: f64) -> EResult<f64> {
116                Ok(*self as f64 * multiplier)
117            }
118            #[inline]
119            fn divide(&self, divisor: f64) -> EResult<f64> {
120                Ok(*self as f64 / divisor)
121            }
122            #[inline]
123            fn round_to(&self, digits: f64) -> EResult<f64> {
124                round_to(*self as f64, digits)
125            }
126            #[inline]
127            fn to_num(&self) -> EResult<f64> {
128                Ok(*self as f64)
129            }
130            #[inline]
131            fn to_bool(&self) -> EResult<bool> {
132                Ok(*self != 0. as $t)
133            }
134            #[inline]
135            fn calc_speed(&self, oid: &OID, interval: f64) -> EResult<Option<f64>> {
136                calculate_growth_speed(oid, *self, $max, interval)
137            }
138            #[inline]
139            fn invert(&self) -> EResult<f64> {
140                Ok(if *self == 0. as $t { 1.0 } else { 0.0 })
141            }
142        }
143    };
144}
145
146fn round_to(value: f64, digits: f64) -> EResult<f64> {
147    match digits {
148        d if d < 0.0 => Err(Error::invalid_params(
149            "round digits can not be less than zero",
150        )),
151        0.0 => Ok(value.round()),
152        d if d < 20.0 => {
153            let m: f64 = (10_u64).pow(digits as u32) as f64;
154            Ok(value.round() + (value.fract() * m).round() / m)
155        }
156        _ => Err(Error::invalid_params(format!(
157            "max round: 19 digits ({})",
158            digits
159        ))),
160    }
161}
162
163impl Transform for String {
164    #[inline]
165    fn multiply(&self, multiplier: f64) -> EResult<f64> {
166        Ok(self.parse::<f64>()? * multiplier)
167    }
168    #[inline]
169    fn divide(&self, divisor: f64) -> EResult<f64> {
170        Ok(self.parse::<f64>()? / divisor)
171    }
172    #[inline]
173    fn round_to(&self, digits: f64) -> EResult<f64> {
174        round_to(self.parse::<f64>()?, digits)
175    }
176    #[inline]
177    fn to_num(&self) -> EResult<f64> {
178        self.parse().map_err(Into::into)
179    }
180    #[inline]
181    fn to_bool(&self) -> EResult<bool> {
182        Ok(self.parse::<f64>()? != 0.0)
183    }
184    #[inline]
185    fn calc_speed(&self, _oid: &OID, _interval: f64) -> EResult<Option<f64>> {
186        Err(Error::not_implemented(
187            "unable to calculate speed for string",
188        ))
189    }
190    #[inline]
191    fn invert(&self) -> EResult<f64> {
192        let f = self.parse::<f64>()?;
193        Ok(if f == 0.0 { 1.0 } else { 0.0 })
194    }
195}
196
197impl Transform for bool {
198    fn multiply(&self, _multiplier: f64) -> EResult<f64> {
199        Ok(0.0)
200    }
201    fn divide(&self, _divisor: f64) -> EResult<f64> {
202        Ok(0.0)
203    }
204    fn round_to(&self, _digits: f64) -> EResult<f64> {
205        Ok(0.0)
206    }
207    fn to_num(&self) -> EResult<f64> {
208        Ok(if *self { 1.0 } else { 0.0 })
209    }
210    fn to_bool(&self) -> EResult<bool> {
211        Ok(*self)
212    }
213    fn invert(&self) -> EResult<f64> {
214        Ok(if *self { 0.0 } else { 1.1 })
215    }
216    fn calc_speed(&self, _oid: &OID, _interval: f64) -> EResult<Option<f64>> {
217        Err(Error::not_implemented(
218            "unable to calculate speed for boolean",
219        ))
220    }
221}
222
223impl_Transform_N!(i8, std::i8::MAX);
224impl_Transform_N!(u8, std::u8::MAX);
225impl_Transform_N!(i16, std::i16::MAX);
226impl_Transform_N!(u16, std::u16::MAX);
227impl_Transform_N!(i32, std::i32::MAX);
228impl_Transform_N!(u32, std::u32::MAX);
229impl_Transform_N!(i64, std::i64::MAX);
230impl_Transform_N!(u64, std::u64::MAX);
231impl_Transform_N!(f32, std::f32::MAX);
232impl_Transform_N!(f64, std::f64::MAX);
233
234#[derive(PartialEq, Eq, Clone, Copy, Debug, Deserialize)]
235pub enum Function {
236    #[serde(rename = "multiply")]
237    Multiply,
238    #[serde(rename = "divide")]
239    Divide,
240    #[serde(rename = "round")]
241    Round,
242    #[serde(rename = "calc_speed")]
243    CalcSpeed,
244    #[serde(rename = "invert")]
245    Invert,
246}