Ein Tutorial zum IoT-Projekt bei Eclipse

Eclipse SCADA Tutorial – Teil 1

Jürgen Rose, Jens Reimann

© Shutterstock.com/Naypong

Eclipse SCADA ist der Ansatz, einen modularen Baukasten zu schaffen, der es ermöglichen soll, eigene SCADA-(Supervisory-Control-and-Data-Acquisition-)Lösungen zu implementieren. Es bietet dabei eine Reihe von Protokolladaptern, Middleware, um Daten von Geräten zu verarbeiten, einige Module, um Basisfunktionen eines SCADA-Systems abzudecken – wie z. B. Alarms und Events, Aufzeichnung historischer Daten – UI-Komponenten, um HMIs zu erstellen und ein Konfigurationsframework.

Für jedes dieser Features sind Implementierungen bereits vorhanden, die meisten davon schon seit dem Vorgänger openSCADA. Einige dieser Funktionen sind sehr umfangreich, ein paar noch relativ rudimentär, aber trotzdem verwendbar. Der größte Teil davon ist schon intensiv debuggt und läuft seit Jahren im produktiven Einsatz. Mit dem Umzug von openSCADA zur Eclipse Foundation wollen wir einerseits die M2M-Initiative unterstützen, hoffen aber auch, von der Arbeit anderer Projekte in diesem Bereich zu profitieren.

Im ersten Teil dieses SCADA Tutorials wird erklärt, wie man schnell einen Client und einen Server erstellen kann und den Server wiederum in der Middleware konfiguriert. Im zweiten Teil wird gezeigt, wie man ein Modbus-Gerät einbindet und die Werte letztlich in das GUI bringt. Da der Umzug zu Eclipse erst seit Kurzem erfolgt, 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.

Einführung

Was ist SCADA? In der deutschen Wikipedia findet man folgende, sehr schöne und knappe Definition: „Unter Supervisory Control and Data Acquisition (SCADA) versteht man das Überwachen und Steuern technischer Prozesse mittels eines Computersystems.“

Der Vorgänger von Eclipse SCADA – openSCADA – wurde 2006 ins Leben gerufen und entstand im Rahmen eines Projekts, bei dem es darum ging, Geschäftsanwendungen Zugriff auf einen Zoo existierender Hardware zu gewähren und eine enge Integration von physischen Workflows in der realen Welt und ihrer Repräsentation in Software zu gewährleisten. Um sowohl eine einfache Integration als auch eine gewisse Plattformunabhängigkeit zu gewährleisten, wurde beschlossen, die Lösung auf der Java-Plattform zu realisieren.

Auch wenn es anfangs nicht darum ging, eine komplette SCADA-Lösung zu schaffen, so war doch klar, dass viele der klassischen SCADA-Funktionalitäten benötigt würden. Zum damaligen Zeitpunkt gab es keine Lösung am Markt, die unseren Anforderungen hinsichtlich Kosten, Flexibilität oder benötigter Features entsprach.

Dies führte zu dem Entschluss, die entsprechenden Funktionalitäten von Grund auf selbst zu entwickeln und die nicht kundenspezifischen Teile Stück für Stück als Open Source zur Verfügung zu stellen. Die nicht veröffentlichten Komponenten sind dann auch im Wesentlichen Protokolladapter für sehr spezifische Hardware, wo sogar zum Teil die Protokolle der Geräte selbst für den Kunden modifiziert waren.

Die grundsätzliche Idee hinter openSCADA war es, ein gemeinsames Protokoll und ein gemeinsames Java-API zu haben. Diese Komponenten sollten die Geschäftsapplikationen nutzen können, um mit den angeschlossenen Geräten zu kommunizieren. Jeder Gerätetyp brauchte dann einen Protokolladapter, der das gerätespezifische Protokoll auf das openSCADA-eigene (damals das so genannte „NET“- oder „GMPP“-Protokoll) umsetzt. In der openSCADA-Terminologie wurden diese Adapter als „Treiber“ oder „Hive“ bezeichnet. Diese verwenden das Server-API, um ihre Dienste zur Verfügung zu stellen. Normalerweise laufen sie als separater Prozess, damit sie einzeln neu gestartet werden können, für den Fall, dass irgendetwas schiefläuft. Die Businessapplikation wiederum verwendet das Client-API, um auf die Treiber zuzugreifen. Einige der Treiber, die im Laufe der Zeit entstanden sind:

  • Exec-Treiber (führt Kommandozeilenbefehle aus und parst die Ausgabe)
  • JDBC-Treiber (ruft periodisch die Datenbank auf und führt Abfragen und Updates durch)
  • Modbus-Treiber
  • SNMP-Treiber
  • OPC-Treiber (basierend auf der von uns separat entwickelten Utgard-Bibliothek)
  • Proxy-Treiber (schaltet zwischen mehreren Instanzen eines Treibers hin und her)

