User Tools

Site Tools


digint:user-en

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.

digint/user-en.txt · Last modified: 2020/07/15 00:38 by rainerk