Une simulation peut exploiter, en entrée, un ou plusieurs fichiers de données constitués d'une série d'enregistrements dont chacun, au-delà du premier, est associé (explicitement ou non) à un instant postérieur à l'instant auquel est associé le précédent.Ces fichiers sont de l'un des deux types suivants, correspondant à deux sous-classes de SerialDataFile :
- les fichiers de données séquentielles (classe SequentialDataFile) : la série d'enregistrements correspond de manière bijective à une série d'intervalles temporels tous de la même longueur. C'est le cas typique d'un fichier de données climatiques journalières. Les enregistrements peuvent contenir un ou des champs identifiant explicitement l'intervalle temporel (typiquement, l'indication du jour). Ce n'est pas obligatoire parce que c'est la dynamique du processus de lecture qui attache un intervalle de temps à chaque enregistrement lu : le processus est lancé en un instant connu et lit un enregistrement à chaque pas de longueur fixe.
- les fichiers de données factuelles (classe FactualDataFile) : les enregistrements correspondent chacun à un instant explicitement précisé par un ou plusieurs de ses champs. Ils sont obligatoirement rangés dans le fichier dans l'ordre croissant des instants. Le fichier est doté d'une méthode (GetRecordClockValue) dont le corps doit être écrit par l'utilisateur, et qui renvoie l'instant auquel est associé l'enregistrement.
Pour chaque fichier de données, le développeur doit donc créer une sous-classe de SequentialDataFile ou de FactualDataFile, et insérer dans le code du simulateur la création d'une instance de la sous-classe concernée
La classe SerialDataFile posséde comme attributs :
- un attribut qui spécifie les champs utiles de l'enregistrement, dans l'ordre de leur position sur la ligne. Ces champs sont de deux types : les champs de clé et les champs de données proprement dit. La valeur de cet attribut mpFieldTab (de type pFieldTab, voir page 'Constantes symboliques générales/Champ d'un enregistrement de fichier') est un tableau de couples type du champ-valeur du champ ;
- une spécification de format de lecture ; c'est l'attribut mFormat ;
- une fonction de lecture exploitant le format ci-dessus et affectant une valeur aux éléments de mpFieldTab.
- seulement pour la sous-classe FactualDataFile, une fonction de renvoi de date qui sait reconnaître l'instant associé à l'enregistrement parmi ses champs, et qui le renvoie.
En outre, SerialDataFile hérite de File un attribut (de nom mpFile) dont la valeur, attribuée par le moteur au moment de l'ouverture du fichier, est un pointeur sur une structure FILE de C.
Remarques :
- Les enregistrements d'un fichier de données doivent tous comporter le même nombre de champs, séparés par un ou plusieurs espaces ou tabulations.
- Ces champs doivent avoir le même type et la même signification dans tous les enregistrements.
- Par contre, pour chaque enregistrement, un champ peut avoir une longueur variable (en nombre de caractères).
Par exemple, pour la ligne de référence suivante
0 Ut = -1 Xtp1 = -0.501177 Vtp1 = -0.00117684les lignes suivantes sont correctement écrites :
2 cUt = -1 Xtp1 = -0.507017 Vtp1 = -0.00349537 300 Ut = -100 Xtp1 = -0.37 Vtp1 = -0.1968 41 Ut = -1 Xtp1 = -0.6 Vtp1 = -0.005737alors que l'ensemble de lignes suivant ne l'est pas :
1 Ut = -1 Xtp1 = -0.503522 = -0.00234488 (manque la chaîne "Vtp1") 2 Ut = -1 Xtp1 = -0.507017 Vtp1 -0.00349537 (manque la chaîne "=") Ut = -1 Xtp1 = -0.517346 Vtp1 = -0.00570937 (manque le premier champ)
- La valeur de l'attribut mFormat peut être affectée de 2 façons différentes (voir illustration plus bas) :
- par la méthode AssignFormat
- par les méthodes AssignKeyFormat et AssignDataFormat, auquel cas le format de lecture sera la concaténation des 2 formats partiels. Noter cependant que les champs lus sous le format de clé n'ont pas, pour la présente classe, de rôle particulier dans l'accès aux enregistrements. Par contre, l'utilisateur peut les exploiter pour vérifier l'identification des valeurs des données lues.
La spécification de format est une chaîne de caractères de même syntaxe que la chaîne de contrôle des instructions d'entrée/sortie de C. En particulier, les sous-chaînes "%d","%s","%f" et "%lf" captent respectivement sur la ligne lue un entier, une chaîne de caractères, un flottant et un double.
- La valeur de l'attribut fonction de lecture est affectée par la méthode AssignReadNextEnrgtMethod.
La lecture d'un enregistrement se fait par un appel à la méthode ReadNextEnrgt. Pour récupérer ensuite la valeur d'un des champs de l'enregistrement, il suffit d'appeler l'une des fonctions suivantes : GetIntFieldValue, GetFloatFieldValue, GetDoubleFieldValue ou GetStringFieldValue, selon le type de la valeur du champ.- Pour la sous-classe FactualDataFile, la valeur de l'attribut fonction de lecture est affectée par la méthode AssignGetRecordClockValueMethod.
La recherche de l'instant associé à l'enregistrement se fait par un appel à la méthode GetRecordClockValue. La valeur renvoyée doit être une valeur de l'horloge de la simulation.
Exemple de réalisation et d'exploitation d'un fichier de données :
1) dans UserType.h
// Symboles de classe des fichiers en série d'enregistrements. enum App_FileId {F_TOBOGGAN = 11};2) dans UserFile.h
// Fonction de lecture spécifique du fichier de données. // Toboggan_ReadNextEnrgt(SequentialDataFile*); // Enumération de tous les champs d'un enregistrement. Les symboles // sont arbitraires. Les valeurs entières (positives ou nulles) // correspondantes sont optionnelles. // enum F_Toboggan_Fields { COMMAND, POSITION, NUMLIGNE, VITESSE };3) dans UserFile.cc
#include "UserFile.h" void CreationFichierToboggan() //---------------------------------------------------------------------------- // Fonction de création du fichier de données. //---------------------------------------------------------------------------- { // création de l'instance SequentialDataFile* pF = new SequentialDataFile(F_TOBOGGAN, READ); // création du tableau des attributs correspondant aux champs // respecter l'ordre des champs sur la ligne pF->AddField(NUMLIGNE, INT ); pF->AddField(COMMAND, INT ); pF->AddField(VITESSE, FLOAT); pF->AddField(POSITION, FLOAT); // affectation d'une fonction de lecture pF->AssignReadNextEnrgtMethod(Toboggan_ReadNextEnrgt); // désignation du nom physique du fichier ; réalise l'ouverture pF->AssignFullName("data_toboggan.txt"); // affectation d'un format de lecture lu en paramètre pF->AssignFormat(GetStringParameterValue("f_toboggan", "format"); }; int Toboggan_ReadNextEnrgt(SequentialDataFile* pF) //---------------------------------------------------------------------------- // Fonction de lecture d'un enregistrement. // Retourne : le nombre de champs lus ou 'EOF' //---------------------------------------------------------------------------- { return fscanf(pF->GetPhysicalFile(), pF->GetFormat(), &pF->mpFieldTab[NUMLIGNE]->mFieldValue.intValue, &pF->mpFieldTab[COMMAND] ->mFieldValue.intValue, &pF->mpFieldTab[VITESSE] ->mFieldValue.floatValue, &pF->mpFieldTab[POSITION]->mFieldValue.floatValue); };4) dans les fonctions utilisant les champs d'un enregistrement :
CreationFichierToboggan(); while(ReadNextEnrgt(F_TOBOGGAN) != EOF) { float MyValue = GetFloatFieldValue(F_TOBOGGAN, VITESSE)); ... }