User Tools

Site Tools


digint:user-de

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.

digint/user-de.txt · Last modified: 2020/07/15 01:02 by rainerk