micropelt 1.1.8

Micropelt device communication definitions
Documentation
use std::io::{Error, ErrorKind, Result};

use micropelt_derive::PartialClose;

use crate::utils::{
    bin_to_bool, bin_to_float_point_five, bin_to_float_point_two_five, bin_to_float_point_zero_two,
    bin_to_ten,
};

use super::device_value::DeviceValue;

const UPLINK_N_BYTES_REV_1_0: usize = 10;
const UPLINK_N_BYTES_REV_1_1: usize = 11;
const UPLINK_N_BYTES_REV_2_A: usize = 12;

#[derive(Clone, Debug, PartialClose)]
pub struct Uplink {
    #[partial_close(resolution = 0.5)]
    flow_raw_value: f32,
    #[partial_close(resolution = 0.5)]
    flow_temperature: f32,
    #[partial_close(resolution = 0.25)]
    ambient_raw_value: f32,
    #[partial_close(resolution = 0.25)]
    ambient_temperature: f32,
    #[partial_close(resolution = 0.25)]
    used_temperature: Option<f32>,
    #[partial_close(resolution = 0.02)]
    battery_v: f32,
    average_current_consumed: u16,
    average_current_generated: u16,
    valve_position: u8,
    device_value: Option<DeviceValue>,
    radio_communication_error: bool,
    radio_signal_strength_low: bool,
    flow_sensor_error: bool,
    ambient_sensor_error: bool,
    battery_low: bool,
    battery_high: bool,
    harvesting: bool,
    temperature_drop_detected: bool,
    motor_error: bool,
    zero_drift: bool,
    reference_run_complete: bool,
    operating_condition_off: bool,
}

impl PartialEq for Uplink {
    fn eq(&self, other: &Self) -> bool {
        self.partial_close(other)
    }
}

impl Uplink {
    pub(crate) fn deserialise(input: &[u8]) -> Result<Self> {
        let (device_value, used_temperature) = match input.len() {
            UPLINK_N_BYTES_REV_1_0 => (None, None),
            UPLINK_N_BYTES_REV_1_1 => (Some(DeviceValue::from_bin(input[9] & 0b111, input[10])?), None),
            UPLINK_N_BYTES_REV_2_A => (Some(DeviceValue::from_bin(input[9] & 0b1111, input[10])?), Some(bin_to_float_point_two_five(input[11]))),
            _ => {
                return Err(Error::new(
                    ErrorKind::InvalidInput,
                    format!(
                        "Payload length should be {UPLINK_N_BYTES_REV_1_0} or {UPLINK_N_BYTES_REV_1_1} or {UPLINK_N_BYTES_REV_2_A}, \
                    got {input:?} which is of length {}", input.len()
                    ),
                ))
            }
        };

        Ok(Self {
            valve_position: input[0],
            flow_raw_value: bin_to_float_point_five(input[1]),
            flow_temperature: bin_to_float_point_five(input[2]),
            ambient_raw_value: bin_to_float_point_two_five(input[3]),
            ambient_temperature: bin_to_float_point_two_five(input[4]),
            temperature_drop_detected: bin_to_bool((input[5] >> 7) & 1)?,
            battery_low: bin_to_bool((input[5] >> 6) & 1)?,
            harvesting: bin_to_bool((input[5] >> 5) & 1)?,
            ambient_sensor_error: bin_to_bool((input[5] >> 4) & 1)?,
            flow_sensor_error: bin_to_bool((input[5] >> 3) & 1)?,
            radio_communication_error: bin_to_bool((input[5] >> 2) & 1)?,
            radio_signal_strength_low: bin_to_bool((input[5] >> 1) & 1)?,
            motor_error: bin_to_bool((input[5]) & 1)?,
            battery_v: bin_to_float_point_zero_two(input[6]),
            average_current_consumed: bin_to_ten(input[7]),
            average_current_generated: bin_to_ten(input[8]),
            operating_condition_off: bin_to_bool((input[9] >> 7) & 1)?,
            battery_high: bin_to_bool((input[9] >> 6) & 1)?,
            zero_drift: bin_to_bool((input[9] >> 5) & 1)?,
            reference_run_complete: bin_to_bool((input[9] >> 4) & 1)?,
            device_value,
            used_temperature,
        })
    }

    pub fn valve_position(&self) -> u8 {
        self.valve_position
    }

    pub fn flow_raw_value(&self) -> f32 {
        self.flow_raw_value
    }

    pub fn flow_temperature(&self) -> f32 {
        self.flow_temperature
    }

    pub fn ambient_raw_value(&self) -> f32 {
        self.ambient_raw_value
    }

    pub fn ambient_temperature(&self) -> f32 {
        self.ambient_temperature
    }

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

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

    pub fn battery_v(&self) -> f32 {
        self.battery_v
    }

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

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

    pub fn average_current_consumed(&self) -> u16 {
        self.average_current_consumed
    }

    pub fn average_current_generated(&self) -> u16 {
        self.average_current_generated
    }

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

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

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

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

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

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

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

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

    pub fn device_value(&self) -> Option<&DeviceValue> {
        self.device_value.as_ref()
    }

    pub fn used_temperature(&self) -> Option<f32> {
        self.used_temperature
    }
}

#[cfg(test)]
#[path = "./test_uplink.rs"]
mod test_uplink;