Am Anfang wurden die meisten der zusätzlichen Funktionalitäten in den Hives selbst implementiert. Dies führte zu immer komplexer werdenden Konfigurationen, und es wurde schwer abzuschätzen, wie die Operationen auf einem Tag (in der Eclipse-SCADA-Terminologie: Item) am Ende abgearbeitet werden. Dies führte zur Erstellung einer Art Middleware, dem „Master Server“.

Damit konnten wir nun die Treiber radikal entschlacken. Sie kümmerten sich ab sofort nur noch um die Protokollumsetzung. Alle andere Funktionalität wanderte nun in den Master Server. Dieser basiert auf einem Equinox OSGi Container, und jeder Funktionsblock läuft als eigenes Bundle. Ein großer Vorteil dieses Ansatzes war, dass wir nun die Konfiguration zur Laufzeit anpassen konnten, was mit dem bisherigen Treiberkonzept nicht funktionierte, da diese ihre Konfiguration aus einer statischen XML-Datei lesen. Neue Treiberimplementierungen konnten natürlich nun ebenfalls das neue Modell nutzen. Öffentlich verfügbare Treiber, basierend auf dieser Art der Implementierung, sind:

  • S7-Treiber (Implementierung des Protokolls für Siemens S7 PLCs)
  • Modbus

Bei den meisten der vorhandenen Treiber sind die Protokollimplementierungen mit der Eclipse-SCADA-spezifischen Treiberfunktionalität eng verwoben. Mit dem Umzug zu Eclipse haben wir nun beschlossen, zukünftig die Protokollimplementierungen vom Eclipse-SCADA-Treiber-Wrapper zu trennen, so wie es bisher bei dem OPC-Treiber schon der Fall ist. Dort ist das Protokoll in einem feingranularen OPC-spezifischen API gekapselt, und der Treiber ist nur noch ein Wrapper, der dann natürlich openSCADA-spezifisch ist.

Wir gehen davon aus, dass in Zukunft das Interesse an frei verfügbaren, hochqualitativen Implementierungen von häufig verwendeten Standardprotokollen wächst. Dabei wollen wir niemanden nötigen, das komplette Eclipse-SCADA-Projekt zu nutzen, obwohl nur mit ein paar Geräten kommuniziert werden soll. In manchen Fällen wird es vielleicht auch nicht möglich sein, die Treiber aus lizenzrechtlichen Gründen mit Eclipse SCADA zu integrieren. Heute schon der Fall ist dies beim SNMP- und beim OPC-Treiber. Beide können nicht mit Eclipse SCADA ausgeliefert werden, da die darunterliegenden Bibliotheken nicht EPL-kompatibel sind. Deshalb wird das Projekt openSCADA weiter existieren und eine Distribution anbieten, in der dann auch solche Treiber enthalten sind.

Die gesamte Protokollkonvertierung, um die es bisher ging, betrachtet nur den „Data-Acquisition“-Teil eines SCADA-Systems. Darauf basierend bietet Eclipse SCADA weitere Features:

  • Alarms und Events (AE): verarbeitet Alarme, Operatoreingriffe, Rückantworten des Systems, Benachrichtigungen und Auditing
  • Historical Data (HD): zeichnet Livewerte (aus DA) für spätere Abfragen auf
  • Configuration Administration (CA): verarbeitet die Konfigurationen der Serverkomponenten und Konfigurationsänderungen im laufenden System
  • Visual Interface (VI): stellt GUI-Komponenten bereit, basierend auf Draw2d

Die Aufteilung in die verschiedenen Schichten und Verantwortlichkeiten ist auch in der Git-Repository-Aufteilung gut zu erkennen (Abb. 1).

