logic_mesh/blocks/math/
average.rs

1// Copyright (c) 2022-2023, Radu Racariu.
2
3use uuid::Uuid;
4
5use crate::{
6    base::{
7        block::{Block, BlockDesc, BlockProps, BlockState},
8        input::{input_reader::InputReader, Input, InputProps},
9        output::Output,
10    },
11    blocks::utils::convert_units,
12};
13
14use libhaystack::val::{kind::HaystackKind, Number, Value};
15
16use crate::{blocks::InputImpl, blocks::OutputImpl};
17
18/// Calculates an average of multiple numbers from the 16 inputs
19/// this block has.
20/// The operation would take into account the units of those input's values,
21/// if the units are not convertible, the block would be in an error state.
22#[block]
23#[derive(BlockProps, Debug)]
24#[dis = "Average"]
25#[category = "math"]
26#[input(kind = "Number", count = 16)]
27pub struct Average {
28    #[output(kind = "Number")]
29    pub out: OutputImpl,
30}
31
32impl Block for Average {
33    async fn execute(&mut self) {
34        self.read_inputs_until_ready().await;
35
36        let val = self
37            .inputs()
38            .into_iter()
39            .filter_map(|input| match input.get_value().as_ref() {
40                Some(Value::Number(num)) => Some(*num),
41                _ => None,
42            })
43            .collect::<Vec<Number>>();
44
45        if let Ok(numbers) = convert_units(&val) {
46            if self.state() != BlockState::Running {
47                self.set_state(BlockState::Running);
48            }
49
50            let avg = numbers.iter().fold(0.0, |acc, n| acc + n.value) / numbers.len() as f64;
51
52            let avg = if let Some(Number {
53                value: _,
54                unit: Some(unit),
55            }) = numbers.first()
56            {
57                Number::make_with_unit(avg, unit)
58            } else {
59                Number::make(avg)
60            };
61
62            self.out.set((avg).into())
63        } else {
64            self.set_state(BlockState::Fault);
65        }
66    }
67}
68
69#[cfg(test)]
70mod test {
71
72    use crate::base::block::test_utils::write_block_inputs;
73    use crate::base::input::input_reader::InputReader;
74    use crate::{base::block::Block, blocks::math::Average};
75
76    #[tokio::test]
77    async fn test_average_block() {
78        let mut block = Average::new();
79
80        write_block_inputs(&mut [(block._inputs.get_mut(0).unwrap(), 1.into())]).await;
81        block.read_inputs().await;
82        write_block_inputs(&mut [(block._inputs.get_mut(15).unwrap(), 9.into())]).await;
83
84        block.execute().await;
85        assert_eq!(block.out.value, 5.into());
86    }
87}