From 4b9317b4fdc8af643b1e135ab74a9ab1f6ec539a Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 14 May 2026 22:38:02 +0200 Subject: [PATCH] feat(iace): lifecycle phases in patterns + broader robot cell scenarios - ApplicableLifecycles field in HazardPattern: patterns now declare which lifecycle phases the hazard applies to (Output, not just filter) - Init handler writes first applicable lifecycle into Hazard.LifecyclePhase - Robot cell patterns HP1600-1601 broadened: "Betrieb, Einrichten, Reinigung, Wartung, Fehlersuche" instead of only "Teach-Betrieb" - All robot cell patterns get ApplicableLifecycles for proper phase display Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/handlers/iace_handler_init.go | 7 ++++++ .../internal/iace/hazard_pattern_types.go | 4 +++ .../iace/hazard_patterns_robot_cell.go | 25 +++++++++++-------- .../internal/iace/pattern_engine.go | 4 ++- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go b/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go index daf03687..e14823a2 100644 --- a/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go +++ b/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go @@ -204,6 +204,12 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) { } } + // Join applicable lifecycles for the LifecyclePhase field + lifecycleStr := "" + if len(mp.ApplicableLifecycles) > 0 { + lifecycleStr = mp.ApplicableLifecycles[0] + } + hz, cerr := h.store.CreateHazard(ctx, iace.CreateHazardRequest{ ProjectID: projectID, ComponentID: compID, @@ -212,6 +218,7 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) { Category: cat, Scenario: mp.ScenarioDE, Function: iace.EncodeOpStates(mp.OperationalStates), + LifecyclePhase: lifecycleStr, TriggerEvent: mp.TriggerDE, PossibleHarm: mp.HarmDE, AffectedPerson: mp.AffectedDE, diff --git a/ai-compliance-sdk/internal/iace/hazard_pattern_types.go b/ai-compliance-sdk/internal/iace/hazard_pattern_types.go index ec502942..4781058c 100644 --- a/ai-compliance-sdk/internal/iace/hazard_pattern_types.go +++ b/ai-compliance-sdk/internal/iace/hazard_pattern_types.go @@ -54,6 +54,10 @@ type HazardPattern struct { // of the listed failure modes is relevant (by ComponentType match against project components). // Empty/nil = fires regardless of failure modes (backwards compatible). RequiredFailureModes []string `json:"required_failure_modes,omitempty"` + // ApplicableLifecycles lists the ISO 12100 lifecycle phases where this hazard + // is relevant. Written into the Hazard's LifecyclePhase field on creation. + // Empty = not set (pattern does not specify lifecycle applicability). + ApplicableLifecycles []string `json:"applicable_lifecycles,omitempty"` } // Standard human roles for machinery interaction (ISO 12100 + BetrSichV). diff --git a/ai-compliance-sdk/internal/iace/hazard_patterns_robot_cell.go b/ai-compliance-sdk/internal/iace/hazard_patterns_robot_cell.go index eb0a7810..7cbc13e7 100644 --- a/ai-compliance-sdk/internal/iace/hazard_patterns_robot_cell.go +++ b/ai-compliance-sdk/internal/iace/hazard_patterns_robot_cell.go @@ -15,23 +15,25 @@ func GetRobotCellPatterns() []HazardPattern { GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M061", "M062", "M054"}, Priority: 95, MachineTypes: []string{"robotics_cobot", "automotive", "metalworking", "general_industry"}, + ApplicableLifecycles: []string{"normal_operation", "setup", "teach_mode", "cleaning", "maintenance", "fault_clearing"}, ScenarioDE: "Person befindet sich im Bewegungsbereich des Roboterarms und wird zwischen Roboterarm und feststehenden Anlagenteilen eingeklemmt.", - TriggerDE: "Roboterarm bewegt sich waehrend Person im Gefahrenbereich steht (z.B. nach Betreten der Roboterzelle).", + TriggerDE: "Roboterarm bewegt sich waehrend Person im Gefahrenbereich steht (z.B. nach Betreten der Roboterzelle zur Reinigung, Wartung oder Stoerungsbeseitigung).", HarmDE: "Quetschungen, Knochenbrueche, innere Verletzungen durch Einklemmen von Koerperteilen.", - AffectedDE: "Bedienpersonal, Einrichter, Wartungspersonal", + AffectedDE: "Bedienpersonal, Einrichter, Wartungspersonal, Reinigungspersonal", ZoneDE: "Roboterarm, feststehende Anlagenteile innerhalb der Roboterzelle", DefaultSeverity: 4, DefaultExposure: 3, }, { - ID: "HP1601", NameDE: "Quetschen bei Teach-Betrieb am Roboter", NameEN: "Crushing during robot teach mode", + ID: "HP1601", NameDE: "Quetschen bei Einrichten/Teachen am Roboter", NameEN: "Crushing during robot setup/teach", RequiredComponentTags: []string{"moving_part"}, GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M054", "M061"}, Priority: 94, MachineTypes: []string{"robotics_cobot", "automotive", "metalworking", "general_industry"}, - ScenarioDE: "Einrichter befindet sich zum Teachen/Programmieren in der Roboterzelle. Roboterarm bewegt sich unerwartet oder mit zu hoher Geschwindigkeit.", - TriggerDE: "Teach-Modus laesst Bewegung zu, Einrichter steht im Schwenkbereich des Arms.", - HarmDE: "Quetschungen, Prellungen durch Kontakt mit bewegtem Roboterarm.", - AffectedDE: "Einrichter, Programmierer", + ApplicableLifecycles: []string{"teach_mode", "setup", "changeover", "fault_clearing"}, + ScenarioDE: "Person befindet sich zum Einrichten, Teachen oder zur Stoerungsbeseitigung in der Roboterzelle. Roboterarm bewegt sich unerwartet oder mit zu hoher Geschwindigkeit.", + TriggerDE: "Betriebsartenwahlschalter steht auf Einrichten, Roboter bewegt sich im Schwenkbereich.", + HarmDE: "Quetschungen, Prellungen durch Kontakt mit bewegtem Roboterarm oder Greifer.", + AffectedDE: "Einrichter, Programmierer, Wartungspersonal", ZoneDE: "Roboterarm, Inneres der Roboterzelle", DefaultSeverity: 3, DefaultExposure: 3, }, @@ -40,7 +42,7 @@ func GetRobotCellPatterns() []HazardPattern { RequiredComponentTags: []string{"moving_part", "guard"}, GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M002", "M061"}, - Priority: 93, + Priority: 93, ApplicableLifecycles: []string{"normal_operation", "setup", "cleaning", "maintenance", "fault_clearing"}, ScenarioDE: "Person greift ueber oder durch den Schutzzaun und erreicht den Bewegungsbereich des Roboterarms.", TriggerDE: "Unzureichender Sicherheitsabstand zwischen Schutzzaun-Oberkante und Roboter-Schwenkbereich.", HarmDE: "Quetschung von Hand oder Arm zwischen Roboterarm und feststehenden Teilen.", @@ -53,7 +55,7 @@ func GetRobotCellPatterns() []HazardPattern { RequiredComponentTags: []string{"moving_part", "guard"}, GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M061", "M054", "M141"}, - Priority: 94, + Priority: 94, ApplicableLifecycles: []string{"normal_operation", "setup", "cleaning", "maintenance", "fault_clearing", "changeover"}, ScenarioDE: "Person befindet sich in der Roboterzelle, Schutztuer wird geschlossen und Roboter startet. Person kann den Gefahrenbereich nicht rechtzeitig verlassen.", TriggerDE: "Schutztuer schliesst waehrend Person im Innenraum. Wiederanlauf des Roboters ohne Quittierung.", HarmDE: "Quetschungen, Stoss durch anlaufenden Roboter. Person kann sich nicht entziehen.", @@ -66,7 +68,7 @@ func GetRobotCellPatterns() []HazardPattern { RequiredComponentTags: []string{"moving_part", "guard"}, GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M061", "M002"}, - Priority: 92, + Priority: 92, ApplicableLifecycles: []string{"normal_operation", "setup", "cleaning", "changeover", "fault_clearing"}, ScenarioDE: "Roboterarm ueberschreitet den vorgesehenen Bewegungsbereich und trifft den Schutzzaun mit hoher Kraft.", TriggerDE: "Fehler in der Bahnplanung oder Ausfall der Achsbegrenzung. Roboter faehrt ueber Software-Endschalter hinaus.", HarmDE: "Teile des Schutzzauns werden herausgeschleudert, Person ausserhalb wird getroffen.", @@ -83,6 +85,7 @@ func GetRobotCellPatterns() []HazardPattern { GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M054", "M061"}, Priority: 94, MachineTypes: []string{"robotics_cobot", "automotive", "metalworking", "general_industry"}, + ApplicableLifecycles: []string{"normal_operation", "setup", "cleaning", "changeover"}, ScenarioDE: "Person greift in den Bereich des Greifers waehrend der Roboter ein Werkstueck aufnimmt oder ablegt. Hand wird zwischen Greifbacken und Werkstueck eingeklemmt.", TriggerDE: "Greiferbacken schliessen waehrend Koerperteil im Greifbereich ist.", HarmDE: "Quetschung oder Amputation von Fingern durch Greifkraft.", @@ -124,7 +127,7 @@ func GetRobotCellPatterns() []HazardPattern { RequiredComponentTags: []string{"entanglement_risk"}, GeneratedHazardCats: []string{"mechanical_hazard"}, SuggestedMeasureIDs: []string{"M002", "M061", "M003"}, - Priority: 93, + Priority: 93, ApplicableLifecycles: []string{"normal_operation", "setup", "cleaning", "maintenance", "fault_clearing"}, ScenarioDE: "Person greift an Foerderband fuer Werkstueckzulauf oder -auslauf und wird zwischen beweglichen und feststehenden Teilen eingeklemmt.", TriggerDE: "Hand oder Finger geraten zwischen Band und Umlenkrolle oder zwischen Werkstueck und Tunnelrahmen.", HarmDE: "Quetschung von Fingern, Einzug von Kleidung oder Haaren.", diff --git a/ai-compliance-sdk/internal/iace/pattern_engine.go b/ai-compliance-sdk/internal/iace/pattern_engine.go index b4905848..d5cae5ef 100644 --- a/ai-compliance-sdk/internal/iace/pattern_engine.go +++ b/ai-compliance-sdk/internal/iace/pattern_engine.go @@ -66,6 +66,7 @@ type PatternMatch struct { HumanRoles []string `json:"human_roles,omitempty"` GeneratedHazardType string `json:"generated_hazard_type,omitempty"` MatchedFailureModes []string `json:"matched_failure_modes,omitempty"` + ApplicableLifecycles []string `json:"applicable_lifecycles,omitempty"` } // HazardSuggestion is a suggested hazard from pattern matching. @@ -217,7 +218,8 @@ func (e *PatternEngine) Match(input MatchInput) *MatchOutput { StateTransitions: p.StateTransitions, HumanRoles: p.HumanRoles, GeneratedHazardType: p.GeneratedHazardType, - MatchedFailureModes: matchedFMs, + MatchedFailureModes: matchedFMs, + ApplicableLifecycles: p.ApplicableLifecycles, }) for _, cat := range p.GeneratedHazardCats {