#![allow(dead_code)]
pub type Severity = u8;
pub type Occurrence = u8;
pub type Detection = u8;
pub type Rpn = u16;
#[derive(Debug, Clone, Copy)]
pub struct FmeaEntry {
pub component: u8,
pub mode: u8,
pub effect: u8,
pub severity: Severity,
pub occurrence: Occurrence,
pub detection: Detection,
}
impl FmeaEntry {
pub fn new(
component: u8,
mode: u8,
effect: u8,
severity: Severity,
occurrence: Occurrence,
detection: Detection,
) -> Self {
Self {
component,
mode,
effect,
severity,
occurrence,
detection,
}
}
#[inline]
pub fn rpn(&self) -> Rpn {
self.severity as Rpn * self.occurrence as Rpn * self.detection as Rpn
}
}
pub struct FmeaTable<const N: usize> {
entries: [Option<FmeaEntry>; N],
count: usize,
}
impl<const N: usize> FmeaTable<N> {
pub fn new() -> Self {
Self {
entries: [None; N],
count: 0,
}
}
pub fn add(&mut self, entry: FmeaEntry) -> bool {
if self.count >= N {
return false;
}
let rpn = entry.rpn();
let mut pos = self.count;
for i in 0..self.count {
if let Some(e) = self.entries[i] {
if rpn > e.rpn() {
pos = i;
break;
}
}
}
let mut j = self.count;
while j > pos {
self.entries[j] = self.entries[j - 1];
j -= 1;
}
self.entries[pos] = Some(entry);
self.count += 1;
true
}
pub fn top_risks(&self, k: usize) -> &[Option<FmeaEntry>] {
let limit = k.min(self.count);
&self.entries[..limit]
}
pub fn filter_by_severity(&self, level: Severity) -> heapless::Vec<FmeaEntry, N> {
let mut result = heapless::Vec::new();
for slot in self.entries[..self.count].iter().flatten() {
if slot.severity >= level {
let _ = result.push(*slot);
}
}
result
}
pub fn total_rpn(&self) -> u32 {
self.entries[..self.count]
.iter()
.flatten()
.map(|e| e.rpn() as u32)
.sum()
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
}
impl<const N: usize> Default for FmeaTable<N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rpn_calculation() {
let entry = FmeaEntry::new(1, 2, 3, 7, 5, 4);
assert_eq!(entry.rpn(), 7 * 5 * 4);
}
#[test]
fn add_sorted_by_rpn_descending() {
let mut table: FmeaTable<8> = FmeaTable::new();
let e1 = FmeaEntry::new(1, 1, 1, 6, 4, 3);
let e2 = FmeaEntry::new(2, 2, 2, 9, 8, 7);
let e3 = FmeaEntry::new(3, 3, 3, 2, 2, 2);
assert!(table.add(e1));
assert!(table.add(e2));
assert!(table.add(e3));
assert_eq!(table.len(), 3);
let top = table.top_risks(1);
assert_eq!(top.len(), 1);
assert_eq!(top[0].unwrap().rpn(), 504);
}
#[test]
fn filter_by_severity_and_total_rpn() {
let mut table: FmeaTable<8> = FmeaTable::new();
table.add(FmeaEntry::new(1, 1, 1, 8, 3, 2)); table.add(FmeaEntry::new(2, 1, 1, 3, 2, 2)); table.add(FmeaEntry::new(3, 1, 1, 9, 5, 4));
let filtered = table.filter_by_severity(8);
assert_eq!(filtered.len(), 2);
assert_eq!(table.total_rpn(), 240);
}
#[test]
fn table_full_returns_false() {
let mut table: FmeaTable<2> = FmeaTable::new();
assert!(table.add(FmeaEntry::new(1, 1, 1, 1, 1, 1)));
assert!(table.add(FmeaEntry::new(2, 2, 2, 2, 2, 2)));
assert!(!table.add(FmeaEntry::new(3, 3, 3, 3, 3, 3)));
}
}