From 48a2f728de2c0eec156ab116a2fc1a618f390823 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Wed, 31 Jul 2024 23:33:58 +0200 Subject: [PATCH] Start to check device relay information --- central_backend/src/constants.rs | 4 + central_backend/src/devices/device.rs | 116 +++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/central_backend/src/constants.rs b/central_backend/src/constants.rs index 792de2b..4bd652d 100644 --- a/central_backend/src/constants.rs +++ b/central_backend/src/constants.rs @@ -36,6 +36,10 @@ impl SizeConstraint { let len = val.trim().len(); len >= self.min && len <= self.max } + + pub fn validate_usize(&self, val: usize) -> bool { + val >= self.min && val <= self.max + } } /// Backend static constraints diff --git a/central_backend/src/devices/device.rs b/central_backend/src/devices/device.rs index 18e2a85..e92870d 100644 --- a/central_backend/src/devices/device.rs +++ b/central_backend/src/devices/device.rs @@ -1,6 +1,7 @@ //! # Devices entities definition use crate::constants::StaticConstraints; +use std::collections::HashMap; /// Device information provided directly by the device during syncrhonisation. /// @@ -75,13 +76,20 @@ pub struct DailyMinRuntime { pub catch_up_hours: Vec, } -#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)] pub struct DeviceRelayID(uuid::Uuid); +impl Default for DeviceRelayID { + fn default() -> Self { + Self(uuid::Uuid::new_v4()) + } +} + /// Single device relay information -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Default)] pub struct DeviceRelay { /// Device relay id. Should be unique across the whole application + #[serde(default)] id: DeviceRelayID, /// Human-readable name for the relay name: String, @@ -103,6 +111,80 @@ pub struct DeviceRelay { conflicts_with: Vec, } +impl DeviceRelay { + /// Check device relay for errors + pub fn error(&self, list: &[DeviceRelay]) -> Option<&'static str> { + let constraints = StaticConstraints::default(); + if !constraints.relay_name_len.validate(&self.name) { + return Some("Invalid relay name length!"); + } + + if !constraints.relay_priority.validate_usize(self.priority) { + return Some("Invalid relay priority!"); + } + + if !constraints + .relay_consumption + .validate_usize(self.consumption) + { + return Some("Invalid consumption!"); + } + + if !constraints + .relay_minimal_uptime + .validate_usize(self.minimal_uptime) + { + return Some("Invalid minimal uptime!"); + } + + if !constraints + .relay_minimal_downtime + .validate_usize(self.minimal_downtime) + { + return Some("Invalid minimal uptime!"); + } + + if let Some(daily) = &self.daily_runtime { + if !constraints + .relay_daily_minimal_runtime + .validate_usize(daily.min_runtime) + { + return Some("Invalid minimal daily runtime!"); + } + + if daily.reset_time > 3600 * 24 { + return Some("Invalid daily reset time!"); + } + + if daily.catch_up_hours.is_empty() { + return Some("No catchup hours defined!"); + } + + if daily.catch_up_hours.iter().any(|h| h > &23) { + return Some("At least one catch up hour is invalid!"); + } + } + + let relays_map = list.iter().map(|r| (r.id, r)).collect::>(); + + if self.depends_on.iter().any(|d| !relays_map.contains_key(d)) { + return Some("A specified dependent relay does not exists!"); + } + + if self + .conflicts_with + .iter() + .any(|d| !relays_map.contains_key(d)) + { + return Some("A specified conflicting relay does not exists!"); + } + + // TODO : check for loops + + None + } +} + /// Device general information /// /// This structure is used to update device information @@ -128,3 +210,33 @@ impl DeviceGeneralInfo { None } } + +#[cfg(test)] +mod tests { + use crate::devices::device::DeviceRelay; + + #[test] + fn check_device_relay_error() { + let unitary = DeviceRelay { + name: "unitary".to_string(), + ..Default::default() + }; + + let bad_name = DeviceRelay { + name: "".to_string(), + ..Default::default() + }; + + let dep_on_unitary = DeviceRelay { + name: "dep_on_unitary".to_string(), + depends_on: vec![unitary.id], + ..Default::default() + }; + + assert_eq!(unitary.error(&[]), None); + assert_eq!(unitary.error(&[unitary.clone(), bad_name.clone()]), None); + assert!(bad_name.error(&[]).is_some()); + assert_eq!(dep_on_unitary.error(&[unitary.clone()]), None); + assert!(dep_on_unitary.error(&[]).is_some()); + } +}