Suche

Eclipse SCADA Tutorial – Teil 2

Jürgen Rose, Jens Reimann

© Shutterstock.com/Naypong

Der zweite Teil des Eclipse SCADA Tutorials zeigt, wie man ein Modbus-Gerät einbindet und die Werte in einer minimalen HMI darstellt.

Im ersten Teil dieses Tutorials wurde erklärt, wie man schnell einen Client und einen Server erstellen kann und den Server in der Middleware konfiguriert. In dieser Ausgabe zeigen wir, wie man ein Modbus-Gerät einbindet und die Werte letztlich in das GUI bringt. Da der Umzug zu Eclipse erst seit Kurzem erfolgt ist, kann es sein, dass sich einige technische Angaben oder Voraussetzungen geändert haben, wenn dieser Artikel erscheint. Eine laufend aktualisierte Version dieses Artikels findet sich in englischer Sprache im Eclipse-SCADA-Wiki.

Modbus

Modbus ist wohl eines der am weitesten verbreiteten Protokolle in der Welt der Automatisierung. Es existiert schon ziemlich lange, ist etabliert, einfach zu verstehen und zu implementieren, und wird aus diesen Gründen von einer großen Zahl von Geräten unterstützt.

Wo in der Softwarewelt von Client und Servern gesprochen wird, ist in der Industrialisierung oft die Rede von Master und Slave. Ein Master fragt einen oder mehrere Slaves ab. Ein Slave ist normalerweise ein Gerät, das Modbus spricht und Daten liefert. Das Modbus-Protokoll unterstützt bis zu 255 Geräte an einer Adresse. Eclipse SCADA kann sowohl als Master als auch als Slave konfiguriert werden (oder beides). Im Folgenden erklären wir, wie man mit Eclipse SCADA Modbus Slaves abfragen kann.

Die Modbus-Konfiguration ist etwas komplizierter als die recht simple Konfiguration von externen Items, wie sie im ersten Teil des Tutorials beschrieben wurde. Dies rührt unter anderem daher, dass wir Modbus so betrachten, wie PLCs gemeinhin arbeiten. Diese manipulieren normalerweise einen zusammenhängenden Speicherbereich, der bei jedem CPU-Zyklus aktualisiert wird. Teile des Speicherbereichs können dann für einen Fernzugriff freigegeben werden. In der Kommunikation wird ebenso genau dieser oder ein Teil des Speicherblocks über das Netz übertragen. Die Interpretation dessen ist dann wiederum Aufgabe des Anfragenden. Auf genau diese Art und Weise funktioniert unser S7-Treiber. Auch wenn dieses Modell für Modbus nicht perfekt passt, lässt es sich trotzdem dafür verwenden.

Da wir nur Speicherbereiche zur Abfrage definieren, benötigen wir einen Weg, daraus die eigentlichen Werte zu extrahieren. Dafür definieren wir ein Variablen-Mapping (das Model dazu heißt einfach nur Variable), das effektiv wie ein Struct aussieht. Die eigentlichen Abfrageblöcke können dann individuell und unabhängig davon definiert werden (Abb. 1).

Abb. 1

Abb. 1

Dies hat zwei Vorteile: Sich wiederholende Strukturen innerhalb eines Speicherbereichs können mit demselben Mapping abgefragt werden, und die Abfragerate kann für jeden Block separat festgelegt werden.

Aus dem vorherigen Artikel haben wir bereits ein Modell, das wir nun ergänzen werden. Der erste Schritt besteht darin, ein Modbus Device unter einem ebenso neu erstellten External Node-Knoten anzulegen. Dies ist der eigentliche Slave, das Gerät, das wir einbinden wollen. In unserem Fall ist es nun ausnahmsweise auch localhost, in der Realität wird es das die IP-Adresse des anzusprechenden Geräts sein. Unterhalb des gerade angelegten Knotens legen wir außerdem einen Slave-Knoten mit der Unit Id 1 und darunter wiederum einen Block an. Für unser Beispiel setzen wir den Block Type auf HOLDING (Register), da man diese sowohl lesen als auch schreiben kann. Wir setzen außerdem Start Address auf 13, Count auf 6 (3 Register * 2 Byte), Id auf B1. Bei Period ist die Polling Rate in Millisekunden gemeint. Wir verwenden hier 250 ms. Timeout setzen wir auf 5 000 ms, d. h. wenn für fünf Sekunden vom Gerät keine Antwort kommt, wird der Wert als fehlerhaft markiert.

