dcs2 0.1.0

An extensible distributed control system framework made in rust with no-std support.
Documentation
use core::cmp::Ordering;
use core::fmt::{Debug, Display};
use core::ops::Add;
use heapless::{LinearMap, Vec};
use serde::{de, Deserialize, Serialize, Serializer};

use crate::nodes::SystemNodeId;
use crate::properties::{MeasurementsVec, CLUSTER_NODE_COUNT, MEASUREMENTS_MAX_COUNT};
use crate::rules::measurements::ClusterType::{HUMIDITY, TEMPERATURE};

/// The types of sensors allowed by the system.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
pub enum ClusterType {
    TEMPERATURE,
    HUMIDITY,
}

impl Display for ClusterType {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            TEMPERATURE => f.write_str("TEMPERATURE"),
            HUMIDITY => f.write_str("HUMIDITY"),
        }
    }
}

impl From<char> for ClusterType {
    fn from(input: char) -> Self {
        match input {
            'T' => TEMPERATURE,
            'H' => HUMIDITY,
            _ => panic!("Unexpected Sensor Type."),
        }
    }
}

/// Measurements are read by the sensors. It comprises a type and an integer value
#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Measurement {
    pub typed: ClusterType,
    pub value: i32,
}

impl Add for Measurement {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self {
            typed: self.typed,
            value: self.value + rhs.value,
        }
    }
}

impl PartialEq<i32> for Measurement {
    fn eq(&self, other: &i32) -> bool {
        &self.value == other
    }
}

impl PartialOrd<i32> for Measurement {
    fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
        Some(self.value.cmp(other))
    }
}

impl Ord for Measurement {
    fn cmp(&self, other: &Self) -> Ordering {
        self.value.cmp(&other.value)
    }
}

impl PartialOrd for Measurement {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Debug for Measurement {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let sensor_type = if self.typed == ClusterType::TEMPERATURE {
            "T"
        } else {
            "H"
        };
        write!(f, "{:}: {:?}", sensor_type, self.value)
    }
}

impl Measurement {
    pub fn new(sensor_type: ClusterType, value: i32) -> Self {
        Self { typed: sensor_type, value }
    }
}

/// The system state is the global state of the system to be coordinated and based on wich rules
/// are evaluated.
#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
pub struct SystemState {
    #[serde(serialize_with = "serialize", deserialize_with = "deserialize")]
    pub measurements: LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT>,
}

#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
pub struct NodeMeasurementsVec {
    measurements: Vec<Measurement, MEASUREMENTS_MAX_COUNT>,
}

impl From<LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT>> for SystemState {
    fn from(
        measurements: LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT>,
    ) -> Self {
        Self { measurements }
    }
}

impl NodeMeasurementsVec {
    pub fn new() -> Self {
        Self {
            measurements: Vec::new(),
        }
    }

    pub fn from(measurement: Measurement) -> Self {
        Self {
            measurements: Vec::from_slice(&[measurement]).unwrap(),
        }
    }

    pub fn pop(&mut self) -> Option<Measurement> {
        self.measurements.pop()
    }

    pub fn push(&mut self, measurement: Measurement) {
        if self.measurements.push(measurement).is_err() {
            self.measurements.remove(0);
            self.measurements.push(measurement).unwrap();
        }
    }

    pub fn last(&self) -> Option<Measurement> {
        self.measurements.last().copied()
    }

    pub fn is_empty(&self) -> bool {
        self.measurements.is_empty()
    }

    pub fn len(&self) -> usize { self.measurements.len() }

    pub fn is_full(&self) -> bool { self.measurements.len() == MEASUREMENTS_MAX_COUNT }

}

impl SystemState {
    pub fn raw(self) -> LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT> {
        self.measurements
    }

    pub fn new(id: SystemNodeId, measurement: Measurement) -> Self {
        let mut state = LinearMap::new();
        state
            .insert(id, NodeMeasurementsVec::from(measurement))
            .unwrap();
        Self {
            measurements: state,
        }
    }

    pub fn update(&mut self, id: SystemNodeId, measurement: Measurement) {
        match self.measurements.get_mut(&id) {
            Some(measurements) => {
                measurements.push(measurement);
            }
            None => {
                self.measurements
                    .insert(id, NodeMeasurementsVec::from(measurement))
                    .unwrap();
            }
        }
    }

    pub fn last_measurements(&self) -> MeasurementsVec {
        let mut measurements = MeasurementsVec::new();
        for (_, node_measurements) in self.measurements.clone().iter() {
            if let Some(it) = node_measurements.last() {
                measurements.push(it).unwrap()
            }
        }
        measurements
    }

    pub fn extend(&mut self, rhs: Self) {
        for (id, node_measurements) in rhs.measurements.into_iter() {
            for measurement in node_measurements.measurements.clone().into_iter() {
                self.update(*id, measurement)
            }
        }
    }
}

#[derive(Debug, Eq, PartialEq, Clone, Default, Serialize, Deserialize)]
struct InnerSystemState {
    state: Vec<Inner, CLUSTER_NODE_COUNT>,
}

#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
struct Inner {
    id: SystemNodeId,
    value: NodeMeasurementsVec,
}

fn deserialize<'de, D>(
    deserializer: D,
) -> Result<LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT>, D::Error>
where
    D: de::Deserializer<'de>,
{
    let inner = <Vec<Inner, CLUSTER_NODE_COUNT>>::deserialize(deserializer)?;

    let mut system_state: LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT> =
        LinearMap::new();
    for Inner { id, value } in inner.into_iter() {
        system_state
            .insert(id, value)
            .expect("Couldn't insert value into state.");
    }
    Ok(system_state)
}

fn serialize<S>(
    map: &LinearMap<SystemNodeId, NodeMeasurementsVec, CLUSTER_NODE_COUNT>,
    ser: S,
) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let mut vec: Vec<Inner, CLUSTER_NODE_COUNT> = Vec::new();
    for (key, value) in map {
        let inner = Inner {
            id: *key,
            value: value.clone(),
        };
        vec.push(inner).expect("Couldn't pusshe value into vec");
    }
    vec.serialize(ser)
}

#[cfg(test)]
mod measurement_test {
    use crate::rules::measurements::Measurement;
    use crate::rules::measurements::ClusterType::TEMPERATURE;

    #[test]
    fn can_get_max_measurements_of_same_type() {
        assert!(Measurement::new(TEMPERATURE, 2) > Measurement::new(TEMPERATURE, 1));
    }

    #[test]
    fn can_cmp_measurement_with_i32() {
        let measurement = Measurement::new(TEMPERATURE, 2);
        assert!(measurement > 1_i32);
    }
}