Skip to main content

autocore_std/fb/
bit_reset_on_delay.rs

1use std::time::Duration;
2use super::ton::Ton;
3
4/// Resets a boolean value after it has been true for a specified duration.
5///
6/// This function block monitors a boolean value and, once it has been
7/// continuously `true` for the preset time `pt`, resets it to `false`.
8/// It uses a [`Ton`] timer internally.
9///
10/// IEC 61131-3 equivalent:
11/// ```text
12/// tonValue(IN := Value^, PT := PT);
13/// IF tonValue.Q THEN
14///     Value^ := FALSE;
15/// END_IF
16/// ```
17///
18/// # Example
19///
20/// ```
21/// use autocore_std::fb::BitResetOnDelay;
22/// use std::time::Duration;
23///
24/// let mut reset_delay = BitResetOnDelay::new();
25/// let mut flag = true;
26///
27/// // Flag is true, timer starts counting
28/// reset_delay.call(&mut flag, Duration::from_millis(50));
29/// assert_eq!(flag, true); // Not yet reset
30///
31/// // After delay elapses, flag is reset to false
32/// std::thread::sleep(Duration::from_millis(60));
33/// reset_delay.call(&mut flag, Duration::from_millis(50));
34/// assert_eq!(flag, false);
35/// ```
36///
37/// # Use Cases
38///
39/// - Auto-clearing acknowledgment flags
40/// - Resetting one-shot signals after a hold time
41/// - Automatically turning off indicators after a display period
42#[derive(Debug, Clone)]
43pub struct BitResetOnDelay {
44    ton: Ton,
45}
46
47impl BitResetOnDelay {
48    /// Creates a new `BitResetOnDelay` with the internal timer in its default state.
49    ///
50    /// # Example
51    ///
52    /// ```
53    /// use autocore_std::fb::BitResetOnDelay;
54    ///
55    /// let reset_delay = BitResetOnDelay::new();
56    /// ```
57    pub fn new() -> Self {
58        Self { ton: Ton::new() }
59    }
60
61    /// Call cyclically. Resets `value` to `false` after it has been
62    /// `true` for the duration `pt`.
63    ///
64    /// When `value` is `false`, the internal timer resets. When `value`
65    /// is `true`, the timer counts up. Once the timer reaches `pt`,
66    /// `value` is set to `false`.
67    ///
68    /// # Arguments
69    ///
70    /// * `value` - Mutable reference to the boolean to monitor and reset
71    /// * `pt` - Duration that `value` must be `true` before it is reset
72    ///
73    /// # Example
74    ///
75    /// ```
76    /// use autocore_std::fb::BitResetOnDelay;
77    /// use std::time::Duration;
78    ///
79    /// let mut reset_delay = BitResetOnDelay::new();
80    /// let mut alarm_ack = false;
81    ///
82    /// // No effect when value is false
83    /// reset_delay.call(&mut alarm_ack, Duration::from_millis(100));
84    /// assert_eq!(alarm_ack, false);
85    ///
86    /// // Set the flag
87    /// alarm_ack = true;
88    /// reset_delay.call(&mut alarm_ack, Duration::from_millis(100));
89    /// assert_eq!(alarm_ack, true); // Still true, timer just started
90    /// ```
91    pub fn call(&mut self, value: &mut bool, pt: Duration) {
92        self.ton.call(*value, pt);
93        if self.ton.q {
94            *value = false;
95        }
96    }
97}
98
99impl Default for BitResetOnDelay {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_resets_after_delay() {
111        let mut fb = BitResetOnDelay::new();
112        let mut flag = true;
113        let pt = Duration::from_millis(50);
114
115        // First call: timer starts, flag stays true
116        fb.call(&mut flag, pt);
117        assert_eq!(flag, true);
118
119        // Wait for timer to elapse
120        std::thread::sleep(Duration::from_millis(60));
121
122        // Timer elapsed: flag should be reset to false
123        fb.call(&mut flag, pt);
124        assert_eq!(flag, false);
125    }
126
127    #[test]
128    fn test_no_effect_when_false() {
129        let mut fb = BitResetOnDelay::new();
130        let mut flag = false;
131        let pt = Duration::from_millis(50);
132
133        fb.call(&mut flag, pt);
134        assert_eq!(flag, false);
135
136        std::thread::sleep(Duration::from_millis(60));
137        fb.call(&mut flag, pt);
138        assert_eq!(flag, false);
139    }
140
141    #[test]
142    fn test_timer_resets_when_value_goes_false() {
143        let mut fb = BitResetOnDelay::new();
144        let mut flag = true;
145        let pt = Duration::from_millis(100);
146
147        // Start timing
148        fb.call(&mut flag, pt);
149        assert_eq!(flag, true);
150
151        std::thread::sleep(Duration::from_millis(40));
152        fb.call(&mut flag, pt);
153        assert_eq!(flag, true);
154
155        // Value goes false before timer elapses
156        flag = false;
157        fb.call(&mut flag, pt);
158        assert_eq!(flag, false);
159
160        // Value goes true again — timer restarts
161        flag = true;
162        fb.call(&mut flag, pt);
163        assert_eq!(flag, true);
164
165        // Not enough time yet
166        std::thread::sleep(Duration::from_millis(40));
167        fb.call(&mut flag, pt);
168        assert_eq!(flag, true);
169
170        // Now enough time
171        std::thread::sleep(Duration::from_millis(70));
172        fb.call(&mut flag, pt);
173        assert_eq!(flag, false);
174    }
175
176    #[test]
177    fn test_default_trait() {
178        let fb = BitResetOnDelay::default();
179        assert!(!fb.ton.q);
180    }
181}