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