autocore-std 3.3.34

Standard library for AutoCore control programs - shared memory, IPC, and logging utilities
Documentation
use std::time::Duration;
use super::ton::Ton;

/// Resets a boolean value after it has been true for a specified duration.
///
/// This function block monitors a boolean value and, once it has been
/// continuously `true` for the preset time `pt`, resets it to `false`.
/// It uses a [`Ton`] timer internally.
///
/// IEC 61131-3 equivalent:
/// ```text
/// tonValue(IN := Value^, PT := PT);
/// IF tonValue.Q THEN
///     Value^ := FALSE;
/// END_IF
/// ```
///
/// # Example
///
/// ```
/// use autocore_std::fb::BitResetOnDelay;
/// use std::time::Duration;
///
/// let mut reset_delay = BitResetOnDelay::new();
/// let mut flag = true;
///
/// // Flag is true, timer starts counting
/// reset_delay.call(&mut flag, Duration::from_millis(50));
/// assert_eq!(flag, true); // Not yet reset
///
/// // After delay elapses, flag is reset to false
/// std::thread::sleep(Duration::from_millis(60));
/// reset_delay.call(&mut flag, Duration::from_millis(50));
/// assert_eq!(flag, false);
/// ```
///
/// # Use Cases
///
/// - Auto-clearing acknowledgment flags
/// - Resetting one-shot signals after a hold time
/// - Automatically turning off indicators after a display period
#[derive(Debug, Clone)]
pub struct BitResetOnDelay {
    ton: Ton,
}

impl BitResetOnDelay {
    /// Creates a new `BitResetOnDelay` with the internal timer in its default state.
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::BitResetOnDelay;
    ///
    /// let reset_delay = BitResetOnDelay::new();
    /// ```
    pub fn new() -> Self {
        Self { ton: Ton::new() }
    }

    /// Call cyclically. Resets `value` to `false` after it has been
    /// `true` for the duration `pt`.
    ///
    /// When `value` is `false`, the internal timer resets. When `value`
    /// is `true`, the timer counts up. Once the timer reaches `pt`,
    /// `value` is set to `false`.
    ///
    /// # Arguments
    ///
    /// * `value` - Mutable reference to the boolean to monitor and reset
    /// * `pt` - Duration that `value` must be `true` before it is reset
    ///
    /// # Example
    ///
    /// ```
    /// use autocore_std::fb::BitResetOnDelay;
    /// use std::time::Duration;
    ///
    /// let mut reset_delay = BitResetOnDelay::new();
    /// let mut alarm_ack = false;
    ///
    /// // No effect when value is false
    /// reset_delay.call(&mut alarm_ack, Duration::from_millis(100));
    /// assert_eq!(alarm_ack, false);
    ///
    /// // Set the flag
    /// alarm_ack = true;
    /// reset_delay.call(&mut alarm_ack, Duration::from_millis(100));
    /// assert_eq!(alarm_ack, true); // Still true, timer just started
    /// ```
    pub fn call(&mut self, value: &mut bool, pt: Duration) {
        self.ton.call(*value, pt);
        if self.ton.q {
            *value = false;
        }
    }
}

impl Default for BitResetOnDelay {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_resets_after_delay() {
        let mut fb = BitResetOnDelay::new();
        let mut flag = true;
        let pt = Duration::from_millis(50);

        // First call: timer starts, flag stays true
        fb.call(&mut flag, pt);
        assert_eq!(flag, true);

        // Wait for timer to elapse
        std::thread::sleep(Duration::from_millis(60));

        // Timer elapsed: flag should be reset to false
        fb.call(&mut flag, pt);
        assert_eq!(flag, false);
    }

    #[test]
    fn test_no_effect_when_false() {
        let mut fb = BitResetOnDelay::new();
        let mut flag = false;
        let pt = Duration::from_millis(50);

        fb.call(&mut flag, pt);
        assert_eq!(flag, false);

        std::thread::sleep(Duration::from_millis(60));
        fb.call(&mut flag, pt);
        assert_eq!(flag, false);
    }

    #[test]
    fn test_timer_resets_when_value_goes_false() {
        let mut fb = BitResetOnDelay::new();
        let mut flag = true;
        let pt = Duration::from_millis(100);

        // Start timing
        fb.call(&mut flag, pt);
        assert_eq!(flag, true);

        std::thread::sleep(Duration::from_millis(40));
        fb.call(&mut flag, pt);
        assert_eq!(flag, true);

        // Value goes false before timer elapses
        flag = false;
        fb.call(&mut flag, pt);
        assert_eq!(flag, false);

        // Value goes true again — timer restarts
        flag = true;
        fb.call(&mut flag, pt);
        assert_eq!(flag, true);

        // Not enough time yet
        std::thread::sleep(Duration::from_millis(40));
        fb.call(&mut flag, pt);
        assert_eq!(flag, true);

        // Now enough time
        std::thread::sleep(Duration::from_millis(70));
        fb.call(&mut flag, pt);
        assert_eq!(flag, false);
    }

    #[test]
    fn test_default_trait() {
        let fb = BitResetOnDelay::default();
        assert!(!fb.ton.q);
    }
}