Bis jetzt haben wir immer noch nicht das Mapping zwischen dem Speicherblock und den Zielvariablen definiert, nach dem Speichern wird uns das vom Validator auch angezeigt. Dazu müssen wir eine neue Type Definition anlegen. Mit CTRL + N öffnen wir den New-Wizard und wählen „Memory Type Model“ unter „Eclipse SCADA Configuration“. Wir nennen es einfach modbus.mtd und fügen es zu unserem Konfigurationsprojekt hinzu. Im Modell fügen wir nun unser Register-Mapping hinzu (Abb. 2).

Abb. 2: Definition das Register-Mappings

Abb. 2: Definition das Register-Mappings

Das Type System nennen wir einfach ModbusTypes. Darunter legen wir eine neue Type-Definition namens T1 an und fügen zwei Variablen hinzu, die wir hier MyValue1 und MyValue2 nennen. Für beide setzen wir den Typ auf 16bit unsigned integer. Die erste Variable bekommt als Index 0, die zweite Variable den Index 4. Der Index ist immer die relative Adresse innerhalb des angefragten Speicherblocks und immer auf Bytes bezogen. Bei der Adressierung des Blocks ist die Startadresse desselben aber immer das eigentliche Modbus-Register, immer zwei Bytes groß.

Jetzt müssen wir es noch dem Infrastrukturmodell hinzufügen. Dazu klicken wir rechts auf den Wurzelknoten, wählen „Load Resource“ und suchen unser modbus.mtd. Jetzt können wir ModbusTypes als Type System für unser Modbus-Gerät setzen. Außerdem können wir nun T1 als Type für unseren Block setzen.

Bis jetzt haben wir das eigentliche Modbus-Gerät mitsamt seines Register-Mappings definiert. Nun müssen wir den eigentlichen Treiber, der sich zu diesem Gerät verbindet, hinzufügen (Abb. 3).

Abb. 3: Den Modbus-Treiber hinzufügen

Abb. 3: Den Modbus-Treiber hinzufügen

Da der Modbus-Treiber auf Equinox basiert, fügen wir dazu unter unserem localhost-Knoten einen neuen Equinox Driver hinzu. Unter „Devices“ können wir nun das existierende Modbus-Gerät auswählen. Driver Type Id ist org.eclipse.scada.da.server.modbus, Instance Number ist 4, Name ist modbus. Nun können wir die Konfiguration erneut erzeugen und sehen, dass es nun ein weiteres Verzeichnis modbus in output gibt.

Mit dem CreateLaunchConfigs-Tool erzeugen wir nun eine neue Launch-Konfiguration, fügen die fehlenden Dependencies hinzu und können nun den Modbus-Treiber starten.Wir verwenden den PLCSimulator, um uns unsere Modbus Slaves zu simulieren. Wenn wir den PLCSimulator starten und für Modbus TCP konfigurieren, können wir nun die Kommunikation nachvollziehen (Abb. 4).

Abb. 4: Kommunikation mit dem Modbus-Gerät funktioniert

Abb. 4: Kommunikation mit dem Modbus-Gerät funktioniert

Ebenso können wir uns nun über den Eclipse SCADA Admin Client zum Modbus-Treiber verbinden und den Wert sehen (Abb. 5).

Abb. 5: Namen der Items im Modbus-Treiber, sowie Register im Gerät

Abb. 5: Namen der Items im Modbus-Treiber, sowie Register im Gerät

Genau wie im Beispiel des Random Hive können wir den Wert in den Master-Server spiegeln (Abb. 6). Dazu legen wir zwei Items, MyModbusValue1 und MyModbusValue2, an und setzen die Eigenschaften wie in Abbildung 7 gezeigt.

Abb. 6: Spiegeln der Modbus Items im Master-Server

Abb. 6: Spiegeln der Modbus Items im Master-Server

Abb. 7: Ansicht aller Werte im OSTC

Abb. 7: Ansicht aller Werte im OSTC

Insgesamt klingt das alles recht aufwändig, um nur drei Items zu mappen. Das kann man so sehen, muss es aber aus einer etwas anderen Perspektive betrachten: Zum einen bietet der Konfigurator eine ganze Reihe von mitgelieferten Funktionen, die die Items auf verschiedene Art und Weise manipulieren. Zum Beispiel kann über die „Customization Tags“ gesteuert werden, welches Suffix die Items bekommen oder ob z. B. eine Skalierung zur Verfügung oder schon voreingestellt wird. Ein weiterer Ansatz ist, dass es vorgesehen ist, dass die erzeugten Konfigurationen nicht direkt eingespielt werden, sondern einer dritten Partei, z. B. dem Kunden, an die Hand gegeben werden, damit diese sie selbst einspielen können. Unter Linux gibt es die Möglichkeit, ein Debian oder RPM Package direkt beim Generieren der Konfiguration zu erstellen. Für Windows ist das Erstellen von MSI-Dateien für das erste Quartal 2014 geplant. Ein neues Feature ist außerdem die Verwendung von „Recipes“, die einerseits einen einfachen Hook-Mechanismus bereitstellen, um in den Generierungsprozess einzugreifen, andererseits einen fertigen Interceptor, der Hostnamen anhand einer Ersetzungstabelle austauscht, mitliefert – sehr nützlich, wenn in der Testumgebung die Server anders benannt sind. Eine weitere Idee hinter dem Konfigurationskonzept ist auch, dass man sich wiederholende Elemente in Komponenten zusammenfasst, damit man das Ausdefinieren der Items vermeidet. Zu guter Letzt ist davon auszugehen, dass bei größeren Anlagen die Tags in Form einer Datenbank oder IO-Listen vorliegen, sodass man dann die Konfigurationsmodelle programmatisch daraus generiert, wobei man sich dann nicht mit den Low-Level-Konfigurationen der Items auseinandersetzen muss.

