aero-agent-roles 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +18 -0
- package/README.md +49 -0
- package/bin/aero-agent-roles.js +127 -0
- package/lib/catalog.js +97 -0
- package/lib/install.js +48 -0
- package/lib/mcp.js +199 -0
- package/manifest.json +516 -0
- package/package.json +44 -0
- package/roles/aerodynamics-engineer/ROLE.md +153 -0
- package/roles/aerodynamics-engineer/SOURCES.md +12 -0
- package/roles/aerodynamics-engineer/cli.py +143 -0
- package/roles/aerodynamics-engineer/core/aerodynamics_core.py +1218 -0
- package/roles/aerodynamics-engineer/templates/aero-design-report-template.md +72 -0
- package/roles/aerodynamics-engineer/tests/test_aerodynamics_core.py +227 -0
- package/roles/aerodynamics-engineer/tests/test_role_aerodynamics.py +94 -0
- package/roles/aircraft-design-engineer/ROLE.md +138 -0
- package/roles/aircraft-design-engineer/SOURCES.md +12 -0
- package/roles/aircraft-design-engineer/cli.py +152 -0
- package/roles/aircraft-design-engineer/core/aircraft_design_core.py +1278 -0
- package/roles/aircraft-design-engineer/templates/concept-design-template.md +99 -0
- package/roles/aircraft-design-engineer/tests/test_aircraft_design_core.py +269 -0
- package/roles/aircraft-design-engineer/tests/test_role_aircraft_design_engineer.py +106 -0
- package/roles/airworthiness-compliance-engineer/ROLE.md +122 -0
- package/roles/airworthiness-compliance-engineer/SOURCES.md +16 -0
- package/roles/airworthiness-compliance-engineer/cli.py +146 -0
- package/roles/airworthiness-compliance-engineer/core/airworthiness_core.py +894 -0
- package/roles/airworthiness-compliance-engineer/templates/compliance-matrix-template.md +46 -0
- package/roles/airworthiness-compliance-engineer/tests/test_airworthiness_core.py +224 -0
- package/roles/airworthiness-compliance-engineer/tests/test_role_airworthiness.py +85 -0
- package/roles/as9100-quality-auditor/ROLE.md +128 -0
- package/roles/as9100-quality-auditor/SOURCES.md +16 -0
- package/roles/as9100-quality-auditor/cli.py +136 -0
- package/roles/as9100-quality-auditor/core/as9100_core.py +857 -0
- package/roles/as9100-quality-auditor/templates/findings-report-template.md +119 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_cli_profile.py +105 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_core.py +204 -0
- package/roles/as9100-quality-auditor/tests/test_role_as9100.py +90 -0
- package/roles/data-bus-avionics-engineer/ROLE.md +146 -0
- package/roles/data-bus-avionics-engineer/SOURCES.md +23 -0
- package/roles/data-bus-avionics-engineer/cli.py +311 -0
- package/roles/data-bus-avionics-engineer/core/data_bus_avionics_core.py +1121 -0
- package/roles/data-bus-avionics-engineer/templates/bus-assessment-template.md +134 -0
- package/roles/data-bus-avionics-engineer/tests/test_bundle_protocol.py +137 -0
- package/roles/data-bus-avionics-engineer/tests/test_data_bus_avionics_core.py +261 -0
- package/roles/data-bus-avionics-engineer/tests/test_role_data_bus_avionics_engineer.py +121 -0
- package/roles/do160-environmental-engineer/ROLE.md +136 -0
- package/roles/do160-environmental-engineer/SOURCES.md +30 -0
- package/roles/do160-environmental-engineer/cli.py +147 -0
- package/roles/do160-environmental-engineer/core/do160_environmental_core.py +1246 -0
- package/roles/do160-environmental-engineer/templates/environmental-qualification-plan-template.md +114 -0
- package/roles/do160-environmental-engineer/tests/test_do160_environmental_core.py +312 -0
- package/roles/do160-environmental-engineer/tests/test_role_do160_environmental_engineer.py +281 -0
- package/roles/do178c-cert-engineer/ROLE.md +120 -0
- package/roles/do178c-cert-engineer/SOURCES.md +16 -0
- package/roles/do178c-cert-engineer/cli.py +129 -0
- package/roles/do178c-cert-engineer/core/do178c_core.py +411 -0
- package/roles/do178c-cert-engineer/templates/psac-template.md +74 -0
- package/roles/do178c-cert-engineer/tests/test_do178c_cert_engineer_core.py +91 -0
- package/roles/do178c-cert-engineer/tests/test_role_do178c.py +109 -0
- package/roles/do254-hardware-engineer/ROLE.md +127 -0
- package/roles/do254-hardware-engineer/SOURCES.md +19 -0
- package/roles/do254-hardware-engineer/cli.py +308 -0
- package/roles/do254-hardware-engineer/core/do254_hardware_core.py +761 -0
- package/roles/do254-hardware-engineer/templates/phac-template.md +87 -0
- package/roles/do254-hardware-engineer/tests/test_do254_bundle_profile.py +152 -0
- package/roles/do254-hardware-engineer/tests/test_do254_hardware_core.py +200 -0
- package/roles/do254-hardware-engineer/tests/test_role_do254_hardware.py +108 -0
- package/roles/engineering-analysis-engineer/ROLE.md +154 -0
- package/roles/engineering-analysis-engineer/SOURCES.md +15 -0
- package/roles/engineering-analysis-engineer/cli.py +151 -0
- package/roles/engineering-analysis-engineer/core/engineering_analysis_engineer_core.py +866 -0
- package/roles/engineering-analysis-engineer/templates/analysis-memo-template.md +73 -0
- package/roles/engineering-analysis-engineer/tests/test_engineering_analysis_engineer_core.py +367 -0
- package/roles/engineering-analysis-engineer/tests/test_role_engineering_analysis_engineer.py +113 -0
- package/roles/flight-management-engineer/ROLE.md +160 -0
- package/roles/flight-management-engineer/SOURCES.md +30 -0
- package/roles/flight-management-engineer/cli.py +258 -0
- package/roles/flight-management-engineer/core/flight_management_core.py +1473 -0
- package/roles/flight-management-engineer/templates/route-assessment-template.md +94 -0
- package/roles/flight-management-engineer/tests/test_flight_management_core.py +437 -0
- package/roles/flight-management-engineer/tests/test_role_flight_management_engineer.py +123 -0
- package/roles/flight-mechanics-engineer/ROLE.md +162 -0
- package/roles/flight-mechanics-engineer/SOURCES.md +51 -0
- package/roles/flight-mechanics-engineer/cli.py +149 -0
- package/roles/flight-mechanics-engineer/core/flight_mechanics_core.py +1507 -0
- package/roles/flight-mechanics-engineer/templates/perf-sc-report-template.md +82 -0
- package/roles/flight-mechanics-engineer/tests/test_flight_mechanics_core.py +277 -0
- package/roles/flight-mechanics-engineer/tests/test_role_flight_mechanics_engineer.py +141 -0
- package/roles/flight-test-engineer/ROLE.md +137 -0
- package/roles/flight-test-engineer/SOURCES.md +24 -0
- package/roles/flight-test-engineer/cli.py +135 -0
- package/roles/flight-test-engineer/core/flight_test_core.py +931 -0
- package/roles/flight-test-engineer/templates/flight-test-template.md +143 -0
- package/roles/flight-test-engineer/tests/test_flight_test_engineer_core.py +227 -0
- package/roles/flight-test-engineer/tests/test_role_flight_test_engineer.py +150 -0
- package/roles/flight-test-performance-engineer/ROLE.md +165 -0
- package/roles/flight-test-performance-engineer/SOURCES.md +20 -0
- package/roles/flight-test-performance-engineer/cli.py +319 -0
- package/roles/flight-test-performance-engineer/core/flight_test_performance_core.py +1398 -0
- package/roles/flight-test-performance-engineer/templates/performance-report-template.md +105 -0
- package/roles/flight-test-performance-engineer/tests/test_flight_test_performance_engineer_core.py +308 -0
- package/roles/flight-test-performance-engineer/tests/test_role_flight_test_performance_engineer.py +165 -0
- package/roles/gnc-engineer/ROLE.md +141 -0
- package/roles/gnc-engineer/SOURCES.md +35 -0
- package/roles/gnc-engineer/cli.py +141 -0
- package/roles/gnc-engineer/core/gnc_core.py +1074 -0
- package/roles/gnc-engineer/templates/gnc-report-template.md +105 -0
- package/roles/gnc-engineer/tests/test_gnc_engineer_core.py +238 -0
- package/roles/gnc-engineer/tests/test_role_gnc_engineer.py +142 -0
- package/roles/guidance-engineer/ROLE.md +156 -0
- package/roles/guidance-engineer/SOURCES.md +43 -0
- package/roles/guidance-engineer/cli.py +280 -0
- package/roles/guidance-engineer/core/guidance_core.py +820 -0
- package/roles/guidance-engineer/templates/guidance-report-template.md +70 -0
- package/roles/guidance-engineer/tests/test_guidance_engineer_core.py +269 -0
- package/roles/guidance-engineer/tests/test_role_guidance_engineer.py +146 -0
- package/roles/high-speed-aerodynamics-engineer/ROLE.md +161 -0
- package/roles/high-speed-aerodynamics-engineer/SOURCES.md +18 -0
- package/roles/high-speed-aerodynamics-engineer/cli.py +282 -0
- package/roles/high-speed-aerodynamics-engineer/core/high_speed_aero_core.py +1566 -0
- package/roles/high-speed-aerodynamics-engineer/templates/high-speed-memo-template.md +75 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_bundle_protocol.py +103 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_high_speed_aero_core.py +280 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_role_high_speed_aerodynamics.py +125 -0
- package/roles/ndt-engineer/ROLE.md +157 -0
- package/roles/ndt-engineer/SOURCES.md +18 -0
- package/roles/ndt-engineer/cli.py +316 -0
- package/roles/ndt-engineer/core/ndt_core.py +1590 -0
- package/roles/ndt-engineer/templates/ndt-plan-template.md +109 -0
- package/roles/ndt-engineer/tests/test_ndt_cli_bundle.py +183 -0
- package/roles/ndt-engineer/tests/test_ndt_core.py +437 -0
- package/roles/ndt-engineer/tests/test_role_ndt_engineer.py +148 -0
- package/roles/numerical-analysis-engineer/ROLE.md +158 -0
- package/roles/numerical-analysis-engineer/SOURCES.md +19 -0
- package/roles/numerical-analysis-engineer/cli.py +414 -0
- package/roles/numerical-analysis-engineer/core/numerical_analysis_core.py +1220 -0
- package/roles/numerical-analysis-engineer/templates/verification-memo-template.md +120 -0
- package/roles/numerical-analysis-engineer/tests/test_bundle_protocol.py +157 -0
- package/roles/numerical-analysis-engineer/tests/test_numerical_analysis_core.py +337 -0
- package/roles/numerical-analysis-engineer/tests/test_role_numerical_analysis_engineer.py +117 -0
- package/roles/propulsion-engineer/ROLE.md +163 -0
- package/roles/propulsion-engineer/SOURCES.md +15 -0
- package/roles/propulsion-engineer/cli.py +168 -0
- package/roles/propulsion-engineer/core/propulsion_core.py +1291 -0
- package/roles/propulsion-engineer/templates/propulsion-report-template.md +92 -0
- package/roles/propulsion-engineer/tests/test_propulsion_engineer_core.py +294 -0
- package/roles/propulsion-engineer/tests/test_role_propulsion_engineer.py +106 -0
- package/roles/space-systems-engineer/ROLE.md +161 -0
- package/roles/space-systems-engineer/SOURCES.md +49 -0
- package/roles/space-systems-engineer/cli.py +173 -0
- package/roles/space-systems-engineer/core/space_systems_core.py +980 -0
- package/roles/space-systems-engineer/templates/space-systems-template.md +88 -0
- package/roles/space-systems-engineer/tests/test_role_space_systems_engineer.py +117 -0
- package/roles/space-systems-engineer/tests/test_space_systems_engineer_core.py +247 -0
- package/roles/state-estimation-engineer/ROLE.md +154 -0
- package/roles/state-estimation-engineer/SOURCES.md +27 -0
- package/roles/state-estimation-engineer/cli.py +326 -0
- package/roles/state-estimation-engineer/core/state_estimation_core.py +958 -0
- package/roles/state-estimation-engineer/templates/nav-state-estimator-design-template.md +76 -0
- package/roles/state-estimation-engineer/tests/test_bundle_protocol.py +110 -0
- package/roles/state-estimation-engineer/tests/test_role_state_estimation_engineer.py +127 -0
- package/roles/state-estimation-engineer/tests/test_state_estimation_engineer_core.py +231 -0
- package/roles/structures-loads-engineer/ROLE.md +171 -0
- package/roles/structures-loads-engineer/SOURCES.md +44 -0
- package/roles/structures-loads-engineer/cli.py +219 -0
- package/roles/structures-loads-engineer/core/structures_loads_core.py +1112 -0
- package/roles/structures-loads-engineer/templates/loads-strength-report-template.md +96 -0
- package/roles/structures-loads-engineer/tests/test_bundle_protocol.py +101 -0
- package/roles/structures-loads-engineer/tests/test_role_structures_loads_engineer.py +147 -0
- package/roles/structures-loads-engineer/tests/test_structures_loads_core.py +261 -0
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""do254_hardware_core.py - DO-254 Airborne Electronic Hardware Engineer core.
|
|
3
|
+
|
|
4
|
+
This is the role's ENGINE: given an airborne electronic hardware item's
|
|
5
|
+
project facts it determines the hardware design assurance level (DAL A-E
|
|
6
|
+
from the function failure condition), classifies the item simple vs
|
|
7
|
+
complex AEH, computes verification expectations (methods, coverage
|
|
8
|
+
ratios, independence), configuration management depth, requirement
|
|
9
|
+
traceability health, and BUILDS the Plan for Hardware Aspects of
|
|
10
|
+
Certification (PHAC) content. It also gate-checks deliverables.
|
|
11
|
+
Standalone: no external repo needed.
|
|
12
|
+
|
|
13
|
+
Domain rules encoded here are public process knowledge as practiced in
|
|
14
|
+
the bound AeroSkills do254 leaves (hardware-planning, verification,
|
|
15
|
+
configuration-management, requirements-capture) and standard
|
|
16
|
+
certification practice summarized in FAA AC 20-152A. DO-254 text is
|
|
17
|
+
never reproduced - the PHAC structure is an original synthesis.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import re
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from datetime import date
|
|
25
|
+
|
|
26
|
+
def _today() -> str:
|
|
27
|
+
import os
|
|
28
|
+
from datetime import date
|
|
29
|
+
return os.environ.get("ROLE_GEN_DATE", date.today().isoformat())
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Domain tables (public process knowledge)
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
# Hardware design assurance level from failure-condition severity
|
|
36
|
+
# (FAR 25.1309 / CS-25.1309 severity categories -> DAL mapping used in
|
|
37
|
+
# DO-254 certification practice; identical severity-to-level logic as
|
|
38
|
+
# the software side).
|
|
39
|
+
SEVERITY_TO_DAL = {
|
|
40
|
+
"catastrophic": "A",
|
|
41
|
+
"hazardous": "B",
|
|
42
|
+
"major": "C",
|
|
43
|
+
"minor": "D",
|
|
44
|
+
"no-safety-effect": "E",
|
|
45
|
+
}
|
|
46
|
+
DALS = ("A", "B", "C", "D", "E")
|
|
47
|
+
|
|
48
|
+
# DO-254 verification logic: requirements-based test coverage ratio by
|
|
49
|
+
# level: 0.98 at levels A/B, 0.95 at levels C/D (as encoded in the bound
|
|
50
|
+
# avionics/do254/verification leaf). Level E (no safety effect) is
|
|
51
|
+
# typically outside DO-254 scope: no coverage target.
|
|
52
|
+
COVERAGE_RATIO = {"A": 0.98, "B": 0.98, "C": 0.95, "D": 0.95, "E": None}
|
|
53
|
+
|
|
54
|
+
# Verification independence by DAL: independent verification expected at
|
|
55
|
+
# the higher hardware design assurance levels (A/B) per the bound
|
|
56
|
+
# avionics/do254/verification leaf.
|
|
57
|
+
INDEPENDENT = {"A": True, "B": True, "C": False, "D": False, "E": False}
|
|
58
|
+
|
|
59
|
+
# Life cycle data set by DAL: the hardware life cycle data items a PHAC
|
|
60
|
+
# must name. Levels with more rigor require the full set; E (no safety
|
|
61
|
+
# effect) is a documented exclusion. Original synthesis of the standard
|
|
62
|
+
# plan-of-attack categories, not reproduced text.
|
|
63
|
+
LIFE_CYCLE_DATA = {
|
|
64
|
+
"A": [
|
|
65
|
+
"plan for hardware aspects of certification (PHAC)",
|
|
66
|
+
"hardware requirements standards",
|
|
67
|
+
"hardware design standards",
|
|
68
|
+
"hardware validation plan",
|
|
69
|
+
"hardware verification plan",
|
|
70
|
+
"hardware configuration management plan",
|
|
71
|
+
"hardware process assurance plan",
|
|
72
|
+
"hardware requirements data",
|
|
73
|
+
"hardware design data",
|
|
74
|
+
"hardware verification data and results",
|
|
75
|
+
"hardware life cycle environment configuration index",
|
|
76
|
+
"hardware configuration index (HCI)",
|
|
77
|
+
"hardware process assurance records",
|
|
78
|
+
"hardware accomplishment summary (HAS)",
|
|
79
|
+
],
|
|
80
|
+
"B": [
|
|
81
|
+
"plan for hardware aspects of certification (PHAC)",
|
|
82
|
+
"hardware requirements standards",
|
|
83
|
+
"hardware design standards",
|
|
84
|
+
"hardware validation plan",
|
|
85
|
+
"hardware verification plan",
|
|
86
|
+
"hardware configuration management plan",
|
|
87
|
+
"hardware process assurance plan",
|
|
88
|
+
"hardware requirements data",
|
|
89
|
+
"hardware design data",
|
|
90
|
+
"hardware verification data and results",
|
|
91
|
+
"hardware life cycle environment configuration index",
|
|
92
|
+
"hardware configuration index (HCI)",
|
|
93
|
+
"hardware process assurance records",
|
|
94
|
+
"hardware accomplishment summary (HAS)",
|
|
95
|
+
],
|
|
96
|
+
"C": [
|
|
97
|
+
"plan for hardware aspects of certification (PHAC)",
|
|
98
|
+
"hardware requirements standards",
|
|
99
|
+
"hardware design standards",
|
|
100
|
+
"hardware verification plan",
|
|
101
|
+
"hardware configuration management plan",
|
|
102
|
+
"hardware requirements data",
|
|
103
|
+
"hardware design data",
|
|
104
|
+
"hardware verification data and results",
|
|
105
|
+
"hardware configuration index (HCI)",
|
|
106
|
+
"hardware process assurance records",
|
|
107
|
+
],
|
|
108
|
+
"D": [
|
|
109
|
+
"plan for hardware aspects of certification (PHAC)",
|
|
110
|
+
"hardware requirements data",
|
|
111
|
+
"hardware design data",
|
|
112
|
+
"hardware verification plan",
|
|
113
|
+
"hardware verification data and results",
|
|
114
|
+
"hardware configuration index (HCI)",
|
|
115
|
+
"hardware process assurance records",
|
|
116
|
+
],
|
|
117
|
+
"E": [
|
|
118
|
+
"hardware requirements data",
|
|
119
|
+
"hardware design data",
|
|
120
|
+
"hardware verification data and results",
|
|
121
|
+
],
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Verification objectives by DAL: objective families expected per level
|
|
125
|
+
# (public practice: planning/verification/CM/process assurance scale with
|
|
126
|
+
# the level; A/B add independence and certification liaison).
|
|
127
|
+
VERIFICATION_FAMILIES = {
|
|
128
|
+
"A": ["planning", "requirements", "design", "verification", "cm",
|
|
129
|
+
"process-assurance", "cert-liaison"],
|
|
130
|
+
"B": ["planning", "requirements", "design", "verification", "cm",
|
|
131
|
+
"process-assurance", "cert-liaison"],
|
|
132
|
+
"C": ["planning", "requirements", "design", "verification", "cm",
|
|
133
|
+
"process-assurance"],
|
|
134
|
+
"D": ["planning", "requirements", "design", "verification", "cm"],
|
|
135
|
+
"E": [],
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# Hardware change classes (DO-254 CM practice, bound
|
|
139
|
+
# configuration-management leaf): class 1 = formal ECR/ECO with baseline
|
|
140
|
+
# update + reverification + independent review; class 2 = documented
|
|
141
|
+
# lighter review.
|
|
142
|
+
CHANGE_CLASS_1_RATIONALE = ("form/fit/function change or safety effect "
|
|
143
|
+
"present or complex hardware")
|
|
144
|
+
CHANGE_CLASS_2_RATIONALE = ("no form/fit/function change, no safety effect, "
|
|
145
|
+
"simple hardware")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass
|
|
149
|
+
class HardwareItem:
|
|
150
|
+
"""Project facts the role needs to build the PHAC."""
|
|
151
|
+
item_name: str
|
|
152
|
+
description: str = ""
|
|
153
|
+
failure_condition: str = "major" # severity category
|
|
154
|
+
system_safety_ref: str = "" # FHA/PSSA reference
|
|
155
|
+
# AEH classification inputs (bound hardware-planning leaf semantics):
|
|
156
|
+
has_programmable_logic: bool = False
|
|
157
|
+
has_internal_state: bool = False
|
|
158
|
+
fully_verifiable_from_top_data: bool = True
|
|
159
|
+
safety_significant: bool = False
|
|
160
|
+
technology: str = "" # CPLD / FPGA / ASIC / ...
|
|
161
|
+
life_cycle_model: str = "waterfall"
|
|
162
|
+
hdl_languages: list = field(default_factory=lambda: ["VHDL"])
|
|
163
|
+
target_device: str = ""
|
|
164
|
+
development_tools: list = field(default_factory=list)
|
|
165
|
+
verification_tools: list = field(default_factory=list)
|
|
166
|
+
tool_qualification_approach: str = "criteria-based per DO-254"
|
|
167
|
+
previously_developed: bool = False
|
|
168
|
+
pds_origin_standard: str = ""
|
|
169
|
+
certification_basis: str = "FAR/CS-25"
|
|
170
|
+
requirements: list = field(default_factory=list)
|
|
171
|
+
|
|
172
|
+
def assurance_level(self) -> str:
|
|
173
|
+
return SEVERITY_TO_DAL.get(self.failure_condition.lower(), "E")
|
|
174
|
+
|
|
175
|
+
def aeh_class(self) -> str:
|
|
176
|
+
return classify_aeh(
|
|
177
|
+
has_programmable_logic=self.has_programmable_logic,
|
|
178
|
+
has_internal_state=self.has_internal_state,
|
|
179
|
+
fully_verifiable_from_top_data=self.fully_verifiable_from_top_data,
|
|
180
|
+
safety_significant=self.safety_significant)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
# AEH classification (grounded in avionics/do254/hardware-planning leaf)
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
_COMPLEX_ARTIFACTS = ("phac", "requirements-capture", "conceptual-design",
|
|
188
|
+
"detailed-design", "verification", "configuration-management",
|
|
189
|
+
"process-assurance")
|
|
190
|
+
_SIMPLE_ARTIFACTS = ("hardware-plan", "verification", "configuration-management")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def classify_aeh(has_programmable_logic, has_internal_state,
|
|
194
|
+
fully_verifiable_from_top_data, safety_significant) -> str:
|
|
195
|
+
"""Return 'complex' or 'simple' for a DO-254 AEH item.
|
|
196
|
+
|
|
197
|
+
Conservative planning default: when in doubt, treat the item as
|
|
198
|
+
complex so the full design assurance process applies (programmable
|
|
199
|
+
logic, internal state, or inability to verify behavior from top-level
|
|
200
|
+
data alone, or safety-significant items).
|
|
201
|
+
"""
|
|
202
|
+
if has_programmable_logic or has_internal_state:
|
|
203
|
+
return "complex"
|
|
204
|
+
if not fully_verifiable_from_top_data:
|
|
205
|
+
return "complex"
|
|
206
|
+
if safety_significant:
|
|
207
|
+
return "complex"
|
|
208
|
+
return "simple"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def planning_artifacts(classification: str) -> list:
|
|
212
|
+
"""Planning artifact set per AEH class; unknown class -> ValueError."""
|
|
213
|
+
if classification == "complex":
|
|
214
|
+
return list(_COMPLEX_ARTIFACTS)
|
|
215
|
+
if classification == "simple":
|
|
216
|
+
return list(_SIMPLE_ARTIFACTS)
|
|
217
|
+
raise ValueError("unknown AEH classification: %r" % (classification,))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
# Verification rules (grounded in avionics/do254/verification leaf)
|
|
222
|
+
# ---------------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
def _check_dal(dal: str) -> None:
|
|
225
|
+
if dal not in DALS:
|
|
226
|
+
raise ValueError("invalid DAL %r" % (dal,))
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def verification_methods_for(aeh_class: str, dal: str) -> set:
|
|
230
|
+
"""Verification methods for a DO-254 hardware item: complex AEH uses
|
|
231
|
+
test, analysis, and review; simple AEH uses reduced verification
|
|
232
|
+
(review). Unknown classes or levels outside A-D raise ValueError."""
|
|
233
|
+
if aeh_class not in ("simple", "complex"):
|
|
234
|
+
raise ValueError("unknown AEH class: %r" % (aeh_class,))
|
|
235
|
+
if dal not in ("A", "B", "C", "D"):
|
|
236
|
+
raise ValueError("invalid hardware design assurance level: %r" % (dal,))
|
|
237
|
+
if aeh_class == "complex":
|
|
238
|
+
return {"test", "analysis", "review"}
|
|
239
|
+
return {"review"}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def independence_required(dal: str) -> bool:
|
|
243
|
+
"""Independent verification is expected at the higher hardware design
|
|
244
|
+
assurance levels (A/B)."""
|
|
245
|
+
_check_dal(dal)
|
|
246
|
+
return INDEPENDENT[dal]
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def coverage_ratio(dal: str) -> float | None:
|
|
250
|
+
"""Requirements-based test coverage ratio by level (None at E)."""
|
|
251
|
+
_check_dal(dal)
|
|
252
|
+
return COVERAGE_RATIO[dal]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def coverage_adequate(dal: str, measured_pct: float) -> bool:
|
|
256
|
+
"""True when measured requirements-based coverage meets the level ratio."""
|
|
257
|
+
ratio = coverage_ratio(dal)
|
|
258
|
+
if ratio is None:
|
|
259
|
+
return True
|
|
260
|
+
return measured_pct >= ratio
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def verification_complete(methods_used, required_methods) -> bool:
|
|
264
|
+
"""True when every required verification method is present in the
|
|
265
|
+
methods used (extra methods do not fail the check)."""
|
|
266
|
+
return set(required_methods).issubset(set(methods_used))
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def hwsw_integration_evidence(present: bool) -> bool:
|
|
270
|
+
"""Whether hardware/software integration evidence is available for the
|
|
271
|
+
item (plain boolean pass-through)."""
|
|
272
|
+
return bool(present)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def structural_methods_text(dal: str) -> str:
|
|
276
|
+
"""Plain-language verification expectation for the level."""
|
|
277
|
+
cov = coverage_ratio(dal)
|
|
278
|
+
indep = independence_required(dal)
|
|
279
|
+
indep_txt = "independent" if indep else "may be conducted by the developer"
|
|
280
|
+
if cov is None:
|
|
281
|
+
return "No requirements-based coverage target (no safety effect)."
|
|
282
|
+
return ("Requirements-based coverage ratio %.2f; verification %s."
|
|
283
|
+
% (cov, indep_txt))
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# ---------------------------------------------------------------------------
|
|
287
|
+
# Configuration management (grounded in avionics/do254/configuration-management)
|
|
288
|
+
# ---------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
VALID_HARDWARE_CLASSES = ("simple", "complex")
|
|
291
|
+
VALID_SAFETY_EFFECTS = ("none", "minor", "major", "hazardous", "catastrophic")
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def hw_change_class(change: dict) -> dict:
|
|
295
|
+
"""Classify a DO-254 hardware change as class 1 or class 2.
|
|
296
|
+
|
|
297
|
+
change: dict with keys hardware_class ('simple'/'complex'),
|
|
298
|
+
safety_effect ('none'/'minor'/'major'/'hazardous'/'catastrophic'),
|
|
299
|
+
functional_change (bool). Class 1 when form/fit/function changes,
|
|
300
|
+
safety effect present, or hardware is complex.
|
|
301
|
+
"""
|
|
302
|
+
if not isinstance(change, dict):
|
|
303
|
+
raise ValueError("change must be a dict")
|
|
304
|
+
hw = change.get("hardware_class")
|
|
305
|
+
se = change.get("safety_effect")
|
|
306
|
+
fc = change.get("functional_change")
|
|
307
|
+
if hw not in VALID_HARDWARE_CLASSES:
|
|
308
|
+
raise ValueError("hardware_class must be simple or complex, got %r" % hw)
|
|
309
|
+
if se not in VALID_SAFETY_EFFECTS:
|
|
310
|
+
raise ValueError("safety_effect must be one of %s, got %r"
|
|
311
|
+
% (", ".join(VALID_SAFETY_EFFECTS), se))
|
|
312
|
+
if not isinstance(fc, bool):
|
|
313
|
+
raise ValueError("functional_change must be a bool, got %r" % fc)
|
|
314
|
+
if fc or se != "none" or hw == "complex":
|
|
315
|
+
return {"class": 1, "rationale": CHANGE_CLASS_1_RATIONALE}
|
|
316
|
+
return {"class": 2, "rationale": CHANGE_CLASS_2_RATIONALE}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def cm_actions(change_class_num: int) -> dict:
|
|
320
|
+
"""Map a change class to required configuration management actions."""
|
|
321
|
+
if change_class_num == 1:
|
|
322
|
+
return {"baseline_update": True, "ecr_required": True,
|
|
323
|
+
"reverification_required": True, "independent_review": True}
|
|
324
|
+
if change_class_num == 2:
|
|
325
|
+
return {"baseline_update": True, "ecr_required": True,
|
|
326
|
+
"reverification_required": False, "independent_review": False}
|
|
327
|
+
raise ValueError("change class must be 1 or 2, got %r" % (change_class_num,))
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def hci_entry(item: str, revision: str, baseline: str) -> str:
|
|
331
|
+
"""Format one hardware configuration index line: 'item rev baseline'."""
|
|
332
|
+
if not isinstance(item, str) or not item.strip():
|
|
333
|
+
raise ValueError("item must be a non-empty string")
|
|
334
|
+
if not isinstance(revision, str) or not revision.strip():
|
|
335
|
+
raise ValueError("revision must be a non-empty string")
|
|
336
|
+
if not isinstance(baseline, str) or not baseline.strip():
|
|
337
|
+
raise ValueError("baseline must be a non-empty string")
|
|
338
|
+
return "%s %s %s" % (item.strip(), revision.strip(), baseline.strip())
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ---------------------------------------------------------------------------
|
|
342
|
+
# Requirements capture (grounded in avionics/do254/requirements-capture)
|
|
343
|
+
# ---------------------------------------------------------------------------
|
|
344
|
+
|
|
345
|
+
VAGUE_TERMS = ("suitable", "adequate", "approximately", "etc", "as required",
|
|
346
|
+
"or better", "and so on", "reasonable")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def req_issues(requirement: dict) -> list:
|
|
350
|
+
"""List of issue flags for one requirement mapping.
|
|
351
|
+
|
|
352
|
+
Recognized flags: missing-id, empty-text, vague, not-traceable."""
|
|
353
|
+
if not isinstance(requirement, dict):
|
|
354
|
+
raise ValueError("requirement must be a mapping")
|
|
355
|
+
issues = []
|
|
356
|
+
rid = requirement.get("id") or ""
|
|
357
|
+
text = requirement.get("text") or ""
|
|
358
|
+
if not str(rid).strip():
|
|
359
|
+
issues.append("missing-id")
|
|
360
|
+
if not str(text).strip():
|
|
361
|
+
issues.append("empty-text")
|
|
362
|
+
lower = str(text).lower()
|
|
363
|
+
if any(term in lower for term in VAGUE_TERMS):
|
|
364
|
+
issues.append("vague")
|
|
365
|
+
if not requirement.get("traceable"):
|
|
366
|
+
issues.append("not-traceable")
|
|
367
|
+
return issues
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def classify_derived(has_higher_level_source: bool) -> str:
|
|
371
|
+
"""'derived' when there is no direct higher-level source, else 'allocated'."""
|
|
372
|
+
return "allocated" if has_higher_level_source else "derived"
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def capture_readiness(requirements: list) -> tuple:
|
|
376
|
+
"""(ready, score) fraction of requirements with no issues.
|
|
377
|
+
|
|
378
|
+
Raises ValueError on an empty list. Ready means score >= 0.7
|
|
379
|
+
(project-defined threshold)."""
|
|
380
|
+
if not requirements:
|
|
381
|
+
raise ValueError("requirements list must not be empty")
|
|
382
|
+
clean = sum(1 for req in requirements if not req_issues(req))
|
|
383
|
+
score = clean / float(len(requirements))
|
|
384
|
+
return (score >= 0.7, score)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def traceability_summary(requirements: list) -> dict:
|
|
388
|
+
"""Traceability health of a hardware requirements set.
|
|
389
|
+
|
|
390
|
+
Returns counts of allocated (upward-traced) vs derived requirements,
|
|
391
|
+
derived requirements lacking a justification, and a per-requirement
|
|
392
|
+
issue list for the design review. Derived requirements are added
|
|
393
|
+
during design or safety analysis with no direct higher-level source;
|
|
394
|
+
they must be identified and justified and they count for verification.
|
|
395
|
+
"""
|
|
396
|
+
allocated = 0
|
|
397
|
+
allocated_traced = 0
|
|
398
|
+
derived = 0
|
|
399
|
+
derived_justified = 0
|
|
400
|
+
issues_by_id = {}
|
|
401
|
+
for req in requirements:
|
|
402
|
+
issues = req_issues(req)
|
|
403
|
+
if classify_derived(bool(req.get("source"))) == "allocated":
|
|
404
|
+
allocated += 1
|
|
405
|
+
if req.get("traceable"):
|
|
406
|
+
allocated_traced += 1
|
|
407
|
+
else:
|
|
408
|
+
derived += 1
|
|
409
|
+
if req.get("justification"):
|
|
410
|
+
derived_justified += 1
|
|
411
|
+
if issues:
|
|
412
|
+
issues_by_id[req.get("id", "?")] = issues
|
|
413
|
+
total = len(requirements)
|
|
414
|
+
readiness = capture_readiness(requirements)
|
|
415
|
+
return {
|
|
416
|
+
"total": total,
|
|
417
|
+
"allocated": allocated,
|
|
418
|
+
"allocated_traced": allocated_traced,
|
|
419
|
+
"derived": derived,
|
|
420
|
+
"derived_justified": derived_justified,
|
|
421
|
+
"issue_count": sum(len(v) for v in issues_by_id.values()),
|
|
422
|
+
"issues_by_id": issues_by_id,
|
|
423
|
+
"readiness_score": round(readiness[1], 4),
|
|
424
|
+
"ready": readiness[0],
|
|
425
|
+
"allocated_traced_complete": (allocated > 0 and
|
|
426
|
+
allocated_traced == allocated),
|
|
427
|
+
"derived_justified_complete": derived == 0 or derived_justified == derived,
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
# ---------------------------------------------------------------------------
|
|
432
|
+
# PHAC builder: produces the actual deliverable content
|
|
433
|
+
# ---------------------------------------------------------------------------
|
|
434
|
+
|
|
435
|
+
def build_phac(item: HardwareItem) -> dict:
|
|
436
|
+
"""Build the complete PHAC content model from project facts."""
|
|
437
|
+
dal = item.assurance_level()
|
|
438
|
+
aeh_class = item.aeh_class()
|
|
439
|
+
methods = sorted(verification_methods_for(aeh_class, dal)
|
|
440
|
+
if dal in ("A", "B", "C", "D") else [])
|
|
441
|
+
return {
|
|
442
|
+
"document_type": "Plan for Hardware Aspects of Certification",
|
|
443
|
+
"status": "draft-for-review",
|
|
444
|
+
"item": item.item_name,
|
|
445
|
+
"item_description": item.description,
|
|
446
|
+
"certification_basis": item.certification_basis,
|
|
447
|
+
"assurance_level": dal,
|
|
448
|
+
"severity_source": item.failure_condition,
|
|
449
|
+
"system_safety_reference": item.system_safety_ref,
|
|
450
|
+
"aeh_class": aeh_class,
|
|
451
|
+
"life_cycle_model": item.life_cycle_model,
|
|
452
|
+
"technology": item.technology,
|
|
453
|
+
"life_cycle_data": life_cycle_data(dal),
|
|
454
|
+
"coverage_ratio": coverage_ratio(dal),
|
|
455
|
+
"verification_methods": methods,
|
|
456
|
+
"independence": independence_required(dal),
|
|
457
|
+
"independence_text": ("independent" if independence_required(dal)
|
|
458
|
+
else "may be conducted by the developer"),
|
|
459
|
+
"hwsw_integration": hwsw_integration_evidence(True),
|
|
460
|
+
"planning_artifacts": planning_artifacts(aeh_class),
|
|
461
|
+
"hdl_languages": item.hdl_languages,
|
|
462
|
+
"target_device": item.target_device,
|
|
463
|
+
"development_tools": item.development_tools,
|
|
464
|
+
"verification_tools": item.verification_tools,
|
|
465
|
+
"tool_qualification_approach": item.tool_qualification_approach,
|
|
466
|
+
"previously_developed": item.previously_developed,
|
|
467
|
+
"pds_origin_standard": item.pds_origin_standard,
|
|
468
|
+
"objectives": VERIFICATION_FAMILIES[dal],
|
|
469
|
+
"traceability": traceability_summary(item.requirements or []),
|
|
470
|
+
"generated": _today(),
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def life_cycle_data(dal: str) -> list:
|
|
475
|
+
_check_dal(dal)
|
|
476
|
+
return LIFE_CYCLE_DATA[dal]
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def render_phac_markdown(model: dict) -> str:
|
|
480
|
+
"""Render the PHAC content model as the deliverable markdown document."""
|
|
481
|
+
t = model["traceability"]
|
|
482
|
+
if model["coverage_ratio"] is not None:
|
|
483
|
+
coverage_line = ("- Requirements-based coverage ratio: "
|
|
484
|
+
f"{model['coverage_ratio']:.2f} "
|
|
485
|
+
f"(level {model['assurance_level']}).")
|
|
486
|
+
else:
|
|
487
|
+
coverage_line = ("- Requirements-based coverage ratio: none "
|
|
488
|
+
"(no safety effect).")
|
|
489
|
+
objectives = model["objectives"] or ["none (no safety effect)"]
|
|
490
|
+
lines = [
|
|
491
|
+
"# Plan for Hardware Aspects of Certification",
|
|
492
|
+
"",
|
|
493
|
+
f"**Item:** {model['item']}",
|
|
494
|
+
f"**Design assurance level:** {model['assurance_level']} "
|
|
495
|
+
f"(from {model['severity_source']} failure condition)",
|
|
496
|
+
f"**AEH class:** {model['aeh_class']}",
|
|
497
|
+
f"**Certification basis:** {model['certification_basis']}",
|
|
498
|
+
f"**Status:** {model['status']}",
|
|
499
|
+
"",
|
|
500
|
+
"## 1. Scope",
|
|
501
|
+
"",
|
|
502
|
+
f"This plan covers the airborne electronic hardware item "
|
|
503
|
+
f"{model['item']}."
|
|
504
|
+
+ (f" {model['item_description']}" if model['item_description'] else ""),
|
|
505
|
+
f"The item is {model['aeh_class']} AEH per DO-254, technology "
|
|
506
|
+
f"{model['technology'] or 'to be defined'}.",
|
|
507
|
+
"",
|
|
508
|
+
"## 2. Design assurance level and failure-condition basis",
|
|
509
|
+
"",
|
|
510
|
+
f"The hardware design assurance level is {model['assurance_level']} "
|
|
511
|
+
f"based on the {model['severity_source']} failure condition"
|
|
512
|
+
+ (f" (system safety reference {model['system_safety_reference']})"
|
|
513
|
+
if model['system_safety_reference'] else "") + ".",
|
|
514
|
+
"",
|
|
515
|
+
"## 3. Hardware life cycle and life cycle data",
|
|
516
|
+
"",
|
|
517
|
+
f"The {model['life_cycle_model']} life cycle is used. The hardware "
|
|
518
|
+
"life cycle data set for this level is:",
|
|
519
|
+
"",
|
|
520
|
+
*[f"- {d}" for d in model["life_cycle_data"]],
|
|
521
|
+
"",
|
|
522
|
+
"## 4. Hardware design assurance environment",
|
|
523
|
+
"",
|
|
524
|
+
f"- Development tools: {', '.join(model['development_tools']) or 'to be defined'}",
|
|
525
|
+
f"- Verification tools: {', '.join(model['verification_tools']) or 'to be defined'}",
|
|
526
|
+
f"- Tool qualification: {model['tool_qualification_approach']}",
|
|
527
|
+
"",
|
|
528
|
+
"## 5. Requirements capture and traceability",
|
|
529
|
+
"",
|
|
530
|
+
f"- Requirements under review: {t['total']} "
|
|
531
|
+
f"(allocated {t['allocated']}, derived {t['derived']}).",
|
|
532
|
+
f"- Allocated requirements traced upward: "
|
|
533
|
+
f"{t['allocated_traced']} of {t['allocated']} "
|
|
534
|
+
f"({'complete' if t['allocated_traced_complete'] else 'INCOMPLETE'}).",
|
|
535
|
+
f"- Derived requirements justified: {t['derived_justified']} of "
|
|
536
|
+
f"{t['derived']} "
|
|
537
|
+
f"({'complete' if t['derived_justified_complete'] else 'INCOMPLETE'}).",
|
|
538
|
+
f"- Capture readiness score: {t['readiness_score']:.2f} "
|
|
539
|
+
f"({'ready' if t['ready'] else 'NOT READY'}).",
|
|
540
|
+
"- Hardware requirements are reviewed for completeness, "
|
|
541
|
+
"correctness, and verifiability with unique identifiers and "
|
|
542
|
+
"trace links; vague wording fails the review.",
|
|
543
|
+
"",
|
|
544
|
+
"## 6. Design and implementation",
|
|
545
|
+
"",
|
|
546
|
+
f"- HDL languages: {', '.join(model['hdl_languages']) or 'to be defined'}.",
|
|
547
|
+
f"- Target device: {model['target_device'] or 'to be defined'}.",
|
|
548
|
+
"- Conceptual design and detailed design data are produced for "
|
|
549
|
+
"complex AEH and reviewed against the requirements.",
|
|
550
|
+
"",
|
|
551
|
+
"## 7. Verification strategy",
|
|
552
|
+
"",
|
|
553
|
+
f"- Verification methods: {', '.join(model['verification_methods']) or 'reduced (level E)'}.",
|
|
554
|
+
coverage_line,
|
|
555
|
+
f"- Verification independence: {model['independence_text']}.",
|
|
556
|
+
"- Hardware/software integration evidence ties the item to the "
|
|
557
|
+
"software it hosts.",
|
|
558
|
+
"",
|
|
559
|
+
"## 8. Configuration management",
|
|
560
|
+
"",
|
|
561
|
+
"The hardware configuration management plan defines baselines, "
|
|
562
|
+
"change control (ECR/ECO), and the hardware configuration index "
|
|
563
|
+
"(HCI). Class 1 changes (form/fit/function, safety effect, or "
|
|
564
|
+
"complex hardware) require baseline update, reverification, and "
|
|
565
|
+
"independent review.",
|
|
566
|
+
"",
|
|
567
|
+
"## 9. Process assurance",
|
|
568
|
+
"",
|
|
569
|
+
"Hardware process assurance monitors conformity of the life cycle "
|
|
570
|
+
"data and records objective evidence of process compliance.",
|
|
571
|
+
"",
|
|
572
|
+
"## 10. Certification liaison and compliance",
|
|
573
|
+
"",
|
|
574
|
+
"Certification authority liaison, means of compliance, and the "
|
|
575
|
+
"acceptance basis (AC 20-152A) are addressed with the airworthiness "
|
|
576
|
+
"authority. Previously developed hardware, when used, is assessed "
|
|
577
|
+
"under the PDS process.",
|
|
578
|
+
"",
|
|
579
|
+
"## 11. Objectives summary",
|
|
580
|
+
"",
|
|
581
|
+
*[f"- {o}" for o in objectives],
|
|
582
|
+
"",
|
|
583
|
+
"---",
|
|
584
|
+
f"*Generated by Aero Agent Roles do254-hardware-engineer core "
|
|
585
|
+
f"({model['generated']}). DRAFT for human hardware certification "
|
|
586
|
+
"engineer review. Not an approval document.*",
|
|
587
|
+
]
|
|
588
|
+
return "\n".join(lines)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
# ---------------------------------------------------------------------------
|
|
592
|
+
# Evidence gate checkers: verify a deliverable meets the role's gates
|
|
593
|
+
# ---------------------------------------------------------------------------
|
|
594
|
+
|
|
595
|
+
GATE_CHECKS = {
|
|
596
|
+
"level_identified": "assurance_level is a single valid DAL letter",
|
|
597
|
+
"aeh_classified": "item classified simple or complex AEH",
|
|
598
|
+
"life_cycle_data_present": "life cycle data set is non-empty and "
|
|
599
|
+
"level-appropriate",
|
|
600
|
+
"coverage_stated": "requirements-based coverage ratio present",
|
|
601
|
+
"methods_stated": "verification methods stated for the class/level",
|
|
602
|
+
"traceability_ready": "allocated traced, derived justified, readiness "
|
|
603
|
+
"score at or above 0.7",
|
|
604
|
+
"sign_off_honest": "document is marked draft-for-review, not approval",
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def check_phac(model: dict) -> dict:
|
|
609
|
+
"""Run the evidence gates against a PHAC model. Pass/fail per gate."""
|
|
610
|
+
dal = model.get("assurance_level", "")
|
|
611
|
+
t = model.get("traceability", {})
|
|
612
|
+
results = {
|
|
613
|
+
"level_identified": dal in DALS,
|
|
614
|
+
"aeh_classified": model.get("aeh_class") in ("simple", "complex"),
|
|
615
|
+
"life_cycle_data_present": bool(model.get("life_cycle_data")),
|
|
616
|
+
"coverage_stated": isinstance(model.get("coverage_ratio"), (int, float))
|
|
617
|
+
or model.get("coverage_ratio") is None,
|
|
618
|
+
"methods_stated": isinstance(model.get("verification_methods"), list),
|
|
619
|
+
"traceability_ready": (bool(t.get("allocated_traced_complete"))
|
|
620
|
+
and bool(t.get("derived_justified_complete"))
|
|
621
|
+
and bool(t.get("ready"))),
|
|
622
|
+
"sign_off_honest": model.get("status") == "draft-for-review",
|
|
623
|
+
}
|
|
624
|
+
results["all_pass"] = all(results.values())
|
|
625
|
+
return results
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def check_phac_markdown(md_text: str, dal: str) -> dict:
|
|
629
|
+
"""Gate-check the rendered markdown deliverable."""
|
|
630
|
+
low = md_text.lower()
|
|
631
|
+
level_pat = re.compile(r"design assurance level[:*\s]*" + dal.lower() + r"\b")
|
|
632
|
+
checks = {
|
|
633
|
+
"has_title": "plan for hardware aspects of certification" in low,
|
|
634
|
+
"has_level": bool(level_pat.search(low)),
|
|
635
|
+
"has_class": ("aeh class" in low),
|
|
636
|
+
"has_coverage": "coverage ratio" in low,
|
|
637
|
+
"has_independence": "independence" in low,
|
|
638
|
+
"has_draft_marker": "draft" in low,
|
|
639
|
+
"has_not_approval": "not an approval" in low,
|
|
640
|
+
}
|
|
641
|
+
checks["all_pass"] = all(checks.values())
|
|
642
|
+
return checks
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def validate_deliverable_text(md_text: str, dal: str) -> dict:
|
|
646
|
+
"""Public entry point used by gate tooling: check a PHAC document."""
|
|
647
|
+
return check_phac_markdown(md_text, dal)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
# ---------------------------------------------------------------------------
|
|
651
|
+
# Example project (for tests and worked-example generation)
|
|
652
|
+
# ---------------------------------------------------------------------------
|
|
653
|
+
|
|
654
|
+
def example_requirements() -> list:
|
|
655
|
+
"""A realistic requirements set for a CPLD/FPGA-based LRU function."""
|
|
656
|
+
return [
|
|
657
|
+
{"id": "HR-001",
|
|
658
|
+
"text": "The autopilot mode logic FPGA shall select the active "
|
|
659
|
+
"flight mode from the mode-select discretes within 50 ms "
|
|
660
|
+
"of the mode change command.",
|
|
661
|
+
"traceable": True, "source": "SYS-REQ-112"},
|
|
662
|
+
{"id": "HR-002",
|
|
663
|
+
"text": "The FPGA shall assert the autopilot engage output only "
|
|
664
|
+
"when all engage interlocks are satisfied.",
|
|
665
|
+
"traceable": True, "source": "SYS-REQ-113"},
|
|
666
|
+
{"id": "HR-003",
|
|
667
|
+
"text": "The FPGA shall de-assert the engage output within 20 ms "
|
|
668
|
+
"of any disengage interlock opening.",
|
|
669
|
+
"traceable": True, "source": "SYS-REQ-114"},
|
|
670
|
+
{"id": "HR-004",
|
|
671
|
+
"text": "The FPGA shall latch the last valid mode state across a "
|
|
672
|
+
"power interruption of up to 10 ms.",
|
|
673
|
+
"traceable": True, "source": "SYS-REQ-115"},
|
|
674
|
+
{"id": "HR-005",
|
|
675
|
+
"text": "The FPGA shall present the active mode on the ARINC 429 "
|
|
676
|
+
"output bus within one output frame.",
|
|
677
|
+
"traceable": True, "source": "SYS-REQ-116"},
|
|
678
|
+
{"id": "HR-006",
|
|
679
|
+
"text": "The FPGA shall ignore mode-select inputs while the "
|
|
680
|
+
"engage interlock is open.",
|
|
681
|
+
"traceable": True, "source": "SYS-REQ-113"},
|
|
682
|
+
{"id": "HR-007",
|
|
683
|
+
"text": "The FPGA shall drive the watchdog output with a pulse "
|
|
684
|
+
"train of period 10 ms plus-or-minus 1 ms while the "
|
|
685
|
+
"internal sequencer is running.",
|
|
686
|
+
"traceable": True, "source": "SYS-REQ-117"},
|
|
687
|
+
{"id": "HR-008",
|
|
688
|
+
"text": "The FPGA shall reset to the disengaged state on power-up "
|
|
689
|
+
"or on watchdog time-out.",
|
|
690
|
+
"traceable": True, "source": "SYS-REQ-114"},
|
|
691
|
+
{"id": "HR-009",
|
|
692
|
+
"text": "The FPGA shall debounce the mode-select discretes over "
|
|
693
|
+
"a 5 ms window before accepting a state change.",
|
|
694
|
+
"traceable": True, "source": "SYS-REQ-112"},
|
|
695
|
+
{"id": "HR-010",
|
|
696
|
+
"text": "The FPGA shall report internal status on the built-in "
|
|
697
|
+
"test output word every 100 ms.",
|
|
698
|
+
"traceable": True, "source": "SYS-REQ-118"},
|
|
699
|
+
# Derived requirement: added during detailed design, justified.
|
|
700
|
+
{"id": "HR-011",
|
|
701
|
+
"text": "The FPGA shall implement a one-hot state encoding for "
|
|
702
|
+
"the mode sequencer to prevent multiple-mode lockup.",
|
|
703
|
+
"traceable": False, "source": "",
|
|
704
|
+
"justification": "One-hot encoding chosen during detailed design "
|
|
705
|
+
"to guarantee single-mode arbitration (safety "
|
|
706
|
+
"analysis of mode lockup)."},
|
|
707
|
+
# Derived requirement: added from safety analysis, justified.
|
|
708
|
+
{"id": "HR-012",
|
|
709
|
+
"text": "The FPGA shall provide a hardware mode-select voting "
|
|
710
|
+
"scheme across the two mode-select discretes.",
|
|
711
|
+
"traceable": False, "source": "",
|
|
712
|
+
"justification": "Dual-discrete voting added following the "
|
|
713
|
+
"FPGA-level FMEA to protect against a stuck "
|
|
714
|
+
"discrete failure."},
|
|
715
|
+
]
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def example_item() -> HardwareItem:
|
|
719
|
+
"""Reference item: a complex CPLD/FPGA-based LRU function."""
|
|
720
|
+
return HardwareItem(
|
|
721
|
+
item_name="Autopilot Mode Logic FPGA (complex CPLD/FPGA-based "
|
|
722
|
+
"LRU function)",
|
|
723
|
+
description="FPGA implementation of the autopilot flight mode "
|
|
724
|
+
"selection and engage logic hosted in the flight "
|
|
725
|
+
"guidance computer LRU.",
|
|
726
|
+
failure_condition="hazardous",
|
|
727
|
+
system_safety_ref="FHA-FGS-001 / PSSA rev C",
|
|
728
|
+
has_programmable_logic=True,
|
|
729
|
+
has_internal_state=True,
|
|
730
|
+
fully_verifiable_from_top_data=False,
|
|
731
|
+
safety_significant=True,
|
|
732
|
+
technology="CPLD/FPGA programmable logic",
|
|
733
|
+
life_cycle_model="waterfall",
|
|
734
|
+
hdl_languages=["VHDL"],
|
|
735
|
+
target_device="FPGA, flight guidance computer LRU",
|
|
736
|
+
development_tools=["HDL synthesis tool", "static timing analyzer"],
|
|
737
|
+
verification_tools=["HDL simulator", "requirements-based test "
|
|
738
|
+
"harness", "code coverage tool"],
|
|
739
|
+
tool_qualification_approach="criteria-based per DO-254 tool "
|
|
740
|
+
"assessment",
|
|
741
|
+
certification_basis="FAR/CS-25",
|
|
742
|
+
requirements=example_requirements(),
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def example_phac_markdown() -> str:
|
|
747
|
+
return render_phac_markdown(build_phac(example_item()))
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
if __name__ == "__main__":
|
|
751
|
+
item = example_item()
|
|
752
|
+
model = build_phac(item)
|
|
753
|
+
md = render_phac_markdown(model)
|
|
754
|
+
print(f"ASSURANCE LEVEL: {model['assurance_level']}")
|
|
755
|
+
print(f"AEH CLASS: {model['aeh_class']}")
|
|
756
|
+
print(f"COVERAGE RATIO: {model['coverage_ratio']:.2f}")
|
|
757
|
+
print(f"INDEPENDENCE: {model['independence_text']}")
|
|
758
|
+
print(f"LIFE CYCLE DATA: {len(model['life_cycle_data'])} items")
|
|
759
|
+
print(f"TRACEABILITY: {model['traceability']}")
|
|
760
|
+
print(f"GATES: {check_phac(model)}")
|
|
761
|
+
print(f"RENDERED: {len(md)} chars")
|