use crate::Result;
use indexmap::{IndexMap, IndexSet};
pub mod filesystem;
mod loadable_device;
pub use loadable_device::LoadableDevice;
#[derive(Clone, Debug)]
pub struct Sampling {
pub bins: IndexMap<String, Bin>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "lowercase", tag = "type", content = "details")]
pub enum Bin {
Add,
Blocker,
CalibrationLookup(CalibrationLookupDetails),
CalmingDetection,
Cosinus,
Cumulate,
Derivation,
Difference,
Divide,
Fifo,
FirstValue,
FixedValues(FixedValuesDetails),
GreaterThan,
Invert,
LinearInterpolatedLookup(LinearInterpolatedLookupDetails),
LinearRegression,
Maximum,
MeanValue,
Minimum,
Multiplex(MultiplexDetails),
Multiply,
Not,
Pipeline(PipelineDetails),
SingleNotNull(SingleNotNullDetails),
Sinus,
Storage,
Subtract,
}
impl PipelineDetails {
pub fn to_arnalisa(
&self,
) -> Result<arnalisa::bins::pipeline::Description> {
let inputs = self
.pipes
.iter()
.filter_map(|pipe| {
if pipe.from.bin.is_empty() {
Some(pipe.from.source.to_string())
} else {
None
}
})
.collect::<IndexSet<_>>();
let bins = self
.bins
.iter()
.map(|(k, v)| {
use self::Bin as B;
use arnalisa::bins::SourceSinkDescription as D;
use arnalisa::bins::*;
use arnalisa::CalibrationSource;
let description = match v {
B::Add => D::Add(add::Description),
B::Blocker => D::Blocker(blocker::Description),
B::CalibrationLookup(details) => {
D::Calibration(calibration::Description {
details: CalibrationSource::Identifier {
id: details.calibration_id.to_string(),
},
reversed: details.reversed,
})
}
B::CalmingDetection => {
D::LastCalmPoint(last_calm_point::Description)
}
B::Cosinus => D::Cosinus(cosinus::Description),
B::Cumulate => D::Cumulation(cumulation::Description),
B::Derivation => {
D::Derivation(derivation::Description)
}
B::Difference => {
D::Difference(difference::Description)
}
B::Divide => D::Divide(divide::Description),
B::Fifo => D::Fifo(fifo::Description),
B::FirstValue => {
D::FirstValue(first_value::Description)
}
B::FixedValues(details) => {
D::FixedValues(fixedvalues::Description {
values: details.fixed_values.clone(),
})
}
B::GreaterThan => {
D::GreaterThan(greater_than::Description)
}
B::Invert => D::Invert(invert::Description),
B::LinearInterpolatedLookup(details) => {
D::Calibration(calibration::Description {
details: CalibrationSource::Embedded {
curve: details.points.clone(),
},
reversed: false,
})
}
B::LinearRegression => {
D::LinearRegression(linear_regression::Description)
}
B::Maximum => D::Maximum(maximum::Description),
B::MeanValue => D::Mean(mean::Description),
B::Minimum => D::Minimum(minimum::Description),
B::Multiplex(details) => {
D::Multiplex(multiplex::Description {
outputs: details.outputs.clone(),
})
}
B::Multiply => D::Multiply(multiply::Description),
B::Not => D::Not(not::Description),
B::Pipeline(details) => {
D::Pipeline(details.to_arnalisa()?)
}
B::SingleNotNull(details) => D::SingleNotNull({
single_not_null::Description {
inputs: details.inputs.clone(),
}
}),
B::Sinus => D::Sinus(sinus::Description),
B::Storage => D::Storage(storage::Description),
B::Subtract => D::Subtract(subtract::Description),
};
Ok((k.to_string(), description))
})
.collect::<Result<IndexMap<_, _>>>()?;
fn pipes_for_bin(
raw_pipes: &[Pipe],
bin: &str,
bins: &IndexMap<String, arnalisa::bins::SourceSinkDescription>,
) -> IndexMap<String, arnalisa::bins::SourceReference> {
let pipes = raw_pipes
.iter()
.filter_map(|pipe| {
if &pipe.to.bin == bin {
Some((
pipe.to.sink.to_string(),
arnalisa::bins::SourceReference {
bin: if pipe.from.bin.is_empty() {
None
} else {
Some(pipe.from.bin.to_string())
},
source: pipe.from.source.to_string(),
},
))
} else {
None
}
})
.collect::<IndexMap<_, _>>();
let pipes = fixup_pipe_sources(pipes, bins);
if let Some(bin) = bins.get(&bin.to_string()) {
fixup_pipe_sinks(pipes, &bin)
} else {
pipes
}
}
fn fixup_pipe_sources(
mut p: IndexMap<String, arnalisa::bins::SourceReference>,
bins: &IndexMap<String, arnalisa::bins::SourceSinkDescription>,
) -> IndexMap<String, arnalisa::bins::SourceReference> {
for pipe in p.values_mut() {
if let Some(ref bin) = pipe.bin {
if let Some(bin) = bins.get(bin) {
use arnalisa::bins::SourceSinkDescription::*;
match bin {
Add(_) => {
if pipe.source == "sum" {
pipe.source = "output".to_string();
}
}
Blocker(_) => {}
Calibration(_) => {}
Cosinus(_) => {}
Cumulation(_) => {}
Derivation(_) => {}
Difference(_) => {
if pipe.source == "difference" {
pipe.source = "output".to_string();
}
}
Divide(_) => {
if pipe.source == "quotient" {
pipe.source = "output".to_string();
}
}
Fifo(_) => {}
FirstValue(_) => {}
FixedValues(_) => {}
GreaterThan(_) => {}
Invert(_) => {}
LastCalmPoint(_) => {}
LinearRegression(_) => {}
Maximum(_) => {}
Mean(_) => {}
Minimum(_) => {}
Multiplex(_) => {}
Multiply(_) => {
if pipe.source == "product" {
pipe.source = "output".to_string();
}
}
Not(_) => {}
Pipeline(_) => {}
SingleNotNull(_) => {}
Sinus(_) => {}
Subtract(_) => {
if pipe.source == "difference" {
pipe.source = "output".to_string();
}
}
Storage(_) => {}
}
}
}
}
p
}
fn fixup_pipe_sinks(
mut p: IndexMap<String, arnalisa::bins::SourceReference>,
bin: &arnalisa::bins::SourceSinkDescription,
) -> IndexMap<String, arnalisa::bins::SourceReference> {
use arnalisa::bins::SourceSinkDescription::*;
match bin {
Add(_) => {
if let Some(a) = p.remove(&"summand1".to_string()) {
p.insert("a".to_string(), a);
}
if let Some(b) = p.remove(&"summand2".to_string()) {
p.insert("b".to_string(), b);
}
p
}
Blocker(_) => p,
Calibration(_) => p,
Cosinus(_) => p,
Cumulation(_) => p,
Derivation(_) => p,
Difference(_) => p,
Divide(_) => {
if let Some(a) = p.remove(&"dividend".to_string()) {
p.insert("a".to_string(), a);
}
if let Some(b) = p.remove(&"divisor".to_string()) {
p.insert("b".to_string(), b);
}
p
}
Fifo(_) => p,
FirstValue(_) => p,
FixedValues(_) => {
if let Some(trigger) =
p.remove(&"finishDetectionInput".to_string())
{
p.insert("trigger".to_string(), trigger);
}
p
}
GreaterThan(_) => p,
Invert(_) => p,
LastCalmPoint(_) => p,
LinearRegression(_) => {
if let Some(trigger) =
p.remove(&"numItems".to_string())
{
p.insert("num_items".to_string(), trigger);
}
p
}
Maximum(_) => p,
Mean(_) => p,
Minimum(_) => p,
Multiplex(_) => {
if let Some(input) = p.remove(&"value".to_string()) {
p.insert("input".to_string(), input);
}
p
}
Multiply(_) => {
if let Some(a) = p.remove(&"factor1".to_string()) {
p.insert("a".to_string(), a);
}
if let Some(b) = p.remove(&"factor2".to_string()) {
p.insert("b".to_string(), b);
}
p
}
Not(_) => p,
Pipeline(_) => p,
SingleNotNull(_) => p,
Sinus(_) => p,
Subtract(_) => {
if let Some(a) = p.remove(&"minuend".to_string()) {
p.insert("a".to_string(), a);
}
if let Some(b) = p.remove(&"subtrahend".to_string()) {
p.insert("b".to_string(), b);
}
p
}
Storage(_) => p,
}
}
let pipes = std::iter::once("".to_string())
.chain(bins.keys().cloned())
.filter_map(|k| {
let pipes = pipes_for_bin(&self.pipes, &k, &bins);
if pipes.is_empty() {
None
} else {
Some((k, pipes))
}
})
.collect::<IndexMap<_, _>>();
Ok(arnalisa::bins::pipeline::Description {
pipes,
bins,
inputs,
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PipelineDetails {
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub bins: IndexMap<String, Bin>,
pub pipes: Vec<Pipe>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CalibrationLookupDetails {
pub calibration_id: String,
pub reversed: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct FixedValuesDetails {
pub fixed_values: IndexMap<String, arnalisa::Item>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LinearInterpolatedLookupDetails {
pub points: Vec<(f64, f64)>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct MultiplexDetails {
pub outputs: IndexMap<String, usize>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Pipe {
pub from: PipeSource,
pub to: PipeSink,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PipeSource {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub bin: String,
pub source: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PipeSink {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub bin: String,
pub sink: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SingleNotNullDetails {
pub inputs: IndexSet<String>,
}