use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FMEA {
pub subject: String,
pub failure_modes: Vec<FailureMode>,
pub rpn_threshold: u32,
pub analyzed_at: String,
pub critical_failures: Vec<(FailureMode, u32)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailureMode {
pub name: String,
pub potential_cause: String,
pub potential_effect: String,
pub severity: u32,
pub occurrence: u32,
pub detection: u32,
pub preventive_actions: Vec<String>,
pub current_controls: Vec<String>,
pub rpn: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PokaYoke {
pub prevention_rules: Vec<PreventionRule>,
pub detection_mechanisms: Vec<DetectionMechanism>,
pub stats: PokaYokeStats,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreventionRule {
pub id: String,
pub prevents: String,
pub implementation: String,
pub rule_type: PreventionType,
pub enforced: bool,
pub implementation_cost: String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum PreventionType {
Physical,
Warning,
Confirmation,
Correction,
Forced,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionMechanism {
pub id: String,
pub detects: String,
pub method: String,
pub detection_rate: f32,
pub response_time_ms: u32,
pub automated: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PokaYokeStats {
pub errors_prevented: u32,
pub errors_detected: u32,
pub defects_caught: u32,
pub false_positives: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MURA {
pub standards: Vec<Standard>,
pub consistency_metrics: BTreeMap<String, f32>,
pub violations: Vec<MuraViolation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Standard {
pub id: String,
pub name: String,
pub definition: String,
pub acceptable_variance: f32,
pub is_critical: bool,
pub verification_method: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MuraViolation {
pub standard_id: String,
pub actual_variance: f32,
pub severity: ViolationSeverity,
pub description: String,
pub corrective_action: String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum ViolationSeverity {
Low,
Medium,
High,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MUDA {
pub waste_types: Vec<WasteType>,
pub waste_items: Vec<WasteItem>,
pub opportunities: Vec<OptimizationOpportunity>,
pub metrics: WasteMetrics,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum WasteType {
Defects,
OverProcessing,
Overproduction,
Transportation,
Inventory,
Waiting,
MotionWaste,
UnusedTalent,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WasteItem {
pub id: String,
pub waste_type: WasteType,
pub description: String,
pub current_impact: String,
pub frequency: String,
pub elimination_difficulty: DifficultyLevel,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum DifficultyLevel {
Easy,
Medium,
Hard,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizationOpportunity {
pub id: String,
pub addresses: String,
pub proposal: String,
pub expected_impact: String,
pub implementation_effort: String,
pub priority: u32,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct WasteMetrics {
pub total_waste_items: u32,
pub waste_by_type: BTreeMap<String, u32>,
pub critical_items: u32,
pub improvement_potential: f32,
}
impl FailureMode {
pub fn calculate_rpn(&mut self) {
self.rpn = self.severity * self.occurrence * self.detection;
}
pub fn severity_level(&self) -> SeverityLevel {
match self.severity {
1..=3 => SeverityLevel::Low,
4..=7 => SeverityLevel::Medium,
8..=10 => SeverityLevel::High,
_ => SeverityLevel::Critical,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum SeverityLevel {
Low,
Medium,
High,
Critical,
}
impl FMEA {
pub fn new(subject: String, rpn_threshold: u32) -> Self {
Self {
subject,
failure_modes: Vec::new(),
rpn_threshold,
analyzed_at: chrono::Utc::now().to_rfc3339(),
critical_failures: Vec::new(),
}
}
pub fn add_failure_mode(&mut self, mut mode: FailureMode) {
mode.calculate_rpn();
if mode.rpn >= self.rpn_threshold {
self.critical_failures.push((mode.clone(), mode.rpn));
}
self.failure_modes.push(mode);
}
pub fn high_risk_failures(&self) -> Vec<&FailureMode> {
self.failure_modes
.iter()
.filter(|fm| fm.rpn >= self.rpn_threshold)
.collect()
}
pub fn analyze_trends(&self) -> FMEATrends {
let mut severity_distribution = BTreeMap::new();
let mut occurrence_distribution = BTreeMap::new();
let mut detection_distribution = BTreeMap::new();
for mode in &self.failure_modes {
*severity_distribution
.entry(mode.severity_level())
.or_insert(0) += 1;
*occurrence_distribution.entry(mode.occurrence).or_insert(0) += 1;
*detection_distribution.entry(mode.detection).or_insert(0) += 1;
}
FMEATrends {
total_failures_analyzed: self.failure_modes.len(),
high_risk_count: self.high_risk_failures().len(),
highest_rpn: self
.failure_modes
.iter()
.map(|fm| fm.rpn)
.max()
.unwrap_or(0),
average_rpn: if self.failure_modes.is_empty() {
0
} else {
self.failure_modes.iter().map(|fm| fm.rpn).sum::<u32>()
/ self.failure_modes.len() as u32
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FMEATrends {
pub total_failures_analyzed: usize,
pub high_risk_count: usize,
pub highest_rpn: u32,
pub average_rpn: u32,
}
impl PokaYoke {
pub fn new() -> Self {
Self {
prevention_rules: Vec::new(),
detection_mechanisms: Vec::new(),
stats: PokaYokeStats::default(),
}
}
pub fn add_prevention_rule(&mut self, rule: PreventionRule) {
self.prevention_rules.push(rule);
}
pub fn add_detection_mechanism(&mut self, mechanism: DetectionMechanism) {
self.detection_mechanisms.push(mechanism);
}
pub fn prevention_effectiveness(&self) -> f32 {
let total_errors = self.stats.errors_prevented + self.stats.errors_detected;
if total_errors == 0 {
100.0
} else {
(self.stats.errors_prevented as f32 / total_errors as f32) * 100.0
}
}
pub fn detection_accuracy(&self) -> f32 {
let total_detections = self.stats.errors_detected + self.stats.false_positives;
if total_detections == 0 {
100.0
} else {
(self.stats.errors_detected as f32 / total_detections as f32) * 100.0
}
}
pub fn overall_improvement(&self) -> f32 {
let total_caught = self.stats.defects_caught + self.stats.errors_prevented;
(total_caught as f32 * self.detection_accuracy() / 100.0) / 10.0 }
}
impl Default for PokaYoke {
fn default() -> Self {
Self::new()
}
}
impl Default for MURA {
fn default() -> Self {
Self::new()
}
}
impl MURA {
pub fn new() -> Self {
Self {
standards: Vec::new(),
consistency_metrics: BTreeMap::new(),
violations: Vec::new(),
}
}
pub fn add_standard(&mut self, standard: Standard) {
self.standards.push(standard);
}
pub fn record_violation(&mut self, violation: MuraViolation) {
self.violations.push(violation);
}
pub fn consistency_score(&self) -> f32 {
if self.consistency_metrics.is_empty() {
100.0
} else {
let sum: f32 = self.consistency_metrics.values().sum();
sum / self.consistency_metrics.len() as f32
}
}
pub fn critical_violations(&self) -> Vec<&MuraViolation> {
self.violations
.iter()
.filter(|v| v.severity == ViolationSeverity::High)
.collect()
}
}
impl Default for MUDA {
fn default() -> Self {
Self::new()
}
}
impl MUDA {
pub fn new() -> Self {
Self {
waste_types: vec![
WasteType::Defects,
WasteType::OverProcessing,
WasteType::Overproduction,
WasteType::Transportation,
WasteType::Inventory,
WasteType::Waiting,
WasteType::MotionWaste,
WasteType::UnusedTalent,
],
waste_items: Vec::new(),
opportunities: Vec::new(),
metrics: WasteMetrics::default(),
}
}
pub fn add_waste_item(&mut self, item: WasteItem) {
let waste_type_str = format!("{:?}", item.waste_type);
*self
.metrics
.waste_by_type
.entry(waste_type_str)
.or_insert(0) += 1;
self.metrics.total_waste_items += 1;
if item.elimination_difficulty == DifficultyLevel::Easy {
self.metrics.improvement_potential += 5.0;
} else if item.elimination_difficulty == DifficultyLevel::Medium {
self.metrics.improvement_potential += 3.0;
}
self.waste_items.push(item);
}
pub fn add_opportunity(&mut self, opportunity: OptimizationOpportunity) {
self.opportunities.push(opportunity);
}
pub fn high_priority_opportunities(&self) -> Vec<&OptimizationOpportunity> {
self.opportunities
.iter()
.filter(|o| o.priority >= 4)
.collect()
}
pub fn quick_wins(&self) -> Vec<&WasteItem> {
self.waste_items
.iter()
.filter(|w| w.elimination_difficulty == DifficultyLevel::Easy)
.collect()
}
}
impl fmt::Display for WasteType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WasteType::Defects => write!(f, "Defects"),
WasteType::OverProcessing => write!(f, "Over-processing"),
WasteType::Overproduction => write!(f, "Over-production"),
WasteType::Transportation => write!(f, "Transportation"),
WasteType::Inventory => write!(f, "Inventory"),
WasteType::Waiting => write!(f, "Waiting"),
WasteType::MotionWaste => write!(f, "Motion Waste"),
WasteType::UnusedTalent => write!(f, "Unused Talent"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fmea_rpn_calculation() {
let mut mode = FailureMode {
name: "Pack version conflict".to_string(),
potential_cause: "Incompatible versions".to_string(),
potential_effect: "Generation failure".to_string(),
severity: 8,
occurrence: 6,
detection: 4,
preventive_actions: vec![],
current_controls: vec![],
rpn: 0,
};
mode.calculate_rpn();
assert_eq!(mode.rpn, 8 * 6 * 4); assert_eq!(mode.severity_level(), SeverityLevel::High);
}
#[test]
fn test_poka_yoke_effectiveness() {
let mut poka = PokaYoke::new();
poka.stats.errors_prevented = 95;
poka.stats.errors_detected = 5;
assert_eq!(poka.prevention_effectiveness(), 95.0);
}
#[test]
fn test_muda_quick_wins() {
let mut muda = MUDA::new();
muda.add_waste_item(WasteItem {
id: "waste-1".to_string(),
waste_type: WasteType::Defects,
description: "Redundant validation".to_string(),
current_impact: "10% overhead".to_string(),
frequency: "Every build".to_string(),
elimination_difficulty: DifficultyLevel::Easy,
});
let quick_wins = muda.quick_wins();
assert_eq!(quick_wins.len(), 1);
}
}