Abb. 1: Eclipse Scada Git Repository: Übersicht, http://git.eclipse.org/c/eclipsescada

Abb. 1: Eclipse Scada Git Repository: Übersicht, http://git.eclipse.org/c/eclipsescada

Der OSTC

Ursprünglich hieß der Client „OpenSCADA Test Client“, wird aber inzwischen zu weitaus mehr verwendet, z. B. zum Einspielen der Konfiguration in den Master Server. Integriert ist aber immer noch ein Testserver, der für erste Experimente verwendet werden kann. Da die Migration des Clients zu Eclipse zurzeit der Drucklegung noch nicht vollständig ist, gehen wir in diesem Tutorial von der Version aus, die über openSCADA verfügbar ist (dies ist die Version 1.2, die hier erhältlich ist).

Nach der Installation und dem Start des Clients wechseln wir in die DA-Perspektive und können nun unter „Testing“ mit „Start Local Test Server“ den Server starten (Abb. 2).

Abb. 2: Den Testserver starten

Abb. 2: Den Testserver starten

Als Nächstes fügen wir eine neue Verbindung unter „User Preferences“ hinzu, in unserem Fall ist das: da:ngp://localhost:2102. Der Testserver verwendet Port 2102, ngp ist das Protokoll, da die Untervariante des Protokolls. Nun können wir uns per Doppelklick auf den URL zum Server verbinden. Der Connection State sollte nun von CLOSED nach BOUND wechseln. Einige Unterknoten sollten nun automatisch unter der Verbindung erscheinen. Wir erweitern den Ordner memory-cell, rechtsklicken auf das Item „control“, wählen „Write Operation“, geben „2“ als Wert aus und klicken „OK“. Automatisch erscheinen jetzt zwei neue Items, „0“ und „1“, direkt oberhalb des Control Items. Wir können nun diese beiden Items in die Real Time List (Abb. 3) ziehen.

Abb. 3: Die neu erzeugten Items in der Real Time List

Abb. 3: Die neu erzeugten Items in der Real Time List

Jetzt können wir entweder mit dem „Write Operation“-Dialog einen Wert auf das Item schreiben oder verwenden den Signal Generator. Danach sollte das Item einen Wert besitzen. Außerdem sollte jetzt das Attribut timestamp existieren.

Abhängig von der Implementierung kann ein Item eine ganze Reihe von Attributen besitzen, universell verfügbar ist allerdings nur timestamp. Auch wenn ein Treiber unter Umständen ein Gerät pollt, um Werte abzufragen, werden sämtliche Wertänderungen Eclipse-SCADA-intern komplett eventorientiert verarbeitet. Solange das Gerät oder Subsystem – wie z. B. der OPC-Server oder einige PLCs – selbst einen Zeitstempel liefert, wird dieser von der ursprünglichen Quelle übernommen. Sonst generiert Eclipse SCADA einen.

Jeder Wert ist vom Typ Variant, der wiederum einen INT32, INT64, DOUBLE, BOOLEAN, STRING oder NULL-Wert enthalten kann. Generell sendet der Server immer den Primärwert gemeinsam mit den Attributen, da im Normalfall zumindest der Timestamp aktualisiert wird. Es mag aber sein, dass der Server nur die Änderung der Attribute sendet, um den Netzwerkverkehr zu reduzieren. Unser nächster Schritt ist es, einen minimalen Client zu schreiben, der genau diese Change Events erhält.

IDE einrichten

Zur Entwicklung sollte das letzte Kepler-Release verwendet werden. Zusätzlich müssen die PDE, EMF-Tools und Acceleo installiert werden. Wir empfehlen hier die „Eclipse IDE for Java and DSL Developers“-Variante als Basis zu verwenden. Folgende Repositories sollten nun geklont und importiert werden:

  • http://git.eclipse.org/gitroot/eclipsescada/org.eclipse.scada.external.git
  • http://git.eclipse.org/gitroot/eclipsescada/org.eclipse.scada.utils.git
  • http://git.eclipse.org/gitroot/eclipsescada/org.eclipse.scada.protocols.git
  • http://git.eclipse.org/gitroot/eclipsescada/org.eclipse.scada.base.git
  • http://git.eclipse.org/gitroot/eclipsescada/org.eclipse.scada.core.git

