2afa5a179b
Phase 17 of the risk-assessment polish. Two pieces:
A) PLr per EN ISO 13849-1 Anhang A (Risikograph)
- HazardPattern.DefaultAvoidability (1 = P1, 2 = P2). Optional;
defaults to P1 if unset (conservative — operator can raise after
review).
- ComputePLr(s,f,p) implements the canonical 8-leaf binary tree
(S1F1P1 -> a, ..., S2F2P2 -> e). Pinned by 8 table-driven tests.
- SeverityToS / ExposureToF map the existing 1-5 fields to the
binary S/F at the documented threshold (3).
- At project initialise, every hazard's Description is appended
with "Risikograph EN ISO 13849-1 (Anhang A): S2 · F1 · P1 -> PLr c"
so the audit value is visible without leaving the hazard view.
- PatternMatch carries DefaultAvoidability so the init handler can
pick it up without a second pattern lookup.
B) Methoden-Kopf am Bericht
- GET /clarifications.html now opens with a standardised methodology
block: ISO 12100 Anhang B (hazard ID) + ISO 13849-1 Anhang A
(PLr graph) + ISO 12100 6.2/6.3/6.4 (reduction hierarchy). Same
wording on every export, ready for the Anlagenbauer-Uebergabe.
- Only norm identifiers — no norm text reproduced.
C) ISO12100Section in Hazard Description
- When a pattern is labeled with ISO12100Section, the hazard
description gets a "Klassifikation: EN ISO 12100 Anhang B,
Abschnitt 6.3.5.4" suffix. Provenance for the auditor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
43 lines
1.2 KiB
Go
43 lines
1.2 KiB
Go
package iace
|
|
|
|
import "testing"
|
|
|
|
// TestComputePLr_Canonical8 pins the 8 leaves of the EN ISO 13849-1
|
|
// Annex A risk graph: S1/S2 x F1/F2 x P1/P2 -> a..e.
|
|
func TestComputePLr_Canonical8(t *testing.T) {
|
|
cases := []struct {
|
|
s, f, p int
|
|
want string
|
|
}{
|
|
{1, 1, 1, "a"},
|
|
{1, 1, 2, "b"},
|
|
{1, 2, 1, "b"},
|
|
{1, 2, 2, "c"},
|
|
{2, 1, 1, "c"},
|
|
{2, 1, 2, "d"},
|
|
{2, 2, 1, "d"},
|
|
{2, 2, 2, "e"}, // worst case: severe + frequent + hardly avoidable
|
|
}
|
|
for _, c := range cases {
|
|
got := ComputePLr(c.s, c.f, c.p)
|
|
if got != c.want {
|
|
t.Errorf("ComputePLr(S%d F%d P%d) = %q, want %q", c.s, c.f, c.p, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestSeverityExposureMapping ensures the 1-5 internal fields map to the
|
|
// correct binary S/F parameter at the documented threshold (3).
|
|
func TestSeverityExposureMapping(t *testing.T) {
|
|
for sev, wantS := range map[int]int{1: 1, 2: 1, 3: 2, 4: 2, 5: 2} {
|
|
if got := SeverityToS(sev); got != wantS {
|
|
t.Errorf("SeverityToS(%d) = %d, want %d", sev, got, wantS)
|
|
}
|
|
}
|
|
for exp, wantF := range map[int]int{1: 1, 2: 1, 3: 2, 4: 2, 5: 2} {
|
|
if got := ExposureToF(exp); got != wantF {
|
|
t.Errorf("ExposureToF(%d) = %d, want %d", exp, got, wantF)
|
|
}
|
|
}
|
|
}
|