Test Station Setup

Créer une interface opérateur en Python

Créez une interface opérateur prête pour la production pour vos tests matériels avec Python, OpenHTF et TofuPilot, sans écrire de code frontend.

JJulien Buteau
intermediate8 min de lecture14 mars 2026

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 :

  • La saisie du numéro de série (lecteur de code-barres ou saisie manuelle)
  • La progression des phases en temps réel pendant le test
  • Les invites opérateur avec champs de saisie (texte, nombres, listes déroulantes, choix par image)
  • Le résultat pass/fail avec un affichage en couleur
  • Les valeurs de mesure avec l'état 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
25 lines
import openhtf as htffrom openhtf.plugs import user_inputfrom 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 le montage."""    prompts.prompt(        "Placez la carte dans le montage de test et fermez la bride. "        "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
22 lines
@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

Une fois les tests automatisés terminés, demandez à l'opérateur de retirer le DUT et d'apposer 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'apposer l'étiquette."""    prompts.prompt(        "Retirez la carte du montage. "        "Apposez 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 diffusée automatiquement vers une URL dans le navigateur.

operator_ui_test.py
from tofupilot.openhtf import uploadtest = htf.Test(    phase_load_dut,    phase_visual_check,    phase_power_and_boot,    phase_wireless_check,    phase_unload,)test.add_output_callbacks(upload())test.execute(test_start=lambda: input("Scan serial: "))

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-barresLe configurer en clavier USB HID (le scan arrive dans le champ de saisie actif)
Texte agrandiRégler le zoom du navigateur à 150 %
Écran tactileLes invites TofuPilot fonctionnent au toucher, sans souris
Stations multiplesChaque station a sa propre URL de streaming

Types de saisie disponibles

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

Type de saisieCas d'usage
TexteNuméros de série, notes de l'opérateur
NombreMesures manuelles, comptages
CurseurRéglages analogiques, appréciations subjectives
Boutons radioChoisir une option dans une liste
Case à cocherConfirmer plusieurs éléments (checklist)
Liste déroulanteChoisir dans une longue liste d'options
Choix par imageChoisir parmi des photos (orientation d'un 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 sous forme de données de mesure.

Schémas courants

Arrêt immédiat avec notification de l'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 fautive mise en évidence. Il n'attend pas l'expiration des phases restantes.

É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 une reprise à l'opérateur."""    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