Migrating from Legacy Systems

Migrer de bases de données de test

Remplacez votre base de données de test PostgreSQL, SQLite ou Access personnalisée par une collecte structurée de données de test avec OpenHTF et TofuPilot.

JJulien Buteau
intermediate9 min de lecture14 mars 2026

De nombreuses équipes hardware construisent des bases de données personnalisées pour les résultats de test. PostgreSQL, SQLite, Microsoft Access. Elles fonctionnent bien au début. Puis quelqu'un demande les tendances de FPY entre les stations, ou vous avez besoin d'un accès multi-sites, ou un auditeur veut l'historique complet des révisions, et la charge de maintenance dépasse la simplicité initiale.

TofuPilot remplace cette infrastructure personnalisée par une plateforme gérée qui prend en charge le stockage, l'analytique et la traçabilité nativement.

Schémas courants de bases de données personnalisées

La plupart des bases de données de test développées en interne suivent l'un des schémas suivants. Voici un exemple typique :

typical_test_schema.sql
-- Le schéma vers lequel la plupart des équipes convergent
CREATE TABLE test_runs (
    id SERIAL PRIMARY KEY,
    serial_number VARCHAR(50),
    test_name VARCHAR(100),
    result VARCHAR(10),  -- 'PASS' ou 'FAIL'
    operator VARCHAR(50),
    station VARCHAR(50),
    started_at TIMESTAMP,
    duration_seconds FLOAT
);

CREATE TABLE measurements (
    id SERIAL PRIMARY KEY,
    run_id INTEGER REFERENCES test_runs(id),
    name VARCHAR(100),
    value FLOAT,
    unit VARCHAR(20),
    lower_limit FLOAT,
    upper_limit FLOAT,
    passed BOOLEAN
);

Cette structure capture les éléments de base. Mais elle ne couvre pas ce qui compte à grande échelle :

  • Pas d'analytique intégrée. Le FPY, le Cpk et les cartes de contrôle nécessitent des requêtes personnalisées et du code de visualisation que quelqu'un doit maintenir.
  • Pas de piste d'audit. Si quelqu'un modifie une ligne, la valeur originale est perdue, sauf si vous avez construit des tables d'historique basées sur des triggers.
  • Pas d'accès multi-sites. Un fichier PostgreSQL ou SQLite local ne peut pas servir des équipes sur plusieurs sites sans travail d'infrastructure.
  • Dérive du schéma. Chaque station de test peut insérer des données légèrement différentes selon qui a écrit l'instruction INSERT.

Utilisateurs de bases de données TestStand

Si vous utilisez le logger de base de données intégré de NI TestStand, vous avez une variante de ce problème. TestStand écrit vers SQL Server, Oracle ou Access en utilisant un schéma fixe avec UUT_RESULT, STEP_RESULT, PROP_RESULT et des tables spécifiques aux types (PROP_NUMERICLIMIT, PROP_NUMERIC, PROP_STRINGVALUE). L'interrogation nécessite 5 à 6 JOINs de tables, et l'ajout de métadonnées personnalisées implique de modifier le mapping de base de données du Process Model.

Le même schéma de migration s'applique : remplacez l'INSERT en base de données (ou le logger de base de données de TestStand) par des mesures OpenHTF et laissez TofuPilot gérer le stockage. Si vous migrez spécifiquement depuis TestStand, consultez le guide dédié sur la migration de NI TestStand vers Python.

Remplacer les insertions personnalisées par OpenHTF

Si votre workflow actuel ressemble à « exécuter le script de test, INSERT les résultats dans la base de données », voici l'équivalent OpenHTF + TofuPilot. Au lieu de gérer la base de données, vous définissez les mesures dans le test et laissez TofuPilot gérer le stockage.

Une insertion typique dans une base de données personnalisée ressemble à ceci :

old_custom_insert.py
# Ce que vous remplacez : des insertions manuelles en BDD après chaque test
import psycopg2

conn = psycopg2.connect("dbname=testdata user=testeng")
cur = conn.cursor()

cur.execute(
    "INSERT INTO test_runs (serial_number, test_name, result, station) "
    "VALUES (%s, %s, %s, %s) RETURNING id",
    ("SN-5001", "thermal_cycle", "PASS", "STATION-3"),
)
run_id = cur.fetchone()[0]

cur.execute(
    "INSERT INTO measurements (run_id, name, value, unit, lower_limit, upper_limit, passed) "
    "VALUES (%s, %s, %s, %s, %s, %s, %s)",
    (run_id, "peak_temp", 84.2, "C", 70.0, 90.0, True),
)

conn.commit()
conn.close()

Voici le même test en OpenHTF avec TofuPilot :

thermal_cycle_test.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot

