use crate::{
error::{Error, ErrorCode},
transport::session::SessionMode,
};
use log::error;
#[derive(PartialEq)]
#[allow(dead_code)]
#[allow(clippy::enum_variant_names)]
enum NocState {
NocNotRecvd,
AddNocRecvd(u8),
UpdateNocRecvd(u8),
}
#[derive(PartialEq)]
pub struct ArmedCtx {
session_mode: SessionMode,
timeout: u8,
noc_state: NocState,
}
#[derive(PartialEq)]
pub enum State {
Idle,
Armed(ArmedCtx),
}
pub struct FailSafe {
state: State,
}
impl FailSafe {
#[inline(always)]
pub const fn new() -> Self {
Self { state: State::Idle }
}
pub fn arm(&mut self, timeout: u8, session_mode: SessionMode) -> Result<(), Error> {
match &mut self.state {
State::Idle => {
self.state = State::Armed(ArmedCtx {
session_mode,
timeout,
noc_state: NocState::NocNotRecvd,
})
}
State::Armed(c) => {
if c.session_mode != session_mode {
error!("Received Fail-Safe Arm with different session modes; current {:?}, incoming {:?}", c.session_mode, session_mode);
Err(ErrorCode::Invalid)?;
}
c.timeout = timeout;
}
}
Ok(())
}
pub fn disarm(&mut self, session_mode: SessionMode) -> Result<(), Error> {
match &mut self.state {
State::Idle => {
error!("Received Fail-Safe Disarm without it being armed");
Err(ErrorCode::Invalid)?;
}
State::Armed(c) => {
match c.noc_state {
NocState::NocNotRecvd => Err(ErrorCode::Invalid)?,
NocState::AddNocRecvd(idx) | NocState::UpdateNocRecvd(idx) => {
if let SessionMode::Case(c) = session_mode {
if c.fab_idx != idx {
error!(
"Received disarm in separate session from previous Add/Update NOC"
);
Err(ErrorCode::Invalid)?;
}
} else {
error!("Received disarm in a non-CASE session");
Err(ErrorCode::Invalid)?;
}
}
}
self.state = State::Idle;
}
}
Ok(())
}
pub fn is_armed(&self) -> bool {
self.state != State::Idle
}
pub fn record_add_noc(&mut self, fabric_index: u8) -> Result<(), Error> {
match &mut self.state {
State::Idle => Err(ErrorCode::Invalid.into()),
State::Armed(c) => {
if c.noc_state == NocState::NocNotRecvd {
c.noc_state = NocState::AddNocRecvd(fabric_index);
Ok(())
} else {
Err(ErrorCode::Invalid.into())
}
}
}
}
pub fn allow_noc_change(&self) -> Result<bool, Error> {
let allow = match &self.state {
State::Idle => false,
State::Armed(c) => c.noc_state == NocState::NocNotRecvd,
};
Ok(allow)
}
}
impl Default for FailSafe {
fn default() -> Self {
Self::new()
}
}