Funktionell haben wir durch das Mapping im Master-Server auch einiges gewonnen. Wir können manuelle Werte setzen, wir wissen, wenn ein Wert fehlerhaft ist, wir werden nur bei tatsächlichen Änderungen benachrichtigt und nicht bei jedem Modbus Poll. Dies schlägt sich im Übrigen auch im Timestamp nieder. Der Master-Server merkt sich immer den letzten Wert, d. h. bei einem Verbindungsverlust des Clients zum Master-Server bekomme ich, wenn die Verbindung wieder da ist und der Wert sich nicht geändert hat, exakt den gleichen Wert mit dem gleichen Timestamp wie vor dem Verlust der Verbindung.

Eine Benutzeroberfläche erstellen

Bisher gibt es zur Erstellung der Benutzeroberflächen noch keinen grafischen Editor. Wir haben die Erfahrung gemacht, dass es in den meisten Fällen gar kein so großer Nachteil ist. Es würde jetzt zu weit führen, im Rahmen dieses Artikels das komplette Setup zu beschreiben. Deshalb verweisen wir hier auf das Sample-Git-Repository (siehe Anfang des Artikels), das den kompletten Code enthält.

Wir hatten verschiedene Ideen, welche Möglichkeiten die HMI bieten sollte. Die beiden wichtigsten Punkte waren, dass sich Symbole schachteln lassen sollten und Eigenschaften von außen nach innen vererben.

Alle Komponenten, die HMI-Funktionen bereitstellen, werden über Extension Points konfiguriert. Dazu gehören auch die konfigurierten Verbindungen. Es ist möglich, mehrere Verbindungen für einen Server zu konfigurieren. Damit ist es machbar, dass der Client sich z. B. sowohl an Eclipse SCADA, aber gleichzeitig auch an einem JEE-Server anmeldet. Eigene Verbindungstypen können jederzeit problemlos selbst implementiert werden.

Views und Dialoge müssen auch als Extensions angegeben werden, aber sie können wiederum Unterkomponenten enthalten, die nicht extra registriert werden müssen. Die konkreten Dialoge und Views können daher während der Entwicklung zur Laufzeit neu geladen werden. Aus der Historie heraus gibt es zwei verschiedene Arten von Ansichten: Views auf Basis von Draw2D und Dialoge auf Basis von normalen SWT-Komponenten. Die Konfiguration ist deshalb auch relativ unterschiedlich. Die Dialoge sind älter, relativ starr und bieten nur vorgefertigte Komponenten. Die neueren Draw2D-basierten Ansichten lassen sich flexibel zusammenbauen und bieten mehr grafische Elemente. Listing 1 zeigt ein Beispiel, wie das Widget für den ersten Modbus-Wert eingebunden wird.

...
<element
  xsi:type="visualInterface:SymbolReference"
  uri="textValue.vi"
  onCreateProperties="properties.put ( 'item',
    prefixed ('MyModbusValue1.S'  ) );">
  <properties key="format" value="%.0f" />
</element>
...
../common/common.js</scriptModules>
...

Effektiv wird ein anderweitig definiertes Element (textValue.vi) platziert. Im onCreateProperties wird per JavaScript eine Map mit Key-Value-Paaren befüllt. Hier wird nun die Funktion prefixed() aufgerufen. Diese ist in common.js definiert. Als zusätzliche Property wird format übergeben, die sich auf alle untergeordneten Elemente weitervererbt.

Abb. 8: Anmelden am Master-Server

Abb. 8: Anmelden am Master-Server

Abb. 9: Hauptansicht mit geöffnetem Detaildialog

Abb. 9: Hauptansicht mit geöffnetem Detaildialog

Das textValue.vi Widget ist nun wie in Listing 2 aufgebaut.

