Diagnoses
Use OpenHTF diagnoses to analyze results after measurement validation, classify failure modes, and drive conditional execution between phases.
Analyze measurements after validation to classify failure modes and drive conditional logic.
After a phase validates its measurements, a diagnoser inspects the results and emits a diagnosis such as OVERVOLTAGE with a priority. A branch sequence then runs a corrective phase only when that diagnosis is present.
- Test start
- phase_voltagevoltage = 10.6 V, limit 10 V
- @diagnose(VoltageDiagnoser)inspects the phase record
- BranchSequenceif OVERVOLTAGE
- phase_discharge
- Test end
Measurements tell you what happened. Diagnoses tell you why. After a phase validates its measurements, diagnosers inspect the results and produce structured diagnosis objects. These can classify failure modes, set priorities, and trigger conditional execution of later phases.
Defining diagnosis results
Create a DiagResultEnum to define the possible outcomes of your diagnosers. Each value must be unique across all diagnosers in the same test.
import enum
import openhtf as htf
from openhtf.core import diagnoses_lib
class PowerDiag(diagnoses_lib.DiagResultEnum):
OVERVOLTAGE = 'overvoltage'
UNDERVOLTAGE = 'undervoltage'
OVERCURRENT = 'overcurrent'Phase diagnosers
A phase diagnoser runs after a single phase completes. It receives the PhaseRecord and returns one or more Diagnosis objects.
import enum
import openhtf as htf
from openhtf.core import diagnoses_lib, measurements
class VoltageDiag(diagnoses_lib.DiagResultEnum):
HIGH = 'voltage_high'
LOW = 'voltage_low'
@diagnoses_lib.PhaseDiagnoser(VoltageDiag)
def voltage_diagnoser(phase_record):
for name, measurement in phase_record.measurements.items():
if measurement.outcome == measurements.Outcome.FAIL:
if measurement.measured_value.value > 12:
return diagnoses_lib.Diagnosis(
VoltageDiag.HIGH,
description='Voltage exceeds 12V limit',
is_failure=True,
)
else:
return diagnoses_lib.Diagnosis(
VoltageDiag.LOW,
description='Voltage below minimum threshold',
is_failure=True,
)
return None
@htf.diagnose(voltage_diagnoser)
@htf.measures(
htf.Measurement('voltage').in_range(3.0, 12.0)
)
def measure_voltage(test):
test.measurements.voltage = 15.0 # Will trigger HIGH diagnosis
def main():
test = htf.Test(measure_voltage)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Test diagnosers
A test diagnoser runs after all phases complete. It receives the full TestRecord and the DiagnosesStore containing all phase-level diagnoses.
import openhtf as htf
from openhtf.core import diagnoses_lib
from openhtf.core.test_record import PhaseOutcome
class TestDiag(diagnoses_lib.DiagResultEnum):
BOARD_DEFECTIVE = 'board_defective'
@diagnoses_lib.TestDiagnoser(TestDiag)
def board_diagnoser(test_record, diagnoses_store):
failed_phases = [
p for p in test_record.phases
if p.outcome == PhaseOutcome.FAIL
]
if len(failed_phases) >= 2:
return diagnoses_lib.Diagnosis(
TestDiag.BOARD_DEFECTIVE,
description='Multiple phase failures indicate defective board',
is_failure=True,
)
return None
def phase_one(test):
return htf.PhaseResult.FAIL_AND_CONTINUE
def phase_two(test):
return htf.PhaseResult.FAIL_AND_CONTINUE
def main():
test = htf.Test(phase_one, phase_two)
test.configure(diagnosers=[board_diagnoser])
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Diagnosis properties
resultDiagResultEnumThe diagnosis result value from your enum.
descriptionstrHuman-readable explanation of the diagnosis.
is_failureboolWhether this diagnosis should cause the phase or test to fail.
priorityDiagPriorityImportance level: HIGHEST, NORMAL (default), or INFORMATIVE.
Conditional measurement validation
You can attach validators that only apply when specific diagnoses are present, using .validate_on().
import openhtf as htf
from openhtf.core import diagnoses_lib
class ModeDiag(diagnoses_lib.DiagResultEnum):
HIGH_POWER = 'high_power'
LOW_POWER = 'low_power'
@htf.measures(
htf.Measurement('current')
.in_range(0.1, 1.0) # Default validator
.validate_on({ModeDiag.HIGH_POWER: htf.Measurement('current').in_range(1.0, 5.0)})
)
def measure_current(test):
test.measurements.current = 3.0
def main():
test = htf.Test(measure_current)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()When HIGH_POWER is diagnosed, the stricter 1.0–5.0 range replaces the default 0.1–1.0 range.
Attachments
Learn how to attach files and artifacts collected during test execution to the OpenHTF test record with step-by-step examples.
Checkpoints & Branching
Control OpenHTF test flow with checkpoints that stop on failure and branch sequences that conditionally execute phases based on diagnosis results.