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
112macro_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}