Test Types & Methods

Enregistrer les résultats HIL

Structurez les résultats de tests HIL avec les versions de firmware et les données environnementales pour le suivi de régression dans TofuPilot.

JJulien Buteau
intermediate10 min de lecture14 mars 2026

Les tests HIL génèrent beaucoup de données : mesures, captures de formes d'onde, versions de firmware, conditions environnementales. Sans journalisation structurée, repérer les régressions entre les versions de firmware se transforme en projet d'archéologie de tableurs. Ce guide montre comment étiqueter, joindre et interroger les résultats HIL dans TofuPilot pour que les régressions remontent automatiquement.

Prérequis

Étape 1 : Étiqueter les exécutions avec la version du firmware et la révision matérielle

Chaque exécution de test HIL doit porter la version du firmware et la révision matérielle du DUT. TofuPilot les stocke comme des métadonnées structurées que vous pouvez filtrer et interroger ultérieurement.

Interrogez la version du firmware depuis le DUT au début du test, puis transmettez-la à TofuPilot via les mesures.

hil_regression/main.py
import openhtf as htf
from tofupilot.openhtf import TofuPilot

from hil_regression.phases import (
    read_firmware_info,
    test_adc_accuracy,
    test_pwm_output,
    test_sleep_current,
)


def main():
    test = htf.Test(
        read_firmware_info,
        test_adc_accuracy,
        test_pwm_output,
        test_sleep_current,
    )

    with TofuPilot(test):
        test.execute(test_start=lambda: input("Scanner le numéro de série du DUT : "))


if __name__ == "__main__":
    main()

La version du firmware et la révision matérielle sont enregistrées comme mesures, ce qui les rend recherchables sur toutes les exécutions.

hil_regression/phases.py
import openhtf as htf
from hil_regression.serial_plug import SerialCommandPlug


@htf.plug(serial=SerialCommandPlug)
@htf.measures(
    htf.Measurement("firmware_version"),
    htf.Measurement("hardware_revision"),
    htf.Measurement("bootloader_version"),
)
def read_firmware_info(test, serial):
    """Enregistrer les identifiants firmware et matériel pour la traçabilité."""
    test.measurements.firmware_version = serial.send_command("VERSION")
    test.measurements.hardware_revision = serial.send_command("HWREV")
    test.measurements.bootloader_version = serial.send_command("BLVER")

Cela vous donne trois champs filtrables sur chaque exécution. Quand le FPY baisse après une mise à jour du firmware, filtrez par firmware_version dans le tableau de bord de TofuPilot pour isoler le changement.

Étape 2 : Joindre les fichiers de formes d'onde et de logs

Les tests HIL capturent souvent des formes d'onde d'oscilloscope, des traces d'analyseur logique ou des logs de console du DUT. Joignez-les à l'exécution de test pour qu'ils soient disponibles pour l'analyse post-mortem sans fouiller dans des partages de fichiers.

hil_regression/capture.py
import csv
import time
import openhtf as htf
from openhtf.plugs import BasePlug


class WaveformCapturePlug(BasePlug):
    """Capture des échantillons analogiques et les enregistre en CSV pour pièce jointe."""

    def setUp(self):
        pass

    def capture_waveform(self, adc, channel: int, duration_s: float, sample_rate_hz: int) -> str:
        """Échantillonne un canal ADC et écrit les résultats dans un fichier CSV."""
        filepath = f"/tmp/waveform_ch{channel}_{int(time.time())}.csv"
        samples = []
        interval = 1.0 / sample_rate_hz

        for i in range(int(duration_s * sample_rate_hz)):
            voltage = adc.read_voltage(channel)
            samples.append((i * interval, voltage))
            time.sleep(interval)

        with open(filepath, "w", newline="") as f:
            writer = csv.writer(f)
            writer.writerow(["time_s", "voltage_v"])
            writer.writerows(samples)

        return filepath

    def tearDown(self):
        pass

Puis joignez le fichier dans votre phase de test :

hil_regression/phases.py
import openhtf as htf
from openhtf.util import units
from hil_regression.capture import WaveformCapturePlug
from hil_regression.analog_plug import AnalogInputPlug
from hil_regression.serial_plug import SerialCommandPlug


@htf.plug(serial=SerialCommandPlug, adc=AnalogInputPlug, capture=WaveformCapturePlug)
@htf.measures(
    htf.Measurement("pwm_voltage_mean").in_range(minimum=1.6, maximum=1.7).with_units(units.VOLT),
)
def test_pwm_output(test, serial, adc, capture):
    """Commander un PWM à 50% et capturer la forme d'onde de sortie."""
    serial.send_command("PWM SET 50")

    # Capturer 1 seconde de données à 1 kHz
    waveform_path = capture.capture_waveform(adc, channel=3, duration_s=1.0, sample_rate_hz=1000)

    # Joindre le CSV de forme d'onde à l'exécution de test
    test.attach("pwm_waveform", waveform_path, "text/csv")

    # Enregistrer également la tension moyenne comme mesure
    import csv
    with open(waveform_path) as f:
        reader = csv.DictReader(f)
        voltages = [float(row["voltage_v"]) for row in reader]
    test.measurements.pwm_voltage_mean = sum(voltages) / len(voltages)

