# File

> Les valeurs qu'un autre programme écrit dans un fichier d'un dossier local ou réseau (le CSV d'une machine d'essai, le journal d'un enregistreur, un résultat en JSON ou XML) lues comme des tags, et des lignes ou des fichiers entiers écrits pour un autre programme : les cartes Dossier et fichier, Texte du fichier et Écriture, ce qu'un tag lit dans chaque format, le dossier d'arrivée, le balayage qui propose un tag par colonne ou par champ, trois exemples complets et ce que le pilote ne fait pas.

Le pilote **File** lit les valeurs qu'un autre programme écrit dans un fichier : le CSV auquel une
machine d'essai ajoute une ligne après chaque échantillon, le journal d'un enregistreur, un fichier de
résultat en JSON ou en XML, un fichier texte qui ne contient qu'un numéro de lot. Chaque tag lit une
valeur du fichier, et ses valeurs arrivent là où arrivent celles de tout autre tag : dans Logic, sur les
tableaux de bord, dans les essais d'un banc de test de [Process](process). Là où l'équipement le
permet, le pilote écrit aussi pour un autre programme : une ligne ajoutée à un fichier, ou un fichier
entier remplacé.

La lecture est l'usage principal, et elle demande trois réponses : le dossier, le fichier et son format.
La façon dont le texte du fichier se lit (son encodage, son séparateur de colonnes, son séparateur
décimal) est lue dans le fichier tant qu'elle reste sur détecter, le balayage propose un tag par colonne
ou par champ, et chaque tag naît en lecture seule. Ce qui relève de l'écriture n'apparaît qu'une fois
l'écriture activée pour l'équipement.

Un équipement de ce pilote est un dossier et le fichier qu'il y lit : deux fichiers du même dossier
sont deux équipements. Le dossier est un lecteur et un dossier (`C:\TestMachine\Logs`) ou un partage
réseau et un dossier dans ce partage (`\\lab-pc\results`), et la station l'atteint avec les droits
Windows du compte sous lequel elle tourne. Les équipements se rangent sous le pilote et peuvent être
classés dans des dossiers, **Pilote → Dossier → Équipement → Tag**, comme sous tout pilote sans ligne
série ni broker.

## La page du pilote {#the-drivers-page}

La ligne sous l'en-tête indique « Chaque équipement lit un fichier, ou les fichiers qui arrivent dans un dossier. Ajoutez un équipement, saisissez son dossier, son fichier et son format, puis balayez-le pour ajouter ses valeurs comme tags. »
Pour Configurer le connector, la page porte la carte Portée du balayage propre au pilote, avec le
fichier que le balayage lit (voir [Découverte](#discovery)). En dessous se trouve le tableau
**Équipements** de tout pilote (voir [Le tableau des équipements](connector-drivers#the-device-table)),
qui grandit avec **Ajouter un équipement**, à droite de son titre ; vide, il indique
« Aucun équipement pour l'instant. » La barre du pilote porte **Découvrir**, grisé tant que la carte
Portée du balayage n'est pas complète. Le pilote est rangé sous **Fichiers et bases de données**, la
catégorie qui se trouve entre Web et API et Tests dans chaque liste de pilotes.

## Équipement {#device}

Un équipement de ce pilote n'a pas de carte Connexion : la carte **Dossier et fichier** en tient la
place, suivie de la carte **Texte du fichier** et, sur un équipement qui lit un fichier, de la carte
**Écriture**. L'**Intervalle de scrutation (ms)** de la carte Équipement, 1000 par défaut, dit à quelle
fréquence les tags lisent le fichier, chaque tag pouvant le remplacer comme sous tout pilote.

Un nouvel équipement n'a pas de dossier : il indique « Dossier pas encore saisi », sa page dit « La station ne lit pas cet équipement tant qu'il n'a pas de dossier : rien n'est donc encore lu depuis lui et il n'y a aucune erreur à afficher. Saisissez le dossier dans la carte Dossier et fichier ci-dessous et la station commence à le lire. »
et rien n'est lu tant que le dossier n'est pas saisi. L'URI de base que montre l'en-tête est l'adresse
d'exécution, avec le dossier et le nom écrits sous forme d'URI, puis ce qui n'est pas sur détecter :
`file://single?folder=C%3A%5CTestMachine%5CLogs&name=Log_%7Byyyy-MM-dd%7D.csv&format=delimited`, ou
`file://inbox?...` pour un dossier d'arrivée.

### La carte Dossier et fichier {#the-folder-and-file-card}

| Champ | Signification | Défaut |
| --- | --- | --- |
| **Chemin du dossier** | « Un lecteur et un dossier, ou un partage réseau. Le dossier est atteint avec les droits Windows du compte sous lequel tourne la station. » La case affiche `C:\TestMachine\Logs` comme indication. | vide |
| **Nom du fichier** | Sur un équipement qui lit un fichier : « Un nom dans le dossier. Les parties entre accolades sont la date et l'heure de la station : Log_{yyyy-MM-dd}.csv est un nouveau fichier chaque jour. » Un sous-dossier du dossier peut faire partie du nom (`results\last.csv`). | vide |
| **Fichiers à lire** | La même case sur un dossier d'arrivée : « Les fichiers à lire, directement dans le dossier : * remplace n'importe quels caractères et ? un seul, comme Test_*.csv. » | vide |
| **Lire depuis** | **Un fichier** : « Le fichier est relu à chaque modification. Un fichier qui grandit est lu à partir de l'endroit où la dernière lecture s'est arrêtée. » **Dossier d'arrivée** : « Chaque nouveau fichier est lu une fois, dans l'ordre d'arrivée, puis déplacé dans le sous-dossier Processed. Rien n'est supprimé. » | **Un fichier** |
| **Format du fichier** | **CSV et texte en colonnes**, **Texte libre**, **JSON** ou **XML** : comment un tag trouve sa valeur dans le fichier (voir [Adressage du tag](#tag-addressing)). | **CSV et texte en colonnes** |

Un nom avec une partie de date s'écrit entre accolades avec un format .NET de date et d'heure, à
l'horloge de la station : `Log_{yyyy-MM-dd}.csv` est `Log_2026-10-03.csv` aujourd'hui et le fichier
du lendemain après minuit, et `Data_{yyyy-MM}.txt` change chaque mois. Sous la case, la carte montre de
quel fichier il s'agit maintenant : « Aujourd'hui : Log_2026-10-03.csv ». Des accolades doublées `{{`
et `}}` sont des accolades du nom. Une partie de date n'écrit jamais de deux-points, qu'aucun nom de
fichier n'admet : `{HH-mm}`, pas `{HH:mm}`.

Une fois l'équipement en marche, la carte dit quel fichier il lit :

| La carte indique | Quand |
| --- | --- |
| « Lecture en cours : Log_2026-10-03.csv » | Le fichier utilisé, tel que l'équipement le lit maintenant. |
| « Lecture en cours : Log_2026-10-03.csv (pas encore présent) » | Le fichier n'existe pas encore : ses tags attendent et l'équipement reste en ligne. |
| « Un tag a écrit ce nom. Après un redémarrage, l'équipement relit le fichier configuré. » | Un tag qui lit le nom du fichier a été écrit et dirige l'équipement vers un autre fichier (voir [Écriture](#writing)). |
| « 3 fichiers en attente dans le dossier d'arrivée » | Un dossier d'arrivée, avec les fichiers qui correspondent à son motif encore à lire (« 1 fichier en attente dans le dossier d'arrivée » pour un seul). |

Un réglage que la carte ne peut pas prendre est signalé sous les cartes, et l'équipement n'est pas
enregistré tant qu'il n'est pas corrigé :

| Vous saisissez | La carte indique |
| --- | --- |
| Un dossier qui n'est pas un chemin complet, comme `Logs` | « Le dossier est un chemin complet : un lecteur et un dossier (C:\Test), ou un partage réseau et un dossier dans ce partage (\\server\tests). » |
| Un dossier avec un caractère qu'aucun nom de dossier n'admet, ou un partage sans dossier à l'intérieur (`\\server`) | « Le dossier contient un caractère qu'aucun nom de dossier n'admet, ou c'est un partage réseau sans dossier à l'intérieur. » |
| Aucun nom de fichier | Pas de refus : l'équipement est enregistré et attend, « Nom du fichier pas encore saisi » (« Fichiers à lire pas encore saisis » sur un dossier d'arrivée), jusqu'à ce qu'un nom soit saisi. |
| Un chemin complet comme nom, comme `C:\Logs\last.csv` | « Le nom du fichier est un nom dans le dossier, pas un chemin complet : le dossier va dans la case Chemin du dossier. » |
| Un nom avec `..`, comme `..\other\last.csv` | « Le nom du fichier sort du dossier avec "..". Le pilote ne quitte jamais son dossier. » |
| Un nom avec `:` ou un autre caractère qu'aucun nom de fichier n'admet, ou une partie qui se termine par un point ou une espace | « Le nom du fichier contient un caractère qu'aucun nom de fichier n'admet (comme : ou \|), ou une partie se termine par un point ou une espace. Une partie de date ne doit pas écrire de deux-points : utilisez {HH-mm}, pas {HH:mm}. » |
| Un nom que Windows réserve à un périphérique, comme `CON` ou `COM1` | « Windows réserve ce nom à un périphérique (CON, NUL, COM1 et autres). Choisissez-en un autre. » |
| `*` ou `?` sur un équipement qui lit un fichier | « Seul le motif d'un dossier d'arrivée accepte * ou ?. Indiquez le nom d'un seul fichier, ou lisez depuis un dossier d'arrivée. » |
| Un sous-dossier dans le motif d'un dossier d'arrivée | « Un dossier d'arrivée est le dossier lui-même : mettez le sous-dossier dans la case Chemin du dossier et ne gardez ici que le motif. » |
| Une partie de date non fermée, ou qui n'est pas un format de date | « Une partie de date du nom n'est pas fermée ou n'est pas un format de date. Écrivez-la entre accolades, comme Log_{yyyy-MM-dd}.csv. » |

L'enregistrement tient les mêmes règles, et le journal des actions le dit quand il en refuse une :
« L'équipement 'Tensile tester' a un réglage de fichier que son pilote ne peut pas utiliser. Ouvrez-le et corrigez le champ que son panneau signale. »

### La carte Texte du fichier {#the-text-of-the-file-card}

Comment se lisent les caractères, les colonnes et les nombres du fichier. Chaque réglage commence sur
**Détecter dans le fichier**, et le pilote le lit dans le fichier utilisé. Sous un réglage laissé ainsi,
une fois que l'équipement a lu le fichier, une ligne dit ce qu'il a trouvé, comme
« Trouvé dans le fichier : UTF-8 ». Tout réglage peut être fixé en le choisissant, pour un fichier
dont les premières lignes ne le disent pas.

| Champ | Affiché | Choix | Défaut |
| --- | --- | --- | --- |
| **Encodage** | toujours | **Détecter dans le fichier**, `UTF-8`, `Windows-1252 (ANSI)` (l'encodage des anciens programmes Windows) | **Détecter dans le fichier** |
| **Séparateur de colonnes** | CSV | **Détecter dans le fichier**, **Point-virgule (;)**, **Virgule (,)**, **Tabulation** | **Détecter dans le fichier** |
| **Séparateur décimal** | toujours | **Détecter dans le fichier**, **Point décimal (12.5)**, **Virgule décimale (12,5)** | **Détecter dans le fichier** |
| **Ligne d'en-tête** | CSV | De 0 à 1000 : « La ligne qui contient les noms des colonnes, comptée à partir de 1. 0 si le fichier n'en a pas. » Les lignes qui la précèdent sont sautées. | 1 |

| Réglage | Ce que la détection lit |
| --- | --- |
| Encodage | Une marque d'ordre des octets au début du fichier, qui l'emporte sur tout choix ; sinon UTF-8 quand les octets sont de l'UTF-8 valide ; sinon Windows-1252. |
| Séparateur de colonnes | Le premier de la tabulation, du point-virgule et de la virgule qui découpe les premières lignes en un même nombre de colonnes ; une première ligne qui n'en contient aucun est un fichier d'une seule colonne. |
| Séparateur décimal | La virgule quand plus de nombres du fichier portent une virgule qu'un point entre leurs chiffres ; sinon le point. Les nombres JSON portent toujours le point ; le réglage vaut pour les nombres écrits en texte. |

### La carte Écriture {#the-writing-card}

Un équipement qui lit un fichier porte la carte **Écriture** ; un dossier d'arrivée n'est que lu et n'en
a pas.

| Champ | Affiché | Signification | Défaut |
| --- | --- | --- | --- |
| **Autoriser l'écriture** | toujours | Désactivé : « L'équipement ne fait que lire. Activez l'écriture pour ajouter des lignes, remplacer le fichier ou passer à un autre fichier pendant que la station tourne. » Activé : « Les tags de cet équipement peuvent écrire dans le fichier. Chaque tag reste en lecture seule tant que son accès ne dit pas autre chose. » | désactivé |
| **Texte d'en-tête** | écriture activée, CSV ou texte libre | « Écrit en premier dans un fichier qu'une écriture crée, comme Time;Pressure. » Une ligne, sans saut de ligne. | vide |
| **Fin de ligne** | écriture activée | **Détecter dans le fichier** (celle du fichier, CR LF dans un nouveau fichier), **CR LF, comme sous Windows** ou **LF**. | **Détecter dans le fichier** |

L'écriture ne se désactive qu'une fois qu'aucun tag de l'équipement n'écrit plus. Tant que des tags
écrivent, l'interrupteur reste dessiné et grisé, et dit pourquoi : « 2 tags de cet équipement écrivent dans le fichier. Passez-les en lecture seule avant de désactiver l'écriture. »
(« Un tag de cet équipement écrit dans le fichier. Passez-le en lecture seule avant de désactiver l'écriture. » pour un seul).

### En ligne, en attente et hors ligne {#online-waiting-and-offline}

| Situation | Ce que font l'équipement et ses tags |
| --- | --- |
| Aucun dossier saisi | L'équipement attend : « Dossier pas encore saisi », point gris, rien n'est lu et aucune erreur. |
| Dossier saisi, aucun nom de fichier | L'équipement attend : « Nom du fichier pas encore saisi », point gris, rien n'est lu et aucune erreur ; sa page dit « La station ne lit pas cet équipement tant qu'il n'a pas de nom de fichier : rien n'est donc encore lu depuis lui et il n'y a aucune erreur à afficher. Saisissez le nom du fichier dans la carte Dossier et fichier ci-dessous et la station commence à le lire. » |
| Dossier d'arrivée, dossier saisi, aucun fichier à lire | L'équipement attend : « Fichiers à lire pas encore saisis », point gris, rien n'est lu et aucune erreur ; sa page dit « La station ne lit pas cet équipement tant qu'elle ne sait pas quels fichiers lire : rien n'est donc encore lu depuis lui et il n'y a aucune erreur à afficher. Saisissez les fichiers dans Fichiers à lire, dans la carte Dossier et fichier ci-dessous (comme Test_*.csv), et la station commence à les lire. » |
| Le dossier ne peut pas être atteint (un partage arrêté, un lecteur disparu, un compte sans accès) | L'équipement est hors ligne et ses tags échouent leur lecture ; le dossier est regardé de nouveau au plus chaque seconde, et l'équipement se connecte dès qu'il revient. |
| Le dossier est atteint, le fichier n'existe pas encore | L'équipement reste en ligne. Les tags qui lisent des valeurs n'ont pas de lecture et attendent ; **Le fichier existe** lit faux. |
| Un autre programme tient le fichier sans le partager | Pas de lecture cette fois ; la lecture suivante réessaie. Le tag garde sa dernière lecture, et une lecture plus ancienne que son intervalle de scrutation plus 10 s compte comme une lecture en échec, comme sous tout pilote. |
| Une valeur n'est pas là, ou ne tient pas dans le type du tag | Une lecture en échec de ce tag, avec la raison ; l'équipement continue de lire ses autres tags. |

## Adressage du tag {#tag-addressing}

### Ce qu'un tag lit {#what-a-tag-reads}

Un tag de ce pilote n'a pas de case d'adresse à lui : sa carte **Source de la valeur** demande ce qu'il
lit et, pour une valeur, où se trouve la valeur dans le format de l'équipement.

| **Lit** | Ce que le tag montre | Type de donnée |
| --- | --- | --- |
| **Valeur** | Une valeur dans le fichier, trouvée par les champs du format de l'équipement ci-dessous. | Boolean, Int32, Int64, Float, Double ou String ; Double pour un nouveau tag |
| **Fichier entier** | Tout le contenu, sans les fins de ligne de la fin : un fichier qui ne contient qu'un numéro de lot ou un état. 4 Mio au plus. | les mêmes ; String pour un nouveau tag |
| **Le fichier existe** | Si le fichier utilisé existe maintenant. | Boolean |
| **Taille du fichier** | La taille du fichier en octets. | Int64 |
| **Dernière modification** | Quand le fichier a été écrit pour la dernière fois : la voir changer est ce qui dit à Logic qu'une donnée nouvelle est arrivée. | DateTime |
| **Nom du fichier** | Le nom du fichier utilisé, dans le dossier. Écrit, il désigne un autre fichier (voir [Écriture](#writing)). | String |
| **Nombre de lignes** | « Compte toutes les lignes du fichier, ligne d'en-tête et lignes vides comprises. » | Int64 |

Un type sous lequel la source ne se lit pas est refusé avec ceux sous lesquels elle se lit : « Choisissez l'un des types de donnée sous lesquels cela se lit : Int64. »
Choisir une autre source apporte son propre type. Le texte pris dans un fichier se lit dans le type du
tag comme le [pilote Generic serial and TCP](driver-generic-serial-tcp#tag-addressing) lit un message,
avec le séparateur décimal du fichier : un Boolean lit `1`, `true`, `on` ou `yes` et `0`, `false`, `off`
ou `no`, un entier lit un nombre entier, et un String prend les caractères tels quels, si bien qu'un code
comme `0412` ne garde son zéro de tête qu'en String.

Chaque tag naît en **Lecture seule**, et sur un équipement qui n'autorise pas l'écriture, le champ
**Accès** ne propose rien d'autre. Une fois que l'équipement autorise l'écriture, un tag qui lit
**Valeur** ou **Fichier entier** peut être en **Lecture et écriture** ou en **Écriture seule**, et un
tag qui lit **Nom du fichier** en **Lecture et écriture** ; les données du fichier (existence, taille,
dernière modification, nombre de lignes) ne s'écrivent jamais : « Une donnée du fichier ne s'écrit pas. Laissez le tag en lecture seule. »

### CSV et texte en colonnes {#csv-and-text-in-columns}

| Champ | Affiché | Signification | Défaut |
| --- | --- | --- | --- |
| **Colonne** | Lit Valeur | « Le nom de la colonne dans la ligne d'en-tête, ou son numéro compté à partir de 1. » Un texte fait seulement de chiffres est un numéro ; un nom ignore la casse et les espaces qui l'entourent. Sur un fichier sans ligne d'en-tête : « Le numéro de la colonne, compté à partir de 1. Le fichier n'a pas de ligne d'en-tête, ses colonnes n'ont donc pas de nom. » | vide |
| **Ligne à lire** | Lit Valeur | **Dernière**, **Par numéro** ou **Par son contenu**. « Les lignes se comptent après la ligne d'en-tête. Par son contenu lit la dernière ligne dont la colonne clé contient la valeur clé. » | **Dernière** |
| **Numéro de ligne** | Par numéro | La ligne de données, comptée à partir de 1 après l'en-tête. | vide |
| **Colonne clé** | Par son contenu | La colonne dont la valeur choisit la ligne, par son nom ou son numéro. | vide |
| **Valeur clé** | Par son contenu | La valeur que la colonne clé contient dans la ligne à lire, comme `0412`. | vide |

Un champ entre guillemets doubles peut contenir le séparateur ; un champ qui contient un saut de ligne
n'est pas lu. Un tag dont les champs ne peuvent pas fonctionner n'est pas enregistré, et sa page dit ce
qui manque :

| Vous laissez ou saisissez | La page du tag indique |
| --- | --- |
| Aucune colonne | « Saisissez la colonne, par son nom dans la ligne d'en-tête ou par son numéro. » |
| La colonne 0 | « Les colonnes se comptent à partir de 1. » |
| Une colonne par son nom sur un fichier sans ligne d'en-tête | « Le fichier n'a pas de ligne d'en-tête, une colonne se donne donc par son numéro. » |
| Par numéro, sans numéro de ligne | « Saisissez le numéro, compté à partir de 1. » |
| Par son contenu, sans colonne clé | « Saisissez la colonne clé : la colonne dont la valeur choisit la ligne. » |
| Par son contenu, sans valeur clé | « Saisissez ce que contient la ligne. » |

La ligne en lecture seule **Adresse source (bus)** montre ce que le pilote reçoit : `column?name=Force`
pour le Force de la dernière ligne, `column?number=3&row=number&rowNumber=5` pour la troisième colonne
de la cinquième ligne, `column?name=Force&row=match&key=Sample&equals=0412` pour la ligne de
l'échantillon 0412.

### Texte libre {#free-text}

| Champ | Affiché | Signification | Défaut |
| --- | --- | --- | --- |
| **Ligne à lire** | Lit Valeur | **Dernière** (la dernière ligne qui n'est pas vide), **Par numéro** ou **Par son contenu**. « Les lignes se comptent depuis le début du fichier. Par son contenu lit la dernière ligne qui contient le texte. » | **Dernière** |
| **Numéro de ligne** | Par numéro | La ligne, comptée à partir de 1 depuis le début du fichier. | vide |
| **La ligne contient** | Par son contenu | Un texte que contient la ligne à lire, comme `Result`. | vide |
| **La valeur est** | Lit Valeur | « Les mêmes choix que le pilote Generic serial and TCP : la ligne entière, une position fixe, un champ entre séparateurs ou une expression régulière. » Ses choix sont ceux de ce pilote, le premier nommé d'après la ligne : **La ligne entière**, **Position fixe** (**Premier caractère** et **Longueur**), **Champ entre séparateurs** (**Séparateur** et **Numéro du champ**) et **Groupe de capture d'un motif** (**Motif**). | **La ligne entière** |

Les positions et les champs se comptent à partir de 1, un motif est une expression régulière .NET avec
exactement un groupe de capture autour de la valeur, et une ligne qu'il met plus de 250 ms à lire n'est
pas lue. Les refus sont ceux du pilote générique lui-même, comme « Indiquez le premier caractère de la valeur, compté à partir de 1. » ou « Le motif doit avoir exactement un groupe de capture, autour de la valeur. »
L'adresse de bus s'écrit `line` suivi de ce qui s'applique, par exemple
`line?row=match&contains=Result&extract=regex&pattern=...`.

### JSON et XML {#json-and-xml}

| Champ | Format | Signification |
| --- | --- | --- |
| **Chemin JSON** | JSON | « Des membres séparés par des points et des positions entre crochets à partir de 0, comme result.force ou items[0].value. » Un membre dont le nom contient un point s'écrit `['flow.rate']`. |
| **Chemin XML** | XML | « Des éléments depuis la racine séparés par des barres obliques, une position à partir de 1 là où un élément se répète, et un attribut en dernier, comme Result/Force ou Result/@unit. » Les noms se comparent par leur nom local, le préfixe d'espace de noms ignoré. |

Un nombre JSON, `true` ou `false` est pris tel quel, et une chaîne se lit dans le type du tag ; un objet
ou une liste ne se lit qu'en String, son JSON. Un document JSON ou XML est lu en entier quand il a
changé, 4 Mio au plus, et aucune DTD n'est lue. L'adresse de bus est `json?path=result.force` ou
`xml?path=Result%2FForce`.

| Vous laissez ou saisissez | La page du tag indique |
| --- | --- |
| Aucun chemin | « Saisissez le chemin vers la valeur. Pour lire le fichier entier, choisissez Fichier entier. » |
| Un chemin JSON qui ne se lit pas | « Ce n'est pas un chemin JSON : des membres séparés par des points et des positions entre crochets, comme result.items[0].value. » |
| Un chemin XML qui ne se lit pas | « Ce n'est pas un chemin XML : des éléments séparés par des barres obliques, une position comme Item[2] et un attribut en dernier, comme @unit. » |

## Lecture {#reading}

À chaque lecture, le pilote demande à Windows la taille et les dates du fichier, ce qui fonctionne sur
un partage réseau, où les avis de changement de Windows ne fonctionnent pas, et n'ouvre le fichier que
si l'une d'elles a changé. Il l'ouvre toujours en partageant la lecture, l'écriture et la suppression,
si bien que le programme qui écrit le fichier n'est jamais bloqué.

| Le fichier | Ce que fait la lecture |
| --- | --- |
| A grandi, et commence et finit toujours là où il le faisait | Seul ce qui a été ajouté est lu, jamais tout le fichier de nouveau : un journal de plusieurs mégaoctets coûte ce que coûtent ses nouvelles lignes. |
| A raccourci, a été réécrit ou recréé | Relu depuis son début. |
| Se termine par une ligne qu'aucune fin de ligne ne ferme encore | Cette ligne attend, pour qu'une valeur ne soit jamais lue à moitié écrite ; une fois que le fichier est resté 2 s sans changer, elle est lue comme la dernière ligne. |
| JSON, XML, ou un tag qui lit le fichier entier | Lu en entier quand il a changé, 4 Mio au plus ; un document qui ne se lit pas alors qu'il a changé il y a moins de 2 s est considéré comme encore en cours d'écriture, et les tags attendent. |
| A une ligne de plus de 1 Mio | Ce n'est pas du texte en lignes : ses tags échouent leur lecture. |

Chaque lecture est datée de la dernière modification du fichier, pas du moment où elle a été faite, si
bien que le dossier d'essai place un échantillon au moment où l'autre programme l'a écrit.

## Le dossier d'arrivée {#the-inbox-folder}

**Lire depuis** **Dossier d'arrivée** sert à la machine qui écrit un fichier par échantillon
(`Test_0412.json`, `Test_0413.json`...) : chaque nouveau fichier qui correspond au motif est lu une
fois, dans l'ordre d'arrivée.

| Étape | Ce qui se passe |
| --- | --- |
| Un fichier arrive | Il est pris une fois qu'il est resté 1 s avec la même taille et la même date et qu'aucun programme ne le tient ouvert pour l'écrire. |
| Lequel d'abord | Celui créé en premier, puis celui écrit en premier, puis par nom. Deux fichiers qui arrivent ensemble sont lus en deux passes : aucun n'est sauté. |
| Sa lecture | Chaque tag de l'équipement lit ce fichier, et le fichier est une lecture, datée de la date du fichier : le dossier d'essai enregistre chacune. **Nom du fichier** dit de quel fichier viennent les valeurs. |
| Une fois que chaque tag l'a lu | Il est déplacé dans le sous-dossier `Processed`, que le pilote crée et dont le nom est le même dans toutes les langues, car d'autres programmes peuvent le chercher ; un nom déjà pris là devient `name (2).json`. Ses valeurs restent la lecture des tags jusqu'au fichier suivant. |
| Un déplacement qui échoue | Réessayé à chaque lecture ; le fichier n'est pas relu entre-temps. |
| La station redémarre avant le déplacement | Le fichier est relu : une lecture comptée deux fois plutôt qu'une perdue. |

Rien n'est jamais supprimé : le sous-dossier `Processed` grandit jusqu'à ce que quelqu'un le vide. On
n'écrit jamais dans un dossier d'arrivée.

## Écriture {#writing}

Sur un équipement qui lit un fichier et autorise l'écriture, un tag qui lit **Valeur** ou
**Fichier entier** et dont l'accès écrit dit ce que fait une écriture :

| Champ | Affiché | Signification | Défaut |
| --- | --- | --- | --- |
| **Une écriture** | le tag écrit | **Ajouter une ligne** : « Ajoute une ligne à la fin du fichier. Chaque écriture compte : aucune n'est abandonnée au profit d'une plus récente. » **Remplacer le fichier** : « Remplace le fichier entier par la valeur, via un fichier temporaire, pour qu'aucun programme ne le lise à moitié écrit. » | **Ajouter une ligne** |
| **Modèle de ligne** | Ajouter une ligne | « Vide : la valeur écrite est la ligne entière. {value} est la valeur, {datetime} le moment de l'écriture, chacun avec un format après un deux-points si besoin, comme {value:0.00}. » La case affiche `{datetime};{value}` comme indication. | vide |

| Le modèle | Écrire 12.5 ajoute |
| --- | --- |
| vide | `12.5` |
| `{datetime};{value}` | `2026-10-03 14:05:12;12.5` |
| `{datetime:dd/MM/yyyy HH:mm};{value:0.00}` | `03/10/2026 14:05;12.50` |

`{value:FORMAT}` prend un format numérique .NET, `{datetime:FORMAT}` un format .NET de date et
d'heure, à l'horloge de la station, `yyyy-MM-dd HH:mm:ss` quand aucun n'est donné. Un nombre s'écrit
avec le séparateur décimal du fichier, si bien que le même modèle écrit `12,50` dans un fichier qui
utilise la virgule. Le modèle d'un tag Boolean peut omettre `{value}` : écrit à vrai, il ajoute la
ligne, comme une commande. Un modèle que le pilote ne peut pas remplir est refusé sur la page du tag :

| Vous saisissez | La page du tag indique |
| --- | --- |
| Une partie qui n'est ni `{value}` ni `{datetime}`, ou une accolade non fermée | « Le modèle de ligne accepte {value} et {datetime}, chacun avec un format après un deux-points si besoin. {{ et }} sont des accolades de la ligne. » |
| Aucun `{value}` sur un tag qui n'est pas Boolean | « Le modèle de ligne n'a pas de {value}, la valeur écrite n'arriverait donc jamais dans le fichier. Seul un tag booléen peut l'omettre : écrit à vrai, il ajoute la ligne. » |
| Un saut de ligne | « Le modèle de ligne tient sur une seule ligne, sans saut de ligne. » |
| Un tag qui écrit, sur un équipement qui n'autorise pas l'écriture | « L'équipement n'autorise pas l'écriture. Activez l'écriture dans l'équipement ou laissez le tag en lecture seule. » |

| Une écriture | Comment elle se passe |
| --- | --- |
| Ajouter une ligne | La ligne part en une seule écriture et est vidée sur le disque avant que l'écriture compte comme faite. Un fichier que l'écriture crée reçoit d'abord le **Texte d'en-tête**, et un fichier dont la dernière ligne n'a pas de fin de ligne en reçoit une d'abord, pour que la ligne soit entière. Une valeur qui contient un saut de ligne est refusée. Chaque écriture compte : une écriture qui attend encore dans la file de l'équipement n'est jamais abandonnée au profit d'une plus récente vers le même tag, comme une commande ne l'est jamais. |
| Remplacer le fichier | La valeur va dans un fichier temporaire à côté du fichier, vidé sur le disque, qui prend la place du fichier en une seule étape. Une valeur plus récente qui attend encore remplace une plus ancienne, comme pour toute consigne. |
| Le nom du fichier | Un tag qui lit **Nom du fichier**, en **Lecture et écriture**, écrit avec un autre nom, dirige l'équipement vers ce fichier du même dossier à partir de ce moment : « Écrire un nom ici fait lire et écrire à l'équipement ce fichier du même dossier à partir de ce moment. Un nom hors du dossier est refusé. » Un chemin complet, `..`, `:` ou un nom que Windows réserve est refusé. Quand l'équipement se reconnecte, redémarrage compris, il relit son fichier configuré. |
| Texte | Dans l'encodage, la fin de ligne et le séparateur décimal du fichier, lus dans le fichier là où l'équipement les laisse sur détecter. Un nouveau fichier est en UTF-8 sans marque d'ordre des octets, avec CR LF et le point décimal, sauf si l'équipement dit autre chose. |
| Réponse | Faite, ou refusée avec la cause : l'équipement n'autorise pas l'écriture, le compte n'a pas la permission, le fichier est marqué en lecture seule (retirer cet attribut laisse passer la prochaine écriture), le disque est plein, ou le fichier est ouvert dans un autre programme. Un remplacement est refusé tant qu'un programme quelconque a le fichier ouvert, un ajout de ligne seulement tant qu'un programme le tient sans le partager ; dès que ce programme ferme le fichier, la prochaine écriture passe. |

Le dossier et les sous-dossiers vers lesquels un nom pointe sont créés quand une écriture en a besoin,
toujours dans le dossier de l'équipement.

## Découverte {#discovery}

### La carte Portée du balayage {#the-scan-scope-card}

Pour Configurer le connector, la page du pilote porte sa carte **Portée du balayage**, sous la ligne
« Lit un fichier et propose un tag pour chaque colonne ou champ trouvé, et pour les données du fichier. Choisissez un équipement pour lire son fichier, ou saisissez un dossier, un fichier et un format. »

| Champ | Signification | Défaut |
| --- | --- | --- |
| **Fichier à balayer** | **Un fichier saisi ici**, ou un équipement du pilote par son nom. Le fichier d'un équipement est lu avec les réglages de l'équipement tels qu'ils sont enregistrés, et une modification de l'équipement qui attend encore d'être enregistrée l'est d'abord. | **Un fichier saisi ici** |
| **Chemin du dossier** | Un fichier saisi : le dossier, comme sur un équipement. | vide |
| **Nom du fichier** ou **Fichiers à lire** | Un fichier saisi : le nom, ou le motif d'un dossier d'arrivée. | vide |
| **Lire depuis** | Un fichier saisi : **Un fichier** ou **Dossier d'arrivée**. | **Un fichier** |
| **Format du fichier** | Un fichier saisi : le format. Tout réglage de texte d'un fichier saisi est détecté, la ligne d'en-tête comprise : la première ligne est un en-tête quand aucun de ses champs n'est un nombre et que la ligne suivante en a un. | **CSV et texte en colonnes** |

Ce qui manque encore à la portée est dit en rouge sous la carte, et Découvrir reste grisé tant qu'elle
n'est pas complète :

| La portée | La carte indique |
| --- | --- |
| Un fichier saisi sans dossier | « Saisissez le dossier où se trouve le fichier. » |
| Un dossier saisi qui n'est pas un chemin complet | « 'Logs' n'est pas un dossier que cette station peut lire. Écrivez un chemin complet, par exemple C:\Test ou \\server\tests. » |
| Un nom saisi qui n'est pas un fichier du dossier | « '..\last.csv' n'est pas le nom d'un fichier du dossier. » |
| Un équipement qui n'existe plus | « L'équipement choisi pour le balayage n'existe plus. Choisissez un autre fichier à balayer. » |
| Un équipement sans son dossier ou son fichier | « L'équipement 'Tensile tester' n'a pas encore de dossier ou de fichier, ou a un réglage que son pilote ne peut pas utiliser. Complétez d'abord sa carte Dossier et fichier. » |

Quand l'équipement à balayer a une modification qui ne peut pas être enregistrée, le balayage ne
démarre pas :
« La découverte n'a pas démarré parce que la modification de l'équipement à balayer n'a pas pu être enregistrée. »

### Ce que le balayage propose {#what-the-scan-offers}

Le balayage lit le fichier qu'un équipement à un fichier lit maintenant, ou le fichier le plus récent
d'un dossier d'arrivée, qu'il ne déplace pas ; un fichier absent, un dossier qui ne peut pas être
atteint et un document qui ne se lit pas terminent le balayage avec la raison. Il propose un candidat,
avec les valeurs trouvées comme tags candidats, tous en **Lecture seule** :

| Format | Tags candidats |
| --- | --- |
| CSV | Un par colonne, qui lit la dernière ligne, nommé d'après son en-tête (par son numéro là où l'en-tête ne lui donne pas de nom ou le donne deux fois), typé d'après les valeurs de ses premières lignes : Double quand chaque valeur est un nombre, Boolean quand chacune est true ou false, String sinon. Un code avec un zéro de tête, comme `0412`, reste un String. |
| Texte libre | La dernière ligne, un String. |
| JSON, XML | Un par valeur, nommé et adressé par son chemin, 500 au plus ; une liste JSON propose ses 20 premiers éléments, et le parcours descend de 8 niveaux. |
| Tout format | Les données du fichier : **Le fichier existe**, **Taille du fichier**, **Dernière modification**, **Nom du fichier** et **Nombre de lignes**. |

Un balayage offre au plus 500 valeurs, colonnes d'un fichier CSV comprises. Un balayage coupé là le
dit dans la ligne qui le termine et au-dessus de ses résultats :
« Le balayage de result.json s'est arrêté tôt. Un balayage offre au plus 500 valeurs : ajoutez les autres comme tags par leur colonne, leur chemin JSON ou leur chemin XML. »

Le candidat d'un fichier saisi porte le nom du fichier (`run` pour `run.csv`), ou celui du dossier pour
un nom avec une partie de date ou un dossier d'arrivée. Un candidat dont le dossier, le nom et le
**Lire depuis** sont déjà lus par un équipement du pilote (l'équipement balayé, ou un autre identique)
est marqué **Déjà configuré** : sa carte ne liste que les valeurs qu'aucun tag de cet équipement ne lit
encore, cochées, et **Ajouter les tags**, avant **Ouvrir**, ajoute celles qui sont cochées à cet
équipement, en lecture seule. Le reste du comportement du balayage se trouve dans
[Découverte](connector-discovery).

La page d'un équipement fait la même chose sans la page de son pilote : **Découvrir les tags**, dans l'en-tête de sa carte **Tags**, lit le fichier de cet équipement tel qu'il est enregistré et ne propose que les valeurs pour lesquelles il n'a pas encore de tag (voir [Découvrir les tags d'un équipement](connector-discovery#discovering-a-devices-tags)).

## Exemple : le CSV quotidien d'une machine d'essai {#example-a-test-machines-daily-csv}

Une machine de traction écrit un CSV par jour dans `C:\TestMachine\Logs`, nommé `Log_2026-10-03.csv`,
avec une ligne d'en-tête `Sample;Force;Elongation` et une ligne comme `0412;12,5;3,1` après chaque
échantillon. Lisez-le par le chemin simple :

1. Sur la page du pilote, **Ajouter un équipement** ; nommez-le `Tensile tester`.
2. Sur la carte **Dossier et fichier**, saisissez **Chemin du dossier** `C:\TestMachine\Logs` et
   **Nom du fichier** `Log_{yyyy-MM-dd}.csv` ; gardez **Un fichier** et **CSV et texte en colonnes**.
   La carte montre « Aujourd'hui : Log_2026-10-03.csv », et une fois que l'équipement le lit,
   « Lecture en cours : Log_2026-10-03.csv ». Laissez la carte **Texte du fichier** sur
   **Détecter dans le fichier** : elle trouve le point-virgule et la virgule décimale.
3. Sur la page du pilote, choisissez `Tensile tester` dans **Fichier à balayer** et **Découvrir**. Le
   candidat est **Déjà configuré** et liste `Sample` (String, qui garde le zéro de `0412`), `Force` et
   `Elongation` (Double), et les données du fichier. **Ajouter les tags**.

Chaque tag lit la dernière ligne ; **Dernière modification** change à chaque nouvel échantillon, et
c'est ainsi qu'une action de Logic sait qu'il en est arrivé un. À minuit, l'équipement passe au fichier
du lendemain, et tant que la machine ne l'a pas écrit, les valeurs n'ont pas de lecture et
**Le fichier existe** lit faux. Un agent fait de même avec les outils :

```
device_upsert name="Tensile tester" driver=File fileFolder="C:\TestMachine\Logs" fileName="Log_{yyyy-MM-dd}.csv" fileFormat=Delimited
device_discover driver=File fileDevice="Tensile tester"
tag_upsert device="Tensile tester" name=Force address=Force opcUaType=Double
tag_upsert device="Tensile tester" name=Sample address=Sample opcUaType=String
tag_upsert device="Tensile tester" name="Last change" fileSource=Modified
```

## Exemple : un dossier d'arrivée de fichiers de résultat JSON {#example-an-inbox-of-json-result-files}

Une cellule d'essai dépose un fichier de résultat par échantillon dans `C:\Results\Inbox`, comme
`Test_0413.json` qui contient `{"sample":"0413","result":{"force":12.5,"ok":true}}` :

1. **Ajouter un équipement** ; nommez-le `Results`, **Chemin du dossier** `C:\Results\Inbox`,
   **Lire depuis** **Dossier d'arrivée**, **Fichiers à lire** `Test_*.json`, **Format du fichier**
   **JSON**.
2. **Ajouter un tag** `Force`, **Chemin JSON** `result.force`, Double ; **Ajouter un tag** `Pass`,
   **Chemin JSON** `result.ok`, Boolean ; **Ajouter un tag** `Sample`, **Chemin JSON** `sample`,
   String.
3. **Ajouter un tag** `Source file`, **Lit** **Nom du fichier** : il montre de quel fichier viennent
   les valeurs.

Chaque fichier est lu une fois, après être resté immobile 1 s sans qu'aucun programme le tienne ouvert
pour l'écrire, dans l'ordre où les fichiers sont arrivés ; chacun est une lecture à lui, datée de la
date du fichier ; une fois que chaque tag l'a lu, il part dans `C:\Results\Inbox\Processed`. Rien n'est
supprimé. Avec les outils :

```
device_upsert name=Results driver=File fileFolder="C:\Results\Inbox" fileName="Test_*.json" fileMode=Inbox fileFormat=Json
tag_upsert device=Results name=Force address=result.force opcUaType=Double
tag_upsert device=Results name=Pass address=result.ok opcUaType=Boolean
tag_upsert device=Results name=Sample address=sample opcUaType=String
tag_upsert device=Results name="Source file" fileSource=Name
```

## Exemple : ajouter une ligne pour un autre programme {#example-appending-a-line-for-another-program}

Un autre programme lit `D:\Records\run.csv`, une ligne par seconde avec l'heure et la pression :

1. **Ajouter un équipement** ; nommez-le `Run log`, **Chemin du dossier** `D:\Records`,
   **Nom du fichier** `run.csv`.
2. Sur la carte **Écriture**, activez **Autoriser l'écriture** et saisissez **Texte d'en-tête**
   `Time;Pressure`.
3. **Ajouter un tag** `Line`, Double, **Accès** **Écriture seule**, **Une écriture**
   **Ajouter une ligne**, **Modèle de ligne** `{datetime};{value:0.00}`.

Écrire 12.5 dans `Line` ajoute `2026-10-03 14:05:12;12.50`, avec la virgule là où le fichier en utilise
déjà une ou là où **Séparateur décimal** indique **Virgule décimale (12,5)** ; un fichier que
l'écriture crée reçoit d'abord `Time;Pressure`. Des écritures qui se suivent de près sont toutes
gardées, dans l'ordre. Pour écrire un fichier par essai, ajoutez un tag `File`, **Lit**
**Nom du fichier**, en **Lecture et écriture** : Logic écrit le numéro de série suivi de `.csv` quand
un essai commence, et les lignes vont dans ce fichier. Avec les outils :

```
device_upsert name="Run log" driver=File fileFolder="D:\Records" fileName=run.csv fileFormat=Delimited fileWrites=true fileHeaderText="Time;Pressure"
tag_upsert device="Run log" name=Line opcUaType=Double accessType=WriteOnly fileWriteMode=AppendLine fileLineTemplate="{datetime};{value:0.00}"
tag_upsert device="Run log" name=File fileSource=Name accessType=ReadAndWrite
```

## Outils de l'agent {#agent-tools}

`driver_capabilities` décrit ce pilote sous `file` : le chemin simple, chaque champ de l'équipement, la
règle du dossier et le confinement à ce dossier, les formes du nom du fichier, les deux façons de lire,
les formats avec la grammaire d'adresse de chacun, les sources et leurs types, les réglages de texte, le
comportement des lectures et des écritures, la découverte et trois exemples complets.
`device_upsert driver=File` prend `fileFolder`, `fileName`, `fileMode` (`SingleFile`, le défaut, ou
`Inbox`), `fileFormat` (`Delimited`, le défaut, `Text`, `Json` ou `Xml`), `fileEncoding`
(`Automatic`, `Utf8` ou `Windows1252`), `fileSeparator` (`Automatic`, `Semicolon`, `Comma` ou `Tab`),
`fileDecimal` (`Automatic`, `Point` ou `Comma`), `fileHeaderRow` (de 0 à 1000), `fileWrites` et, avec
l'écriture activée, `fileHeaderText` et `fileLineEnd` (`Automatic`, `CrLf` ou `Lf`) ; un équipement
qu'il renvoie porte `file`, avec `fileStatus`, ce que le pilote en marche a trouvé : le fichier utilisé,
s'il existe, l'encodage, les séparateurs et la fin de ligne qu'il a lus, et les fichiers en attente
dans un dossier d'arrivée. `tag_upsert` prend `fileSource` (`Value`, `WholeFile`, `Exists`, `Size`,
`Modified`, `Name` ou `Lines`), `fileRow` (`Last`, `Number` ou `Matching`), `fileRowNumber`,
`fileKeyColumn`, `fileMatchText`, `fileWriteMode` (`AppendLine` ou `ReplaceFile`) et
`fileLineTemplate` ; la colonne ou le chemin est l'`address`, une valeur de texte libre prend les
`valueExtraction`, `valueStart`, `valueLength`, `valueSeparator`, `valueField` et `valuePattern` du
pilote générique, et un nouveau tag est en lecture seule et prend le type propre à la source, sauf si
`opcUaType` dit autre chose. `device_discover driver=File` lit soit `fileDevice`, le nom d'un
équipement du pilote, soit `fileFolder`, `fileName`, `fileMode` et `fileFormat` saisis pour le
balayage ; les tags qui manquent à un équipement configuré s'ajoutent avec `tag_upsert`.
`connector_browse` renvoie `fileFolder`, `fileName`, `fileMode` et `fileFormat` pour chaque
équipement. Les autres outils sont dans [Outils de l'agent](agent-tools).

## Limites à connaître {#limits-worth-knowing}

- Les classeurs Excel ne sont pas lus : un classeur se lit une fois que son programme l'enregistre en
  CSV.
- Le pilote ne supprime jamais de fichier. Un dossier d'arrivée déplace ce qu'il a lu dans `Processed`,
  et seul le fichier temporaire d'un remplacement qui a échoué est jamais retiré.
- « Le fichier le plus récent du dossier » n'est pas une façon de lire : il perd les fichiers qui
  arrivent ensemble et peut montrer les valeurs de l'échantillon précédent. Une machine qui écrit un
  fichier par échantillon se lit par un dossier d'arrivée.
- Une valeur n'est pas modifiée sur place dans un fichier (une cellule d'un CSV, un membre du JSON d'un
  autre programme) : une écriture ajoute une ligne ou remplace le fichier entier, car deux programmes
  qui écrivent le même fichier peuvent perdre la modification l'un de l'autre.
- Un partage réseau est atteint avec les droits Windows du compte sous lequel tourne la station :
  l'équipement ne garde ni utilisateur ni mot de passe, le partage doit donc laisser entrer ce compte
  (pour lire, et pour écrire et déplacer sur un équipement qui écrit ou un dossier d'arrivée).
- Le pilote ne touche jamais rien hors de son dossier : un nom qui pointe ailleurs est refusé là où il
  est saisi et là où il est écrit.
- Le fichier est regardé à chaque lecture : un changement se voit à la lecture suivante, jamais à
  l'instant où l'autre programme l'écrit.
- Un champ CSV qui contient un saut de ligne n'est pas lu, et un tag JSON, XML ou de fichier entier lit
  4 Mio au plus.
