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