Note préalable : pour cet article ont été utilisé OpenRails 1.4 et Python 3.10.
Nous avons parlé jusqu’à présent de la commande d’OpenRails à l’aide notamment de l’émulation de clavier. Comme dit lors de la présentation de la structure du système, l’autre fonction majeure de l’interface du simulateur est de permettre un retour d’information depuis le jeu de simulation (ici OpenRails) vers le pupitre.
Lorsque le projet FerroviSim a débuté, il n’existait pas de solution simple pour réaliser cette fonction, il a donc fallu inventer un système de lecture des pixels de l’écran pour extraire la valeur des différentes jauges. Mais OpenRails intègre depuis peu une fonctionnalité de serveur web local qui met les variables de simulation à disposition d’applications externes.
Pour réaliser cette acquisition des données d’OpenRails, nous allons utiliser le langage de programmation Python qu’il est nécessaire au préalable d’installer. De même, je supposerai dans cet article que vous maitrisez les bases de Python, en particulier ce qu’est un script, comment l’exécuter… Je présente succinctement ces notions dans l’article ci-dessus.
Pour avoir plus d’informations sur l’API web d’OpenRails, rendez-vous dans le manuel d’OpenRails.
Activer le serveur web
La première étape est d’activer le serveur web dans les options d’OpenRails. Le choix du port peut être laissé par défaut.

Consulter le contenu de l’API
Conformément au manuel d’OpenRails, il est possible de consulter les données renvoyées par l’API au moyen d’un simple navigateur comme Google Chrome ou encore Firefox.
Commencez par lancer OpenRails (avec une ligne et une machine) en mode fenêtré (pour garder la possibilité d’ouvrir le navigateur en parallèle). Une fois la simulation lancée, ouvrez ensuite votre navigateur et tapez localhost:2150/api/cabcontrols dans la barre de recherche.
Si tout fonctionne correctement, vous devriez voir apparaitre la fenêtre suivante :

