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>
This commit is contained in:
Benjamin Admin
2026-05-05 09:29:03 +02:00
parent ea8353f1a0
commit 8dd1581fae
9 changed files with 430 additions and 150 deletions
@@ -0,0 +1,139 @@
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
}