Table of Contents
User Command Station Library
UUDP
The UUDP library in Rocrail provides an UDP interface for user libraries on port 21111.
The used protocol is RCP without header.
Initially a logon must be send: <logon/>
Java Example
This example is just fast coded to show the UDP communication, and is not a base for developing a CS library.
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
A MQTT user CS library must connect to a MQTT broker to get commands, and to publish field events.
With this method, the CS library is completely independent of the Rocrail sources, and can also be used with other software.
The programming language of choice is also independent, as long as its possible to connect to a MQTT broker.
Python
Extra Python Libraries
Install the extra Python library:
pip install paho-mqtt pip install pyserial
In case of Linux or macOS the commands must be executed in super user context:
sudo pip install paho-mqtt sudo pip install pyserial
Example
This example tries to make a serial connection with the DCC++ Command Station, and tries to subscribe to the MQTT broker.
Save the script as 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()
There are Python modules which can be used to parse the incoming XML Rocrail commands in RCP Format.
The commands are relative simple structured, and can also be parsed with standard text functions.
If the iid attribute is set in a command, it must be checked if the command should be processed by this user library.
Session
Python
Start the script with 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 of User Libraries
The configuration of user libraries is not supported by Rocrail and must be done externally. For example with a text .conf file.