Migrating from Legacy Systems

Migrer vos scripts OpenHTF vers la CLI

Exécutez vos scripts OpenHTF existants sur la CLI TofuPilot sans réécriture, et décidez lesquels méritent un portage vers le Framework.

JJulien Buteau
intermediate8 min de lecture9 mai 2026
Validez un test avant de déployer.
Test existant
Connecter les résultats
Comparer un test
Déployer

Si vous avez déjà des scripts de test OpenHTF en production, la première chose à savoir est que vous n'avez pas à les réécrire. La CLI TofuPilot exécute OpenHTF nativement : phases, mesures, logs, pièces jointes et invites opérateur sont transmis au dashboard sans aucune modification de code. Ce guide couvre ce chemin, puis la question distincte de savoir quand un portage vers le TofuPilot Framework vaut l'effort.

Étape 1 : exécuter votre script existant sur la CLI

Installez la CLI :

install.sh
curl -fsSL https://www.tofupilot.app/install | sh

Pointez-la vers votre point d'entrée OpenHTF existant :

run.sh
tofupilot run ./main.py

C'est toute la migration pour la plupart des scripts. La CLI détecte le test OpenHTF, l'exécute et diffuse le run. Vous obtenez le streaming temps réel, l'interface opérateur, la mise en file d'attente hors ligne et le déploiement au push sans toucher au code de test.

Étape 2 : choisir comment les résultats arrivent au dashboard

Il existe deux chemins pris en charge, et ils ne sont pas exclusifs.

Par la CLI. Exécutez avec --upload sur une procédure liée. Rien ne change dans le script.

upload.sh
tofupilot logintofupilot linktofupilot run ./main.py --upload

Par le callback Python. Si vous gérez votre propre distribution et ne voulez pas la CLI dans la boucle, le paquet tofupilot fournit un callback de sortie OpenHTF :