<?xml version="1.0" encoding="UTF-8"?>
<visualInterface:Symbol
  xmi:version="2.0"
  xmlns:xmi="http://www.omg.org/XMI"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:visualInterface="http://eclipse.org/SCADA/VisualInterface"
  onInit="initTextValue();"
  onUpdate="updateValue('A');">
  <root xsi:type="visualInterface:FigureContainer"
  ...
  <children>
    <element
      xsi:type="visualInterface:Text"
      name="label"
      foregroundColor="#000000"
      backgroundColor="#F0F0F0"
      border="MARGIN:[r=10]"
      opaque="true"
      text=""
      labelAlignment="RIGHT"
      textAlignment="RIGHT"/>
  </children>
  ...
  <scriptModules>textValue.js</scriptModules>
...

Jedes Widget hat automatisch einen assoziierten Controller. Der stellt nun einige wenige Methoden bereit und sorgt dafür, dass für jedes Symbol als Erstes onInit() aufgerufen wird. Dort kann man nun eine eigene Funktion aufrufen. Hier ist es initTextValue(). Dort wird das Item im Controller unter einem lokalen Namen (hier A) registriert. Nach der Registrierung wird bei jeder Änderung von irgendeinem der registrierten Items die onUpdate()-Methode aufgerufen:

function initTextValue ()
{
  ...
  controller.registerItem ( "A", controller.getProperty ( "item" ), controller.getProperty("connection"));
}

In unserem Fall wird updateValue aufgerufen. Abgesehen von einigen Formatierungsoptionen wird aus dem Controller der aktuelle Wert geholt und das über den Namen referenzierte Element als Text gesetzt (Listing 3).

function updateValue ( itemName )
{
  ...
  var value = data.getPrimaryValue (itemName).asDouble ( null );
  if ( value == null ) {
    value = controller.getProperty ( "nullString", "" );
    controller.getElement("label").setText ( value );
  } else {
    controller.getElement("label").setText ( java.lang.String.format ( format, [value] ) );
  }
}

So viel mehr Funktionalität bietet der Controller gar nicht, aber durch die Möglichkeit der Kaskadierung und die Fähigkeit, eigene JavaScript-Funktionen aufzurufen, sind der eigenen Kreativität kaum Grenzen gesetzt.

Das Einfachste ist, das Beispiel-Repository zu klonen und dann ein paar Änderungen zu machen, um die Auswirkungen selbst zu sehen. Tipp: Ein Rechtsklick auf das Zahnrad rechts oben im GUI lädt die aktuelle View neu.

Momentan gehen wir davon aus, dass für ein Projekt ein spezifischer Client gebaut wird, der die Views enthält. Dies ist bei einer größeren Zahl von Installationen immer schwieriger zu warten, gerade wenn man bedenkt, dass Client und Server zusammenpassen müssen. Deshalb wird voraussichtlich in der ersten Hälfte von 2014 ein automatischer Updatemechanismus entstehen, der immer die aktuellsten Views vom Master-Server oder einem anderen Ort holt.

Fazit und Ausblick

Wir haben gelernt, wie man ein Modbus-Gerät konfiguriert und in den Master-Server einbindet. Diese Werte haben wir anschließend in der HMI visualisiert.

Die Konfigurationsgenerierung ist momentan immer noch in einer Betaphase, funktioniert aber schon ausgezeichnet. Das bedeutet aber auch, dass sich hier jederzeit etwas ändern kann. Ein geplantes Feature für nächstes Jahr ist die Integration von WIX zur automatischen Erstellung von MSI-Paketen.

Im GUI-Bereich wird die automatische Aktualisierung eines der nächsten zu implementierenden Features sein. Bei den Protokollen stehen als Nächstes Implementierungen von IEC 60870 und 61850 an. Ein längerfristig angelegtes, komplett neues Projekt wird die Implementierung eines Historian werden, der auch mit wirklich großen Datenmengen performant umgehen kann.

Aufmacherbild: Hand with PC RAM and computer monitor von Shutterstock / Urheberrecht: Napong

Verwandte Themen:

Geschrieben von
Jürgen Rose
Jürgen Rose ist Senior Developer bei der IBH SYSTEMS GmbH in München. Er ist Mitentwickler des Eclipse-SCADA-Projekts. Obwohl eigentlich ein Python-Liebhaber, findet er Java inzwischen gar nicht mehr so übel.
Jens Reimann
Jens Reimann ist Entwicklungsleiter bei der IBH SYSTEMS GmbH in München. Er ist Gründer und Chefentwickler des Eclipse-SCADA-Projekts (bisher bekannt als openSCADA, http://openscada.org). Seine Spezialität ist das Reverse Engineering und die Implementierung von Protokollen. Er ist Koautor eines Buchs zum Thema MES (Manufacturing Execution System).
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: