1use crate::Result;
2use indexmap::{IndexMap, IndexSet};
3
4#[derive(Serialize, Deserialize, Debug, Clone)]
5#[serde(rename_all = "lowercase", tag = "type", content = "details")]
6pub enum Bin {
7 Add,
8 Blocker,
9 CalibrationLookup(CalibrationLookupDetails),
10 CalmingDetection,
11 Cosinus,
12 Cumulate,
13 Derivation,
14 Difference,
15 Divide,
16 Fifo,
17 FirstValue,
18 FixedValues(FixedValuesDetails),
19 GreaterThan,
20 Invert,
21 LinearInterpolatedLookup(LinearInterpolatedLookupDetails),
22 LinearRegression,
23 Maximum,
24 MeanValue,
25 Minimum,
26 Multiplex(MultiplexDetails),
27 Multiply,
28 Not,
29 Pipeline(PipelineDetails),
30 SingleNotNull(SingleNotNullDetails),
31 Sinus,
32 Storage,
33 Subtract,
34}
35
36impl PipelineDetails {
37 pub fn to_arnalisa(
38 &self,
39 ) -> Result<arnalisa::bins::pipeline::Description> {
40 let inputs = self
41 .pipes
42 .iter()
43 .filter_map(|pipe| {
44 if pipe.from.bin.is_empty() {
45 Some(pipe.from.source.to_string())
46 } else {
47 None
48 }
49 })
50 .collect::<IndexSet<_>>();
51
52 let bins = self
53 .bins
54 .iter()
55 .map(|(k, v)| {
56 use self::Bin as B;
57 use arnalisa::bins::SourceSinkDescription as D;
58 use arnalisa::bins::*;
59 use arnalisa::CalibrationSource;
60 let description = match v {
61 B::Add => D::Add(add::Description),
62 B::Blocker => D::Blocker(blocker::Description),
63 B::CalibrationLookup(details) => {
64 D::Calibration(calibration::Description {
65 details: CalibrationSource::Identifier {
66 id: details.calibration_id.to_string(),
67 },
68 reversed: details.reversed,
69 })
70 }
71 B::CalmingDetection => {
72 D::LastCalmPoint(last_calm_point::Description)
73 }
74 B::Cosinus => D::Cosinus(cosinus::Description),
75 B::Cumulate => D::Cumulation(cumulation::Description),
76 B::Derivation => {
77 D::Derivation(derivation::Description)
78 }
79 B::Difference => {
80 D::Derivation(derivation::Description)
81 }
82 B::Divide => D::Divide(divide::Description),
83 B::Fifo => D::Fifo(fifo::Description),
84 B::FirstValue => {
85 D::FirstValue(first_value::Description)
86 }
87 B::FixedValues(details) => {
88 D::FixedValues(fixedvalues::Description {
89 values: details.fixed_values.clone(),
90 })
91 }
92 B::GreaterThan => {
93 D::GreaterThan(greater_than::Description)
94 }
95 B::Invert => D::Invert(invert::Description),
96 B::LinearInterpolatedLookup(details) => {
97 D::Calibration(calibration::Description {
98 details: CalibrationSource::Embedded {
99 curve: details.points.clone(),
100 },
101 reversed: false,
102 })
103 }
104 B::LinearRegression => {
105 D::LinearRegression(linear_regression::Description)
106 }
107 B::Maximum => D::Maximum(maximum::Description),
108 B::MeanValue => D::Mean(mean::Description),
109 B::Minimum => D::Minimum(minimum::Description),
110 B::Multiplex(details) => {
111 D::Multiplex(multiplex::Description {
112 outputs: details.outputs.clone(),
113 })
114 }
115 B::Multiply => D::Multiply(multiply::Description),
116 B::Not => D::Not(not::Description),
117 B::Pipeline(details) => {
118 D::Pipeline(details.to_arnalisa()?)
119 }
120 B::SingleNotNull(details) => D::SingleNotNull({
121 single_not_null::Description {
122 inputs: details.inputs.clone(),
123 }
124 }),
125 B::Sinus => D::Sinus(sinus::Description),
126 B::Storage => D::Storage(storage::Description),
127 B::Subtract => D::Subtract(subtract::Description),
128 };
129 Ok((k.to_string(), description))
130 })
131 .collect::<Result<IndexMap<_, _>>>()?;
132
133 fn pipes_for_bin(
134 raw_pipes: &[Pipe],
135 bin: &str,
136 bins: &IndexMap<String, arnalisa::bins::SourceSinkDescription>,
137 ) -> IndexMap<String, arnalisa::bins::SourceReference> {
138 let pipes = raw_pipes
139 .iter()
140 .filter_map(|pipe| {
141 if pipe.to.bin == bin {
142 Some((
143 pipe.to.sink.to_string(),
144 arnalisa::bins::SourceReference {
145 bin: if pipe.from.bin.is_empty() {
146 None
147 } else {
148 Some(pipe.from.bin.to_string())
149 },
150 source: pipe.from.source.to_string(),
151 },
152 ))
153 } else {
154 None
155 }
156 })
157 .collect::<IndexMap<_, _>>();
158 let pipes = fixup_pipe_sources(pipes, bins);
159 if let Some(bin) = bins.get(&bin.to_string()) {
160 fixup_pipe_sinks(pipes, &bin)
161 } else {
162 pipes
163 }
164 }
165
166 fn fixup_pipe_sources(
167 mut p: IndexMap<String, arnalisa::bins::SourceReference>,
168 bins: &IndexMap<String, arnalisa::bins::SourceSinkDescription>,
169 ) -> IndexMap<String, arnalisa::bins::SourceReference> {
170 for pipe in p.values_mut() {
171 if let Some(ref bin) = pipe.bin {
172 if let Some(bin) = bins.get(bin) {
173 use arnalisa::bins::SourceSinkDescription::*;
174 match bin {
175 Add(_) => {
176 if pipe.source == "sum" {
177 pipe.source = "output".to_string();
178 }
179 }
180 Blocker(_) => {}
181 Calibration(_) => {}
182 Cosinus(_) => {}
183 Cumulation(_) => {}
184 Derivation(_) => {
185 if pipe.source == "difference" {
186 pipe.source = "output".to_string();
187 }
188 }
189 Divide(_) => {
190 if pipe.source == "quotient" {
191 pipe.source = "output".to_string();
192 }
193 }
194 Fifo(_) => {}
195 FirstValue(_) => {}
196 FixedValues(_) => {}
197 GreaterThan(_) => {}
198 Invert(_) => {}
199 LastCalmPoint(_) => {}
200 LinearRegression(_) => {}
201 Maximum(_) => {}
202 Mean(_) => {}
203 Minimum(_) => {}
204 Multiplex(_) => {}
205 Multiply(_) => {
206 if pipe.source == "product" {
207 pipe.source = "output".to_string();
208 }
209 }
210 Not(_) => {}
211 Pipeline(_) => {}
212 SingleNotNull(_) => {}
213 Sinus(_) => {}
214 Subtract(_) => {
215 if pipe.source == "difference" {
216 pipe.source = "output".to_string();
217 }
218 }
219 Storage(_) => {}
220 }
221 }
222 }
223 }
224
225 p
226 }
227
228 fn fixup_pipe_sinks(
229 mut p: IndexMap<String, arnalisa::bins::SourceReference>,
230 bin: &arnalisa::bins::SourceSinkDescription,
231 ) -> IndexMap<String, arnalisa::bins::SourceReference> {
232 use arnalisa::bins::SourceSinkDescription::*;
233 match bin {
234 Add(_) => {
235 if let Some(a) = p.swap_remove(&"summand1".to_string())
236 {
237 p.insert("a".to_string(), a);
238 }
239 if let Some(b) = p.swap_remove(&"summand2".to_string())
240 {
241 p.insert("b".to_string(), b);
242 }
243 p.sort_keys();
244 p
245 }
246 Blocker(_) => p,
247 Calibration(_) => p,
248 Cosinus(_) => p,
249 Cumulation(_) => p,
250 Derivation(_) => p,
251 Divide(_) => {
252 if let Some(a) = p.swap_remove(&"dividend".to_string())
253 {
254 p.insert("a".to_string(), a);
255 }
256 if let Some(b) = p.swap_remove(&"divisor".to_string())
257 {
258 p.insert("b".to_string(), b);
259 }
260 p.sort_keys();
261 p
262 }
263 Fifo(_) => p,
264 FirstValue(_) => p,
265 FixedValues(_) => {
266 if let Some(trigger) =
267 p.swap_remove(&"finishDetectionInput".to_string())
268 {
269 p.insert("trigger".to_string(), trigger);
270 }
271 p.sort_keys();
272 p
273 }
274 GreaterThan(_) => p,
275 Invert(_) => p,
276 LastCalmPoint(_) => {
277 if let Some(y_max_delta) =
278 p.swap_remove(&"yMaxDelta".to_string())
279 {
280 p.insert("y_max_delta".to_string(), y_max_delta);
281 }
282 p
283 }
284 LinearRegression(_) => {
285 if let Some(trigger) =
286 p.swap_remove(&"numItems".to_string())
287 {
288 p.insert("num_items".to_string(), trigger);
289 }
290 p.sort_keys();
291 p
292 }
293 Maximum(_) => p,
294 Mean(_) => p,
295 Minimum(_) => p,
296 Multiplex(_) => {
297 if let Some(input) =
298 p.swap_remove(&"value".to_string())
299 {
300 p.insert("input".to_string(), input);
301 }
302 p.sort_keys();
303 p
304 }
305 Multiply(_) => {
306 if let Some(a) = p.swap_remove(&"factor1".to_string())
307 {
308 p.insert("a".to_string(), a);
309 }
310 if let Some(b) = p.swap_remove(&"factor2".to_string())
311 {
312 p.insert("b".to_string(), b);
313 }
314 p.sort_keys();
315 p
316 }
317 Not(_) => p,
318 Pipeline(_) => p,
319 SingleNotNull(_) => p,
320 Sinus(_) => p,
321 Subtract(_) => {
322 if let Some(a) = p.swap_remove(&"minuend".to_string())
323 {
324 p.insert("a".to_string(), a);
325 }
326 if let Some(b) =
327 p.swap_remove(&"subtrahend".to_string())
328 {
329 p.insert("b".to_string(), b);
330 }
331 p.sort_keys();
332 p
333 }
334 Storage(_) => p,
335 }
336 }
337
338 let pipes = std::iter::once("".to_string())
339 .chain(bins.keys().cloned())
340 .filter_map(|k| {
341 let pipes = pipes_for_bin(&self.pipes, &k, &bins);
342 if pipes.is_empty() {
343 None
344 } else {
345 Some((k, pipes))
346 }
347 })
348 .collect::<IndexMap<_, _>>();
349
350 Ok(arnalisa::bins::pipeline::Description {
351 pipes,
352 bins,
353 inputs,
354 })
355 }
356}
357
358#[derive(Serialize, Deserialize, Debug, Clone)]
359#[serde(rename_all = "camelCase")]
360pub struct PipelineDetails {
361 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
362 pub bins: IndexMap<String, Bin>,
363 pub pipes: Vec<Pipe>,
364}
365
366#[derive(Serialize, Deserialize, Debug, Clone)]
367#[serde(rename_all = "camelCase")]
368pub struct CalibrationLookupDetails {
369 pub calibration_id: String,
370 pub reversed: bool,
371}
372
373#[derive(Serialize, Deserialize, Debug, Clone)]
374#[serde(rename_all = "camelCase")]
375pub struct FixedValuesDetails {
376 pub fixed_values: IndexMap<String, arnalisa::Item>,
377}
378
379#[derive(Serialize, Deserialize, Debug, Clone)]
380#[serde(rename_all = "camelCase")]
381pub struct LinearInterpolatedLookupDetails {
382 pub points: Vec<(f64, f64)>,
383}
384
385#[derive(Serialize, Deserialize, Debug, Clone)]
386#[serde(rename_all = "camelCase")]
387pub struct MultiplexDetails {
388 pub outputs: IndexMap<String, usize>,
389}
390
391#[derive(Serialize, Deserialize, Debug, Clone)]
392#[serde(rename_all = "camelCase")]
393pub struct Pipe {
394 pub from: PipeSource,
395 pub to: PipeSink,
396}
397
398#[derive(Serialize, Deserialize, Debug, Clone)]
399#[serde(rename_all = "camelCase")]
400pub struct PipeSource {
401 #[serde(default, skip_serializing_if = "String::is_empty")]
402 pub bin: String,
403 pub source: String,
404}
405
406#[derive(Serialize, Deserialize, Debug, Clone)]
407#[serde(rename_all = "camelCase")]
408pub struct PipeSink {
409 #[serde(default, skip_serializing_if = "String::is_empty")]
410 pub bin: String,
411 pub sink: String,
412}
413
414#[derive(Serialize, Deserialize, Debug, Clone)]
415#[serde(rename_all = "camelCase")]
416pub struct SingleNotNullDetails {
417 pub inputs: IndexSet<String>,
418}