Danach wird es noch einige Kompilierfehler geben. Der Grund ist, dass sämtliche Projekte die Abhängigkeiten über OSGi auflösen. Daher müssen wir zuerst eine Target-Plattform einrichten. Dazu liegt im Projekt org.eclipse.scada.external-target eine Target-Plattformdefinition, die einfach gesetzt werden kann. Vermutlich beschwert sich Eclipse nun, dass keine API-Baseline gesetzt ist. Mit dem Quick Fix lässt sich das schnell beheben, indem man die aktuelle Eclipse-Instanz selbst als Referenz setzt.

Ein einfacher Client

Jedes Item, für das man Updates erhalten will, muss erst abonniert werden. Der Code ist relativ unkompliziert (Listing 1).

package org.eclipse.scada.eclipsemagazin;
 
import java.util.Observable;
import java.util.Observer;
 
import org.eclipse.scada.core.ConnectionInformation;
import org.eclipse.scada.core.client.AutoReconnectController;
import org.eclipse.scada.core.client.ConnectionFactory;
import org.eclipse.scada.core.client.ConnectionState;
import org.eclipse.scada.core.client.ConnectionStateListener;
import org.eclipse.scada.da.client.Connection;
import org.eclipse.scada.da.client.DataItem;
import org.eclipse.scada.da.client.DataItemValue;
import org.eclipse.scada.da.client.ItemManagerImpl;
 
public class SampleClient {
  
  public static void main(String[] args) throws InterruptedException {
    // the ConnectionFactory works a bit like JDBC,
    // every implementation registers itself when its loaded
    // alternatively it is also possible to use the connection
    // directly, but that would mean the code would have to be aware
    // which protocol is used, which is not desirable
    try {
      Class.forName("org.eclipse.scada.da.client.ngp.ConnectionImpl");
    } catch (ClassNotFoundException e) {
      System.err.println(e.getMessage());
      System.exit(1);
    }
 
    final String uri = "da:ngp://localhost:2102";
 
    final ConnectionInformation ci = ConnectionInformation.fromURI(uri);
 
    final Connection connection = (Connection) ConnectionFactory.create(ci);
    if (connection == null) {
      System.err.println("Unable to find a connection driver for specified URI");
      System.exit(1);
    }
 
    // just print the current connection state
    connection.addConnectionStateListener(new ConnectionStateListener() {
        @Override
        public void stateChange(org.eclipse.scada.core.client.Connection connection, ConnectionState state, Throwable error) {
          System.out.println("Connection state is now: " + state);
        }
    });
 
    // although it is possible to use the plain connection, the
    // AutoReconnectController automatically connects to the server
    // again if the connection is lost
    final AutoReconnectController controller = new AutoReconnectController(connection);
    controller.connect();
 
    // although it is possible to subscribe to an item directly,
    // the recommended way is to use the ItemManager, which handles the
    // subscriptions automatically
    final ItemManagerImpl itemManager = new ItemManagerImpl(connection);
 
    final DataItem dataItem = new DataItem("memory-cell-0", itemManager);
    dataItem.addObserver(new Observer() {
        @Override
        public void update(final Observable observable, final Object update) {
          final DataItemValue div = (DataItemValue) update;
          System.out.println(div);
        }
    });
  }
}

Die Ausgabe sollte in etwa so aussehen wie in Listing 2 (wenn wir den Signal Generator verwenden).

Connection state is now: LOOKUP
Connection state is now: CONNECTING
NULL#[][none]
NULL#[][none]
Connection state is now: CONNECTED
Connection state is now: BOUND
NULL#[C][none]
DOUBLE#8.568166046545908[C][2013-10-21 12:03:14,390]
DOUBLE#7.645008970862307[C][2013-10-21 12:03:14,491]
DOUBLE#6.7023593269157775[C][2013-10-21 12:03:14,592]
DOUBLE#5.742622883254675[C][2013-10-21 12:03:14,692]
DOUBLE#4.768242160934602[C][2013-10-21 12:03:14,793]
DOUBLE#3.7915245591862776[C][2013-10-21 12:03:14,894]
DOUBLE#2.7954270074926617[C][2013-10-21 12:03:14,994]
...

 Um einen Wert zu schreiben, würde der Code in etwa so aussehen:

