Table of Contents
Benutzer-Zentralen-Bibliothek
UUDP
Die UUDP-Bibliothek in Rocrail bietet eine UDP-Schnittstelle für Benutzer-Bibliotheken auf Port 21111.
Das verwendetet Protokoll ist RCP ohne Header.
Initial muss ein logon gesendet werden: <logon/>
Java-Beispiel
Dieses Beispiel ist nur schnell codiert, um die UDP-Kommunikation zu zeigen und stellt keine Basis für die Entwicklung einer Zentralen-Bibliothek dar.
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
Eine MQTT-Benutzer-Zentralen-Bibliothek muss mit einem MQTT-Broker verbunden sein, um Befehle zu erhalten und Feld-Ereignisse zu veröffentlichen.
Mit dieser Methode ist die Zentralen-Bibliothek von den Rocrail-Quellen vollständig unabhängig und kann auch mit anderer Software verwendet werden.
Die Programmiersprache der Wahl ist auch unabhängig, solange eine Verbindung zu einem MQTT-Broker hergestellt werden kann.
Python
Zusätzliche Python-Bibliotheken
Installation der zusätzlichen Python-Bibliothek:
pip install paho-mqtt pip install pyserial
Im Fall von Linux oder macOS müssen die Befehle Superuser-Kontext gegeben werden:
sudo pip install paho-mqtt sudo pip install pyserial
Beispiel
Dieses Beispiel versucht ein serielle Verbindung mit der DCC++-Zentrale und versucht, den MQTT-Broker zu abonnieren.
Das Script als mqtt-cs.py speichern.
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()
Es gibt Python-Module, mit denen die eingehenden XML-Rocrail-Befehle im RCP-Format ausgewertet werden können.
Die Befehle sind relativ einfach strukturiert und können auch mit Standard-Textfunktionen ausgewertet werden.
Wenn das Attribut iid (Schnittstellen-Kennung) in einem Befehl enthalten ist, muss geprüft werden, ob der Befehl von dieser Benutzer-Bibliothek verarbeitet werden soll.
Sitzung
Python
Das Script mit python mqtt-cs.py starten.
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=[]
Konfiguration von Benutzer-Bibliotheken
Die Konfiguration von Benutzer-Bibliotheken wird durch Rocrail nicht unterstützt und muss extern erfolgen. Zum Beispiel mit einer .conf-Text-Datei.