Les pièces jointes apparaissent dans la vue détaillée de l'exécution dans TofuPilot. Vous pouvez les télécharger ultérieurement pour une analyse hors ligne ou une comparaison entre versions de firmware.

Étape 3 : Utiliser les sous-unités pour les configurations HIL multi-cartes

De nombreux produits contiennent plusieurs cartes (carte CPU principale, carte d'alimentation, carte capteur) qui sont testées ensemble dans un montage HIL. Les sous-unités de TofuPilot permettent de suivre chaque carte indépendamment tout en les maintenant liées à l'assemblage parent.

hil_regression/multi_board.py
import openhtf as htf
from tofupilot.openhtf import TofuPilot

from hil_regression.phases import (
    test_cpu_board,
    test_power_board,
    test_sensor_board,
)


def main():
    test = htf.Test(
        test_cpu_board,
        test_power_board,
        test_sensor_board,
    )

    with TofuPilot(
        test,
        sub_units=[
            {"serial_number": "CPU-BRD-0042", "part_number": "PCB-CPU-R3"},
            {"serial_number": "PWR-BRD-0019", "part_number": "PCB-PWR-R2"},
            {"serial_number": "SNS-BRD-0088", "part_number": "PCB-SNS-R1"},
        ],
    ):
        test.execute(test_start=lambda: input("Scanner le numéro de série de l'assemblage : "))


if __name__ == "__main__":
    main()

Chaque sous-unité obtient son propre enregistrement de traçabilité dans TofuPilot. Si la carte capteur commence à échouer après un changement de révision matérielle, vous pouvez filtrer par PCB-SNS-R1 et voir exactement quand les défaillances ont commencé.

hil_regression/board_phases.py
import openhtf as htf
from openhtf.util import units
from hil_regression.analog_plug import AnalogInputPlug
from hil_regression.serial_plug import SerialCommandPlug


@htf.plug(serial=SerialCommandPlug)
@htf.measures(
    htf.Measurement("cpu_clock_mhz").in_range(minimum=79, maximum=81).with_units(units.HERTZ),
    htf.Measurement("cpu_temp").in_range(maximum=85).with_units(units.DEGREE_CELSIUS),
)
def test_cpu_board(test, serial):
    """Vérifier l'horloge et la température de la carte CPU."""
    test.measurements.cpu_clock_mhz = float(serial.send_command("CLOCK?"))
    test.measurements.cpu_temp = float(serial.send_command("TEMP?"))


@htf.plug(adc=AnalogInputPlug)
@htf.measures(
    htf.Measurement("pwr_12v_rail").in_range(minimum=11.4, maximum=12.6).with_units(units.VOLT),
    htf.Measurement("pwr_efficiency_pct").in_range(minimum=85),
)
def test_power_board(test, adc):
    """Vérifier les rails de sortie et le rendement de la carte d'alimentation."""
    test.measurements.pwr_12v_rail = adc.read_voltage(channel=0)
    vin = adc.read_voltage(channel=4)
    vout = adc.read_voltage(channel=5)
    test.measurements.pwr_efficiency_pct = (vout / vin) * 100 if vin > 0 else 0


@htf.plug(serial=SerialCommandPlug, adc=AnalogInputPlug)
@htf.measures(
    htf.Measurement("sensor_offset_mv").in_range(minimum=-5, maximum=5),
    htf.Measurement("sensor_gain_error_pct").in_range(minimum=-1, maximum=1),
)
def test_sensor_board(test, serial, adc):
    """Vérifier la calibration de la carte capteur."""
    serial.send_command("SENSOR CAL_CHECK")
    test.measurements.sensor_offset_mv = float(serial.send_command("SENSOR OFFSET?"))
    test.measurements.sensor_gain_error_pct = float(serial.send_command("SENSOR GAIN_ERR?"))

Détecter les régressions dans TofuPilot

TofuPilot suit chaque valeur de mesure sur toutes les exécutions. Pour détecter les régressions firmware :

  1. Filtrer par version de firmware. Ouvrez l'onglet Analytique de la procédure et filtrez les exécutions par la mesure firmware_version. Comparez les taux de réussite et les distributions de mesures entre les versions.
  2. Vérifier les tendances des mesures. Les graphiques de tendance de TofuPilot montrent les valeurs de mesure dans le temps. Un changement soudain après une mise à jour firmware est un signal clair de régression.
  3. Comparer le Cpk par version. Si le Cpk d'une mesure baisse après un changement de firmware, la capabilité du processus s'est dégradée.

Suivi manuel vs TofuPilot

AspectTableur / ManuelTofuPilot
Étiquetage version firmwareCopier-coller dans une colonneMétadonnées automatiques par exécution
Stockage des formes d'ondeDisque partagé avec conventions de nommageJoint à l'exécution, toujours trouvable
Traçabilité multi-cartesFeuilles ou onglets séparésSous-unités liées à l'assemblage parent
Détection de régressionInspection manuelle des graphiquesFiltrer par version firmware, comparer les tendances
Comparaison inter-stationsFusionner des fichiers de différents PCToutes les stations envoient vers un seul espace de travail
Recherche historique« Dans quel dossier c'était déjà ? »Recherche par numéro de série, référence ou date
Piste d'auditEspérer que personne n'a supprimé une ligneEnregistrements immuables avec horodatage

Plus de guides

Mettez ce guide en pratique