pub type AlarmCondition = Box<dyn Fn(&[(String, f64)]) -> bool + Send + Sync>;
#[derive(Debug, Clone, PartialEq)]
pub enum AlarmState {
Idle,
Active { since_tick: u64 },
Cooldown { until_tick: u64 },
}
pub struct AlarmRule {
pub name: String,
pub condition: AlarmCondition,
pub cooldown_ticks: u64,
pub state: AlarmState,
pub last_fired_tick: u64,
}
impl AlarmRule {
pub fn new(
name: impl Into<String>,
condition: AlarmCondition,
cooldown_ticks: u64,
) -> Self {
AlarmRule {
name: name.into(),
condition,
cooldown_ticks,
state: AlarmState::Idle,
last_fired_tick: 0,
}
}
pub fn evaluate(&mut self, data: &[(String, f64)], current_tick: u64) -> bool {
let triggered = (self.condition)(data);
match &self.state {
AlarmState::Idle => {
if triggered {
self.state = AlarmState::Active { since_tick: current_tick };
self.last_fired_tick = current_tick;
return true;
}
}
AlarmState::Active { since_tick: _ } => {
if !triggered {
self.state = AlarmState::Cooldown {
until_tick: current_tick + self.cooldown_ticks,
};
}
}
AlarmState::Cooldown { until_tick } => {
if current_tick >= *until_tick {
if triggered {
self.state = AlarmState::Active { since_tick: current_tick };
self.last_fired_tick = current_tick;
return true;
} else {
self.state = AlarmState::Idle;
}
}
}
}
false
}
pub fn reset(&mut self) {
self.state = AlarmState::Idle;
}
}
impl core::fmt::Debug for AlarmRule {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("AlarmRule")
.field("name", &self.name)
.field("cooldown_ticks", &self.cooldown_ticks)
.field("state", &self.state)
.finish()
}
}