Interface opérateur avec OpenHTF
OpenHTF peut mettre un test en pause pour interagir avec l'opérateur via son plug user_input. Quand le test s'exécute sur une station TofuPilot, ces invites s'affichent dans le kiosque de la station : une image optionnelle, votre message, et soit un bouton de confirmation soit un champ de saisie. Ce guide couvre toute cette surface avec des exemples, et se termine par le rendu sur la station.
Ce que voit l'opérateur
Une invite apparaît sur un écran avec :
- Une image optionnelle, au-dessus du message
- Le texte du message
- Un bouton Continuer, ou un champ de saisie, selon l'appel
L'opérateur n'ouvre jamais de terminal ni de visionneuse séparée.
Prérequis
- Python 3.10+
- OpenHTF installé (
pip install openhtf)
Le plug UserInput
Un seul appel pilote l'invite. Attachez le plug UserInput à une phase et appelez prompt().
from openhtf.plugs import user_inputprompts.prompt( message, # texte affiché à l'opérateur text_input=False, # False = bouton de confirmation, True = champ de saisie timeout_s=None, # optionnel, lève PromptUnansweredError au timeout image_url=None, # image optionnelle affichée dans l'invite)Il retourne le texte de l'opérateur (une chaîne vide quand text_input=False).
Message et bouton Continuer
import openhtf as htffrom openhtf.plugs import user_input@htf.plug(prompts=user_input.UserInput)def power_cycle(test, prompts): prompts.prompt("Faites un cycle d'alimentation, attendez la LED, puis cliquez sur Continuer.")Saisie texte
@htf.plug(prompts=user_input.UserInput)def scan_serial(test, prompts): serial = prompts.prompt("Scannez le numéro de série :", text_input=True) test.dut_id = serialDécisions Oui / Non
OpenHTF n'a pas de widget à deux boutons Oui/Non. Le schéma est toujours le même : demander une réponse tapée, puis brancher dessus, en général en renvoyant PhaseResult.CONTINUE pour continuer ou PhaseResult.STOP pour arrêter le test. L'exemple ci-dessous conditionne le test à un contrôle LED.
@htf.plug(prompts=user_input.UserInput)def led_check(test, prompts): answer = prompts.prompt("La LED clignote-t-elle en vert ? Tapez y ou n :", text_input=True) return htf.PhaseResult.CONTINUE if answer.strip().lower() == "y" \ else htf.PhaseResult.STOPLe même schéma couvre beaucoup de décisions opérateur, par exemple : confirmer qu'un connecteur est bien enfoncé avant la mise sous tension, juger un pass/fail visuel sur une finition ou une étiquette, ou décider si une unité part en reprise après une inspection.
Afficher une image dans l'invite
Passez image_url et l'image s'affiche, au-dessus du message, en direct pendant le run. Cela fonctionne avec un bouton de confirmation et avec un champ de saisie. L'URL est tout ce qu'accepte une balise image HTML.
Image hébergée
@htf.plug(prompts=user_input.UserInput)def connector_check(test, prompts): prompts.prompt( "Branchez le câble comme indiqué, puis cliquez sur Continuer.", image_url="http://localhost:8080/reference/connector.png", )Image locale en data URI (sans serveur)
Sur une station, c'est en général le plus simple : lisez un fichier local et intégrez-le en data URI base64, sans rien à héberger.
import base64def data_uri(path, mime="image/png"): with open(path, "rb") as f: return f"data:{mime};base64," + base64.b64encode(f.read()).decode("ascii")@htf.plug(prompts=user_input.UserInput)def visual_inspection(test, prompts): reference = data_uri("/opt/station/reference/board_top.jpg", "image/jpeg") answer = prompts.prompt( "La carte correspond-elle à la référence ? Tapez y ou n :", text_input=True, image_url=reference, ) return htf.PhaseResult.CONTINUE if answer.strip().lower() == "y" \ else htf.PhaseResult.STOPImage capturée pendant le test
@htf.plug(prompts=user_input.UserInput)def confirm_capture(test, prompts): path = "/tmp/capture.png" camera.grab_frame(path) # votre code de capture prompts.prompt( "L'image capturée est-elle nette et centrée ?", text_input=True, image_url=data_uri(path, "image/png"), )Remarques : utilisez image/png ou image/jpeg ; l'URL doit se charger depuis le navigateur qui exécute le kiosque, donc un data URI est le plus sûr sur une station. image_url affiche l'image en direct ; pour la garder aussi dans l'enregistrement du run, attachez-la avec test.attach_from_file(path).
Texte de statut en direct
OpenHTF n'a pas de ligne de console qui se met à jour toute seule pour l'opérateur. Pour faire remonter la progression pendant une phase, utilisez test.logger : chaque appel émet un enregistrement de log en direct, diffusé vers l'UI opérateur qui affiche le run (l'UI web d'OpenHTF, ou le kiosque TofuPilot) et conservé dans l'enregistrement du test. Utilisez-le pour la progression, puis une courte invite une fois l'étape terminée.
@htf.plug(prompts=user_input.UserInput)def discharge(test, prompts): test.logger.info("En attente de la décharge du condensateur...") # entrée de log en direct wait_for_discharge() # votre code prompts.prompt("Condensateur déchargé. Cliquez sur Continuer.")Ce que voit l'opérateur dans TofuPilot
Ces invites s'affichent dans le kiosque de la station TofuPilot, l'UI opérateur que la CLI sert localement et ouvre dans un navigateur. Activez le kiosque sur la station, ou forcez-le pour un run avec tofupilot run --kiosk. Quand une phase appelle prompt(), le kiosque affiche l'image, le message et le bouton Continuer ou le champ de saisie sur un écran ; l'opérateur répond et le run continue. Pour suivre depuis le dashboard, ouvrez la vue opérateur de la station sur <votre-url-tofupilot>/<org>/operator/<station-id>.
Si vous avez besoin de saisies plus riches que ces invites OpenHTF, comme des listes, checklists, curseurs, interrupteurs ou une barre de progression en direct, vous pouvez les ajouter avec une procédure du framework TofuPilot qui déclare des composants UI.