Table of Contents
Bibliothèques de Centrales Numériques Utilisateurs
UUDP
La bibliothèque UUDP dans Rocrail fournie une interface UDP pour les bibliothèques utilisateurs sur le port 21111.
Le protocole utilisé est RCP sans entêtes.
Initialement une ouverture de session doit être envoyée: <logon/>
Exemple Java
Cet exemple est un codage rapide pour montrer la communication UDP, et n'est pas une base pour le développement d'une bibliothèque de Centrale Numérique.
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UUDP { static DatagramSocket ds = null; static InetAddress ip = null; static UUDPreader reader = null; static boolean running = true; public static void main(String[] args) { System.out.println("UUDP example"); try { UUDP.ds = new DatagramSocket(); UUDP.ip = InetAddress.getLocalHost(); byte buf[] = null; buf = "<logon/>".getBytes(); DatagramPacket DpSend = new DatagramPacket(buf, buf.length, UUDP.ip, 21111); UUDP.ds.send(DpSend); UUDP.reader = new UUDP.UUDPreader(); Thread th = new Thread(UUDP.reader); th.start(); boolean onoff = true; for (int i = 0; i < 4; i++) { buf = ("<fb addr=\"123\" state=\"" + (onoff ? "true" : "false") + "\"/>").getBytes(); DpSend = new DatagramPacket(buf, buf.length, UUDP.ip, 21111); UUDP.ds.send(DpSend); Thread.sleep(1000); onoff = !onoff; } running = false; buf = "<logoff/>".getBytes(); DpSend = new DatagramPacket(buf, buf.length, UUDP.ip, 21111); UUDP.ds.send(DpSend); th.interrupt(); } catch (Exception e) { } } static class UUDPreader implements Runnable { public void run() { try { while (running) { byte[] receive = new byte[256]; DatagramPacket DpReceive = new DatagramPacket(receive, receive.length); ds.receive(DpReceive); System.out.println("received: " + new String(receive)); } } catch (Exception e) { } } } }
MQTT
Une bibliothèque de Centrale Numérique utilisateur MQTT pour faire une connexion à un broker MQTT pour obtenir les commandes, et pour publier les évènements du terrain.
Avec cette méthode, la bibliothèque de Centrale Numérique est complètement indépendante des sources Rocrail, et peut également être utilisée avec un autre logiciel.
Le choix du langage de programmation est également indépendant, tant qu'il est possible de se connecter à un broker MQTT.
Python
Bibliothèques supplémentaires Python
Installer la bibliothèque supplémentaire Python:
pip install paho-mqtt pip install pyserial
Dans le cas de Linux ou macOS les commandes doivent être exécutées dans le contexte de super utilisateur:
sudo pip install paho-mqtt sudo pip install pyserial
Exemple
Cet exemple essaie de faire une connexion série avec la Centrale Numérique DCC++, et essaie de s'abonner au broker MQTT.
Sauvegarder le script sous le nom mqtt-cs.py.
import time import os import serial import xml.etree.ElementTree as ET #https://pypi.org/project/paho-mqtt/1.0/ import paho.mqtt.client as paho # Open a serial connection with the DCC++ command station: ser = serial.Serial( port='/dev/tty.usbmodem14101', baudrate=115200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_TWO, bytesize=8 ) if ser.isOpen(): print "Serial connection established." def sendDCCpp(msg): if ser.isOpen(): rc = ser.write(msg); print "" + str(rc) + " bytes send to DCC++" # Handle system commands/events: def onSys(node): if node.get('cmd') == "shutdown": print("SYSTEM SHUTDOWN... Clean up and exit...") #os._exit(0) if node.get('cmd') == "stop": sendDCCpp("<0>") if node.get('cmd') == "go": sendDCCpp("<1>") # Handle output commands/events: def onOutput(node): print("set output " + node.get('addr') + ":" + node.get('port') + " to " + node.get('cmd') ) # Handle clock commands/events: def onClock(node): print(node.get('cmd') + " clock to " + node.get('hour') + ":" + node.get('minute') ) # MQTT callback function: def on_message(client, userdata, message): #evaluate the Rocrail command print("broker message = " + str(message.payload.decode("utf-8"))) node = ET.fromstring(str(message.payload.decode("utf-8"))) if node.tag == "sys": onSys(node) if node.tag == "co": onOutput(node) if node.tag == "clock": onClock(node) broker="nasRR" #this is the IP of the computer on which the MQTT broker is installed. # Create client object client= paho.Client("client-001") # Bind function to callback client.on_message=on_message print "connecting to broker " + broker client.connect(broker)#connect # Start loop to process received MQTT messages client.loop_start() # Subscribe: topic = "rocrail/service/command" print "subscribe to " + topic client.subscribe(topic) # Evaluate and handle DCC++ messages: def onDCCpp(serin): print "DCCpp: " + serin if serin == "<p0>": print "DCC++ reports power OFF" client.publish("rocrail/service/field","<state iid=\"python\" power=\"false\"/>") if serin == "<p1>": print "DCC++ reports power ON" client.publish("rocrail/service/field","<state iid=\"python\" power=\"true\"/>") #evaluate the field sensors and publish addr = "4711" state = "true" client.publish("rocrail/service/field","<fb iid=\"python\" addr=\""+addr+"\" state=\""+state+"\"/>") # Main loop for reading the serial connection: while ser.isOpen(): serin = "" if ser.inWaiting() > 0: serin = ser.read(1) if serin == '<': serchar = '' while serchar != '>': serchar = ser.read(1) serin += serchar if serin != '': onDCCpp(serin) if ser.inWaiting() == 0: time.sleep(0.01) # Clean up ser.close() client.disconnect() client.loop_stop()
Il y a des modules Python qui peuvent être utilisés pour analyser les commandes Rocrail XML arrivant au Format RCP.
Les commandes sont structurées relativement simplement, et elles peuvent également être analysées avec les fonctions de texte standard.
Si l'attribut iid est réglé dans une commande, il doit être vérifié si la commande doit traitée par cette bibliothèque utilisateur.
Session
Python
Démarrer le script avec python mqtt-cs.py
MBA2019:digint robversluis$ python mqtt-cs.py connecting to broker nasRR subscribe to rocrail/service/command broker message = <sys cmd="shutdown" informall="true" val="0"/> SYSTEM SHUTDOWN... Clean up and exit... DCCpp: <iDCC++ BASE STATION FOR ARDUINO UNO / ARDUINO MOTOR SHIELD: V-1.2.1+ / Jun 6 2019 07:58:21> DCCpp: <N0: SERIAL> broker message = <fbinfo cmd="fbmods"> <fbmods bus="0" modules="0,1,2"/> </fbinfo> broker message = <boosterlist/> broker message = <clock divider="1" hour="8" minute="39" wday="4" mday="6" month="6" year="2019" time="1559803151" temp="20" bri="255" cmd="set"/> set clock to 8:39
Rocrail
20190421.104604.509 r9999I clocktic OClntCon 0606 mqtt publish to [rocrail/service/command]:[<clock divider="1" hour="10" minute="46"] 20190421.104828.200 r9999I mqttread OClntCon 0799 broker published [rocrail/service/field]:[<fb iid="python" addr="4711" state="true"/>] 20190421.104828.201 r9999a mqttread OModel 5124 trying to match sensor event: [python] 0:4711 uidname=[] state=1 code= 20190421.104828.201 r9999a mqttread OModel 5160 sensor key: 0_4711_python_ 20190421.104828.201 r9999a mqttread OModel 5180 unregistered sensor event: [python] 0:4711 uidname=[]
Configuration des bibliothèques Utilisateur
La configuration des bibliothèques utilisateur n'est pas supportée par Rocrail et doit être faite à l'extérieur. Par exemple avec un fichier texte .conf.