starduck/components/
data_requirement.rs

1use std::{fmt::Display, ops::Add};
2
3use anyhow::{Ok, Result};
4use chrono::{Duration, NaiveDateTime};
5use serde::{Deserialize, Serialize};
6
7use uuid::Uuid;
8
9use serde_with::serde_as;
10
11use crate::{
12    traits::{UpdateState, UpdateStateFrom},
13    SCMessage, Status,
14};
15
16use super::{Component, IoTOutput};
17
18#[serde_as]
19#[derive(Debug, Clone, Deserialize, Serialize)]
20pub struct DataRequirement {
21    pub components: Vec<Component>,
22    pub required: bool,
23    pub count: usize,
24    #[serde_as(as = "Option<serde_with::DurationSeconds<i64>>")]
25    pub timeout: Option<Duration>,
26    pub status: Status,
27    pub output: IoTOutput,
28}
29
30impl DataRequirement {
31    pub fn new(count: usize, required: bool, timeout: Option<Duration>, output: IoTOutput) -> Self {
32        if let Some(dur) = timeout {
33            if dur.is_zero() {}
34        }
35
36        Self {
37            components: Vec::new(),
38            count,
39            required,
40            timeout,
41            status: Status::Uninitialized,
42            output,
43        }
44    }
45
46    pub fn with_defaults(component: Component, required: bool, output: IoTOutput) -> Self {
47        let default_count = 0;
48        let default_duration = None;
49
50        let mut new_data_req = Self::new(default_count, required, default_duration, output);
51        new_data_req.status = Status::Coherent;
52        new_data_req.components.push(component);
53
54        new_data_req
55    }
56
57    pub fn get_component_by_uuid(&self, uuid_to_find: Uuid) -> Option<&Component> {
58        self.components
59            .iter()
60            .find(|&component| component.uuid.map_or(false, |uuid| uuid == uuid_to_find))
61    }
62
63    pub fn find_mut_component_by_uuid(&mut self, uuid_to_find: Uuid) -> Option<&mut Component> {
64        self.components
65            .iter_mut()
66            .find(|component| component.uuid.map_or(false, |uuid| uuid == uuid_to_find))
67    }
68
69    fn valid_component_count(&self) -> usize {
70        self.components
71            .iter()
72            .filter(|&comp| comp.status == Status::Coherent)
73            .count()
74    }
75
76    fn set_all_component_status(&mut self, status: Status) {
77        for comp in self.components.iter_mut() {
78            comp.status = status;
79        }
80    }
81
82    fn validate_timeout(&mut self) -> bool {
83        // False means it doesn't have a timeout, true means it has a valid one
84        if let Some(timeout) = self.timeout {
85            if timeout.is_zero() {
86                // This means that it's not really needed
87                self.timeout = None;
88                return false;
89            }
90        } else {
91            return false;
92        }
93        true
94    }
95}
96
97impl UpdateState for DataRequirement {
98    fn update_state(&mut self) -> Result<()> {
99        let valid_count = self.valid_component_count();
100
101        if valid_count >= self.count {
102            self.status = Status::Coherent;
103        } else {
104            self.status = Status::Fault;
105        };
106
107        return Ok(());
108    }
109}
110
111impl UpdateStateFrom<&SCMessage> for DataRequirement {
112    fn update_state_from(&mut self, message: &SCMessage) -> Result<()> {
113        if let Some(comp) = self.find_mut_component_by_uuid(message.device_uuid) {
114            return comp.update_state_from(message);
115        }
116
117        let mut new_comp =
118            Component::with_defaults(&message.generate_name(), Some(message.device_uuid));
119        new_comp.update_state_from(message)?;
120
121        self.components.push(new_comp);
122
123        Ok(())
124    }
125}
126
127impl UpdateStateFrom<NaiveDateTime> for DataRequirement {
128    fn update_state_from(&mut self, timestamp: NaiveDateTime) -> Result<()> {
129        // Guard for timeout-less data requirements
130        if !self.validate_timeout() {
131            self.set_all_component_status(Status::Coherent);
132            return self.update_state();
133        };
134
135        for comp in self.components.iter_mut() {
136            if let Some(last_time) = comp.last_reading {
137                let timeout_time = last_time.add(self.timeout.unwrap());
138
139                let time_diff = timestamp.signed_duration_since(timeout_time);
140
141                // Means that time timeout is earlier than the current, ergo Fault
142                if time_diff.num_seconds() >= 0 {
143                    comp.status = Status::Fault;
144                } else {
145                    comp.status = Status::Coherent;
146                }
147
148                continue;
149            }
150
151            // If the component has no last_reading, then we default to fault
152            comp.status = Status::Fault;
153        }
154
155        self.update_state()
156    }
157}
158
159impl Display for DataRequirement {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        write!(f, "{}", serde_json::to_string_pretty(&self).unwrap())
162    }
163}