NotifyFuture<WriteResult> future = ((Connection) connection)
  .startWrite("memory-cell-1", Variant
  .valueOf(new Random().nextLong()),
  null, null);

Die letzten beiden Parameter in diesem Beispiel sind: operationParameters und callbackHandler. Teil der operationParameters sind z. B. die Benutzerauthentifizierungsinformationen. Der callbackHandler wird normalerweise verwendet, um vom Client weitere Informationen anzufordern, etwa, ob eine Authentifizierung erforderlich ist oder eine kryptografische Signatur benötigt wird.

Ein einfacher Treiber

Ein Treiber besteht in Wirklichkeit aus zwei Teilen: einer Hive-Implementierung und einem Protokollexporter. Um einen Treiber zu schreiben, muss nur der Hive implementiert werden, jeder Exporter sollte dann automatisch damit funktionieren. Das ist es, was wir meinen, wenn wir davon reden, dass das Java-API von der Implementierung des Protokolls separiert ist.

Die mitgelieferte Basisimplementierung des Hives (HiveCommon) liefert eine ganze Reihe von Features mit, darunter Browsing, Subscribe/Unsubscribe von Items und Wertaktualisierung. Items können sich wiederum in Ordern befinden, die natürlich auch geschachtelt sein können (Listing 3).

package org.eclipse.scada.eclipsemagazin.hive;
 
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
 
import org.eclipse.scada.core.Variant;
import org.eclipse.scada.da.server.browser.common.FolderCommon;
import org.eclipse.scada.da.server.common.AttributeMode;
import org.eclipse.scada.da.server.common.DataItemInputCommon;
import org.eclipse.scada.da.server.common.impl.HiveCommon;
import org.eclipse.scada.utils.collection.MapBuilder;
import org.eclipse.scada.utils.concurrent.NamedThreadFactory;
 
public class RandomHive extends HiveCommon {
  
  private final FolderCommon rootFolder;
  
  private ScheduledExecutorService scheduler = null;
  
  private final Random random = new Random();
  
  public RandomHive() {
    super();
 
    // since it is possible to use a custom folder implementation
    // the root folder has to be explicitly set
    this.rootFolder = new FolderCommon();
    setRootFolder(this.rootFolder);
  }
 
  @Override
  public String getHiveId() {
    return "org.eclipse.scada.eclipsemagazin.hive";
  }
 
  @Override
  protected void performStart() throws Exception {
    super.performStart();
 
    // the executor is only used for the dataItem itself
    this.scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(
        "org.eclipse.scada.eclipsemagazin.hive"));
 
    // create the item
    final RandomItem randomItem = new RandomItem("random");
    // register item with hive (initializes lifecycle)
    this.registerItem(randomItem);
    // add item to folder
    // the folder name does not have to be the same as the itemId!
    this.rootFolder.add("random", randomItem, null);
  }
 
  @Override
  protected void performStop() throws Exception {
    // just shut down scheduler
    if (this.scheduler != null) {
      this.scheduler.shutdownNow();
    }
    super.performStop();
  }
 
  private class RandomItem extends DataItemInputCommon implements Runnable {
 
    public RandomItem(final String name) {
      super(name);
      // every second call run
      scheduler.scheduleAtFixedRate(this, 0, 1, TimeUnit.SECONDS);
    }
 
    @Override
    public void run() {
      // just update item with a new random value, also set current
      // timestamp
      updateData(
        Variant.valueOf(random.nextLong()),
        new MapBuilder<String, Variant>()
        .put("description", Variant.valueOf("a random long value"))
        .put("timestamp", Variant.valueOf(System.currentTimeMillis()))
        .getMap(), AttributeMode.UPDATE);
    }
  }
}

Als Nächstes konfigurieren wir einen Launcher, der den Hive mit einem Protokollexporter startet. Die Exporter liefern alle einen Wrapper namens Application mit, der als Argument den Klassennamen des Hives und eine Konfiguration erwartet. Da wir keine Konfiguration haben, können wir dieses Argument weglassen, dafür setzen wir explizit einen Port. Für das vorliegende Beispiel können wir im Classpath der Einfachheit halber alle Projekte hinzufügen (Abb. 4 und 5).

