use std::any::type_name;
use std::fmt::Debug;
use std::iter::Iterator;
use std::{collections::BTreeMap, ops::Range};
use serde::{Deserialize, Serialize};
mod orchestrator;
pub(crate) use orchestrator::CalcOrchestrator;
mod affine;
mod butter;
mod constant;
mod inverse_affine;
mod pid;
mod polynomial;
mod rtd_pt100;
mod sin;
mod tc_ktype;
pub mod sequence_machine;
pub use affine::Affine;
pub use butter::Butter2;
pub use constant::Constant;
pub use inverse_affine::InverseAffine;
pub use pid::Pid;
pub use polynomial::Polynomial;
pub use rtd_pt100::RtdPt100;
pub use sequence_machine::SequenceMachine;
pub use sin::Sin;
pub use tc_ktype::TcKtype;
use crate::ControllerCtx;
pub type PeripheralName = String;
pub type PeripheralInputName = String;
pub type FieldName = String;
pub type CalcName = String;
pub type CalcInputName = String;
pub type CalcOutputName = String;
pub type CalcConfigName = String;
pub type SrcIndex = usize;
pub type DstIndex = usize;
impl Clone for Box<dyn Calc> {
fn clone(&self) -> Box<dyn Calc> {
let new: Box<dyn Calc> =
serde_json::from_str(&serde_json::to_string(&self).unwrap()).unwrap();
new
}
}
#[typetag::serde(tag = "type")]
pub trait Calc: Send + Sync + Debug {
fn init(
&mut self,
ctx: ControllerCtx,
input_indices: Vec<usize>,
output_range: Range<usize>,
) -> Result<(), String>;
fn terminate(&mut self) -> Result<(), String>;
fn eval(&mut self, tape: &mut [f64]) -> Result<(), String>;
fn get_input_map(&self) -> BTreeMap<CalcInputName, FieldName>;
fn update_input_map(&mut self, field: &str, source: &str) -> Result<(), String>;
fn get_save_outputs(&self) -> bool;
fn set_save_outputs(&mut self, save_outputs: bool);
fn get_config(&self) -> BTreeMap<String, f64>;
fn set_config(&mut self, cfg: &BTreeMap<String, f64>) -> Result<(), String>;
fn get_input_names(&self) -> Vec<CalcInputName>;
fn get_output_names(&self) -> Vec<CalcOutputName>;
fn get_output_units(&self) -> Vec<Option<String>> {
vec![None; self.get_output_names().len()]
}
fn kind(&self) -> String {
type_name::<Self>().split(":").last().unwrap().into()
}
}
#[macro_export]
macro_rules! calc_config {
($( $field:ident ),*) => {
fn get_save_outputs(&self) -> bool {
self.save_outputs
}
fn set_save_outputs(&mut self, save_outputs: bool) {
self.save_outputs = save_outputs;
}
fn get_config(&self) -> BTreeMap<String, f64> {
#[allow(unused_mut)]
let mut cfg = BTreeMap::<String, f64>::new();
$({cfg.insert(stringify!($field).to_owned(), self.$field);})*
cfg
}
#[allow(unused)]
fn set_config(&mut self, cfg: &BTreeMap<String, f64>) -> Result<(), String> {
$({
let f = stringify!($field);
self.$field = *cfg.get(f).ok_or(format!("Config missing key `{f}`"))?;
})*
Ok(())
}
}
}
#[macro_export]
macro_rules! calc_input_names {
($( $field:ident ),*) => {
fn get_input_names(&self) -> Vec<CalcInputName> {
#[allow(unused_mut)]
let mut names = vec![];
$({
names.push(stringify!($field).to_owned());
})*
names
}
}
}
#[macro_export]
macro_rules! calc_output_names {
($( $field:ident ),*) => {
fn get_output_names(&self) -> Vec<CalcOutputName> {
let mut names = vec![];
$({
names.push(stringify!($field).to_owned());
})*
names
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_units_len_matches_names_len(calc: &dyn Calc) {
assert_eq!(
calc.get_output_units().len(),
calc.get_output_names().len(),
"get_output_units().len() != get_output_names().len() for {}",
calc.kind()
);
}
#[test]
fn affine_units_len_matches_names_len() {
let calc = Affine::new("x".to_owned(), 1.0, 0.0, false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn inverse_affine_units_len_matches_names_len() {
let calc = InverseAffine::new("x".to_owned(), 1.0, 0.0, false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn polynomial_units_len_matches_names_len() {
let calc = Polynomial::new("x".to_owned(), vec![1.0, 2.0], String::new(), false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn constant_units_len_matches_names_len() {
let calc = Constant::new(0.0, false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn sin_units_len_matches_names_len() {
let calc = Sin::new(1.0, 0.0, -1.0, 1.0, false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn butter2_units_len_matches_names_len() {
let calc = Butter2::new("x".to_owned(), 10.0, false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn pid_units_len_matches_names_len() {
let calc = Pid::new(
"measurement".to_owned(),
"setpoint".to_owned(),
1.0,
0.0,
0.0,
100.0,
false,
);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn rtd_pt100_units_len_matches_names_len() {
let calc = RtdPt100::new("resistance_ohm".to_owned(), false);
assert_units_len_matches_names_len(&*calc);
}
#[test]
fn tc_ktype_units_len_matches_names_len() {
let calc = TcKtype::new("voltage_V".to_owned(), "cold_junction_K".to_owned(), false);
assert_units_len_matches_names_len(&*calc);
}
}