Objektorientierung mit Siemens TIA
Für den Modultest von Siemens S7-1200/S7-1500 Steuerungen hat die KMU Automation GmbH eine objektorientierte Baustein-Bibliothek entwickelt, welche testgetriebene Entwicklung auf der SPS ermöglicht.
In Prozess- und Anlagesteuerungen sind speicherprogrammierbare Steuerungen (SPS) von Siemens weit verbreitet. Die Programmierung erfolgt in einer der vier Programmiersprachen KOP, FUP, SCL oder GRAPH. Siemens hält sich dabei mehr oder weniger an den Standard IEC 61131-3 (kurz IEC 1131) aus dem Jahre 2003. Durch die Normierung ist der Austausch von Programmen zwischen Steuerungen verschiedener Hersteller theoretisch möglich. Allerdings zementierte man mit der Standardisierung Technologie aus den Anfängen der Computer-Technik.
Der sogenannte Kontaktplan KOP ist einer Relais-Steuerung nachempfunden. Solche Maschinen wurden Ende des zweiten Weltkrieges entwickelt. FUP (Funktionsplan) sieht zwar etwas moderner aus, ist aber logisch äquivalent zu KOP. Für grössere Programme verwenden die meisten Strukturierten Text (SCL). Dies ist eine Pascal-ähnliche Programmiersprache, etwa auf dem Stand von Modula-2 (1978).
Objektorientierung
Seit den 70er Jahren hat die Informatik eine gewaltige Entwicklung durchgemacht. Für die immer grösseren und komplexeren Systeme benötigte man skalierbare Konzepte. Eine der fruchtbarsten Ideen ist die Objektorientierung (kurz OO). Die Idee ist einfach: Was in der Realität ein eigenständiges Objekt ist, wird auch in der Software als Objekt abgebildet. Jedes Objekt hat eine Aufgabe, welche es selbständig erledigt. Kann es die Aufgabe nicht alleine bewältigen, schickt es Nachrichten an andere Objekte, damit diese einen Teil der Aufgabe übernehmen. Durch die Trennung von interner Objekt-Struktur und öffentlicher Nachrichten-Schnittstelle erreicht man eine maximale Entkopplung der Teilsysteme und damit Änderbarkeit und Erweiterbarkeit der gesamten Software.
Die interne Objekt-Struktur wird in einer sogenannten Klasse definiert. Die öffentliche Nachrichten-Schnittstelle nennt man englisch Interface. Für den Benutzer des Interface ist die interne Struktur völlig egal: Hauptsache, das Objekt reagiert wie abgemacht auf die Nachricht. Dasselbe Interface kann deshalb von ganz unterschiedlichen Klassen realisiert werden (Polymorphie). Es ist sogar möglich, die Benutzung einer Schnittstelle zu programmieren, bevor die realisierende Klasse überhaupt entsteht (späte Bindung, englisch: late binding).
Grafisch stellt man das in der Unified Modeling Language UML wie in Abbildung 4 dar: Ein Anwender User
benutzt das Interface Machine
. Ob sich dahinter ein Objekt vom MachineTypeA
oder B versteckt, ist ihm egal. Er will die Maschine einfach ein- und ausschalten können. Maschinen-Objekte der Klasse MachineTypeA
verwalten ihren Zustand im Attribut isRunning
. Maschinen-Objekte der Klasse MachineTypeB
delegieren die Aufgabe dagegen an zwei Motor
en, welche die Maschine antreiben.
Interfaces und Klassen werden durch Rechtecke dargestellt, welche in drei Bereiche aufgeteilt sind: Oben steht der Name, in der Mitte die Attribute und unten die sogenannten Methoden. Diese entsprechen den Nachrichten, welche an das Objekt gesendet werden können. Die Werte der Attribute und die aktuellen Beziehungen zu anderen Objekten definieren den Zustand eines Objekts, die Methoden legen sein Verhalten fest.
In einem objektorientierten Design ist es einfach, die Implementation zu ändern oder gar neue Klassen hinzuzufügen. In Abbildung 5 wurde die Anzahl Motoren von MachineTypeB
auf drei geändert und ein neuer MachineTypeC
hinzugefügt. All dies ist für den User
völlig transparent, d.h. bedarf keiner Änderungen auf Seiten des Anwenders.
Basierend auf der Objektorientierung wurde ein ganzer Baukasten von Entwurfsmustern [1] und Software-Architekturen [2] entworfen. Junge Ingenieure bringen dieses Knowhow heute bereits ab Studium mit. Man kann ohne Übertreibung sagen, dass die Objektorientierung zu einem Kultursprung in der Software-Entwicklung geführt hat. Keiner will freiwillig zurück in die Steinzeit.
Umsetzung mit Siemens TIA
Sie werden sich zurecht fragen, weshalb die Objektorientierung nicht längst in die Norm IEC 1131 Einzug gehalten hat. Nun das hat sie: In der Version von 2013 sind objektorientierte Ansätze enthalten. Beckhoff und Sigmatek haben dies teilweise umgesetzt. Von Siemens gibt es offiziell noch nicht einmal einen Umsetzungsplan. Die Frage sei erlaubt, wie lange sich die Kundschaft noch veraltete Technologie verkaufen lässt.
Nun, in der Zwischenzeit müssen wir uns mit dem begnügen, was uns Siemens' Totally Integrated Automation (TIA) bietet:
- Globale Funktionen FC
- Funktionsblöcke FB (diese entsprechen Klassen mit einer einzigen Methode, welche gleich heisst wie die Klasse)
- Benutzerdefinierte Datentypen UDT (benannte Strukturen)
- keine Namensräume
- kein Zugriff auf Konstanten von FB und FC
Öffentliche Methoden
Zum Glück kennt TIA das Konzept der Parameter-Instanz. Das heisst, ich kann einen Funktionsbaustein FB oder einen benutzerdefinierten Datentyp UDT als Parameter einer Funktion FC übergeben. Damit kann ich zu einem Objekt beliebig viele Methoden bereitstellen.
Private Methoden
Wiederkehrende Aufgaben innerhalb einer Klasse extrahiert man normalerweise in private Methoden. Da man in der aktuellen Version TIA 15.1 aber keinen Zugriff auf den eigenen Funktionsblock hat (es gibt keinen this
-Pointer), kann man ihn nicht als Parameter-Instanz an die private Methode übergeben. Eine Möglichkeit dies zu umgehen, besteht in der Auslagerung aller Member-Variablen in einen benutzerdefinierten Datentyp UDT MotorMem
.
Im Prinzip könnte man ganz auf den Funktionsbaustein FB Motor
verzichten und nur mit Funktionen FC und benutzerdefinierten Datentypen UDT arbeiten. Damit würde der Unterschied zwischen öffentlichen und privaten Methoden hinfällig. Da Siemens S7-Programmierer gemäss Programmierstyleguide [3] nicht auf Instanzen von Funktionsbaustein zugreifen sollten, bietet die präsentierte Lösung aber immerhin einen psychologischen Schutz der privaten Member-Variablen.
Export von Konstanten
Wie oben erwähnt, gibt es in der aktuellen Version von TIA keine Möglichkeit auf Konstanten eines Funktionsblock FB zuzugreifen. Abgesehen von globalen Konstanten (wovon im Siemens Styleguide [3] explizit abgeraten wird) bleibt einem nur die Kopie in eine gleichnamige Variable.
Interfaces
Implementiert eine Klasse MachineTypeA
ein Interface Machine
, so sagt man in der Objektorientierung: «MachineTypeA ist eine Machine.» Da Siemens TIA keine Vererbung oder sonstige ist-ein-Beziehung bereitstellt, verwenden wir einen benutzerdefinierten Datentyp UDT als Parameter-Instanz.
Das Interface Machine
wird in einem Datenbaustein MachineDB
instanziiert, damit es für einen Benutzer UserX
direkt zugreifbar ist. Alternativ kann es einem Benutzer UserY
auch als Parameter-Instanz übergeben werden. Der Eigentümer Owner
instanziiert ein oder mehrere Objekte von MachineTypeA
als Multi-Instanz und übergibt das Interface Machine
der Instanz beim Aufruf.
Man beachte, dass die beiden Methoden run()
und halt()
asynchron aufgerufen werden. Um dies zu verdeutlichen, wird das execute-done-Muster gemäss PLCopen Compliant Library Guideline [4] mit execute
, done
, aborted
und error
Flags implementiert. Die Gruppierung in einer (eigentlich namenlosen) Struktur MachineCmd
soll zeigen, dass es sich um die Implementation einer einzigen Methode handelt. In dieser Struktur könnten natürlich auch Methoden-Parameter übergeben werden. Wer welches Flag schreiben darf, lässt sich leider nicht programmatisch abbilden und muss als Konvention festgelegt werden.
Modultest-Framework
In den Mickey Mouse Beispielen von oben sieht das Ganze ziemlich überrissen aus. Den praktischen Nutzen erkennt man erst in einer echten Anwendung. Um Test Driven Development (TDD) auch auf Siemens Steuerungen zu ermöglichen, haben wir eine ans JUnit-Framework angelehnte Test-Bibliothek LibTest entwickelt.
Analyse-Modell
Um die Klasse MyClass
zu testen, wird eine Testklasse MyClassTest
angelegt. Diese benützt eine oder mehrere Test-Methoden, um das tatsächliche Verhalten von MyClass
mit der Spezifikation zu vergleichen. Genauer: Im Test Driven Development ist der Test ein Teil der Spezifikation!
Die Methode testComplexBehaviour
ist als Funktions-Objekt modelliert, weil das Verhalten des getesteten Objekts in einem zyklischen System normalerweise nicht in einem Schritt überprüft werden kann. Alle Testfälle MyClass_testXY
implementieren das Interface TestCase
.
Da alle Tests nach demselben Muster ablaufen, ist der Grossteil der Arbeit in die Klasse TestBase
ausgelagert. Ein konkreter Test MyClassTest
kann aber die Methoden beforeAll()
, beforeEach()
, afterEach()
und afterAll()
überschreiben, um die Test-Umgebung vorzubereiten respektive wieder aufzuräumen. Gegen aussen ist nur das Interface Test
sichtbar.
Test-Suite und Logging wurden der Übersichtlichkeit halber weggelassen.
Design-Modell
In der Implementation sind alle oben erklärten Konzepte umgesetzt worden. Das leicht vereinfachte Design-Modell zeigt die gewählte Lösung.
Fazit
Objektorientierte Analyse und Design sind aus der Software-Entwicklung nicht mehr wegzudenken, zu gross sind die Vorteile. Die objektbasierte Umsetzung kann mit Siemens TIA auch ohne hässliche Konstrukte wie Variants gelingen, wenn man sich auf die wesentlichen Konzepte der Objektorientierung beschränkt. Sobald Siemens die fehlenden Sprachkonstrukte (Namensräume, öffentliche Konstanten, Klassen und Methoden, Vererbung) nachliefert, kann die hier vorgestellte Lösung relativ schmerzlos angepasst werden, weil das Analyse-Modell unverändert bleibt.
Wenn Sie einen eingefleischten SPS-Programmierer zum Thema Objektorientierung befragen, wird er vermutlich immer noch sagen: «Kennen wir nicht, haben wir nicht, brauchen wir nicht.» Wir müssen uns aber bewusst sein, dass dies eine aussterbende Spezies ist. Sie sind gefragt für die Wartung bestehender Systeme, aber die jungen Leute scharen sich an Automatisierungsmessen um innovative Firmen, welche den Mut weg von klassischer SPS-Programmierung in Richtung Objektorientierung haben.
Literatur
[1] | Erich Gamma et al. Entwurfsmuster. Addison-Wesley, 1996. |
[2] | Frank Buschmann et al. Patternorientierte Softwarearchitektur. Addison-Wesley, 1998. |
[3] | Siemens. Programmierstyleguide für S7-1200/S7-1500. Version 1.2, 2016. Siemens Beitrags-ID: 81318674. |
[4] | PLCopen. Creating PLCopen Compliant Libraries. Version 1.0, 2017. PLCopen Software Construction Guidelines. |