feat(iace): Sprint 4D — Failure Mode Layer (FMEA-Faehigkeit)

150 Failure Modes in 11 ComponentTypes:
- Sensor (20): Signalverlust, Drift, Falschmeldung, Encoder-spezifisch
- Controller (20): Watchdog, Speicher, Bus, Safety-SPS CCF, Antrieb
- Actuator (15): Blockiert, Ueberlast, Haltekraftverlust, Schuetz verschweisst
- Mechanical (20): Ermuedungsbruch, Lagerschaden, Kettenriss, Werkzeugbruch
- Electrical (15): Isolation, Kurzschluss, Erdschluss, Lichtbogen
- Software (15): Exception, Race Condition, Buffer Overflow, Timing
- Hydraulic/Pneumatic (15): Schlauchplatzer, Ventil blockiert, Kavitation
- Safety Device (15): Failure-to-trip, CCF, Bremsenverschleiss, PL-Degradation
- Network (10): Paketverlust, Latenz, Man-in-the-Middle
- AI/ML (5): Model Drift, Adversarial Input, Bias

Architektur:
- FailureModeEntry Struct mit FMEA-Scores (Severity/Occurrence/Detection 1-10)
- RPZ = S x O x D (max 1000, Schwelle >= 100 = Massnahme erforderlich)
- RequiredFailureModes auf HazardPattern fuer FM-gesteuertes Pattern-Matching
- MatchInput.FailureModes + MatchReason "failure_mode" (Explainability)
- GET /failure-modes?component_type= API-Endpoint

10 Tests: Count, UniqueIDs, ValidTypes, NonEmpty, Distribution, RPZ (3x), NilFires, RPZDistribution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-10 22:24:02 +02:00
parent 9cbbc6ee2f
commit 9c0d471277
8 changed files with 448 additions and 2 deletions
@@ -17,6 +17,9 @@ type MatchInput struct {
// HumanRoles are the human roles interacting with the machine in this project.
// Used to filter patterns that only apply to specific roles (e.g. programmer, maintenance_tech).
HumanRoles []string `json:"human_roles,omitempty"`
// FailureModes are the active failure mode IDs relevant for this project.
// Used to filter patterns that require specific failure modes.
FailureModes []string `json:"failure_modes,omitempty"`
}
// MatchOutput contains the results of pattern matching.
@@ -56,8 +59,9 @@ type PatternMatch struct {
RequiresExpert bool `json:"requires_expert,omitempty"`
OperationalStates []string `json:"operational_states,omitempty"`
StateTransitions []string `json:"state_transitions,omitempty"`
HumanRoles []string `json:"human_roles,omitempty"`
GeneratedHazardType string `json:"generated_hazard_type,omitempty"`
HumanRoles []string `json:"human_roles,omitempty"`
GeneratedHazardType string `json:"generated_hazard_type,omitempty"`
MatchedFailureModes []string `json:"matched_failure_modes,omitempty"`
}
// HazardSuggestion is a suggested hazard from pattern matching.
@@ -209,6 +213,17 @@ func (e *PatternEngine) Match(input MatchInput) *MatchOutput {
reasons = append(reasons, MatchReason{Type: "human_role", Tag: r, Met: roleSet[r]})
}
}
var matchedFMs []string
if len(p.RequiredFailureModes) > 0 {
fmSet := toSet(input.FailureModes)
for _, fm := range p.RequiredFailureModes {
met := fmSet[fm]
reasons = append(reasons, MatchReason{Type: "failure_mode", Tag: fm, Met: met})
if met {
matchedFMs = append(matchedFMs, fm)
}
}
}
matchedPatterns = append(matchedPatterns, PatternMatch{
PatternID: p.ID,
@@ -230,6 +245,7 @@ func (e *PatternEngine) Match(input MatchInput) *MatchOutput {
StateTransitions: p.StateTransitions,
HumanRoles: p.HumanRoles,
GeneratedHazardType: p.GeneratedHazardType,
MatchedFailureModes: matchedFMs,
})
for _, cat := range p.GeneratedHazardCats {
@@ -382,6 +398,21 @@ func patternMatches(p HazardPattern, tagSet map[string]bool, input MatchInput) b
}
}
// If pattern requires specific failure modes, at least one must match.
if len(p.RequiredFailureModes) > 0 && len(input.FailureModes) > 0 {
found := false
fmSet := toSet(input.FailureModes)
for _, fm := range p.RequiredFailureModes {
if fmSet[fm] {
found = true
break
}
}
if !found {
return false
}
}
return true
}