8dd1581fae
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>
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
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
|
||
}
|