Abb. 4: Launch-Konfiguration #1

Abb. 4: Launch-Konfiguration #1

Abb. 5: Launch-Konfiguration #2

Abb. 5: Launch-Konfiguration #2

Wenn wir nun den Treiber starten und eine neue Verbindung mit Port 2103 im OSTC einrichten, sollte das Ergebnis in etwa so aussehen wie in Abbildung 6.

Abb. 6: Mit dem OSTC auf den Random Hive zugreifen

Abb. 6: Mit dem OSTC auf den Random Hive zugreifen

In einer echten Treiberimplementierung müsste man natürlich das Kommunikationsprotokoll für das Gerät implementieren und die Tags, Register oder IOs nach entsprechenden Items mappen.

Der Master Server

Bis jetzt ist unser Server ziemlich dumm. Er übersetzt nur ein Protokoll in ein anderes. Jede Anreicherung, sei es zusätzliche Werttransformationen, Monitoring oder Alarmierung, wird durch den Master Server erledigt. Dieser besteht aus einer Reihe von Diensten, die in einem OSGi Container laufen, in unserem Fall Equinox.

Die eigentliche Schwierigkeit, den Master innerhalb der IDE zum Laufen zu bringen, liegt darin, zu definieren, welche Dienste gestartet werden müssen. Wir arbeiten an einer Lösung dies zu vereinfachen, momentan ist es allerdings noch ein manueller Prozess.

Um ein richtiges Projekt anzulegen, das die Konfiguration des Master Servers enthält, müssen wir zuerst das Configuration-Plug-in in Eclipse installieren. Den letzten Integration Build kann man unter [5] finden. Folgende Plug-ins müssen installiert werden:

  • Eclipse SCADA Configuration Tools
  • Eclipse IDE DA Server Starter

Als Nächstes müssen wir ein neues Configuration Project erstellen. Dazu drücken wir CTRL + N, um den „New Wizard“ zu öffnen, und wählen „Eclipse Scada Configuration/Configuration Project“ aus. Danach sollte ein Projekt mit einer Beispielkonfiguration erstellt worden sein. Um zu prüfen, ob alles funktioniert, wie es soll, klicken wir rechts auf „world.escm“ und wählen Eclipse Scada Configuration | Run Generator. Das sollte nach einer Bedenksekunde dazu führen, dass ein output-Verzeichnis mit den generierten Konfigurationsartefakten erzeugt wird.

Unseren vorher erstellten RandomHive wollen wir nun in unsere Masterkonfiguration integrieren. Dazu entfernen wir alles, was wir nicht brauchen. Die Konfiguration ist grundsätzlich in zwei Modelle aufgeteilt: das Komponentenmodell und das Infrastrukturmodell.

Das Komponentenmodell legt fest, wie die Hierarchie der Knoten aussehen soll und wie sie benannt werden. Das Infrastrukturmodell hält fest, auf welchen Systemen welche Dienste laufen sollen.

Vom Infrastrukturmodell entfernen wir nun Knoten node2 und benennen node1 in localhost um. Außerdem entfernen wir den Exec-Treiber und den hdserver. Im Komponentenmodell entfernen wir alle Levelknoten außer REGION1/SITE1. Das Ergebnis sollte jetzt aussehen wie in Abbildung 7 und einen etwas anderen Output erzeugen.

Abb. 7: Minimale Konfiguration

Abb. 7: Minimale Konfiguration

Die generierte Konfiguration enthält unter anderem eine *.profile.xml-Datei, die definiert, welche Bundles im OSGi Container gestartet werden sollen. Momentan nützt uns dies herzlich wenig innerhalb der IDE, da die Dateien für ein Server-Deployment ausgelegt sind.

Für die Zwecke des Artikels haben wir ein kleines Tool geschrieben, das nun einfach eine Launch-Konfiguration aus den Profilen erstellt. Es heißt CreateLaunchConfigs.java, befindet sich in org.eclipse.scada.eclipsemagazin.util und muss manuell gestartet werden, nachdem der Generator ausgeführt wurde. Da das Tool die Abhängigkeiten nicht automatisch auflöst, muss dies vor dem Start manuell in der Launch-Konfiguration gemacht werden, über „Add required Bundles“ (Abb. 8).

