40 Jahre SPS
Programmierung auf dem Prüfstand
Die SPS wird 40 Jahre alt. Im Laufe dieser Zeitspanne haben sich die Anforderungen insbesondere an die Software grundlegend geändert und es stellt sich die Frage: Ist die gängige, zyklusorientierte SPS-Programmiersprache noch zeitgemäß oder wäre eine prozessorientierte Hochsprache mit Multitasking die bessere Wahl?
Sprichwörtlich im Kern hat sich die Steuerungstechnik in den zurückliegenden vier Jahrzehnten nur unwesentlich verändert. Auch wenn es teilweise übergeordnete Schichten gibt, die den Schein von Parallelverarbeitung haben, oder in den tieferen Schichten der Echtzeit-Betriebssysteme Multitasking-Mechanismen aktiv sind – das zyklische Abarbeiten der Programme durch die SPS-Betriebssysteme ist immer noch Stand der Technik! Echtes ablauforientiertes und damit prozessnahes Multitasking ist in der Welt der SPS-Steuerungen hingegen selten zu finden. Dabei entsprechen die hinter diesen Prinzipien stehenden Mechanismen viel eher dem natürlichen Ablauf der Prozesse in den heutigen Maschinen und Anlagen, bei denen komplexe Disziplinen wie Regelungen, Achspositionierung, Netzwerk-Handling, Kommunikation und Datenverwaltung mit gemischten Datentypen mittels einer Steuerung zu lösen gilt. Neben den klassischen Maschinenzyklen finden sich hier eine Menge weiterer Prozesse, welche immer parallel und in den meisten Fällen völlig autark ablaufen. Häufig sind die Anlagen vernetzt und an ein SCADA-System angeschlossen, welches die Daten der verschiedenen Steuerungen in den Anlagen verarbeitet.
Angesichts dieser Entwicklung muss es vorrangiges Ziel sein, dass der Anwender für all diese Aufgaben dieselbe Programmiersprache verwenden kann. Gerade für den OEM-Anlagenbauer ist es zudem elementar, dass er kundenspezifische Anpassungen mit geringem Aufwand realisieren kann. Dafür eignet sich eine objektorientierte Vorgehensweise sehr gut – einmal angelegte Objekte lassen sich in weitere Projekte „vererben“ und auch erweitern (siehe Kasten).
Die Programmiersprache JetSym STX bietet eine Reihe hilfreicher Funktionen, wie zum Beispiel das „Try and Catch”œ für die Überwachung mehrerer vorhersehbarer Fehlerzustände.
Ingenieure der heutigen Generation, welche eine klassische Hochsprache wie C++ oder Java beherrschen, legen eine von der Hardware losgelöste Datenstruktur an und sind es nicht gewohnt, dass sie sich um den Aufbau des geräte-internen Speichers kümmern müssen. Auch das Anlegen von Datenstrukturen oder Arrays ist bei Hochsprachen eine Selbstverständlichkeit. Bei den aktuellen komplexeren Automatisierungslösungen ist dies ebenso ein Muss, um vernünftige und gut lesbare Programme erstellen zu können. Gerade für Positionieraufgaben sind indizierte Arrays sehr hilfreich, da mit einem einzigen Move-Befehl ganze Datensätze abgefahren werden können. Mit anderen Worten: Wo bislang lange Variablen-Zuweisungen im Deklarationsteil zu finden waren, sind es beim Arbeiten mit indizierten Arrays gerade mal ein paar Zeilen, um beispielsweise mehrere Produkte oder Werkstückträger zu definieren – egal wie viele Parameter ein Produkt bestimmt.
Wenn wir schon beim Beispiel Produkt sind: Es ist außerdem sinnvoll, eine Struktur in ein Array einzubinden, in der neben Zahlenvariablen wie Integer, Float, Real oder Bool auch Strings (Zeichenketten) zur Beschreibung des Produktnamens Verwendung finden. Doch damit nicht genug: Was nützt die beste Programmiersprache, wenn das Debugging sich schwierig bis unmöglich gestaltet? Ergo sollte ein modernes Tool diesbezüglich mit Breakpoints, Trace-Funktionen und Variablen-Setup dem Programmierer die benötigten Hilfsfunktionen bieten.
Prozessorientiert und parallel
Ein Beispiel für ein solches „echtes“ Hochsprachen-Programmiertool ist Jet-Sym STX von Jetter. Die Abkürzung ST im Namenszusatz steht dabei für die Verwendung von Strukturiertem Text gemäß der IEC-Norm 61131-3, das X steht für „Extended“ beziehungsweise einen erweiterten Befehlsumfang, der dem Anwender hilft, komplexe Aufgaben zu lösen. Hierzu zählen beispielsweise die Nutzung der IT-Funktionalitäten wie Netzwerk-Befehle, Datei-Operationen und E-Mails versenden, Zeichenkettenverarbeitung, Schleifenbefehle wie For, While und Repeat sowie Befehle für Motion.
Zentrale Eigenschaft dieser Programmiersprache ist, dass sie von Grund auf als Multitasking ausgelegt ist. Das bedeutet: Programme werden nicht zyklisch abgearbeitet, sondern ereignis- beziehungsweise prozessorientiert. Das zugrundeliegende Multitasking-Betriebssystem verwaltet bis zu 100 Task, welche in sich selber völlig autark sind. Dies entspricht dem natürlichen Abbild der verschiedenen Prozesse einer Anlage. Jedem dieser Task lässt sich eine eigene Priorität zuordnen. Es können aber auch alle 100 Tasks dieselbe Priorität besitzen.
Mittels objektorientierter Programmierung und deren Eigenschaften, wie etwa der Vererbung, lassen sich Anwendungen in der Automatisierung effizienter programmieren und Programme besser pflegen.
© objektorientierte ProgrammierungIn Summe bietet das System sieben verschiedene Prioritätsklassen. Jede dieser Klassen definiert zwischen einem und 100 Tasks, welchen wiederum vom Scheduler jeweils eine Zeitscheibe zugeordnet wird. Das Besondere daran ist, dass diese Zeitscheibe in mehrere kleinere Zeitscheiben aufgespaltet wird, da typischerweise eine höhere Priorität eine kürzere Reaktionszeit erfordert. Der Anwender kann von den 100 Tasks bis zu 32 zyklische Tasks mit einer fixen Abtastzeit definieren. Diese Abtastzeit ist zur Laufzeit des Programmablaufs veränderbar. Zyklische Tasks werden mit dem Prozess-Abbild der IO-Daten synchronisiert. Im Ergebnis führt dies zu kürzeren Reaktionszeiten und mehr Performance für die jeweilige Anwendung.
Ein wichtiger Aspekt bei einem modernen Programmiertool ist darüber hinaus die Möglichkeit, einen partiellen Programm-Download vorzunehmen. Dies hört sich zunächst nach einem „alten Hut“ an, da dieses Feature in der SPS-Welt schon länger bekannt ist. Nur: In einem Multitasking-System ist das partielle Downloaden von Programmteilen deutlich komplizierter als bei einem zyklisch abarbeitenden Programm. Der Grund: Bei zyklischer Programm-Abarbeitung lässt sich der auszuwechselnde Programmteil in der Zeit zwischen der letzten Abarbeitung und dem nächsten Zyklus austauschen. In der Praxis wird der alte Programmteil deaktiviert und der neue eingeschoben. Da bei einem Mulitasking-Betriebssystem häufigere Task-Wechsel vorkommen, kann nicht so einfach ein gesamter Task stillgelegt werden, bis der neue Programmabschnitt eingeschoben wurde.
Die Möglichkeiten der Strukturierung
Wenn der Prozess sequenziell abläuft, soll er auch sequenziell programmiert werden. Dazu steht in JetSym STX der Befehl When <Bedingung> continue <Anweisung> zur Verfügung. Kommt dieser Befehl zur Anwendung, wartet der entsprechende Task an der Stelle, bis die Bedingung erfüllt ist. Die anderen Tasks werden davon nicht beeinflusst und gemäß dem Scheduler abgearbeitet. Eine erweiterte Form des When-Befehls ist die When_Max-Abfrage. Sie erlaubt eine zur Bedingung zusätzliche Time-out-Überwachung. Typisches Anwendungsbeispiel ist die Bewegung eines Zylinders: Wird die Endlage in einer definierten Zeit erreicht, läuft der Task normal weiter; bei Überschreitung der Time-out-Zeit kommt es zur Ausführung einer Fehlerbehandlung. Sollen Fall-Unterscheidungen getroffen werden, stehen einige Erweiterungen des klassischen If-Befehls zur Verfügung, wie etwa:
- If <Bedingung> Exit;
- If <Bedingung> Return;
- If <Bedingung> Call <Index> [Of <Unterprogr. 0>, <Unterprogr. 1>, ... , <Unterprogr. n>];
- If <Bedingung> Task_Exit;
- If <Bedingung> Task_Break;
Neben den klassischen Schleifenbefehlen wie For, While und Repeat enthält Jet-Sym STX diverse nützliche Befehlsketten – zum Beispiel When <Bedingung> Else_when <Bedingung> Else_time. Ist keine der Bedingungen erfüllt, wird der ausführende Task zunächst verlassen. Ist eine Bedingung erfüllt, kommt es zur Ausführung der zugehörigen Anweisungen und das Programm springt zum nächsten Befehl nach der End_When-Anweisung. Für Fälle, in denen die Bedingungen nie erfüllt sein könnten, besteht die Möglichkeit, einen Time-out-Zweig anzugeben. Die Anweisungen nach Else_Time werden nur einmal ausgeführt, wenn innerhalb der angegebenen Zeit keine der anderen Bedingungen erfüllt war.
Stringvariablen und Daten-Arrays werden gemischt in ein File geschrieben. Das File wird anschließend als E-Mail versendet.
Ein weiterer, für die Automatisierung nützlicher Befehl ist die Try/catch-Anweisung für Ausnahmebehandlungen. Was hat es damit auf sich? Beim Programmieren gibt es häufig Situationen, in denen mehrere, zumeist vorhersehbare Fehler eintreten können. Ein Beispiel hierfür wäre ein Bohrvorgang, bei dem die Kühlflüssigkeitszufuhr unterbrochen wird, der Bohrer bricht oder es zu einer Überschreitung der zulässigen Temperatur kommt. Eine elegante Lösung besteht deshalb im Einsatz von geschützten Anweisungen beziehungsweise geschützten Blöcken. Dabei werden Anweisungsblöcke versuchsweise ausgeführt. Sind keine Fehler aufgetreten, war der Versuch erfolgreich. Tritt jedoch ein Fehler auf, wird der Block sofort verlassen und eine vordefinierte Fehlerbehandlung ausgeführt.
Dieses Vorgehen führt zu einer logischen Trennung der auszuführenden Anweisungen von der Fehlerprüfung und deren Behandlung und damit letztlich zu klarer strukturierten und nicht zuletzt fehlertoleranteren Programmen. Programme, die auf diese Weise erstellt wurden, lassen sich zudem einfach debuggen, da sie dem Prozess folgen. Im Klartext: Der Task wartet an der Stelle, an der ein When-Befehl erscheint, bis die folgende Bedingung erfüllt ist.
Relevant im Zusammenhang mit der Steuerungsprogrammierung ist schließlich die Deklaration von Variablen. Bei JetSym STX ist es nicht notwendig, die Variable einer Hardware-Adresse zuzuweisen. Der Programmierer braucht sich demzufolge nicht auszurechnen, wie viel Platz seine Float-Variable benötigt und welche Adresse die nächste ist. Eine Variable lässt sich dabei sowohl lokal als auch global anlegen. Lokal bedeutet, dass die Variable nur in dem Task gültig ist, in dem sie angelegt wurde. Globale Daten sind in allen Tasks, Unterprogrammen und Funktionen anwendbar. Neben einer einfachen Zuweisung mit einer anderen Variablen oder Konstanten, sind folgende mathematische Operationen ausführbar:
- die vier Grundoperationen,
- Vergleichsoperationen,
- trigonometrische Funktionen
- sowie weitere mathematische Funktionen wie logarithmische Funktionen, Exponential-Funktionen oder Quadratwurzel.
Darüber hinaus verfügt STX über eine Reihe von Funktionen zur komfortablen Verarbeitung von Strings, zum Beispiel für das Filtern eines definierten Zeichens aus einer größeren Zeichenkette.
Datenhandling und -verwaltung
Um das immer höhere Datenaufkommen innerhalb der Anlagen zu bewältigen, sind spezielle Datenverwaltungsbefehle und Datei-Operationen vonnöten. Bei JetSym STX gibt es diesbezüglich neben den klassischen, internen Lösch- und Kopierbefehlen spezielle Anweisungen, um die Daten in einer Datei abzuspeichern oder aus einer Datei zu lesen. Eine weitere Möglichkeit sind Zeichenketten-Operationen. Zeichenketten lassen sich im Programm mit anderen Zeichenketten oder Variablen beliebig zusammensetzen. Außerdem ist es machbar, Zahlen-Datentypen in ASCII-Zeichenketten zu wandeln, welche dann zum Beispiel über eine Schnittstelle ausgegeben oder in eine Datei gespeichert werden.
Jetter-Steuerungen der neuen Generation enthalten ein eigenes Dateisystem, auf welches der Anwender über das Programmiertool entweder direkt zugreifen kann, oder aber von einem übergeordneten System aus via FTP-Protokoll und Ethernet-TCP/IP. Hierzu existieren eine Reihe Verzeichnis- und Datei-Operationen wie zum Beispiel:
- FileOpen – Öffnen einer Datei;
- FileExist – Prüfung auf Existenz einer Datei;
- FileRemove – Löschen einer Datei;
- FileGetCharacter – Lesen eines Zeichens aus einer Datei;
- FilePutString – Schreiben einer Zeichenkette in eine Datei;
- FileGetSize – Bestimmung der Größe einer Datei;
- DirChange – Wechsel in ein anderes Verzeichnis;
- DirRename – Umbenennung eines Verzeichnisses.
In Summe lässt sich demnach festhalten: Durch die Verwendung objektbeziehungsweise hochsprachenorientierter Programmiersprachen werden die Programme sehr kompakt und damit lesbarer. Entsprechende Tools entwickeln sich ständig weiter, vor allem im Hinblick auf die automatische Generierung von Variablen-Strukturen aufgrund der verwendeten Hardware. Im Fall von Jetsym STX wird in Zukunft zum Beispiel ein im Hardwaremanager angelegtes Eingangsmodul automatisch die benötigten Variablen zur Verwendung generieren. Auch das führt zu einer weiteren Einsparung von Zeit, was letztlich nichts anderes als Kostenreduktion bedeutet.
Autoren
Dr. Uwe Altenburg beschäftigt sich mit Software-Entwicklung im Bereich Compilerbau bei Jetter.
Tobias Föll ist Entwicklungsleiter Steuerungen bei Jetter.
Andreas Leu ist tätig im Bereich Technisches Marketing und Seminare bei
Jetter in Ludwigsburg.
Objektorientiertes Programmieren – was ist das?
Ein Objekt im Sinne der objektorientierten Programmierung ist die Verbindung oder auch Kapselung einer Datenstruktur mit den Funktionen oder Unterprogrammen, welche auf dieser Datenstruktur operieren; und zwar mit dem Ziel einer besseren Wiederverwendbarkeit. Das Objekt selber ist dabei nur die Beschreibung eines strukturierten Datentyps. Im Programm verwendet man dagegen Instanzen (konkrete Variablen) eines Objekttyps. Die Elemente eines Objekts werden als Eigenschaften bezeichnet – die Summe aller Eigenschaften beschreibt dann den aktuellen Zustand einer Instanz.
Die Funktionen oder Unterprogramme eines Objekts heißen Methoden. Die namentliche Unterscheidung erklärt sich daher, dass Methoden implizit Zugriff auf alle Eigenschaften ihres Objekts haben. Methoden sind also Funktionen, die einen „unsichtbaren“ Parameter – die aktuelle Instanz – übergeben bekommen. Am ehesten lässt sich ein Objekt mit einem Funktionsblock aus der klassischen SPS-Programmierung (IEC61131-3) vergleichen. Während ein Funktionsblock jedoch nur eine Funktion besitzt, die den Zustand des Funktionsblocks beeinflusst, kann ein Objekt beliebig viele Funktionen beinhalten. Darüber hinaus können Objekte auch Eigenschaften und Methoden anderer Objekte übernehmen und diese erweitern. Dieser Vorgang nennt sich Vererbung. Gerade diese Fähigkeit – ein Grundprinzip der objektorientierten Programmierung – ist für die Automatisierungstechnik hochinteressant. Einzelne Teile einer Anlage, wie etwa eine Achse, ein Drehtisch oder ein Ventil sind damit als Software-Objekte im Steuerungsprogramm 1:1 abbildbar.