@htf.measures(
    htf.Measurement("peak_temp")
        .with_units(units.DEGREE_CELSIUS)
        .in_range(70.0, 90.0),
    htf.Measurement("settling_time")
        .with_units(units.SECOND)
        .in_range(maximum=30.0),
    htf.Measurement("thermal_resistance")
        .in_range(0.5, 2.0),
)
def thermal_cycle(test):
    test.measurements.peak_temp = 84.2
    test.measurements.settling_time = 18.7
    test.measurements.thermal_resistance = 1.1

def main():
    test = htf.Test(thermal_cycle)
    with TofuPilot(test):
        test.execute(test_start=lambda: "SN-5001")

if __name__ == "__main__":
    main()

Pas de code de connexion à la base de données. Pas de SQL. Pas de maintenance de schéma. Les mesures sont définies avec leurs limites dans le test lui-même, ce qui garantit que chaque exécution est validée et structurée de manière identique.

Migration des données historiques

Vous avez des années de données de test dans votre base de données personnalisée. L'API REST de TofuPilot vous permet de les importer avec préservation complète des horodatages.

import_from_custom_db.py
import psycopg2
from tofupilot import TofuPilotClient

client = TofuPilotClient()
conn = psycopg2.connect("dbname=testdata user=testeng")
cur = conn.cursor()

# Récupérer les exécutions avec leurs mesures
cur.execute("""
    SELECT r.serial_number, r.test_name, r.result, r.started_at, r.duration_seconds,
           m.name, m.value, m.unit, m.lower_limit, m.upper_limit
    FROM test_runs r
    JOIN measurements m ON m.run_id = r.id
    ORDER BY r.id
""")

current_run = None
steps = []

for row in cur:
    serial, test_name, result, started_at, duration, m_name, m_val, m_unit, m_low, m_high = row

    if current_run and current_run != (serial, started_at):
        # Envoyer l'exécution précédente
        prev_serial, prev_started = current_run
        client.create_run(
            procedure_id=test_name,
            unit_under_test={"serial_number": prev_serial},
            run_passed=result == "PASS",
            started_at=prev_started,
            duration=duration,
            steps=steps,
        )
        steps = []

    current_run = (serial, started_at)

    # Trouver ou créer l'étape
    step = next((s for s in steps if s["name"] == test_name), None)
    if not step:
        step = {"name": test_name, "step_passed": result == "PASS", "measurements": []}
        steps.append(step)

    step["measurements"].append({
        "name": m_name,
        "measured_value": m_val,
        "unit": m_unit,
        "lower_limit": m_low,
        "upper_limit": m_high,
    })

conn.close()

Adaptez la requête à votre schéma. L'essentiel est de faire correspondre vos tables à la structure de TofuPilot : chaque ligne de test_runs devient une exécution, chaque ligne de measurements devient une mesure au sein d'une étape.

Ce qui remplace vos requêtes personnalisées

Si vous avez construit des requêtes SQL ou des scripts Python personnalisés pour l'analytique, le tableau de bord de TofuPilot les remplace.

Votre requête / script personnaliséFonctionnalité intégrée de TofuPilot
SELECT COUNT(*) ... GROUP BY result pour le rendementTendances FPY, mises à jour en temps réel
Scripts de contrôle statistique des procédésCpk et cartes de contrôle par mesure
WHERE result = 'FAIL' GROUP BY measurementPareto des défaillances avec analyse détaillée
GROUP BY station pour le débitTableau de bord de débit par station
WHERE serial_number = 'X' pour la traçabilitéRecherche par numéro de série avec historique complet
Scripts de reporting personnalisésRapports exportables et API REST

TofuPilot suit tout cela automatiquement. Ouvrez l'onglet Analytique pour voir le FPY, le Cpk et l'analyse des défaillances pour n'importe quelle procédure.

Exécuter les deux systèmes pendant la transition

Vous pouvez faire fonctionner votre base de données personnalisée et TofuPilot en parallèle pour valider la migration :

  1. Ajoutez TofuPilot à une station de test tout en gardant vos insertions de base de données existantes actives. Les deux systèmes reçoivent les mêmes résultats.
  2. Comparez les données pendant une semaine. Vérifiez que les valeurs de mesure, les comptages pass/fail et les horodatages correspondent entre votre base de données et TofuPilot.
  3. Supprimez les insertions de la base de données personnalisée de cette station une fois que vous êtes confiant. Passez à la station suivante.
  4. Conservez votre ancienne base de données en lecture seule comme archive. Vous pouvez toujours l'interroger pour une vérification historique.

La base de données personnalisée n'a pas besoin de disparaître immédiatement. Elle cesse simplement d'être le système de référence une fois que TofuPilot prend le relais.

Plus de guides

Mettez ce guide en pratique