install_sdk.sh
pip install "tofupilot[openhtf]"
main.py
20 lines
import openhtf as htffrom tofupilot.openhtf import uploaddef phase_one(test):    return htf.PhaseResult.CONTINUEdef main():    test = htf.Test(        phase_one,        procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",        part_number="PCB01",    )    test.add_output_callbacks(upload())    test.execute(lambda: "SN-0001")if __name__ == "__main__":    main()

upload() lit TOFUPILOT_API_KEY depuis l'environnement. add_output_callbacks est le point d'extension propre à OpenHTF, donc le callback se compose avec ceux que vous utilisez déjà, comme json_factory.

Pour une instance auto-hébergée avec une autorité de certification privée :

main.py
test.add_output_callbacks(upload(    url="https://tofupilot.yourcompany.com",    verify="/path/to/ca-certificate.pem",))

Étape 3 : abandonner le gestionnaire de contexte déprécié

Si vos scripts utilisent with TofuPilot(test):, ce wrapper est déprécié. Sur tofupilot>=2.16.0 il fonctionne encore comme une fine enveloppe autour du même callback et émet un FutureWarning. Les versions 2.15.0 à 2.15.2 ne l'incluent pas du tout et lèvent une ImportError.

Remplacez-le par la forme callback en deux lignes :

before.py
from tofupilot import TofuPilotwith TofuPilot(test):    test.execute(test_start=lambda: input("Serial: "))
after.py
from tofupilot.openhtf import uploadtest.add_output_callbacks(upload())test.execute(test_start=lambda: input("Serial: "))

Trois détails à connaître lors de ce changement :

OpenHTF est un extra depuis 2.15.0. Installez tofupilot[openhtf], et non tofupilot seul, pour qu'une station qui n'utilise que le client API ne tire pas un framework de test qu'elle n'importe jamais.

Le streaming MQTT a disparu. stream=True est accepté mais ignoré avec un avertissement dans les logs ; passez stream=False pour le faire taire. L'interface opérateur en direct est fournie par la CLI, donc exécutez avec tofupilot run si vous en dépendiez.

Ctrl-C se comporte différemment. Le wrapper supprimait KeyboardInterrupt, donc Ctrl-C sortait silencieusement. Le callback simple le laisse remonter. Si vos scripts de banc dépendaient de cette sortie silencieuse, entourez test.execute(...) d'un try/except KeyboardInterrupt.

Étape 4 : déployer sur les stations

Une fois le script exécuté sous la CLI, le déploiement est un push Git. La station récupère la nouvelle version, avec des artefacts de build immuables et un retour arrière instantané en cas de problème. Les runs sont mis en file d'attente localement quand le réseau tombe et se synchronisent à son retour.

Rien de tout cela n'exige le TofuPilot Framework. Les procédures OpenHTF se déploient de la même façon.

Quand porter vers le TofuPilot Framework

Le portage est optionnel. Il vaut la peine pour des raisons précises, pas par principe.

Raison de porterPourquoi OpenHTF ne peut pas le faire
La ligne est limitée par le débitLes phases OpenHTF s'exécutent séquentiellement ; le Framework exécute les phases indépendantes en parallèle
Plusieurs cartes sur un même montageExécution multi-slot, chaque slot ayant son propre run et son propre verdict
Les opérateurs ont besoin de workflows guidés richesFormulaires et affichages déclaratifs, aucun travail frontend
Validation de la forme d'une courbe ou d'un balayageAgrégations et validateurs par axe
Vous voulez des limites relisibles comme des donnéesLes limites vivent en YAML, versionnées séparément de la logique
Raison de ne pas porter
Ça fonctionne et la ligne n'est pas limitée par le débitLe coût de migration n'achète rien
Un paradigme Python pur est une préférence d'équipeLa couche YAML est un vrai concept à apprendre
Apache 2.0 compte pour votre revue juridiqueLa concession de brevet d'OpenHTF

Le portage, concrètement

Si vous portez, la correspondance est mécanique.

OpenHTFTofuPilot Framework
Fonction de phase avec @htf.measuresFonction simple ; mesures déclarées en YAML
.in_range(3.2, 3.4)validators avec >= et <=
.equals("2.1.0")validators avec operator: "=="
Sous-classe de BasePlugClasse Python simple
setUp / tearDown__init__ / close
@htf.plug(dut=DutPlug)Plug injecté par nom de paramètre
test.measurements.x = vmeasurements.x = v
htf.Test(phase1, phase2)Liste main: dans la procédure
@htf.PhaseOptions(timeout_s=10)timeout sur la phase
Ordre des phases dans htf.Test(...)depends_on

Avant :

openhtf_version.py
26 lines
import openhtf as htffrom openhtf.plugs import BasePlugfrom openhtf.util import unitsclass DutPlug(BasePlug):    def setUp(self):        self.connected = True    def read_voltage(self) -> float:        return 3.31    def tearDown(self):        self.connected = False@htf.measures(    htf.Measurement("rail_3v3").in_range(3.2, 3.4).with_units(units.VOLT),)@htf.plug(dut=DutPlug)def test_power(test, dut):    test.measurements.rail_3v3 = dut.read_voltage()test = htf.Test(test_power, part_number="PCBA-100")test.execute(test_start=lambda: input("Serial: "))

Après :

plugs/dut.py
class Dut:    def __init__(self):        self.connected = True    def read_voltage(self) -> float:        return 3.31    def close(self):        self.connected = False
phases/power.py
def power(measurements, dut):    measurements.rail_3v3 = dut.read_voltage()
procedure.yaml
24 lines
name: PCBA Functional Testversion: 1.0.0unit:  serial_number:    default_value: "PCBA000001"  part_number:    default_value: "PCBA-100"plugs:  - name: dut    python: plugs.dut:Dutmain:  - name: Power    python: phases.power    measurements:      - name: Rail 3V3        unit: V        validators:          - operator: ">="            expected_value: 3.2          - operator: "<="            expected_value: 3.4

Valider avant de basculer

Quel que soit le chemin choisi, exécutez l'ancienne et la nouvelle version sur les mêmes unités, y compris des unités connues comme mauvaises, et comparez mesure par mesure. Une limite mal transcrite pendant un portage est le mode de défaillance qui laisse partir de mauvaises cartes, et il est peu coûteux de l'attraper ainsi.

Gardez des noms de mesures identiques de part et d'autre du portage. Renommer rail_3v3 scinde l'historique et vous perdez la tendance que vous cherchiez à préserver.

Mélanger les deux

Vous n'avez pas à choisir un seul modèle pour toute l'usine. Gardez OpenHTF là où il tourne déjà, et utilisez le Framework sur les nouvelles stations qui ont besoin de slots parallèles ou de workflows opérateur plus riches. Les deux tournent sur la même CLI et remontent vers le même dashboard, donc le rendement et la traçabilité restent cohérents dans ce mélange.

Voir TofuPilot Framework contre OpenHTF pour la comparaison complète.

Erreurs fréquentes

ErreurConséquenceCorrection
Installer tofupilot seul pour un script OpenHTFImportError sur tofupilot.openhtfInstaller tofupilot[openhtf]
Conserver with TofuPilot(test):FutureWarning, et ImportError sur 2.15.xPasser à add_output_callbacks(upload())
Attendre que stream=True fasse quelque choseSilencieusement ignoréUtiliser tofupilot run pour l'interface en direct
Renommer des mesures pendant un portageL'historique de tendance se scinde en deuxGarder les noms strictement identiques
Porter sans validation en parallèleUne erreur de transcription atteint la productionExécuter les deux sur les mêmes unités d'abord
Porter parce que c'est plus récentUn coût sans bénéficePorter pour le parallélisme, l'interface ou les courbes

Plus de guides

Mettez ce guide en pratique