Abb. 8: Fehlende Bundles der Launch-Konfiguration hinzufügen

Abb. 8: Fehlende Bundles der Launch-Konfiguration hinzufügen

Wenn wir dies nun ausführen, können wir uns nun mit Username/Passwort (admin/admin12) via OSTC auf Port 2101 mit dem Master Server verbinden. Da wir noch nichts weiter konfiguriert haben, sehen wir nur einige standardmäßig vorhandene Items.

Nach dem Start befindet sich nun auch ein Ordner _config im Workspace. Dort legt der Master Server seine Konfiguration ab. Beim Start kann man eine Konfigurationsdatei angeben, die als initiale Befüllung verwendet werden soll. Dies ist ein einmaliger Prozess für den Fall, dass der Ordner, in den die Konfiguration abgelegt wird, nicht vorhanden ist. Das heißt: Jedes Mal, wenn wir die Konfiguration ändern, müssen wir den _config-Ordner löschen, bevor wir den Master Server neu starten. Alternativ kann man natürlich die Konfiguration auch per OSTC einspielen, der eben beschriebene Weg ist aber schneller.

Nun fügen wir unser Random-Item aus unserem RandomHive hinzu. Dazu erstellen wir als Erstes einen neuen External Driver-Knoten unter dem localhost System Node und setzen die Portnummer auf 2103 (dies ist der Port, den wir vorher im Exporter angegeben haben). Wir müssen auch dem Master mitteilen, dass er eine Verbindung zu diesem herstellen soll. Dazu fügen wir den RandomHive-Treiber der Property Driver im Master-Server-Knoten hinzu.

Im Komponentenmodell fügen wir einen neuen Knoten External Value zu SITE1 hinzu. Dort wählen wir die randomHive @ localhost Connection, fügen „input“ unter „Customization Tags“ hinzu, setzen den Datentyp auf INT64, wählen den Master bei „Master On“, setzen den Namen als MyRandomItem und setzen random als Source Name. Die Modelle sehen nun aus wie in Abbildung 9.

Abb. 9: Modelleigenschaften

Abb. 9: Modelleigenschaften

Nun können wir die Konfiguration erneut generieren. Mit dem Tool erstellen wir eine neue Launch-Konfiguration, lösen die Abhängigkeiten und starten den Master Server, nachdem wir _config gelöscht haben. Das neu konfigurierte Item sollte jetzt unter dem Namen MyRandomItem.V neu erschienen sein und eine ganze Reihe von Attributen mehr besitzen.

Eines der Features, die nun verfügbar sind, ist das Setzen eines manuellen Werts. Hintergrund ist der folgende Use Case: Wenn ein Gerät einen ungültigen Wert liefert, kann dieser übersteuert werden. Wenn z. B. ein Temperatursensor fehlerhafte Werte liefert, kann trotzdem jemand manuell die Temperatur messen und diesen Wert dort eintragen. Falls es Formeln gibt, die auf dem Wert basieren, wie z. B. eine Dichteberechnung, so können diese nun wieder valide Werte liefern, die je nach Anwendungsfall hinreichend genau sein können.

Um dies auszuprobieren, doppelklicken wir auf das Item, wechseln zum Tab „Manual Value“, geben den Wert „0“ ein und bestätigen mit „OK“. Dies sollte zu einer Anzeige wie der in Abbildung 10 führen.

Abb. 10: Manueller Wert

Abb. 10: Manueller Wert

Wie man erkennen kann, ist der Primärwert nun eine 0, auch wenn er nun als manueller Wert gekennzeichnet wird. Alle abhängigen Werte sehen nun genau diesen Wert 0, erben allerdings auch das Flag manual. Die Konvention in Eclipse SCADA ist, manuelle Werte blau zu kennzeichnen.

Fazit

Wir haben gelernt, wie man einen Client schreibt, um an Daten aus Eclipse-SCADA-Systemen zu kommen, haben einen Server unter Verwendung des Java-API erstellt und den Wert wiederum über den Master Server freigegeben. Zur generischen Visualisierung haben wir dabei den OSTC verwendet.

Im nächsten Artikel sehen wir uns an, wie wir ein Modbusgerät einbinden, und werden die Werte in einer minimalen HMI darstellen.

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
4000
  Subscribe  
Benachrichtige mich zu: