From 8dd1581fae59b59422183b38b19badfd99e8ee74 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 5 May 2026 09:29:03 +0200 Subject: [PATCH] feat: IACE SIL/PL calculator + Cobot patterns + library extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../internal/iace/component_library.go | 7 + .../internal/iace/hazard_pattern_types.go | 22 +++ .../internal/iace/hazard_patterns.go | 147 +----------------- .../internal/iace/hazard_patterns_cobot.go | 86 ++++++++++ .../internal/iace/hazard_patterns_press.go | 141 +++++++++++++++++ .../internal/iace/keyword_dictionary.go | 21 +++ .../internal/iace/pattern_engine.go | 10 +- .../internal/iace/sil_pl_calculator.go | 139 +++++++++++++++++ .../internal/iace/tag_taxonomy.go | 7 + 9 files changed, 430 insertions(+), 150 deletions(-) create mode 100644 ai-compliance-sdk/internal/iace/hazard_pattern_types.go create mode 100644 ai-compliance-sdk/internal/iace/hazard_patterns_cobot.go create mode 100644 ai-compliance-sdk/internal/iace/hazard_patterns_press.go create mode 100644 ai-compliance-sdk/internal/iace/sil_pl_calculator.go diff --git a/ai-compliance-sdk/internal/iace/component_library.go b/ai-compliance-sdk/internal/iace/component_library.go index ddf3fe8..cf09bc2 100644 --- a/ai-compliance-sdk/internal/iace/component_library.go +++ b/ai-compliance-sdk/internal/iace/component_library.go @@ -188,6 +188,13 @@ func GetComponentLibrary() []ComponentLibraryEntry { {ID: "C133", NameDE: "Schwungrad", NameEN: "Flywheel", Category: "mechanical", DescriptionDE: "Energiespeicher fuer mechanische Pressen (Drehenergie).", TypicalHazardCategories: []string{"mechanical_hazard"}, TypicalEnergySources: []string{"EN02", "EN03"}, MapsToComponentType: "mechanical", Tags: []string{"rotating_part", "stored_energy", "high_speed"}, SortOrder: 133}, {ID: "C134", NameDE: "Kistenwechselstation", NameEN: "Bin Changeover Station", Category: "mechanical", DescriptionDE: "Bereich zum manuellen Wechsel von Auffangkisten waehrend des Betriebs.", TypicalHazardCategories: []string{"mechanical_hazard", "ergonomic"}, TypicalEnergySources: []string{"EN04"}, MapsToComponentType: "mechanical", Tags: []string{"moving_part", "gravity_risk", "ergonomic"}, SortOrder: 134}, {ID: "C135", NameDE: "Waage / Pruefstation", NameEN: "Scale / Inspection Station", Category: "sensor", DescriptionDE: "Inline-Wiegestation oder Pruefeinrichtung zur Qualitaetskontrolle.", TypicalHazardCategories: []string{}, TypicalEnergySources: []string{}, MapsToComponentType: "sensor", Tags: []string{"sensor_part"}, SortOrder: 135}, + + // ── Extended: Cobot / Collaborative Robot Components (C136-C140) ───── + {ID: "C136", NameDE: "Roboter-Endeffektor / Werkzeug", NameEN: "Robot End Effector / Tool", Category: "mechanical", DescriptionDE: "Am Roboterarm montiertes Werkzeug oder Greifer — bestimmt das Verletzungsrisiko im kollaborierenden Betrieb.", TypicalHazardCategories: []string{"mechanical_hazard"}, TypicalEnergySources: []string{"EN01"}, MapsToComponentType: "mechanical", Tags: []string{"moving_part", "cutting_part", "pinch_point", "tool_at_robot"}, SortOrder: 136}, + {ID: "C137", NameDE: "Werkstueck (generisch, variabel)", NameEN: "Workpiece (generic, variable)", Category: "mechanical", DescriptionDE: "Wechselnde Werkstuecke am Roboter — erfordern separate Beurteilung je nach Geometrie und Gewicht.", TypicalHazardCategories: []string{"mechanical_hazard"}, TypicalEnergySources: []string{"EN04"}, MapsToComponentType: "mechanical", Tags: []string{"moving_part", "gravity_risk", "variable_workpiece"}, SortOrder: 137}, + {ID: "C138", NameDE: "Sicherheitsscanner (Cobot-Bereich)", NameEN: "Safety Scanner (Cobot Zone)", Category: "sensor", DescriptionDE: "Laserscanner zur Ueberwachung des Kollaborationsbereichs — triggert Betriebsartwechsel.", TypicalHazardCategories: []string{}, TypicalEnergySources: []string{}, MapsToComponentType: "sensor", Tags: []string{"sensor_part", "safety_device"}, SortOrder: 138}, + {ID: "C139", NameDE: "Kollaborierender Roboter (Cobot)", NameEN: "Collaborative Robot (Cobot)", Category: "mechanical", DescriptionDE: "Roboter fuer den direkten Mensch-Maschine-Betrieb ohne trennende Schutzeinrichtungen.", TypicalHazardCategories: []string{"mechanical_hazard", "ergonomic"}, TypicalEnergySources: []string{"EN01", "EN02"}, MapsToComponentType: "mechanical", Tags: []string{"moving_part", "has_software", "programmable", "collaborative_operation", "force_limited"}, SortOrder: 139}, + {ID: "C140", NameDE: "Kraft-/Momentsensor (Roboter)", NameEN: "Force/Torque Sensor (Robot)", Category: "sensor", DescriptionDE: "Sensor zur Erkennung von Kollisionen und Kraftbegrenzung im kollaborierenden Betrieb.", TypicalHazardCategories: []string{}, TypicalEnergySources: []string{}, MapsToComponentType: "sensor", Tags: []string{"sensor_part", "safety_device", "force_limited"}, SortOrder: 140}, } } diff --git a/ai-compliance-sdk/internal/iace/hazard_pattern_types.go b/ai-compliance-sdk/internal/iace/hazard_pattern_types.go new file mode 100644 index 0000000..b742765 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/hazard_pattern_types.go @@ -0,0 +1,22 @@ +package iace + +// HazardPattern defines a rule that matches component/energy tags to +// hazards, measures, and evidence. When a pattern's required tags are all +// present (AND) and none of its excluded tags are present (NOT), it fires. +type HazardPattern struct { + ID string `json:"id"` + NameDE string `json:"name_de"` + NameEN string `json:"name_en"` + RequiredComponentTags []string `json:"required_component_tags"` + RequiredEnergyTags []string `json:"required_energy_tags"` + RequiredLifecycles []string `json:"required_lifecycle_phases"` + ExcludedComponentTags []string `json:"excluded_component_tags"` + GeneratedHazardCats []string `json:"generated_hazard_categories"` + SuggestedMeasureIDs []string `json:"suggested_measure_ids"` + SuggestedEvidenceIDs []string `json:"suggested_evidence_ids"` + Priority int `json:"priority"` + // Expert calculation hint — shown when pattern fires and expert validation is needed + RequiresExpertCalculation bool `json:"requires_expert_calculation,omitempty"` + ExpertHintDE string `json:"expert_hint_de,omitempty"` + ExpertHintEN string `json:"expert_hint_en,omitempty"` +} diff --git a/ai-compliance-sdk/internal/iace/hazard_patterns.go b/ai-compliance-sdk/internal/iace/hazard_patterns.go index b527b1e..b15f0eb 100644 --- a/ai-compliance-sdk/internal/iace/hazard_patterns.go +++ b/ai-compliance-sdk/internal/iace/hazard_patterns.go @@ -1,21 +1,6 @@ package iace -// HazardPattern defines a rule that matches component/energy tags to -// hazards, measures, and evidence. When a pattern's required tags are all -// present (AND) and none of its excluded tags are present (NOT), it fires. -type HazardPattern struct { - ID string `json:"id"` - NameDE string `json:"name_de"` - NameEN string `json:"name_en"` - RequiredComponentTags []string `json:"required_component_tags"` - RequiredEnergyTags []string `json:"required_energy_tags"` - RequiredLifecycles []string `json:"required_lifecycle_phases"` - ExcludedComponentTags []string `json:"excluded_component_tags"` - GeneratedHazardCats []string `json:"generated_hazard_categories"` - SuggestedMeasureIDs []string `json:"suggested_measure_ids"` - SuggestedEvidenceIDs []string `json:"suggested_evidence_ids"` - Priority int `json:"priority"` -} +// HazardPattern is defined in hazard_pattern_types.go // GetBuiltinHazardPatterns returns ~44 built-in hazard patterns organized // by domain (mechanical, electrical, thermal, hydraulic/pneumatic, @@ -456,134 +441,4 @@ func GetBuiltinHazardPatterns() []HazardPattern { }, // ================================================================ - // Press/Forming Machine Patterns (HP045-HP058) - // ================================================================ - { - ID: "HP045", NameDE: "Stoesselabsturz durch Druckverlust", NameEN: "Ram drop due to pressure loss", - RequiredComponentTags: []string{"hydraulic_part", "gravity_risk", "high_force"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"mechanical_hazard", "pneumatic_hydraulic"}, - SuggestedMeasureIDs: []string{"M001", "M051", "M054", "M131"}, - SuggestedEvidenceIDs: []string{"E01", "E08", "E20"}, - Priority: 98, - }, - { - ID: "HP046", NameDE: "Quetschen im Werkzeugeinbauraum", NameEN: "Crushing in die space", - RequiredComponentTags: []string{"crush_point", "high_force"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"mechanical_hazard"}, - SuggestedMeasureIDs: []string{"M001", "M005", "M051", "M106"}, - SuggestedEvidenceIDs: []string{"E01", "E08"}, - Priority: 97, - }, - { - ID: "HP047", NameDE: "Oelnebelexposition Atemwege", NameEN: "Oil mist inhalation exposure", - RequiredComponentTags: []string{"hydraulic_part", "oil_mist_risk"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"material_environmental"}, - SuggestedMeasureIDs: []string{"M124", "M141"}, - SuggestedEvidenceIDs: []string{"E20"}, - Priority: 80, - }, - { - ID: "HP048", NameDE: "Verbrennung durch heisse Werkstuecke", NameEN: "Burns from hot workpieces", - RequiredComponentTags: []string{"moving_part"}, - RequiredEnergyTags: []string{"thermal"}, - GeneratedHazardCats: []string{"thermal_hazard"}, - SuggestedMeasureIDs: []string{"M054", "M141"}, - SuggestedEvidenceIDs: []string{"E08"}, - Priority: 85, - }, - { - ID: "HP049", NameDE: "Schwebende Last (Hubwerk/Aufzug)", NameEN: "Suspended load (hoist/elevator)", - RequiredComponentTags: []string{"gravity_risk", "person_under_load"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"mechanical_hazard"}, - SuggestedMeasureIDs: []string{"M001", "M005", "M051"}, - SuggestedEvidenceIDs: []string{"E01", "E08"}, - Priority: 95, - }, - { - ID: "HP050", NameDE: "Einziehen/Scheren Transfersystem", NameEN: "Draw-in/shearing at transfer system", - RequiredComponentTags: []string{"moving_part", "shear_risk"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"mechanical_hazard"}, - SuggestedMeasureIDs: []string{"M001", "M005", "M051"}, - SuggestedEvidenceIDs: []string{"E01", "E08"}, - Priority: 90, - }, - { - ID: "HP051", NameDE: "Sturzgefahr Auswurfbereich", NameEN: "Fall hazard at ejection area", - RequiredComponentTags: []string{"gravity_risk"}, - RequiredEnergyTags: []string{}, - ExcludedComponentTags: []string{"safety_device"}, - GeneratedHazardCats: []string{"mechanical_hazard"}, - SuggestedMeasureIDs: []string{"M051", "M141"}, - SuggestedEvidenceIDs: []string{"E20"}, - Priority: 75, - }, - { - ID: "HP052", NameDE: "Druckfreisetzung Hydraulikspeicher", NameEN: "Pressure release from hydraulic accumulator", - RequiredComponentTags: []string{"hydraulic_part", "high_pressure"}, - RequiredEnergyTags: []string{"stored_energy"}, - GeneratedHazardCats: []string{"pneumatic_hydraulic"}, - SuggestedMeasureIDs: []string{"M051", "M131"}, - SuggestedEvidenceIDs: []string{"E01", "E08"}, - Priority: 92, - }, - { - ID: "HP053", NameDE: "Impulslaerm Pressvorgang", NameEN: "Impact noise during press operation", - RequiredComponentTags: []string{"noise_source", "high_force"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"noise_vibration"}, - SuggestedMeasureIDs: []string{"M141"}, - SuggestedEvidenceIDs: []string{"E20"}, - Priority: 70, - }, - { - ID: "HP054", NameDE: "Schwungrad-Restenergie nach Abschaltung", NameEN: "Flywheel residual energy after shutdown", - RequiredComponentTags: []string{"rotating_part", "stored_energy"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"mechanical_hazard"}, - SuggestedMeasureIDs: []string{"M001", "M054"}, - SuggestedEvidenceIDs: []string{"E01", "E08"}, - Priority: 88, - }, - { - ID: "HP055", NameDE: "Umgehung Schutzeinrichtung (Pressentuer)", NameEN: "Bypass of safety guard (press door)", - RequiredComponentTags: []string{"interlocked", "crush_point"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"safety_function_failure"}, - SuggestedMeasureIDs: []string{"M005", "M106"}, - SuggestedEvidenceIDs: []string{"E01", "E08"}, - Priority: 96, - }, - { - ID: "HP056", NameDE: "Fehlbedienung Zweihandschaltung", NameEN: "Two-hand control misoperation", - RequiredComponentTags: []string{"two_hand_control_required"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"safety_function_failure"}, - SuggestedMeasureIDs: []string{"M106"}, - SuggestedEvidenceIDs: []string{"E01"}, - Priority: 90, - }, - { - ID: "HP057", NameDE: "Hydraulikoelleckage + Rutschgefahr", NameEN: "Hydraulic oil leakage + slip hazard", - RequiredComponentTags: []string{"hydraulic_part", "chemical_risk"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"material_environmental"}, - SuggestedMeasureIDs: []string{"M141"}, - SuggestedEvidenceIDs: []string{"E20"}, - Priority: 65, - }, - { - ID: "HP058", NameDE: "Ergonomische Belastung Kistenwechsel", NameEN: "Ergonomic strain during bin changeover", - RequiredComponentTags: []string{"ergonomic", "moving_part"}, - RequiredEnergyTags: []string{}, - GeneratedHazardCats: []string{"ergonomic"}, - SuggestedMeasureIDs: []string{"M141"}, - SuggestedEvidenceIDs: []string{"E20"}, - Priority: 55, - }, - } } diff --git a/ai-compliance-sdk/internal/iace/hazard_patterns_cobot.go b/ai-compliance-sdk/internal/iace/hazard_patterns_cobot.go new file mode 100644 index 0000000..c543130 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/hazard_patterns_cobot.go @@ -0,0 +1,86 @@ +package iace + +// GetCobotHazardPatterns returns patterns specific to collaborative robots, +// press-specific expert hints, and the afterrun/SIL calculation domain. +// These extend the builtin patterns (HP045-HP058 for press, HP059+ for cobot). +func GetCobotHazardPatterns() []HazardPattern { + return []HazardPattern{ + // ================================================================ + // Collaborative Robot (Cobot) Patterns (HP059-HP065) + // ================================================================ + { + ID: "HP059", NameDE: "Kollision Mensch-Roboter (Kraft/Geschwindigkeit)", NameEN: "Human-robot collision (force/speed)", + RequiredComponentTags: []string{"collaborative_operation", "moving_part"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M054"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 98, + RequiresExpertCalculation: true, + ExpertHintDE: "Sicherheitsabstaende und maximal zulaessige Kraefte/Geschwindigkeiten muessen berechnet werden.", + ExpertHintEN: "Safety distances and maximum permissible forces/speeds must be calculated.", + }, + { + ID: "HP060", NameDE: "Quetschen durch Werkzeug am Cobot", NameEN: "Crushing by tool on cobot", + RequiredComponentTags: []string{"tool_at_robot", "collaborative_operation"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M054", "M141"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 95, + RequiresExpertCalculation: true, + ExpertHintDE: "Werkzeug-/Werkstueckgeometrie bestimmt das Quetschrisiko. Separate Beurteilung pro Werkzeug erforderlich.", + ExpertHintEN: "Tool/workpiece geometry determines crushing risk. Separate assessment per tool required.", + }, + { + ID: "HP061", NameDE: "Nachlauf nach Stopp (Reaktionszeit)", NameEN: "Afterrun after stop (reaction time)", + RequiredComponentTags: []string{"afterrun_risk", "moving_part"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M054"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 90, + RequiresExpertCalculation: true, + ExpertHintDE: "Nachlaufwegberechnung erforderlich. Sicherheitsfeld muss entsprechend dimensioniert werden.", + ExpertHintEN: "Afterrun distance calculation required. Safety field must be dimensioned accordingly.", + }, + { + ID: "HP062", NameDE: "Fehlprogrammierung Kraft-/Geschwindigkeitsgrenzwerte", NameEN: "Misprogramming of force/speed limits", + RequiredComponentTags: []string{"programmable", "force_limited"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"safety_function_failure"}, + SuggestedMeasureIDs: []string{"M106"}, + SuggestedEvidenceIDs: []string{"E01"}, + Priority: 88, + ExpertHintDE: "Grenzwerte duerfen nur nach Risikobeurteilung veraendert werden. Zugriffsschutz erforderlich.", + }, + { + ID: "HP063", NameDE: "Werkstueck-Herabfallen vom Roboter", NameEN: "Workpiece drop from robot", + RequiredComponentTags: []string{"variable_workpiece", "gravity_risk"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M051"}, + SuggestedEvidenceIDs: []string{"E08"}, + Priority: 82, + }, + { + ID: "HP064", NameDE: "Quetschen im Roboter-Arbeitsraum (nicht-kollaborierend)", NameEN: "Crushing in robot workspace (non-collaborative)", + RequiredComponentTags: []string{"moving_part", "high_force", "sensor_part"}, + RequiredEnergyTags: []string{}, + ExcludedComponentTags: []string{"collaborative_operation"}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M005", "M051"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 94, + }, + { + ID: "HP065", NameDE: "Einklemmen am Arbeitstisch (Cobot-Zelle)", NameEN: "Trapping at work table (cobot cell)", + RequiredComponentTags: []string{"pinch_point", "structural_part"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001"}, + SuggestedEvidenceIDs: []string{"E08"}, + Priority: 70, + }, + } +} diff --git a/ai-compliance-sdk/internal/iace/hazard_patterns_press.go b/ai-compliance-sdk/internal/iace/hazard_patterns_press.go new file mode 100644 index 0000000..f4943e0 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/hazard_patterns_press.go @@ -0,0 +1,141 @@ +package iace + +// GetPressHazardPatterns returns patterns specific to hydraulic/mechanical presses, +// forming machines, and related equipment (HP045-HP058). +func GetPressHazardPatterns() []HazardPattern { + return []HazardPattern{ + // ================================================================ + // Press/Forming Machine Patterns (HP045-HP058) + // ================================================================ + { + ID: "HP045", NameDE: "Stoesselabsturz durch Druckverlust", NameEN: "Ram drop due to pressure loss", + RequiredComponentTags: []string{"hydraulic_part", "gravity_risk", "high_force"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard", "pneumatic_hydraulic"}, + SuggestedMeasureIDs: []string{"M001", "M051", "M054", "M131"}, + SuggestedEvidenceIDs: []string{"E01", "E08", "E20"}, + Priority: 98, + RequiresExpertCalculation: true, + ExpertHintDE: "SIL/PL-Nachweis fuer Stoesselabsturzsicherung erforderlich.", + ExpertHintEN: "SIL/PL verification for ram drop protection required.", + }, + { + ID: "HP046", NameDE: "Quetschen im Werkzeugeinbauraum", NameEN: "Crushing in die space", + RequiredComponentTags: []string{"crush_point", "high_force"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M005", "M051", "M106"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 97, + }, + { + ID: "HP047", NameDE: "Oelnebelexposition Atemwege", NameEN: "Oil mist inhalation exposure", + RequiredComponentTags: []string{"hydraulic_part", "oil_mist_risk"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"material_environmental"}, + SuggestedMeasureIDs: []string{"M124", "M141"}, + SuggestedEvidenceIDs: []string{"E20"}, + Priority: 80, + }, + { + ID: "HP048", NameDE: "Verbrennung durch heisse Werkstuecke", NameEN: "Burns from hot workpieces", + RequiredComponentTags: []string{"moving_part"}, + RequiredEnergyTags: []string{"thermal"}, + GeneratedHazardCats: []string{"thermal_hazard"}, + SuggestedMeasureIDs: []string{"M054", "M141"}, + SuggestedEvidenceIDs: []string{"E08"}, + Priority: 85, + }, + { + ID: "HP049", NameDE: "Schwebende Last (Hubwerk/Aufzug)", NameEN: "Suspended load (hoist/elevator)", + RequiredComponentTags: []string{"gravity_risk", "person_under_load"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M005", "M051"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 95, + }, + { + ID: "HP050", NameDE: "Einziehen/Scheren Transfersystem", NameEN: "Draw-in/shearing at transfer system", + RequiredComponentTags: []string{"moving_part", "shear_risk"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M005", "M051"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 90, + }, + { + ID: "HP051", NameDE: "Sturzgefahr Auswurfbereich", NameEN: "Fall hazard at ejection area", + RequiredComponentTags: []string{"gravity_risk"}, + RequiredEnergyTags: []string{}, + ExcludedComponentTags: []string{"safety_device"}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M051", "M141"}, + SuggestedEvidenceIDs: []string{"E20"}, + Priority: 75, + }, + { + ID: "HP052", NameDE: "Druckfreisetzung Hydraulikspeicher", NameEN: "Pressure release from hydraulic accumulator", + RequiredComponentTags: []string{"hydraulic_part", "high_pressure"}, + RequiredEnergyTags: []string{"stored_energy"}, + GeneratedHazardCats: []string{"pneumatic_hydraulic"}, + SuggestedMeasureIDs: []string{"M051", "M131"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 92, + }, + { + ID: "HP053", NameDE: "Impulslaerm Pressvorgang", NameEN: "Impact noise during press operation", + RequiredComponentTags: []string{"noise_source", "high_force"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"noise_vibration"}, + SuggestedMeasureIDs: []string{"M141"}, + SuggestedEvidenceIDs: []string{"E20"}, + Priority: 70, + }, + { + ID: "HP054", NameDE: "Schwungrad-Restenergie nach Abschaltung", NameEN: "Flywheel residual energy after shutdown", + RequiredComponentTags: []string{"rotating_part", "stored_energy"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"mechanical_hazard"}, + SuggestedMeasureIDs: []string{"M001", "M054"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 88, + }, + { + ID: "HP055", NameDE: "Umgehung Schutzeinrichtung (Pressentuer)", NameEN: "Bypass of safety guard (press door)", + RequiredComponentTags: []string{"interlocked", "crush_point"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"safety_function_failure"}, + SuggestedMeasureIDs: []string{"M005", "M106"}, + SuggestedEvidenceIDs: []string{"E01", "E08"}, + Priority: 96, + }, + { + ID: "HP056", NameDE: "Fehlbedienung Zweihandschaltung", NameEN: "Two-hand control misoperation", + RequiredComponentTags: []string{"two_hand_control_required"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"safety_function_failure"}, + SuggestedMeasureIDs: []string{"M106"}, + SuggestedEvidenceIDs: []string{"E01"}, + Priority: 90, + }, + { + ID: "HP057", NameDE: "Hydraulikoelleckage + Rutschgefahr", NameEN: "Hydraulic oil leakage + slip hazard", + RequiredComponentTags: []string{"hydraulic_part", "chemical_risk"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"material_environmental"}, + SuggestedMeasureIDs: []string{"M141"}, + SuggestedEvidenceIDs: []string{"E20"}, + Priority: 65, + }, + { + ID: "HP058", NameDE: "Ergonomische Belastung Kistenwechsel", NameEN: "Ergonomic strain during bin changeover", + RequiredComponentTags: []string{"ergonomic", "moving_part"}, + RequiredEnergyTags: []string{}, + GeneratedHazardCats: []string{"ergonomic"}, + SuggestedMeasureIDs: []string{"M141"}, + SuggestedEvidenceIDs: []string{"E20"}, + Priority: 55, + }, + } +} diff --git a/ai-compliance-sdk/internal/iace/keyword_dictionary.go b/ai-compliance-sdk/internal/iace/keyword_dictionary.go index 3f2795a..ba235ac 100644 --- a/ai-compliance-sdk/internal/iace/keyword_dictionary.go +++ b/ai-compliance-sdk/internal/iace/keyword_dictionary.go @@ -126,6 +126,27 @@ func GetKeywordDictionary() []KeywordEntry { {Keywords: []string{"luefter", "fan", "geblaese"}, ComponentIDs: []string{"C096"}, ExtraTags: []string{"rotating_part", "noise_source"}}, {Keywords: []string{"spannvorrichtung", "fixture", "clamp"}, ComponentIDs: []string{"C100"}, ExtraTags: []string{"clamping_part"}}, + // ── Cobot / Kollaborativer Betrieb ────────────────────────────── + {Keywords: []string{"kollaborier", "kollaboration", "collaborative"}, ComponentIDs: []string{"C139"}, ExtraTags: []string{"collaborative_operation", "force_limited"}}, + {Keywords: []string{"cobot"}, ComponentIDs: []string{"C139"}, ExtraTags: []string{"collaborative_operation"}}, + {Keywords: []string{"endeffektor", "end effector", "werkzeug am roboter"}, ComponentIDs: []string{"C136"}, ExtraTags: []string{"tool_at_robot"}}, + {Keywords: []string{"werkstueck", "workpiece"}, ComponentIDs: []string{"C137"}, ExtraTags: []string{"variable_workpiece"}}, + {Keywords: []string{"sicherheitsscanner", "safety scanner"}, ComponentIDs: []string{"C138"}, ExtraTags: []string{"safety_device"}}, + {Keywords: []string{"kraftsensor", "momentsensor", "force sensor", "torque sensor"}, ComponentIDs: []string{"C140"}, ExtraTags: []string{"force_limited"}}, + {Keywords: []string{"nachlauf", "afterrun"}, ExtraTags: []string{"afterrun_risk"}}, + {Keywords: []string{"traglast", "payload"}, ExtraTags: []string{"gravity_risk"}}, + {Keywords: []string{"reichweite", "reach"}, ExtraTags: []string{"moving_part"}}, + + // ── Elektromotor-spezifisch ─────────────────────────────────���─── + {Keywords: []string{"elektromotor", "electric motor", "asynchronmotor", "synchronmotor"}, ComponentIDs: []string{"C031"}, EnergyIDs: []string{"EN02", "EN05"}, ExtraTags: []string{"rotating_part"}}, + {Keywords: []string{"wicklung", "winding", "stator", "rotor"}, ComponentIDs: []string{"C031"}, EnergyIDs: []string{"EN05"}, ExtraTags: []string{"rotating_part", "high_voltage"}}, + {Keywords: []string{"lager", "bearing", "waelzlager", "kugellager"}, ComponentIDs: []string{"C013"}, ExtraTags: []string{"rotating_part"}}, + {Keywords: []string{"luefter", "ventilator", "kuehlung"}, ComponentIDs: []string{"C096"}, ExtraTags: []string{"rotating_part", "noise_source"}}, + {Keywords: []string{"klemmenkasten", "terminal box"}, ComponentIDs: []string{"C061"}, EnergyIDs: []string{"EN05"}, ExtraTags: []string{"high_voltage"}}, + {Keywords: []string{"welle", "shaft", "abtriebswelle"}, ComponentIDs: []string{"C031"}, EnergyIDs: []string{"EN02"}, ExtraTags: []string{"rotating_part", "entanglement_risk"}}, + {Keywords: []string{"drehmoment", "torque"}, EnergyIDs: []string{"EN02"}, ExtraTags: []string{"high_force", "rotating_part"}}, + {Keywords: []string{"isolierung", "insulation"}, ExtraTags: []string{"high_voltage"}}, + // ── Kontext-Keywords (keine Komponente, nur Tags) ─────────────── {Keywords: []string{"vollautomatisch", "automatisch"}, ExtraTags: []string{"auto_operation"}}, {Keywords: []string{"schutzausruestung", "psa", "ppe"}, ExtraTags: []string{"ppe_required"}}, diff --git a/ai-compliance-sdk/internal/iace/pattern_engine.go b/ai-compliance-sdk/internal/iace/pattern_engine.go index 55044ba..fcb2df1 100644 --- a/ai-compliance-sdk/internal/iace/pattern_engine.go +++ b/ai-compliance-sdk/internal/iace/pattern_engine.go @@ -52,11 +52,13 @@ type PatternEngine struct { patterns []HazardPattern } -// NewPatternEngine creates a PatternEngine with built-in + extended patterns and resolver. +// NewPatternEngine creates a PatternEngine with all pattern sources and resolver. func NewPatternEngine() *PatternEngine { - // Combine built-in (HP001-HP044) and extended (HP045+) patterns - patterns := GetBuiltinHazardPatterns() - patterns = append(patterns, GetExtendedHazardPatterns()...) + // Combine all pattern sources + patterns := GetBuiltinHazardPatterns() // HP001-HP044 + patterns = append(patterns, GetExtendedHazardPatterns()...) // HP045+ from rule library + patterns = append(patterns, GetPressHazardPatterns()...) // HP045-HP058 press-specific + patterns = append(patterns, GetCobotHazardPatterns()...) // HP059-HP065 cobot-specific return &PatternEngine{ resolver: NewTagResolver(), patterns: patterns, diff --git a/ai-compliance-sdk/internal/iace/sil_pl_calculator.go b/ai-compliance-sdk/internal/iace/sil_pl_calculator.go new file mode 100644 index 0000000..7283496 --- /dev/null +++ b/ai-compliance-sdk/internal/iace/sil_pl_calculator.go @@ -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 +} diff --git a/ai-compliance-sdk/internal/iace/tag_taxonomy.go b/ai-compliance-sdk/internal/iace/tag_taxonomy.go index 57b7bca..3a47013 100644 --- a/ai-compliance-sdk/internal/iace/tag_taxonomy.go +++ b/ai-compliance-sdk/internal/iace/tag_taxonomy.go @@ -96,6 +96,13 @@ func GetTagTaxonomy() []TagEntry { {ID: "gravity_suspended_load", Domain: "hazard", DescriptionDE: "Schwebende Last unter Schwerkraft", DescriptionEN: "Suspended load under gravity"}, {ID: "bypass_risk", Domain: "hazard", DescriptionDE: "Risiko der Sicherheitsumgehung", DescriptionEN: "Safety bypass risk"}, + // ── Extended: Cobot/Collaborative tags ────────────────────────────── + {ID: "collaborative_operation", Domain: "component", DescriptionDE: "Kollaborierender Betrieb (Mensch und Maschine ohne Trennung)", DescriptionEN: "Collaborative operation (human-machine without separation)"}, + {ID: "force_limited", Domain: "component", DescriptionDE: "Kraft-/Geschwindigkeitsbegrenzung aktiv", DescriptionEN: "Force/speed limitation active"}, + {ID: "tool_at_robot", Domain: "component", DescriptionDE: "Werkzeug am Roboterarm montiert", DescriptionEN: "Tool mounted on robot arm"}, + {ID: "variable_workpiece", Domain: "component", DescriptionDE: "Wechselnde Werkstuecke (separate Beurteilung)", DescriptionEN: "Variable workpieces (separate assessment required)"}, + {ID: "afterrun_risk", Domain: "hazard", DescriptionDE: "Nachlaufgefahr nach Stopp", DescriptionEN: "Afterrun risk after stop"}, + // ── Domain: measure (~10 tags) ────────────────────────────────────── {ID: "guard_measure", Domain: "measure", DescriptionDE: "Trennende Schutzeinrichtung", DescriptionEN: "Separating guard measure"}, {ID: "interlock_measure", Domain: "measure", DescriptionDE: "Verriegelungsmassnahme", DescriptionEN: "Interlock measure"},