package iace // Interlocked-enclosure model (EN ISO 14120 / EN ISO 12100). // // A contact or entanglement hazard from a moving part is removed during NORMAL // operation when that part is inaccessible behind an interlocked guard. The // hazard then remains only when the guard is open — maintenance, cleaning or // fault clearing. Patterns flagged GuardableByEnclosure express this; a project // emits the "interlocked_enclosure" tag (interlocked door/hood, see // keyword_dictionary.go) to declare the guard. // // This is GENERIC: it applies to every enclosed machine (dishwasher spray arm, // enclosed mixer, centrifuge ...) and is regression-safe — machines that do not // emit interlocked_enclosure are unaffected. const ( phaseMaintenance = "maintenance" phaseCleaning = "cleaning" phaseFaultClearing = "fault_clearing" ) // suppressedByEnclosure reports whether a guardable hazard must be dropped: the // part is enclosed AND none of the project's lifecycle phases opens the guard. func suppressedByEnclosure(p HazardPattern, tagSet map[string]bool, lifecycles []string) bool { if !p.GuardableByEnclosure || !tagSet["interlocked_enclosure"] || len(lifecycles) == 0 { return false } for _, lc := range lifecycles { if lc == phaseMaintenance || lc == phaseCleaning || lc == phaseFaultClearing { return false // guard is open in some phase → hazard remains there } } return true } // guardedLifecycles re-scopes a guardable hazard to the guard-open phases when // the project declares an interlocked enclosure, so it is documented as a // maintenance/cleaning hazard rather than a normal-operation one. func guardedLifecycles(p HazardPattern, tagSet map[string]bool) []string { if p.GuardableByEnclosure && tagSet["interlocked_enclosure"] { return []string{phaseMaintenance, phaseCleaning} } return p.ApplicableLifecycles } // Domain-specific supersession. // // A generic pattern that fires via a broad tag (e.g. high_temperature) can // duplicate a domain-specific pattern that describes the same hazard more // precisely. When the domain is present, the specific pattern wins and the // generic duplicate is dropped. Scoped to the domain tag, so machines outside // the domain keep the generic pattern — regression-safe by construction. // // HP016 (generic hot surfaces) -> HP2201 (Boiler/Tank/Spuelkammer) // HP018 (actuator burn) -> HP2201 (same contact-burn hazard) var genericSupersededByWarewashing = map[string]bool{ "HP016": true, "HP018": true, } // supersededByDomainSpecific reports whether a generic pattern is replaced by a // more precise equivalent that the project's domain already provides. func supersededByDomainSpecific(p HazardPattern, tagSet map[string]bool) bool { return tagSet["dom_warewashing"] && genericSupersededByWarewashing[p.ID] }