Test Station Setup

Interface opérateur de test en Python

Créez une interface opérateur prête pour la production pour les tests hardware en utilisant Python, OpenHTF et TofuPilot. Aucun code frontend requis.

JJulien Buteau
intermediate8 min de lecture14 mars 2026

Comment créer une interface opérateur de test en Python

La plupart des ingénieurs de test commencent avec la sortie du terminal. Cela fonctionne pendant le développement, mais les opérateurs de production ont besoin d'un écran dédié avec des indicateurs pass/fail, des invites et des champs de saisie. Ce guide montre comment créer une interface opérateur avec OpenHTF et TofuPilot sans écrire de code frontend.

Ce que voit l'opérateur

L'interface opérateur terminée fonctionne dans un navigateur et affiche :

  • Saisie du numéro de série (lecteur de code-barres ou saisie manuelle)
  • Progression des phases en temps réel pendant le test
  • Invites opérateur avec champs de saisie (texte, nombres, listes déroulantes, choix basés sur des images)
  • Résultat pass/fail avec affichage coloré
  • Valeurs de mesure avec statut des limites

L'opérateur ne voit jamais de terminal, de navigateur de fichiers ni de code Python.

Prérequis

  • Python 3.10+
  • OpenHTF installé (pip install openhtf)
  • SDK Python TofuPilot 1.11.0+ installé (pip install tofupilot)

Étape 1 : Créer des phases de test avec des invites

Utilisez le plug user_input d'OpenHTF pour ajouter des interactions opérateur. Chaque invite met le test en pause jusqu'à ce que l'opérateur réponde.

operator_ui_test.py
import openhtf as htf
from openhtf.plugs import user_input
from openhtf.util import units


@htf.plug(prompts=user_input.UserInput)
def phase_load_dut(test, prompts):
    """Attendre que l'opérateur charge le DUT dans la fixture."""
    prompts.prompt(
        "Placez la carte dans la fixture de test et fermez le clamp. "
        "Appuyez sur Entrée quand vous êtes prêt."
    )


@htf.plug(prompts=user_input.UserInput)
@htf.measures(
    htf.Measurement("label_present").with_args(docstring="Résultat de l'inspection de l'étiquette"),
)
def phase_visual_check(test, prompts):
    """Demander à l'opérateur de vérifier que l'étiquette est présente et correcte."""
    result = prompts.prompt(
        "L'étiquette du produit est-elle présente et correctement alignée ?",
        text_input=True,
    )
    test.measurements.label_present = result

Étape 2 : Ajouter des mesures automatisées

Mélangez les invites opérateur avec les mesures automatisées des instruments. L'opérateur voit les deux types de phases dans la même interface.

operator_ui_test.py
@htf.measures(
    htf.Measurement("supply_voltage_V")
    .in_range(minimum=4.9, maximum=5.1)
    .with_units(units.VOLT),
    htf.Measurement("boot_time_ms")
    .in_range(maximum=2000)
    .with_units(units.MILLISECOND),
)
def phase_power_and_boot(test):
    """Automatisé : mesurer la tension d'alimentation et le temps de démarrage."""
    test.measurements.supply_voltage_V = 5.02
    test.measurements.boot_time_ms = 1340


@htf.measures(
    htf.Measurement("wifi_rssi_dBm")
    .in_range(minimum=-70, maximum=-20)
    .with_units(units.DBM),
)
def phase_wireless_check(test):
    """Automatisé : vérifier la puissance du signal WiFi."""
    test.measurements.wifi_rssi_dBm = -42

Étape 3 : Ajouter une étape opérateur finale

Après les tests automatisés, demandez à l'opérateur de retirer le DUT et d'appliquer une étiquette pass/fail.

operator_ui_test.py
@htf.plug(prompts=user_input.UserInput)
def phase_unload(test, prompts):
    """Demander à l'opérateur de retirer le DUT et d'appliquer l'étiquette."""
    prompts.prompt(
        "Retirez la carte de la fixture. "
        "Appliquez une étiquette PASS si le test a réussi. "
        "Appuyez sur Entrée quand c'est fait."
    )

Étape 4 : Connecter à TofuPilot et exécuter

Reliez toutes les phases ensemble et connectez-vous à TofuPilot. L'interface opérateur est automatiquement diffusée vers une URL dans le navigateur.

operator_ui_test.py
from tofupilot.openhtf import TofuPilot

test = htf.Test(
    phase_load_dut,
    phase_visual_check,
    phase_power_and_boot,
    phase_wireless_check,
    phase_unload,
)

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

Lorsque vous exécutez ce script, TofuPilot affiche une URL dans la console. Ouvrez-la dans un navigateur sur la station de l'opérateur. L'opérateur voit chaque phase dans l'ordre, répond aux invites et obtient un résultat pass/fail clair.

Étape 5 : Configurer la station pour la production

ParamètreComment
Mode kiosqueLancer Chrome avec --kiosk --app=<streaming-url>
Démarrage automatiqueAjouter la commande au script de démarrage du système
Lecteur de code-barresConfigurer comme clavier USB HID (le scan va dans le champ de saisie actif)
Gros texteRégler le zoom du navigateur à 150 %
Écran tactileLes invites TofuPilot fonctionnent avec la saisie tactile, pas besoin de souris
Stations multiplesChaque station a sa propre URL de streaming

Types de saisie disponibles

L'interface opérateur TofuPilot prend en charge des types de saisie structurés au-delà du simple texte :

Type de saisieCas d'utilisation
TexteNuméros de série, notes de l'opérateur
NombreMesures manuelles, comptages
CurseurAjustements analogiques, évaluations subjectives
Boutons radioChoisir une option dans une liste
Case à cocherConfirmer plusieurs éléments (checklist)
Liste déroulanteSélectionner dans une longue liste d'options
Choix par imageChoisir parmi des photos (orientation de composant, type de défaut)
InterrupteurDécisions oui/non

Ces saisies s'affichent automatiquement dans le navigateur. L'opérateur les remplit et les valeurs remontent au script de test en tant que données de mesure.

Cas d'utilisation courants

Échec rapide avec notification opérateur

operator_ui_test.py
from openhtf import PhaseResult


@htf.measures(
    htf.Measurement("power_good").equals("PASS"),
)
def phase_power_check(test):
    """Arrêter tôt si la mise sous tension échoue. Inutile de tester une carte morte."""
    result = "PASS"
    test.measurements.power_good = result
    if result != "PASS":
        return PhaseResult.STOP

Lorsque cette phase échoue, l'opérateur voit immédiatement l'échec dans le navigateur avec la mesure défaillante mise en évidence. Il n'attend pas que les phases restantes expirent.

Étapes opérateur conditionnelles

operator_ui_test.py
@htf.plug(prompts=user_input.UserInput)
@htf.measures(
    htf.Measurement("rework_action").with_args(docstring="Reprise effectuée"),
)
def phase_rework_prompt(test, prompts):
    """Si une phase précédente a signalé un problème, demander à l'opérateur de reprendre."""
    action = prompts.prompt(
        "L'inspection de soudure a signalé le pad U3. "
        "Reprenez le joint et appuyez sur Entrée quand c'est fait.",
        text_input=True,
    )
    test.measurements.rework_action = action

Plus de guides

Mettez ce guide en pratique