Die Echtzeit Programmiersprache PEARL
Der Name PEARL steht für Process and Experiment Automation Realtime Language und darf nicht mit Perl, der Practical Extraction and Report Language, verwechselt werden.
PEARL ist eine höhere Programmiersprache, die eine komfortable, sichere und weitgehend rechnerunabhängige Programmierung von Multitasking- und Echtzeit-Aufgaben erlaubt, und wurde seit 1977 in verschiedenen Entwicklungsstufen genormt, zuletzt 1998 als PEARL-90 (DIN 66253-2 1998, Berlin, Beuth-Verlag, 1998).
Wichtiger Grundsatz bei der Entwicklung von PEARL war, neben einer möglichst leichten Abbildbarkeit der prozeßrechentechnischen Probleme, die einfache Erlernbarkeit für den Programmierer. Jeder, der schon eine prozedurale Programmiersprache kennt, wird sich in sehr kurzer Zeit mit PEARL anfreunden können.
Alle grundlegenden Datentypen und Sprachstrukturen anderer prozeduraler Programmiersprachen sind in PEARL vorhanden. PEARL bietet darüber hinaus komfortable Sprachelemente zur Bearbeitung von Multitasking- und Echtzeitaufgaben.
Grundlegende Datentypen und Sprachstrukturen
Datentypen:
- Grunddatentypen
- Festkomma, Gleitkomma
- Zeichen, Zeichenketten
- Bitvariablen, Bitketten
- Zusammengesetzte Datentypen
- Strukturen
- Felder beliebiger Dimension mit vorgebbaren Unter- und Obergrenzen bei den einzelnen Dimensionen.
- Funktionen, Prozeduren (Parameterübergabe per Value, per IDENT und als Zeiger)
- typbehaftete Zeiger auf alle Objekte, auch auf Funktionen und Prozeduren
- Typfreie Zeiger und Type-Casting
Blockstruktur, Gültigkeit von Objekten:
- Objektdeklarationen innerhalb eines
BEGIN
-END
-Blockes - Prozedur- und funktionsweite Objekte
- Modulweite Objekte
- Zugriff auf Objekte anderer Module durch globale Spezifikation von Daten
- Bekanntmachung von Objekten für andere Module durch globale Deklaration
Kontrollstrukturen:
- Bedingte Anweisungen
IF-THEN-ELSE-FIN
CASE-ALT-...-ALT-OUT-FIN
- Wiederholungen
FOR-REPEAT-END
WHILE-REPEAT-END
Genaue Informationen über den Aufbau von PEARL sind in
- DIN 66253-2 1998, Berlin, Beuth-Verlag, 1998
- PEARL 90 - Sprachreport, Version 2.2., GI-Fachgruppe 4.4.2, Bonn, GI Gesellschaft für Informatik e.V., 1998
enthalten
Bessere Hardwareunabhängigkeit
Um eine Entkopplung von hardwareabhängigen Komponenten, wie z.B. Ein-/ und Ausgabeschnittstellen, zu dem hardwareunabhängigen Programm zu erreichen, wird ein PEARL-Modul in zwei Sektionen unterteilt:
- Im sogenannten Systemteil, der ab dem Schlüsselwort
SYSTEM
beginnt, werden die Namen für die hardwareabhängigen I/O-Schnittstellen bekannt gemacht, und ihre Eigenschaften definiert. Auch Interruptquellen sind hier zu definieren. - Der sogenannte Problemteil, welcher mit
PROBLEM
beginnt, enthält beispielsweise Variablen, Konstanten, Tasks und Prozeduren. Tasks und Prozeduren können auf die im SYSTEM-Teil definierten Schnittstellen zugreifen.
Multitasking-Anweisungen
Dem Betriebssystem bekannte Tasks können aus einem PEARL-Programm beliebig in ihrem Zustand verändert werden.
ACTIVATE
Taskname
Startet die Task mit dem Namen Taskname sofort.TERMINATE
Taskname
Die Task mit dem Namen Taskname wird abgebrochen.SUSPEND
Taskname
Die Task mit dem Namen Taskname wird angehalten.CONTINUE
Taskname
Die angehaltene Task mit dem Namen Taskname wird fortgesetzt.PREVENT
Taskname
Die Task mit dem Namen Taskname, deren Aktivierung mit einem Ereignis gekoppelt wurde, wird wieder ausgeplant. Sie wird also nicht mehr von diesem Ereignis aktiviert bzw. fortgesetzt.
Einplanung auf Ereignisse und Zeitpunkte
Die Aktivierung und die Fortsetzung von Tasks läßt sich auch an externe Ereignisse oder Zeitpunkte koppeln. Einplanungen zu einer festen Uhrzeit, aber auch Einplanungen beim Auftreten äußerer Ereignisse (Interrupts), sind möglich.
Beispiele:
ALL 0.00005 SEC ACTIVATE Highspeedregler;
Zyklische Aktivierung eines Reglers mit einer Frequenz von 20 kHzAT 12:00 ALL 4 SEC UNTIL 12:30 ACTIVATE Mittagspause PRIO 1;
Zyklische Einplanung, alle 4 Sekunden zwischen 12:00 Uhr und 12:30 mit einer hohen Priorität-
WHEN Feuer ACTIVATE Loesch;
Aktivierung der Task Loesch, wenn Interrupt Feuer eintrifft.
Tasksynchronisation
Eine Synchronisation von Tasks ist immer dann notwendig, wenn diese Daten gemeinsam nutzen, da sonst Inkonsistenzen auftreten können.
Die drei folgenden Beispiele zeigen, wie sich mit Hilfe von Semaphor- und Boltvariablen Tasks synchronisieren lassen.
Beabsichtigt eine Task A den Zugriff auf einen Datensatz, der gerade von einer Task B bearbeitet wird, so blockiert das Betriebssystem
die Task A solange, bis B die Daten freigibt. Bei Semaphoren erlaubt PEARL an Stelle einer Blockade auch das Testen mit Eintritt bei Freiheit
(Schlüsselwort TRY
im Beispiel).
PROBLEM;
DCL A CHAR(255); ! String: 255 Bytes
DCL SEMVAR SEMA PRESET(1); ! Init: Zugriff erlaubt
DCL FAILED FIXED INIT(0);
T1: TASK;
DCL B1 CHAR(255);
REQUEST SEMVAR; A=B1; RELEASE SEMVAR;
END;
T2: TASK;
DCL B2 CHAR(255);
REQUEST SEMVAR; A=B2; RELEASE SEMVAR;
END;
TEST: TASK;
IF TRY SEMVAR THEN
RELEASE SEMVAR; !Semaphor wieder freigeben
ELSE
FAILED=FAILED+1;
FIN;
! Selbstaktivierung nach 10 Sekunden
AFTER 10 SEC ACTIVATE TEST;
END;
Producer-Consumer-Schemata
Produziert eine Task A Daten, die zur Weiterverarbeitung durch eine oder mehrere andere Tasks vorgesehen sind, so spricht man von einem Producer-Consumer-Schema. Hier ein Beispiel eines Ringspeichers mit 1024 Zeichen, von denen die lesende Task immer 2 gleichzeitig braucht.
PROBLEM;
DCL PLATZDA SEMA PRESET(1024); ! Init: 1024 frei
DCL ZEICHENDA SEMA PRESET(0); ! Init: Am Anfang leer
HOLDATEN: TASK;
REQUEST ZEICHENDA; REQUEST ZEICHENDA;
HOLE_ZWEI_ZEICHEN_AUS_DEM_RINGPUFFER;
RELEASE PLATZDA; RELEASE PLATZDA;
END;
SCHREIBDATEN: TASK;
REQUEST PLATZDA;
SCHREIB_EIN_ZEICHEN_IN_RINGPUFFER;
RELEASE ZEICHENDA;
END;
Producer-Consumer-Schemata entstehen auch bei der Meßwerterfassung und der Automatisierung von Fertigungsprozessen.
Datenbanken
Möchte der Programmierer erlauben, daß mehrere Tasks gleichzeitig einen Datensatz lesen dürfen (im Beispiel die Tasks
LESER1
und LESER2
), aber jeweils nur eine schreiben kann, und dies auch nur, falls keine andere
liest, sind BOLT-Variablen das Mittel der Wahl.
PROBLEM;
DCL A CHAR(255); ! String: 255 Bytes
DCL BOLTVAR BOLT; ! Default: frei
LESER1: TASK;
DCL L1 CHAR(255);
! ENTER erlaubt, dass alle weiteren Tasks mit ENTER
! ebenfalls weiterlaufen koennen
ENTER BOLTVAR; L1=A; LEAVE BOLTVAR;
END;
LESER2: TASK;
DCL L2 CHAR(255);
ENTER BOLTVAR; L2=A; LEAVE BOLTVAR;
END;
SCHREIBER1: TASK;
DCL S1 CHAR(255);
! Nach einem RESERVE laeuft eine Task nur weiter,
! wenn kein anderer Leser oder Schreiber einen
! ENTER/RESERVE durchgefuehrt hat.
RESERVE BOLTVAR; A=S1; FREE BOLTVAR;
END;
SCHREIBER2: TASK;
DCL S2 CHAR(255);
RESERVE BOLTVAR; A=S2; FREE BOLTVAR;
END;
Ein- und Ausgabe
PEARL sieht für die verschiedenen Ein- und Ausgabeformen jeweils eigene Schlüsselworte vor. In den folgenden
Beispielen steht eine alphic_dation
z.B. für eine Festplatte, Terminal, LCD-Display und serielle oder
parallele Schnittstelle, eine basic_dation
z.B. für eine analoge oder digitale Prozeß-Ein-/Ausgabe.
- Ausgabe
- Formatierte Ausgabe
PUT objekt1, ... TO alphic_dation BY formatliste;
- Binäre Ausgabe
WRITE objekt1, ... TO alphic_dation;
- Ausgabe an Prozeßperipherie
SEND objekt1, ... TO basic_dation;
- Formatierte Ausgabe
- Eingabe
- Formatiertes Einlesen
GET objekt1, ... FROM alphic_dation BY formatliste;
- Binäres Einlesen
READ objekt1, ... FROM alphic_dation;
- Einlesen aus der Prozeßperipherie
TAKE objekt1, ... FROM basic_dation;
- Formatiertes Einlesen
Spezielle Datentypen
CLOCK, DURATION
Diese beiden Datentypen beschreiben Zeitpunkte und -räume. Bei den zeitlichen Einplanungen treten diese Datentypen ebenfalls auf. In den angegebenen Beispielen ist12:00
eine Konstante vom TypCLOCK
und0.00005 SEC
eine Konstante vom TypDURATION
. Auch Berechnungen zwischen diesen Datentypen sind möglich, wie die beiden folgenden Beispiele zeigen.durationvar = clockvar - clockvar
clockvar = clockvar + durationvar
INTERRUPT
Diese Datentypen müssen im Systemteil eines Modules mit einem Hardware- Interrupteingang verbunden werden. Im Problemteil können sie dann mit den oben beschriebenen Mitteln (WHEN ..
) zum Ändern eines Taskzustandes verwendet werden.SEMA
Semaphor-Variablen dienen zur Synchronisation zwischen verschiedenen Tasks. Hauptsächlich dienen sie zum Schutz von Verbunddatenobjekten, die von mehreren Tasks gleichzeitig genutzt werden. Sie werden bei kritischen Pfaden oder Producer-Consumer-Schemata benutzt. Eine Semaphor-Nachbildung läßt sich ohne Betriebssystem-Unterstützung nicht erreichen, da die gewöhnlichen Hochsprach-Operationen belegen, ggf. blockieren bzw. freigeben oder blockierte Task fortsetzen in einem Multitasking-System nicht unteilbar sind.BOLT
Boltvariablen erlauben im Gegensatz zu Semaphoren einen gleichzeitigen Lesezugriff auf Daten von mehreren Tasks und blockieren schreibende Tasks, wenn eine andere einen Lese- oder Schreibzugriff begonnen hat. Bezüglich der Nachbildung von diesen Variablen ohne Betriebssystemunterstützung gelten die Ausführung zu Semaphoren entsprechend.