Pas de panique, essayons de comprendre ensemble ce qu’on y trouve. En regardant de plus prêt, on remarque de nombreux noms de variables de simulation d’OpenRails (Speedometer, Bracke_cyl…). Il s’agit en fait de toutes les variables de simulation utilisée dans le fichier de définition de cabine (.cvf). L’API nous renvoie une liste de blocs entre crochets, qui correspondent chacun à une variable. Prenons un de ces blocs :
{ « TypeName »: « EQ_RES », « MinValue »: 0.0, « MaxValue »: 9.5, « RangeFraction »: 0.4732947051525116 }
Il contient 4 valeurs :
TypeName, qui indique le nom de la variable (ici « EQ_RES », la pression au réservoir égalisateur)
MinValue, la valeur minimale que peut prendre la variable
MaxValue, la valeur maximale que peut prendre la variable
RangeFraction, à quelle fraction (entre 0 et 1) de la différence entre valeur max et valeur min est la variable
La formule pour retrouver la valeur de la variable est donc la suivante (produit en croix) :
variable = MinValue + (MaxValue – MinValue) * RangeFraction
Ici, on a donc pressionEQ = 0 + (9.5 – 0) * 0.473 = 9.5 * 0.473 = 4.49 bars
(souvent, comme ici, MinValue = 0)
Lire l’API avec un programme Python
Il est bien beau d’afficher ces données dans un navigateur, mais ce n’est pas de cette façon que l’on pourra les envoyer à notre interface pour un affichage sur un pupitre ! C’est là qu’un programme Python se révèle bien utile.
Nous allons avoir besoin d’installer la bibliothèque « requests » pour effectuer une requête HTTP sur l’API. Pour installer la bibliothèque, la démarche est détaillée sur la page d’introduction à Python. Tapez donc dans l’invite de commande Windows :
pip install requests
Ouvrez ensuite un nouveau script Python. Nous allons commencer par importer 2 bibliothèques : requests (installée à l’instant) et json (native dans votre installation de Python).
import requests
import json
A l’aide de la bibliothèque requests, nous allons pouvoir questionner l’API, et récupérer ce qu’elle a à nous donner. J’utilise pour cela une requête HTTP « get ».
import requests
import json
api_url = 'http://localhost:2150/API/CABCONTROLS/'
response = requests.get(api_url)
Avant de tester ce code, il est nécessaire d’avoir lancé OpenRails en ayant activé le serveur web, sinon Python plantera car il n’arrive pas à joindre l’API. Pour remplacer ce plantage par un message d’erreur, on peut utiliser les mots clés Python « try » et « except » de la façon suivante :
try:
response = requests.get(api_url)
except:
print("Echec de la connexion à l'API")
« response » est ici un objet qui contient plusieurs informations. En particulier, si tout s’est bien passé, le statut de la réponse devrait être « 200 » qui dans le protocole HTTP signifie « OK ». Je peux donc tester ce statut et afficher le contenu de la requête si tout va bien :
import requests
import json
api_url = 'http://localhost:2150/API/CABCONTROLS/'
try:
response = requests.get(api_url)
if response.status_code == 200:
print(response.content.decode('utf-8')) // affichage de la requête décodée
else:
print("L'API renvoie une erreur")
except:
print("Echec de la connexion à l'API")
Pour l’instant, la réponse de l’API est au format texte. Afin de pouvoir utiliser le contenu de la réponse comme un objet Python (accéder aux différents éléments comme dans une liste), il est nécessaire de le convertir à l’aide de la bibliothèque json :
data = json.loads(response.content.decode('utf-8'))
Je vous propose de remettre notre code au propre dans une fonction, afin de pouvoir l’appeler aisément :
import requests
import json
api_url = 'http://localhost:2150/API/CABCONTROLS/'
def get_API_data():
try:
response = requests.get(api_url)
except:
print("Problème de connexion à l'API, vérifiez que la simulation est lancée")
return False
if response.status_code == 200:
return json.loads(response.content.decode('utf-8'))
else:
return False
data = get_API_data()
L’objet data est désormais un objet itérable, ce qui signifie que l’on peut accéder à ses éléments à l’aide d’indices. Si vous tapez par exemple :
print(data[0])
vous obtiendrez le bloc de la réponse correspondant à la première variable (les indices commencent à 0). Du point du vue des objets Pythons, data[0] est un « dictionnaire », c’est à dire une sorte de liste où les indices sont des chaînes de caractères et non des nombres. Ainsi, pour accéder au nom de la première variable, il suffit de taper :
print(data[0]["TypeName"])
Pour visualiser toutes les variables et leur valeur, on peut boucler sur les éléments de réponse de la façon suivante :
for var in data:
valeur = var["MinValue"] + (var["MaxValue"]-var["MinValue"])*var["RangeFraction"]
print(var["TypeName"] + " = " + str(valeur)) #str() permet de transformer un nombre en chaine de caractères
Comme l’objectif final n’est pas d’afficher les variables à l’écran mais bien de les envoyer à l’interface du pupitre, je vous propose de modifier la boucle d’affichage que nous venons d’écrire pour en faire une boucle de sauvegarde dans un dictionnaire. Ainsi il sera possible par la suite d’accéder très simplement aux variables et à leurs valeurs.
data_dict = {} #création d'un dictionnaire vide
for var in data:
valeur = var["MinValue"] + (var["MaxValue"]-var["MinValue"])*var["RangeFraction"]
data_dict.update({var["TypeName"] : valeur})
#Exemple d'accès à une variable :
print(data_dict["EQ_RES"])
Reprenons le code depuis le début, et insérons le dans une boucle infinie, avec un délais permettant d’effectuer une requête toutes les 500ms (ce délais peut être ajusté). Au passage, le programme est restructuré en fonctions indépendantes. Je vous invite à adopter ce genre d’habitudes de programmation : cela permet de s’y retrouver dans son code car chaque bloc a sa fonction. Il existe de très nombreux sites pour s’exercer à la programmation de fonctions en python.
import requests
import json
import time # Bibliothèque permetant de gèrer des delais
api_url = 'http://localhost:2150/API/CABCONTROLS/'
def get_API_data():
try:
response = requests.get(api_url)
except:
print("Problème de connexion à l'API, vérifiez que la simulation est lancée")
if response.status_code == 200:
return json.loads(response.content.decode('utf-8'))
else:
return None
def data_to_dict(data): # Fonction renvoyant un dictionnaire des variables de simulation à partir des données brutes
data_dict = {}
for var in data:
valeur = var["MinValue"] + (var["MaxValue"]-var["MinValue"])*var["RangeFraction"]
data_dict.update({var["TypeName"] : valeur})
return data_dict
# Boucle infinie
while True:
data = get_API_data()
data_dict = data_to_dict(data)
time.sleep(0.5)
Remarque importante : pour interrompre la boucle infinie sans fermer la fenêtre d’exécution, il suffit d’utiliser le raccourci clavier Ctrl + C.
Il est temps de remarquer les limites de l’API :
– L’API affiche les variables définies dans les fichiers de cabine (.cvf). Si le fichier comporte des erreurs, il est possible que certaines variables ne fonctionnent pas (j’ai vu l’exemple d’une machine ou la vitesse restait toujours nulle). Il est possible de corriger le .cvf en conséquence, j’y reviendrai probablement dans un articule ultérieur.
– Si une variable est appelée plusieurs fois dans le fichier .cvf, l’API va la renvoyer plusieurs fois. Il faudra alors modifier le programme pour n’en garder qu’une. Pour cela, au lieu d’une boucle for, vous pouvez directement utiliser les indices des éléments de « data » (data[4] par exemple renverra la 4ème variable). A noter que cela fonctionnera tant que vous ne changez pas de locomotive dans OpenRails, puisque chaque locomotive à son fichier .cvf.
A l’issue de ces manipulations, sauf problème lié au fichier .cvf, vous disposez d’un programme capable d’acquérir les variables de simulation d’OpenRails. Comme il est également possible avec Python de communiquer avec une carte Arduino via un port série, nous verrons dans un prochain article comment transmettre ces données à l’interface pour un affichage sur le pupitre.
Salut, je suis en train de me faire un simulateur avec un pupitre de BB27300 et franchement tu me sauve la vie!!! Je te remercie d’avoir mis tous ces articles de programmation !! Encore merci ! Bonne continuation à toi !!!!!
J’aimeAimé par 1 personne