feat(iace): Sprint 3B — Human Interaction Model
- 6 Standard-Rollen: operator, maintenance_tech, programmer, cleaning_staff, bystander, supervisor - HumanRoles []string Feld in HazardPattern, MatchInput, PatternMatch - patternMatches() filtert Patterns nach Rolle (nil = feuert fuer alle Rollen) - MatchReason um human_role Typ erweitert (Explainability) - 25 bestehende Patterns mit Rollen annotiert: - Cobot HP059/062/064 → operator/programmer - Maintenance HP700-714 → maintenance_tech/programmer - Operational HP070/073-078/080 → operator/maintenance_tech/programmer - Init + Parser Handler reichen Roles an MatchInput durch - 4 neue Tests: NilFiresAlways, MaintenanceTechFilter, ProgrammerTeachMode, RoleCount Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -104,6 +104,7 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
|
|||||||
CustomTags: parseResult.CustomTags,
|
CustomTags: parseResult.CustomTags,
|
||||||
OperationalStates: parseResult.OperationalStates,
|
OperationalStates: parseResult.OperationalStates,
|
||||||
StateTransitions: parseResult.StateTransitions,
|
StateTransitions: parseResult.StateTransitions,
|
||||||
|
HumanRoles: parseResult.Roles,
|
||||||
})
|
})
|
||||||
steps = append(steps, InitStep{
|
steps = append(steps, InitStep{
|
||||||
Name: "Patterns abgeglichen",
|
Name: "Patterns abgeglichen",
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ func (h *IACEHandler) ParseNarrative(c *gin.Context) {
|
|||||||
CustomTags: parseResult.CustomTags,
|
CustomTags: parseResult.CustomTags,
|
||||||
OperationalStates: parseResult.OperationalStates,
|
OperationalStates: parseResult.OperationalStates,
|
||||||
StateTransitions: parseResult.StateTransitions,
|
StateTransitions: parseResult.StateTransitions,
|
||||||
|
HumanRoles: parseResult.Roles,
|
||||||
}
|
}
|
||||||
matchOutput := engine.Match(matchInput)
|
matchOutput := engine.Match(matchInput)
|
||||||
|
|
||||||
|
|||||||
@@ -42,4 +42,26 @@ type HazardPattern struct {
|
|||||||
// Hazards at transitions are critical — e.g. "maintenance→automatic_operation"
|
// Hazards at transitions are critical — e.g. "maintenance→automatic_operation"
|
||||||
// is where "unexpected restart" fatalities occur. Empty = no transition filter.
|
// is where "unexpected restart" fatalities occur. Empty = no transition filter.
|
||||||
StateTransitions []string `json:"state_transitions,omitempty"`
|
StateTransitions []string `json:"state_transitions,omitempty"`
|
||||||
|
// HumanRoles restricts this pattern to projects where specific human roles
|
||||||
|
// interact with the machine. Empty/nil = fires for all roles (backwards compatible).
|
||||||
|
// Standard roles: operator, maintenance_tech, programmer, cleaning_staff, bystander, supervisor.
|
||||||
|
HumanRoles []string `json:"human_roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard human roles for machinery interaction (ISO 12100 + BetrSichV).
|
||||||
|
const (
|
||||||
|
RoleOperator = "operator"
|
||||||
|
RoleMaintenanceTech = "maintenance_tech"
|
||||||
|
RoleProgrammer = "programmer"
|
||||||
|
RoleCleaningStaff = "cleaning_staff"
|
||||||
|
RoleBystander = "bystander"
|
||||||
|
RoleSupervisor = "supervisor"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AllHumanRoles returns the 6 standard human roles.
|
||||||
|
func AllHumanRoles() []string {
|
||||||
|
return []string{
|
||||||
|
RoleOperator, RoleMaintenanceTech, RoleProgrammer,
|
||||||
|
RoleCleaningStaff, RoleBystander, RoleSupervisor,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func GetCobotHazardPatterns() []HazardPattern {
|
|||||||
// Collaborative Robot (Cobot) Patterns (HP059-HP065)
|
// Collaborative Robot (Cobot) Patterns (HP059-HP065)
|
||||||
// ================================================================
|
// ================================================================
|
||||||
{
|
{
|
||||||
ID: "HP059", NameDE: "Kollision Mensch-Roboter (Kraft/Geschwindigkeit)", NameEN: "Human-robot collision (force/speed)",
|
ID: "HP059", HumanRoles: []string{"operator"}, NameDE: "Kollision Mensch-Roboter (Kraft/Geschwindigkeit)", NameEN: "Human-robot collision (force/speed)",
|
||||||
RequiredComponentTags: []string{"collaborative_operation", "moving_part"},
|
RequiredComponentTags: []string{"collaborative_operation", "moving_part"},
|
||||||
RequiredEnergyTags: []string{},
|
RequiredEnergyTags: []string{},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
@@ -63,7 +63,7 @@ func GetCobotHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 4, DefaultExposure: 3,
|
DefaultSeverity: 4, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP062", OperationalStates: []string{"teach_mode"}, NameDE: "Fehlprogrammierung Kraft-/Geschwindigkeitsgrenzwerte", NameEN: "Misprogramming of force/speed limits",
|
ID: "HP062", OperationalStates: []string{"teach_mode"}, HumanRoles: []string{"programmer"}, NameDE: "Fehlprogrammierung Kraft-/Geschwindigkeitsgrenzwerte", NameEN: "Misprogramming of force/speed limits",
|
||||||
RequiredComponentTags: []string{"programmable", "force_limited"},
|
RequiredComponentTags: []string{"programmable", "force_limited"},
|
||||||
RequiredEnergyTags: []string{},
|
RequiredEnergyTags: []string{},
|
||||||
GeneratedHazardCats: []string{"safety_function_failure"},
|
GeneratedHazardCats: []string{"safety_function_failure"},
|
||||||
@@ -94,7 +94,7 @@ func GetCobotHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 3, DefaultExposure: 3,
|
DefaultSeverity: 3, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP064", OperationalStates: []string{"automatic_operation"}, NameDE: "Quetschen im Roboter-Arbeitsraum (nicht-kollaborierend)", NameEN: "Crushing in robot workspace (non-collaborative)",
|
ID: "HP064", OperationalStates: []string{"automatic_operation"}, HumanRoles: []string{"operator", "maintenance_tech"}, NameDE: "Quetschen im Roboter-Arbeitsraum (nicht-kollaborierend)", NameEN: "Crushing in robot workspace (non-collaborative)",
|
||||||
RequiredComponentTags: []string{"moving_part", "high_force", "sensor_part"},
|
RequiredComponentTags: []string{"moving_part", "high_force", "sensor_part"},
|
||||||
RequiredEnergyTags: []string{},
|
RequiredEnergyTags: []string{},
|
||||||
ExcludedComponentTags: []string{"collaborative_operation"},
|
ExcludedComponentTags: []string{"collaborative_operation"},
|
||||||
|
|||||||
@@ -5,70 +5,70 @@ package iace
|
|||||||
func GetMaintenanceExtPatterns() []HazardPattern {
|
func GetMaintenanceExtPatterns() []HazardPattern {
|
||||||
return []HazardPattern{
|
return []HazardPattern{
|
||||||
// — Wartung allgemein (HP700-HP709) —
|
// — Wartung allgemein (HP700-HP709) —
|
||||||
{ID: "HP700", OperationalStates: []string{"maintenance"}, NameDE: "LOTO-Fehler: Maschine nicht freigeschaltet", NameEN: "LOTO failure: not locked out",
|
{ID: "HP700", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "LOTO-Fehler: Maschine nicht freigeschaltet", NameEN: "LOTO failure: not locked out",
|
||||||
RequiredComponentTags: []string{"moving_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"moving_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard", "safety_function_failure"},
|
GeneratedHazardCats: []string{"mechanical_hazard", "safety_function_failure"},
|
||||||
SuggestedMeasureIDs: []string{"M054", "M082"}, SuggestedEvidenceIDs: []string{"E08", "E20"}, Priority: 95,
|
SuggestedMeasureIDs: []string{"M054", "M082"}, SuggestedEvidenceIDs: []string{"E08", "E20"}, Priority: 95,
|
||||||
ScenarioDE: "Arbeit ohne Freischaltung der Maschine", TriggerDE: "Fehlende LOTO-Prozedur",
|
ScenarioDE: "Arbeit ohne Freischaltung der Maschine", TriggerDE: "Fehlende LOTO-Prozedur",
|
||||||
HarmDE: "Erfassen durch anlaufende Teile, Tod", AffectedDE: "Instandhalter", ZoneDE: "Gesamte Maschine",
|
HarmDE: "Erfassen durch anlaufende Teile, Tod", AffectedDE: "Instandhalter", ZoneDE: "Gesamte Maschine",
|
||||||
DefaultSeverity: 5, DefaultExposure: 3},
|
DefaultSeverity: 5, DefaultExposure: 3},
|
||||||
{ID: "HP701", OperationalStates: []string{"maintenance"}, NameDE: "Restenergie trotz Abschaltung", NameEN: "Residual energy after shutdown",
|
{ID: "HP701", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Restenergie trotz Abschaltung", NameEN: "Residual energy after shutdown",
|
||||||
RequiredComponentTags: []string{"moving_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"moving_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard", "pneumatic_hydraulic"},
|
GeneratedHazardCats: []string{"mechanical_hazard", "pneumatic_hydraulic"},
|
||||||
SuggestedMeasureIDs: []string{"M054", "M082"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 90,
|
SuggestedMeasureIDs: []string{"M054", "M082"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 90,
|
||||||
ScenarioDE: "Gespeicherte Energie entlaedt sich bei Wartung", TriggerDE: "Nicht abgelassener Druckspeicher",
|
ScenarioDE: "Gespeicherte Energie entlaedt sich bei Wartung", TriggerDE: "Nicht abgelassener Druckspeicher",
|
||||||
HarmDE: "Unkontrollierte Bewegung, Quetschung", AffectedDE: "Instandhalter", ZoneDE: "Antriebe, Speicher",
|
HarmDE: "Unkontrollierte Bewegung, Quetschung", AffectedDE: "Instandhalter", ZoneDE: "Antriebe, Speicher",
|
||||||
DefaultSeverity: 5, DefaultExposure: 3},
|
DefaultSeverity: 5, DefaultExposure: 3},
|
||||||
{ID: "HP702", OperationalStates: []string{"maintenance"}, NameDE: "Falsches Werkzeug bei Wartung", NameEN: "Wrong tool during maintenance",
|
{ID: "HP702", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Falsches Werkzeug bei Wartung", NameEN: "Wrong tool during maintenance",
|
||||||
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 50,
|
SuggestedMeasureIDs: []string{"M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 50,
|
||||||
ScenarioDE: "Ungeeignetes oder defektes Werkzeug", TriggerDE: "Falscher Schraubenschluessel",
|
ScenarioDE: "Ungeeignetes oder defektes Werkzeug", TriggerDE: "Falscher Schraubenschluessel",
|
||||||
HarmDE: "Abrutschen, Quetschung", AffectedDE: "Instandhalter", ZoneDE: "Wartungszugang",
|
HarmDE: "Abrutschen, Quetschung", AffectedDE: "Instandhalter", ZoneDE: "Wartungszugang",
|
||||||
DefaultSeverity: 3, DefaultExposure: 4},
|
DefaultSeverity: 3, DefaultExposure: 4},
|
||||||
{ID: "HP703", OperationalStates: []string{"maintenance"}, NameDE: "Fehlende Qualifikation des Instandhalters", NameEN: "Insufficient maintainer qualification",
|
{ID: "HP703", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Fehlende Qualifikation des Instandhalters", NameEN: "Insufficient maintainer qualification",
|
||||||
RequiredComponentTags: []string{"electrical_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"electrical_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"electrical_hazard", "safety_function_failure"},
|
GeneratedHazardCats: []string{"electrical_hazard", "safety_function_failure"},
|
||||||
SuggestedMeasureIDs: []string{"M082", "M141"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 70,
|
SuggestedMeasureIDs: []string{"M082", "M141"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 70,
|
||||||
ScenarioDE: "Unqualifiziertes Personal an Elektrik", TriggerDE: "Keine Elektrofachkraft",
|
ScenarioDE: "Unqualifiziertes Personal an Elektrik", TriggerDE: "Keine Elektrofachkraft",
|
||||||
HarmDE: "Stromschlag, Fehlverdrahtung", AffectedDE: "Instandhalter", ZoneDE: "Schaltschrank",
|
HarmDE: "Stromschlag, Fehlverdrahtung", AffectedDE: "Instandhalter", ZoneDE: "Schaltschrank",
|
||||||
DefaultSeverity: 4, DefaultExposure: 3},
|
DefaultSeverity: 4, DefaultExposure: 3},
|
||||||
{ID: "HP704", OperationalStates: []string{"maintenance"}, NameDE: "Herabfallen schwerer Teile bei Demontage", NameEN: "Heavy parts falling during disassembly",
|
{ID: "HP704", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Herabfallen schwerer Teile bei Demontage", NameEN: "Heavy parts falling during disassembly",
|
||||||
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M082", "M141"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 75,
|
SuggestedMeasureIDs: []string{"M082", "M141"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 75,
|
||||||
ScenarioDE: "Schwere Teile fallen bei Demontage herab", TriggerDE: "Fehlende Abstuetzung",
|
ScenarioDE: "Schwere Teile fallen bei Demontage herab", TriggerDE: "Fehlende Abstuetzung",
|
||||||
HarmDE: "Quetschung, Frakturen, Tod", AffectedDE: "Instandhalter", ZoneDE: "Wartungsbereich",
|
HarmDE: "Quetschung, Frakturen, Tod", AffectedDE: "Instandhalter", ZoneDE: "Wartungsbereich",
|
||||||
DefaultSeverity: 5, DefaultExposure: 3},
|
DefaultSeverity: 5, DefaultExposure: 3},
|
||||||
{ID: "HP705", OperationalStates: []string{"maintenance"}, NameDE: "Vergessenes Werkzeug in Maschine", NameEN: "Forgotten tool in machine",
|
{ID: "HP705", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Vergessenes Werkzeug in Maschine", NameEN: "Forgotten tool in machine",
|
||||||
RequiredComponentTags: []string{"moving_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"moving_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 65,
|
SuggestedMeasureIDs: []string{"M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 65,
|
||||||
ScenarioDE: "Zurueckgelassenes Werkzeug wird Geschoss", TriggerDE: "Keine Werkzeugkontrolle",
|
ScenarioDE: "Zurueckgelassenes Werkzeug wird Geschoss", TriggerDE: "Keine Werkzeugkontrolle",
|
||||||
HarmDE: "Herausgeschleudertes Teil, Verletzungen", AffectedDE: "Bedienpersonal", ZoneDE: "Arbeitsraum",
|
HarmDE: "Herausgeschleudertes Teil, Verletzungen", AffectedDE: "Bedienpersonal", ZoneDE: "Arbeitsraum",
|
||||||
DefaultSeverity: 4, DefaultExposure: 2},
|
DefaultSeverity: 4, DefaultExposure: 2},
|
||||||
{ID: "HP706", OperationalStates: []string{"maintenance"}, NameDE: "Schnittwunden an scharfkantigen Teilen", NameEN: "Cuts on sharp-edged parts",
|
{ID: "HP706", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Schnittwunden an scharfkantigen Teilen", NameEN: "Cuts on sharp-edged parts",
|
||||||
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M005", "M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 45,
|
SuggestedMeasureIDs: []string{"M005", "M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 45,
|
||||||
ScenarioDE: "Scharfe Kanten und Grate verletzen", TriggerDE: "Fehlende Schutzhandschuhe",
|
ScenarioDE: "Scharfe Kanten und Grate verletzen", TriggerDE: "Fehlende Schutzhandschuhe",
|
||||||
HarmDE: "Schnittwunden, Abschuerfungen", AffectedDE: "Instandhalter", ZoneDE: "Blechverkleidungen",
|
HarmDE: "Schnittwunden, Abschuerfungen", AffectedDE: "Instandhalter", ZoneDE: "Blechverkleidungen",
|
||||||
DefaultSeverity: 2, DefaultExposure: 4},
|
DefaultSeverity: 2, DefaultExposure: 4},
|
||||||
{ID: "HP707", OperationalStates: []string{"maintenance"}, NameDE: "Verbrennung an heissen Teilen bei Wartung", NameEN: "Burn on hot parts during maintenance",
|
{ID: "HP707", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Verbrennung an heissen Teilen bei Wartung", NameEN: "Burn on hot parts during maintenance",
|
||||||
RequiredComponentTags: []string{"high_temperature"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"high_temperature"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"thermal_hazard"},
|
GeneratedHazardCats: []string{"thermal_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M005", "M082"}, SuggestedEvidenceIDs: []string{"E10"}, Priority: 60,
|
SuggestedMeasureIDs: []string{"M005", "M082"}, SuggestedEvidenceIDs: []string{"E10"}, Priority: 60,
|
||||||
ScenarioDE: "Maschine nicht abgekuehlt vor Wartung", TriggerDE: "Zu kurze Abkuehlzeit",
|
ScenarioDE: "Maschine nicht abgekuehlt vor Wartung", TriggerDE: "Zu kurze Abkuehlzeit",
|
||||||
HarmDE: "Kontaktverbrennungen", AffectedDE: "Instandhalter", ZoneDE: "Heizplatten, Motorgehaeuse",
|
HarmDE: "Kontaktverbrennungen", AffectedDE: "Instandhalter", ZoneDE: "Heizplatten, Motorgehaeuse",
|
||||||
DefaultSeverity: 3, DefaultExposure: 4},
|
DefaultSeverity: 3, DefaultExposure: 4},
|
||||||
{ID: "HP708", OperationalStates: []string{"maintenance"}, NameDE: "Fehlende Wartungsfreigabe", NameEN: "Missing maintenance permit",
|
{ID: "HP708", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Fehlende Wartungsfreigabe", NameEN: "Missing maintenance permit",
|
||||||
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"structural_part"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"safety_function_failure"},
|
GeneratedHazardCats: []string{"safety_function_failure"},
|
||||||
SuggestedMeasureIDs: []string{"M082", "M141"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 70,
|
SuggestedMeasureIDs: []string{"M082", "M141"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 70,
|
||||||
ScenarioDE: "Wartung ohne formale Freigabe", TriggerDE: "Fehlender Erlaubnisschein",
|
ScenarioDE: "Wartung ohne formale Freigabe", TriggerDE: "Fehlender Erlaubnisschein",
|
||||||
HarmDE: "Unerwarteter Maschinenbetrieb", AffectedDE: "Instandhalter", ZoneDE: "Gesamte Maschine",
|
HarmDE: "Unerwarteter Maschinenbetrieb", AffectedDE: "Instandhalter", ZoneDE: "Gesamte Maschine",
|
||||||
DefaultSeverity: 5, DefaultExposure: 3},
|
DefaultSeverity: 5, DefaultExposure: 3},
|
||||||
{ID: "HP709", OperationalStates: []string{"maintenance"}, NameDE: "Biologische Gefaehrdung bei KSS-Wartung", NameEN: "Biological hazard MWF maintenance",
|
{ID: "HP709", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Biologische Gefaehrdung bei KSS-Wartung", NameEN: "Biological hazard MWF maintenance",
|
||||||
RequiredComponentTags: []string{"chemical_risk"}, RequiredLifecycles: []string{"maintenance"},
|
RequiredComponentTags: []string{"chemical_risk"}, RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"material_environmental"},
|
GeneratedHazardCats: []string{"material_environmental"},
|
||||||
SuggestedMeasureIDs: []string{"M005", "M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 50,
|
SuggestedMeasureIDs: []string{"M005", "M082"}, SuggestedEvidenceIDs: []string{"E20"}, Priority: 50,
|
||||||
@@ -76,28 +76,28 @@ func GetMaintenanceExtPatterns() []HazardPattern {
|
|||||||
HarmDE: "Hautinfektionen, Atemwegsbeschwerden", AffectedDE: "Instandhalter", ZoneDE: "KSS-System",
|
HarmDE: "Hautinfektionen, Atemwegsbeschwerden", AffectedDE: "Instandhalter", ZoneDE: "KSS-System",
|
||||||
DefaultSeverity: 2, DefaultExposure: 3},
|
DefaultSeverity: 2, DefaultExposure: 3},
|
||||||
// — Einrichten / Umruesten (HP710-HP719) —
|
// — Einrichten / Umruesten (HP710-HP719) —
|
||||||
{ID: "HP710", OperationalStates: []string{"teach_mode"}, NameDE: "Falsche Parameter nach Umruestung", NameEN: "Wrong parameters after changeover",
|
{ID: "HP710", OperationalStates: []string{"teach_mode"}, HumanRoles: []string{"programmer"}, NameDE: "Falsche Parameter nach Umruestung", NameEN: "Wrong parameters after changeover",
|
||||||
RequiredComponentTags: []string{"programmable"}, RequiredLifecycles: []string{"setup"},
|
RequiredComponentTags: []string{"programmable"}, RequiredLifecycles: []string{"setup"},
|
||||||
GeneratedHazardCats: []string{"safety_function_failure"},
|
GeneratedHazardCats: []string{"safety_function_failure"},
|
||||||
SuggestedMeasureIDs: []string{"M106", "M082"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 75,
|
SuggestedMeasureIDs: []string{"M106", "M082"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 75,
|
||||||
ScenarioDE: "Falsche Maschinenparameter nach Produktwechsel", TriggerDE: "Falsche Rezeptnummer",
|
ScenarioDE: "Falsche Maschinenparameter nach Produktwechsel", TriggerDE: "Falsche Rezeptnummer",
|
||||||
HarmDE: "Uebergeschwindigkeit, Werkzeugbruch", AffectedDE: "Einrichter", ZoneDE: "Bedienterminal",
|
HarmDE: "Uebergeschwindigkeit, Werkzeugbruch", AffectedDE: "Einrichter", ZoneDE: "Bedienterminal",
|
||||||
DefaultSeverity: 4, DefaultExposure: 3},
|
DefaultSeverity: 4, DefaultExposure: 3},
|
||||||
{ID: "HP711", OperationalStates: []string{"maintenance"}, NameDE: "Quetschung bei Werkzeugwechsel", NameEN: "Crushing during tool change",
|
{ID: "HP711", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Quetschung bei Werkzeugwechsel", NameEN: "Crushing during tool change",
|
||||||
RequiredComponentTags: []string{"moving_part", "high_force"}, RequiredLifecycles: []string{"setup"},
|
RequiredComponentTags: []string{"moving_part", "high_force"}, RequiredLifecycles: []string{"setup"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M003", "M054"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 80,
|
SuggestedMeasureIDs: []string{"M003", "M054"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 80,
|
||||||
ScenarioDE: "Schwere Werkzeuge manuell gewechselt", TriggerDE: "Kein Hebezeug, Finger eingeklemmt",
|
ScenarioDE: "Schwere Werkzeuge manuell gewechselt", TriggerDE: "Kein Hebezeug, Finger eingeklemmt",
|
||||||
HarmDE: "Quetschung, Amputation", AffectedDE: "Einrichter", ZoneDE: "Werkzeugaufnahme",
|
HarmDE: "Quetschung, Amputation", AffectedDE: "Einrichter", ZoneDE: "Werkzeugaufnahme",
|
||||||
DefaultSeverity: 4, DefaultExposure: 4},
|
DefaultSeverity: 4, DefaultExposure: 4},
|
||||||
{ID: "HP712", OperationalStates: []string{"teach_mode", "manual_operation"}, NameDE: "Unkontrollierte Bewegung bei Testlauf", NameEN: "Uncontrolled movement test run",
|
{ID: "HP712", OperationalStates: []string{"teach_mode", "manual_operation"}, HumanRoles: []string{"programmer", "maintenance_tech"}, NameDE: "Unkontrollierte Bewegung bei Testlauf", NameEN: "Uncontrolled movement test run",
|
||||||
RequiredComponentTags: []string{"moving_part", "programmable"}, RequiredLifecycles: []string{"setup"},
|
RequiredComponentTags: []string{"moving_part", "programmable"}, RequiredLifecycles: []string{"setup"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
SuggestedMeasureIDs: []string{"M106", "M054"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 80,
|
SuggestedMeasureIDs: []string{"M106", "M054"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 80,
|
||||||
ScenarioDE: "Testlauf mit Person im Gefahrenbereich", TriggerDE: "Volle Geschwindigkeit, kein Schutz",
|
ScenarioDE: "Testlauf mit Person im Gefahrenbereich", TriggerDE: "Volle Geschwindigkeit, kein Schutz",
|
||||||
HarmDE: "Erfassen, Quetschen", AffectedDE: "Einrichter", ZoneDE: "Arbeitsraum",
|
HarmDE: "Erfassen, Quetschen", AffectedDE: "Einrichter", ZoneDE: "Arbeitsraum",
|
||||||
DefaultSeverity: 5, DefaultExposure: 3},
|
DefaultSeverity: 5, DefaultExposure: 3},
|
||||||
{ID: "HP713", OperationalStates: []string{"teach_mode"}, NameDE: "Einrichtbetrieb ohne reduzierte Geschwindigkeit", NameEN: "Setup without reduced speed",
|
{ID: "HP713", OperationalStates: []string{"teach_mode"}, HumanRoles: []string{"programmer"}, NameDE: "Einrichtbetrieb ohne reduzierte Geschwindigkeit", NameEN: "Setup without reduced speed",
|
||||||
RequiredComponentTags: []string{"moving_part", "programmable"}, RequiredLifecycles: []string{"setup"},
|
RequiredComponentTags: []string{"moving_part", "programmable"}, RequiredLifecycles: []string{"setup"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard", "safety_function_failure"},
|
GeneratedHazardCats: []string{"mechanical_hazard", "safety_function_failure"},
|
||||||
SuggestedMeasureIDs: []string{"M106", "M082"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 85,
|
SuggestedMeasureIDs: []string{"M106", "M082"}, SuggestedEvidenceIDs: []string{"E08"}, Priority: 85,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 4, DefaultExposure: 3,
|
DefaultSeverity: 4, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP070", NameDE: "Eingriff in laufende Maschine bei Stoerung", OperationalStates: []string{"recovery_mode"}, NameEN: "Intervention in running machine during fault",
|
ID: "HP070", NameDE: "Eingriff in laufende Maschine bei Stoerung", OperationalStates: []string{"recovery_mode"}, HumanRoles: []string{"operator"}, NameEN: "Intervention in running machine during fault",
|
||||||
RequiredComponentTags: []string{"moving_part"},
|
RequiredComponentTags: []string{"moving_part"},
|
||||||
RequiredLifecycles: []string{"fault_clearing"},
|
RequiredLifecycles: []string{"fault_clearing"},
|
||||||
ExcludedComponentTags: []string{"interlocked"},
|
ExcludedComponentTags: []string{"interlocked"},
|
||||||
@@ -120,7 +120,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
// Wartung / Instandhaltung (HP073-HP079)
|
// Wartung / Instandhaltung (HP073-HP079)
|
||||||
// ================================================================
|
// ================================================================
|
||||||
{
|
{
|
||||||
ID: "HP073", NameDE: "Wartung ohne LOTO (Lockout/Tagout)", OperationalStates: []string{"maintenance"}, NameEN: "Maintenance without LOTO",
|
ID: "HP073", NameDE: "Wartung ohne LOTO (Lockout/Tagout)", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameEN: "Maintenance without LOTO",
|
||||||
RequiredComponentTags: []string{"moving_part"},
|
RequiredComponentTags: []string{"moving_part"},
|
||||||
RequiredLifecycles: []string{"maintenance"},
|
RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"maintenance_hazard"},
|
GeneratedHazardCats: []string{"maintenance_hazard"},
|
||||||
@@ -136,7 +136,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 5, DefaultExposure: 3,
|
DefaultSeverity: 5, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP074", OperationalStates: []string{"maintenance"}, NameDE: "Sturz von Wartungsbuehne / Leiter", NameEN: "Fall from maintenance platform / ladder",
|
ID: "HP074", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Sturz von Wartungsbuehne / Leiter", NameEN: "Fall from maintenance platform / ladder",
|
||||||
RequiredComponentTags: []string{"structural_part"},
|
RequiredComponentTags: []string{"structural_part"},
|
||||||
RequiredLifecycles: []string{"maintenance", "cleaning"},
|
RequiredLifecycles: []string{"maintenance", "cleaning"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
@@ -150,7 +150,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 4, DefaultExposure: 2,
|
DefaultSeverity: 4, DefaultExposure: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP075", OperationalStates: []string{"maintenance"}, NameDE: "Kontakt mit heissen Teilen bei Wartung", NameEN: "Contact with hot parts during maintenance",
|
ID: "HP075", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Kontakt mit heissen Teilen bei Wartung", NameEN: "Contact with hot parts during maintenance",
|
||||||
RequiredComponentTags: []string{"high_temperature"},
|
RequiredComponentTags: []string{"high_temperature"},
|
||||||
RequiredLifecycles: []string{"maintenance"},
|
RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"thermal_hazard"},
|
GeneratedHazardCats: []string{"thermal_hazard"},
|
||||||
@@ -165,7 +165,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 3, DefaultExposure: 3,
|
DefaultSeverity: 3, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP076", OperationalStates: []string{"maintenance"}, NameDE: "Kontakt mit Gefahrstoffen bei Wartung", NameEN: "Contact with hazardous substances during maintenance",
|
ID: "HP076", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Kontakt mit Gefahrstoffen bei Wartung", NameEN: "Contact with hazardous substances during maintenance",
|
||||||
RequiredComponentTags: []string{"chemical_risk"},
|
RequiredComponentTags: []string{"chemical_risk"},
|
||||||
RequiredLifecycles: []string{"maintenance", "cleaning"},
|
RequiredLifecycles: []string{"maintenance", "cleaning"},
|
||||||
GeneratedHazardCats: []string{"material_environmental"},
|
GeneratedHazardCats: []string{"material_environmental"},
|
||||||
@@ -179,7 +179,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 3, DefaultExposure: 3,
|
DefaultSeverity: 3, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP077", OperationalStates: []string{"maintenance"}, NameDE: "Elektrischer Schlag bei Wartungsarbeiten", NameEN: "Electric shock during maintenance",
|
ID: "HP077", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Elektrischer Schlag bei Wartungsarbeiten", NameEN: "Electric shock during maintenance",
|
||||||
RequiredComponentTags: []string{"high_voltage"},
|
RequiredComponentTags: []string{"high_voltage"},
|
||||||
RequiredLifecycles: []string{"maintenance", "fault_clearing"},
|
RequiredLifecycles: []string{"maintenance", "fault_clearing"},
|
||||||
GeneratedHazardCats: []string{"electrical_hazard"},
|
GeneratedHazardCats: []string{"electrical_hazard"},
|
||||||
@@ -195,7 +195,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
DefaultSeverity: 5, DefaultExposure: 3,
|
DefaultSeverity: 5, DefaultExposure: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "HP078", OperationalStates: []string{"maintenance"}, NameDE: "Ergonomische Belastung bei Wartungszugang", NameEN: "Ergonomic strain at maintenance access",
|
ID: "HP078", OperationalStates: []string{"maintenance"}, HumanRoles: []string{"maintenance_tech"}, NameDE: "Ergonomische Belastung bei Wartungszugang", NameEN: "Ergonomic strain at maintenance access",
|
||||||
RequiredComponentTags: []string{"structural_part"},
|
RequiredComponentTags: []string{"structural_part"},
|
||||||
RequiredLifecycles: []string{"maintenance"},
|
RequiredLifecycles: []string{"maintenance"},
|
||||||
GeneratedHazardCats: []string{"ergonomic"},
|
GeneratedHazardCats: []string{"ergonomic"},
|
||||||
@@ -228,7 +228,7 @@ func GetOperationalHazardPatterns() []HazardPattern {
|
|||||||
// Einrichten / Umruesten / Werkzeugwechsel (HP080-HP085)
|
// Einrichten / Umruesten / Werkzeugwechsel (HP080-HP085)
|
||||||
// ================================================================
|
// ================================================================
|
||||||
{
|
{
|
||||||
ID: "HP080", NameDE: "Quetschen bei Werkzeugwechsel", OperationalStates: []string{"maintenance", "teach_mode"}, NameEN: "Crushing during tool change",
|
ID: "HP080", NameDE: "Quetschen bei Werkzeugwechsel", OperationalStates: []string{"maintenance", "teach_mode"}, HumanRoles: []string{"maintenance_tech", "programmer"}, NameEN: "Crushing during tool change",
|
||||||
RequiredComponentTags: []string{"crush_point", "high_force"},
|
RequiredComponentTags: []string{"crush_point", "high_force"},
|
||||||
RequiredLifecycles: []string{"changeover", "setup"},
|
RequiredLifecycles: []string{"changeover", "setup"},
|
||||||
GeneratedHazardCats: []string{"mechanical_hazard"},
|
GeneratedHazardCats: []string{"mechanical_hazard"},
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ type MatchInput struct {
|
|||||||
// StateTransitions are active state transitions (format: "from→to").
|
// StateTransitions are active state transitions (format: "from→to").
|
||||||
// Used to detect transition-specific hazards like unexpected restart.
|
// Used to detect transition-specific hazards like unexpected restart.
|
||||||
StateTransitions []string `json:"state_transitions,omitempty"`
|
StateTransitions []string `json:"state_transitions,omitempty"`
|
||||||
|
// HumanRoles are the human roles interacting with the machine in this project.
|
||||||
|
// Used to filter patterns that only apply to specific roles (e.g. programmer, maintenance_tech).
|
||||||
|
HumanRoles []string `json:"human_roles,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchOutput contains the results of pattern matching.
|
// MatchOutput contains the results of pattern matching.
|
||||||
@@ -53,6 +56,7 @@ type PatternMatch struct {
|
|||||||
RequiresExpert bool `json:"requires_expert,omitempty"`
|
RequiresExpert bool `json:"requires_expert,omitempty"`
|
||||||
OperationalStates []string `json:"operational_states,omitempty"`
|
OperationalStates []string `json:"operational_states,omitempty"`
|
||||||
StateTransitions []string `json:"state_transitions,omitempty"`
|
StateTransitions []string `json:"state_transitions,omitempty"`
|
||||||
|
HumanRoles []string `json:"human_roles,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HazardSuggestion is a suggested hazard from pattern matching.
|
// HazardSuggestion is a suggested hazard from pattern matching.
|
||||||
@@ -195,6 +199,12 @@ func (e *PatternEngine) Match(input MatchInput) *MatchOutput {
|
|||||||
reasons = append(reasons, MatchReason{Type: "state_transition", Tag: t, Met: transSet[t]})
|
reasons = append(reasons, MatchReason{Type: "state_transition", Tag: t, Met: transSet[t]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(p.HumanRoles) > 0 {
|
||||||
|
roleSet := toSet(input.HumanRoles)
|
||||||
|
for _, r := range p.HumanRoles {
|
||||||
|
reasons = append(reasons, MatchReason{Type: "human_role", Tag: r, Met: roleSet[r]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
matchedPatterns = append(matchedPatterns, PatternMatch{
|
matchedPatterns = append(matchedPatterns, PatternMatch{
|
||||||
PatternID: p.ID,
|
PatternID: p.ID,
|
||||||
@@ -214,6 +224,7 @@ func (e *PatternEngine) Match(input MatchInput) *MatchOutput {
|
|||||||
RequiresExpert: p.RequiresExpertCalculation,
|
RequiresExpert: p.RequiresExpertCalculation,
|
||||||
OperationalStates: p.OperationalStates,
|
OperationalStates: p.OperationalStates,
|
||||||
StateTransitions: p.StateTransitions,
|
StateTransitions: p.StateTransitions,
|
||||||
|
HumanRoles: p.HumanRoles,
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, cat := range p.GeneratedHazardCats {
|
for _, cat := range p.GeneratedHazardCats {
|
||||||
@@ -351,6 +362,21 @@ func patternMatches(p HazardPattern, tagSet map[string]bool, input MatchInput) b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If pattern requires specific human roles, at least one must match.
|
||||||
|
if len(p.HumanRoles) > 0 && len(input.HumanRoles) > 0 {
|
||||||
|
found := false
|
||||||
|
roleSet := toSet(input.HumanRoles)
|
||||||
|
for _, r := range p.HumanRoles {
|
||||||
|
if roleSet[r] {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -419,6 +419,114 @@ func TestStandardStateTransitions_Valid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Human Interaction Model tests ──────────────────────────────────
|
||||||
|
|
||||||
|
func TestPatternEngine_HumanRole_NilFiresAlways(t *testing.T) {
|
||||||
|
engine := NewPatternEngine()
|
||||||
|
result1 := engine.Match(MatchInput{
|
||||||
|
ComponentLibraryIDs: []string{"C001"},
|
||||||
|
EnergySourceIDs: []string{"EN01"},
|
||||||
|
})
|
||||||
|
result2 := engine.Match(MatchInput{
|
||||||
|
ComponentLibraryIDs: []string{"C001"},
|
||||||
|
EnergySourceIDs: []string{"EN01"},
|
||||||
|
HumanRoles: []string{"operator"},
|
||||||
|
})
|
||||||
|
if len(result1.MatchedPatterns) == 0 {
|
||||||
|
t.Fatal("expected patterns without roles")
|
||||||
|
}
|
||||||
|
if len(result2.MatchedPatterns) == 0 {
|
||||||
|
t.Fatal("expected patterns with operator role")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatternEngine_HumanRole_MaintenanceTechFilter(t *testing.T) {
|
||||||
|
// HP073 has HumanRoles: ["maintenance_tech"]
|
||||||
|
engine := NewPatternEngine()
|
||||||
|
|
||||||
|
// With maintenance_tech → HP073 should fire
|
||||||
|
resultMT := engine.Match(MatchInput{
|
||||||
|
ComponentLibraryIDs: []string{"C001"},
|
||||||
|
EnergySourceIDs: []string{"EN01"},
|
||||||
|
LifecyclePhases: []string{"maintenance"},
|
||||||
|
OperationalStates: []string{"maintenance"},
|
||||||
|
HumanRoles: []string{"maintenance_tech"},
|
||||||
|
})
|
||||||
|
|
||||||
|
// With only operator → HP073 should NOT fire
|
||||||
|
resultOp := engine.Match(MatchInput{
|
||||||
|
ComponentLibraryIDs: []string{"C001"},
|
||||||
|
EnergySourceIDs: []string{"EN01"},
|
||||||
|
LifecyclePhases: []string{"maintenance"},
|
||||||
|
OperationalStates: []string{"maintenance"},
|
||||||
|
HumanRoles: []string{"operator"},
|
||||||
|
})
|
||||||
|
|
||||||
|
hasHP073MT := false
|
||||||
|
for _, p := range resultMT.MatchedPatterns {
|
||||||
|
if p.PatternID == "HP073" {
|
||||||
|
hasHP073MT = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasHP073Op := false
|
||||||
|
for _, p := range resultOp.MatchedPatterns {
|
||||||
|
if p.PatternID == "HP073" {
|
||||||
|
hasHP073Op = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasHP073MT {
|
||||||
|
t.Error("HP073 should fire for maintenance_tech role")
|
||||||
|
}
|
||||||
|
if hasHP073Op {
|
||||||
|
t.Error("HP073 should NOT fire for operator role")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPatternEngine_HumanRole_ProgrammerTeachMode(t *testing.T) {
|
||||||
|
// HP062 has OperationalStates: ["teach_mode"], HumanRoles: ["programmer"]
|
||||||
|
engine := NewPatternEngine()
|
||||||
|
|
||||||
|
// Programmer in teach mode with cobot components
|
||||||
|
result := engine.Match(MatchInput{
|
||||||
|
ComponentLibraryIDs: []string{"C139"}, // Cobot: moving_part, programmable, collaborative_operation
|
||||||
|
EnergySourceIDs: []string{"EN01"},
|
||||||
|
OperationalStates: []string{"teach_mode"},
|
||||||
|
HumanRoles: []string{"programmer"},
|
||||||
|
})
|
||||||
|
|
||||||
|
hasHP062 := false
|
||||||
|
for _, p := range result.MatchedPatterns {
|
||||||
|
if p.PatternID == "HP062" {
|
||||||
|
hasHP062 = true
|
||||||
|
// Verify explainability
|
||||||
|
hasRoleReason := false
|
||||||
|
for _, r := range p.MatchReasons {
|
||||||
|
if r.Type == "human_role" && r.Tag == "programmer" && r.Met {
|
||||||
|
hasRoleReason = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasRoleReason {
|
||||||
|
t.Error("HP062 should include human_role:programmer reason")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasHP062 {
|
||||||
|
t.Error("HP062 should fire for programmer in teach_mode with cobot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllHumanRoles_Count(t *testing.T) {
|
||||||
|
roles := AllHumanRoles()
|
||||||
|
if len(roles) != 6 {
|
||||||
|
t.Errorf("expected 6 human roles, got %d", len(roles))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func splitTransition(tr string) []string {
|
func splitTransition(tr string) []string {
|
||||||
// Split on → (UTF-8: 0xE2 0x86 0x92)
|
// Split on → (UTF-8: 0xE2 0x86 0x92)
|
||||||
idx := 0
|
idx := 0
|
||||||
|
|||||||
Reference in New Issue
Block a user