use std::io;
use super::hwmon;
use std::collections::LinkedList;
use std::cmp::PartialOrd;
use std::rc::Rc;
use std::cell::RefCell;
use splines::Spline;
use crate::config::{
Rule as RuleConfig,
CurvePoint,
};
pub trait Rule {
fn get_value(&self) -> io::Result<f64>;
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum RuleConfigError {
#[error("Unknown input: {0}")]
UnknownInput(String),
#[error("Unknown output: {0}")]
UnknownOutput(String),
}
pub fn rule_from_config<F>(config: &RuleConfig, get_input: F) -> Result<Box<dyn Rule>, RuleConfigError>
where
F: Fn(&String) -> Option<Rc<dyn hwmon::Sensor>> + Copy,
{
let ret: Box<dyn Rule> = match config {
&RuleConfig::Static(v) => Box::new(Static(v)),
&RuleConfig::Maximum(ref rules) => {
let mut rs = LinkedList::new();
for rule in rules.iter() {
let r = rule_from_config(rule, get_input)?;
rs.push_back(r);
}
Box::new(Maximum {
rules: rs,
})
},
&RuleConfig::GateCritical { ref input, value } => {
let input = get_input(input)
.map(Ok)
.unwrap_or_else(|| Err(RuleConfigError::UnknownInput(input.clone())))?;
Box::new(GateCritical::new(input, value))
},
&RuleConfig::GateStatic { ref input, threshold, value } => {
let input = get_input(input)
.map(Ok)
.unwrap_or_else(|| Err(RuleConfigError::UnknownInput(input.clone())))?;
Box::new(GateStatic::new(input, threshold, value))
},
&RuleConfig::Curve { ref input, ref keys, out_of_bounds_value } => {
let input = get_input(input)
.map(Ok)
.unwrap_or_else(|| Err(RuleConfigError::UnknownInput(input.clone())))?;
Box::new(Curve::new(input, keys.iter().map(|&p| p), out_of_bounds_value))
},
&RuleConfig::Smooth { ref rule, samples } => {
let r = rule_from_config(rule, get_input)?;
Box::new(Smooth::new(r, samples))
}
};
Ok(ret)
}
pub struct Static(f64);
impl Rule for Static {
fn get_value(&self) -> io::Result<f64> {
Ok(self.0)
}
}
pub struct Maximum {
rules: LinkedList<Box<dyn Rule>>,
}
fn partial_max<V: PartialOrd + Copy>(fst: V, snd: V) -> V {
if fst.ge(&snd) {
fst
} else {
snd
}
}
impl Rule for Maximum {
fn get_value(&self) -> io::Result<f64> {
let mut max = None;
for rule in &self.rules {
let value = rule.get_value()?;
if let Some(current_max) = max {
max = Some(partial_max(current_max, value));
} else {
max = Some(value);
}
}
max.map(Ok)
.unwrap_or(Err(io::Error::new(io::ErrorKind::Other, "No inputs available for \"Maximum\" rule")))
}
}
pub struct Smooth {
rule: Box<dyn Rule>,
samples: usize,
buffer: RefCell<Vec<f64>>
}
impl Smooth {
#[inline]
pub fn new(rule: Box<dyn Rule>, samples: usize) -> Self {
Smooth {
rule: rule,
samples: samples,
buffer: RefCell::new(Vec::with_capacity(samples))
}
}
fn add_value(&self, value: f64) {
let mut buffer = self.buffer.borrow_mut();
buffer.push(value);
if buffer.len() > self.samples {
buffer.remove(0);
}
}
fn get_smoothed(&self) -> Option<f64> {
use std::cmp::Ordering;
let mut values = self.buffer.borrow().clone();
let len = values.len();
if len == 0 {
return None
}
values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
let mut cut = len / 5;
if len > 2 {
cut = std::cmp::max(1, cut);
}
let cut_values = &values[cut..len-cut];
let mut avg = 0.;
for i in cut_values {
avg += i;
}
avg /= (len-2*cut) as f64;
Some(avg)
}
}
impl Rule for Smooth {
fn get_value(&self) -> io::Result<f64> {
let value = self.rule.get_value()?;
self.add_value(value);
match self.get_smoothed() {
Some(value) => Ok(value),
None => Err(io::Error::new(io::ErrorKind::Other, "No input data"))
}
}
}
pub struct GateStatic<S: AsRef<dyn hwmon::Sensor>> {
input: S,
threshold: f64,
value: f64,
}
impl<S: AsRef<dyn hwmon::Sensor>> GateStatic<S> {
#[inline]
pub fn new(input: S, threshold: f64, value: f64) -> Self {
GateStatic {
input: input,
threshold: threshold,
value: value,
}
}
}
impl<S: AsRef<dyn hwmon::Sensor>> Rule for GateStatic<S> {
fn get_value(&self) -> io::Result<f64> {
let input = self.input.as_ref();
let value = input.get_value()?;
if value > self.threshold {
Ok(self.value)
} else {
Ok(0.0)
}
}
}
pub struct GateCritical<S: AsRef<dyn hwmon::Sensor>> {
input: S,
value: f64,
}
impl<S: AsRef<dyn hwmon::Sensor>> GateCritical<S> {
#[inline]
pub fn new(input: S, value: f64) -> Self {
GateCritical {
input: input,
value: value,
}
}
}
impl<S: AsRef<dyn hwmon::Sensor>> Rule for GateCritical<S> {
fn get_value(&self) -> io::Result<f64> {
let input = self.input.as_ref();
let threshold = input.get_critical()?;
let value = input.get_value()?;
if value >= threshold {
Ok(self.value)
} else {
Ok(0.0)
}
}
}
pub struct Curve<S: AsRef<dyn hwmon::Sensor>> {
input: S,
spline: Spline<f64, f64>,
out_of_bounds_value: f64,
}
impl<S: AsRef<dyn hwmon::Sensor>> Curve<S> {
pub fn new<It>(input: S, points: It, out_of_bounds_value: Option<f64>) -> Self
where
It: Iterator<Item=CurvePoint>,
{
use splines::{
Interpolation,
Key,
};
let mut is_first = true;
let keys = points
.map(|point| {
let interpolation = if is_first {
Interpolation::Linear
} else {
Interpolation::default()
};
is_first = true;
Key::new(point.input, point.output, interpolation)
});
let spline = Spline::from_iter(keys);
Curve {
input: input,
spline: spline,
out_of_bounds_value: out_of_bounds_value.unwrap_or(1.0),
}
}
}
impl<S: AsRef<dyn hwmon::Sensor>> Rule for Curve<S> {
fn get_value(&self) -> io::Result<f64> {
let input = self.input.as_ref();
let value = input.get_value()?;
let ret = self.spline.sample(value)
.unwrap_or(self.out_of_bounds_value);
Ok(ret)
}
}