#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SafetyError {
ASILViolation {
required: ASILLevel,
actual: ASILLevel,
},
SafetyFunctionFailure,
DiagnosticCoverageInsufficient,
SILViolation {
required: SILLevel,
actual: SILLevel,
},
SIFFailure,
ProofTestCoverageInsufficient,
DALViolation {
required: DALLevel,
actual: DALLevel,
},
SoftwareLevelFailure,
VerificationCoverageInsufficient,
SafetyMonitorTimeout,
RedundancyFailure,
SafetyBarrierBreach,
FailSafeStateUnreachable,
SafetyCriticalDataCorruption,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ASILLevel {
QM = 0,
A = 1,
B = 2,
C = 3,
D = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SILLevel {
SIL1 = 1,
SIL2 = 2,
SIL3 = 3,
SIL4 = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DALLevel {
E = 0,
D = 1,
C = 2,
B = 3,
A = 4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SafetyLevel {
ASIL(ASILLevel),
SIL(SILLevel),
DAL(DALLevel),
Custom(u8),
}
impl SafetyLevel {
pub const fn numeric_level(&self) -> u8 {
match self {
Self::ASIL(level) => *level as u8,
Self::SIL(level) => *level as u8,
Self::DAL(level) => *level as u8,
Self::Custom(level) => *level,
}
}
pub const fn is_compatible_with(&self, other: &Self) -> bool {
match (self, other) {
(Self::ASIL(_), Self::ASIL(_)) => true,
(Self::SIL(_), Self::SIL(_)) => true,
(Self::DAL(_), Self::DAL(_)) => true,
(Self::Custom(_), Self::Custom(_)) => true,
_ => false,
}
}
pub const fn satisfies(&self, required: &Self) -> bool {
if !self.is_compatible_with(required) {
return false;
}
self.numeric_level() >= required.numeric_level()
}
}
impl PartialOrd for SafetyLevel {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
if self.is_compatible_with(other) {
Some(self.numeric_level().cmp(&other.numeric_level()))
} else {
None
}
}
}
impl SafetyError {
pub const fn is_critical(&self) -> bool {
match self {
Self::ASILViolation { required, .. } => matches!(required, ASILLevel::C | ASILLevel::D),
Self::SILViolation { required, .. } => {
matches!(required, SILLevel::SIL3 | SILLevel::SIL4)
}
Self::DALViolation { required, .. } => matches!(required, DALLevel::A | DALLevel::B),
Self::SafetyFunctionFailure
| Self::SIFFailure
| Self::SoftwareLevelFailure
| Self::RedundancyFailure
| Self::SafetyBarrierBreach
| Self::FailSafeStateUnreachable
| Self::SafetyCriticalDataCorruption => true,
_ => false,
}
}
pub const fn domain(&self) -> &'static str {
match self {
Self::ASILViolation { .. }
| Self::SafetyFunctionFailure
| Self::DiagnosticCoverageInsufficient => "Automotive",
Self::SILViolation { .. } | Self::SIFFailure | Self::ProofTestCoverageInsufficient => {
"Industrial"
}
Self::DALViolation { .. }
| Self::SoftwareLevelFailure
| Self::VerificationCoverageInsufficient => "Aerospace",
_ => "Generic",
}
}
}
impl From<ASILLevel> for SafetyLevel {
fn from(level: ASILLevel) -> Self {
Self::ASIL(level)
}
}
impl From<SILLevel> for SafetyLevel {
fn from(level: SILLevel) -> Self {
Self::SIL(level)
}
}
impl From<DALLevel> for SafetyLevel {
fn from(level: DALLevel) -> Self {
Self::DAL(level)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_asil_level_ordering() {
assert!(ASILLevel::D > ASILLevel::C);
assert!(ASILLevel::C > ASILLevel::B);
assert!(ASILLevel::B > ASILLevel::A);
assert!(ASILLevel::A > ASILLevel::QM);
}
#[test]
fn test_safety_level_compatibility() {
let asil_d = SafetyLevel::ASIL(ASILLevel::D);
let asil_c = SafetyLevel::ASIL(ASILLevel::C);
let sil_4 = SafetyLevel::SIL(SILLevel::SIL4);
assert!(asil_d.is_compatible_with(&asil_c));
assert!(!asil_d.is_compatible_with(&sil_4));
assert!(asil_d.satisfies(&asil_c));
assert!(!asil_c.satisfies(&asil_d));
}
#[test]
fn test_safety_error_criticality() {
let critical_error = SafetyError::ASILViolation {
required: ASILLevel::D,
actual: ASILLevel::B,
};
let non_critical_error = SafetyError::ASILViolation {
required: ASILLevel::A,
actual: ASILLevel::QM,
};
assert!(critical_error.is_critical());
assert!(!non_critical_error.is_critical());
}
#[test]
fn test_safety_error_domain() {
let automotive_error = SafetyError::SafetyFunctionFailure;
let industrial_error = SafetyError::SIFFailure;
let aerospace_error = SafetyError::SoftwareLevelFailure;
let generic_error = SafetyError::SafetyMonitorTimeout;
assert_eq!(automotive_error.domain(), "Automotive");
assert_eq!(industrial_error.domain(), "Industrial");
assert_eq!(aerospace_error.domain(), "Aerospace");
assert_eq!(generic_error.domain(), "Generic");
}
}