Files
breakpilot-compliance/ai-compliance-sdk/internal/iace/sil_pl_calculator.go
T
Benjamin Admin 8dd1581fae feat: IACE SIL/PL calculator + Cobot patterns + library extensions
SIL/PL Calculator: Deterministic S×E×P → PL (a-e) → SIL (1-3) mapping
Cobot Patterns (HP059-HP065): Human-robot collision, afterrun, misprogramming
Press Patterns split into separate file (500-line guardrail)
5 new components (C136-C140), 5 new tags, 18 keyword entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 09:29:03 +02:00

140 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package iace
// SILPLResult contains the deterministic SIL/PL recommendation
// derived from the risk assessment S×E×P values, plus expert override fields.
type SILPLResult struct {
RecommendedPL string `json:"recommended_pl"` // "a", "b", "c", "d", "e"
RecommendedSIL string `json:"recommended_sil"` // "none", "SIL 1", "SIL 2", "SIL 3"
RiskGraphInput RiskGraphIn `json:"risk_graph_input"`
// Expert override fields (filled by Fachmann)
OverriddenPL string `json:"overridden_pl,omitempty"`
OverriddenSIL string `json:"overridden_sil,omitempty"`
OverrideReason string `json:"override_reason,omitempty"`
NormReferences []string `json:"norm_references,omitempty"`
ExpertValidated bool `json:"expert_validated"`
ValidatedBy string `json:"validated_by,omitempty"`
ValidatedAt string `json:"validated_at,omitempty"`
}
// RiskGraphIn shows the mapped ISO risk graph factors.
type RiskGraphIn struct {
S string `json:"s"` // "S1" or "S2"
F string `json:"f"` // "F1" or "F2"
P string `json:"p"` // "P1" or "P2"
}
// CalculateSILPL derives Performance Level (PL) and Safety Integrity Level (SIL)
// from the IACE risk assessment factors (Severity 1-5, Exposure 1-5, Probability 1-5).
//
// This is a deterministic mapping based on the general risk graph principle:
// - Severity ≥ 4 → S2 (serious/fatal), else S1 (minor/reversible)
// - Exposure ≥ 3 → F2 (frequent/continuous), else F1 (rare/short)
// - Probability ≥ 4 → P2 (hardly avoidable), else P1 (avoidable)
//
// NO normative text is reproduced. This is our own implementation.
func CalculateSILPL(severity, exposure, probability int) SILPLResult {
s, f, p := mapToRiskGraph(severity, exposure, probability)
pl := calculatePL(s, f, p)
sil := plToSIL(pl)
return SILPLResult{
RecommendedPL: pl,
RecommendedSIL: sil,
RiskGraphInput: RiskGraphIn{S: s, F: f, P: p},
}
}
// mapToRiskGraph converts 1-5 scale factors to binary S1/S2, F1/F2, P1/P2.
func mapToRiskGraph(severity, exposure, probability int) (string, string, string) {
s := "S1"
if severity >= 4 {
s = "S2"
}
f := "F1"
if exposure >= 3 {
f = "F2"
}
p := "P1"
if probability >= 4 {
p = "P2"
}
return s, f, p
}
// calculatePL determines Performance Level from the risk graph.
//
// Risk graph (own formulation, no norm text):
//
// S1+F1+P1 a (lowest)
// S1+F1+P2 → b
// S1+F2+P1 → b
// S1+F2+P2 → c
// S2+F1+P1 → c
// S2+F1+P2 → d
// S2+F2+P1 → d
// S2+F2+P2 → e (highest)
func calculatePL(s, f, p string) string {
if s == "S1" {
if f == "F1" {
if p == "P1" {
return "a"
}
return "b"
}
// F2
if p == "P1" {
return "b"
}
return "c"
}
// S2
if f == "F1" {
if p == "P1" {
return "c"
}
return "d"
}
// S2+F2
if p == "P1" {
return "d"
}
return "e"
}
// plToSIL maps Performance Level to Safety Integrity Level.
//
// PL a, b → no SIL requirement
// PL c → SIL 1
// PL d → SIL 2
// PL e → SIL 3
func plToSIL(pl string) string {
switch pl {
case "a", "b":
return "none"
case "c":
return "SIL 1"
case "d":
return "SIL 2"
case "e":
return "SIL 3"
default:
return "none"
}
}
// GetEffectivePL returns the expert-overridden PL if set, otherwise the recommendation.
func (r *SILPLResult) GetEffectivePL() string {
if r.OverriddenPL != "" {
return r.OverriddenPL
}
return r.RecommendedPL
}
// GetEffectiveSIL returns the expert-overridden SIL if set, otherwise the recommendation.
func (r *SILPLResult) GetEffectiveSIL() string {
if r.OverriddenSIL != "" {
return r.OverriddenSIL
}
return r.RecommendedSIL
}