Perle der Mur

Im Flusstal der Mur in Österreich beweist Perl seine Tauglichkeit unter Industriebedingungen. Dabei spielt es nicht nur seine Stärken bei der String-Verarbeitung aus, sondern realisiert unter Windows ein komfortables Tool zur Konfiguration von Unix-Prozessen.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 13 Min.
Von
  • László Kovács
  • Wolfgang Wagner
Inhaltsverzeichnis

So manches spielt sich im grauen EDV-Alltag anders ab, als man es aus den Lehrbüchern kennt: Unter Zeitdruck entstehen Lösungen, die sich hinterher als nicht sonderlich elegant und flexibel erweisen. Gemäß der Devise ‘Wiederverwendung’ werden einfach Quellen kopiert und geändert, und so sammeln sich Programme an, die alle fast genau dasselbe erledigen - bloß jedes ein bisschen anders. Eine Jahr-2000-Revision/-Umstellung bietet Gelegenheit, betagte Programme unter die Lupe zu nehmen und neu zu frisieren.

So geschah es in einem Projekt der beiden Autoren, bei dem es um eine Großanlage der Papierindustrie ging. Die Anbindung der Prozesskontroll- und Messsysteme (kurz PLS für Prozessleitsysteme) an die Abteilungen, die Produktqualität, Produktionszahlen und -abläufe et cetera überwachen, war neu zu konzipieren und zu implementieren.

PLS-Systeme kennen überwiegend asynchron-serielle Kommunikation (RS-232 oder RS-422). Sicherlich unter anderem, weil das die einfachste und billigste Methode ist, aber auch weil es in den meisten Anwendungsfällen keine hohen Ansprüche an Durchsatz und an Latenzzeit bestehen und diese Verbindungen hinreichend stabil und sicher sind. Diese PLS-Systeme sind über hausinterne Telefonleitungen an einen Modempool angeschlossen (Abbildung 1). Die Modems sind einerseits notwendig, um die relativ großen Entfernung innerhalb des Werks zu überbrücken, andererseits sorgen sie für die galvanische Trennung der Prozess- und der IT-Systeme. Ihre Ausgänge sind an einem Portkonzentrator zusammengefasst, der den Übergang ins TCP/IP-Hausnetz bildet. Mit einem herstellerspezifischen Protokoll bildet er die seriellen Anschlüsse vom Gerät auf dem unter AIX laufenden Kommunikationsserver transparent ab, sodass sie über die Gerätedateien /dev/tty?? ansprechbar sind.

Über Telefonleitungen erreichen die PLS-Telegramme aus der Produktion via Modems und Konzentrator die Abteilungsrechner (Abb. 1).

PLS-Systeme benutzen meistens relativ simple ASCII-basierte Protokolle, die sich jedoch von Hersteller zu Hersteller unterscheiden. Während die Link-Level sich ähneln, hat man auf Nachrichtenebene mit einem Wildwuchs von Datenformaten zu tun. Für jedes der PLS-Systeme gab es früher ein handgestricktes Programm, das die Nachrichten (‘Telegramme’) in ein verdauliches Format umsetzte und zur Verarbeitung weiterschickte.

Es lag nahe, für die Formatumwandlungen Perl mit seinen mächtigen String-Funktionen einzusetzen. Eine Analyse der existierenden Programme zeigte außerdem, dass teilweise nicht nur eine reine Konvertierung und Skalierung der Daten stattfindet. Einige Fälle erfordern recht komplexe Berechnungen und Aggregationen. Nach unserem Ermessen war Perl mit seinem einheitlichen Fließkomma-Datentyp auch für diese Aufgaben denkbar geeignet. Schließlich war für die Entscheidung zugunsten Perl ein ebenso wichtiges Argument, dass es für den kommerziellen Einsatz frei ist. Perl-Scripts und mit dem Interpreter gelinkte Programme fallen nämlich nicht automatisch unter die GPL.

In der EDV-Umwelt des Auftraggebers hat sich seit einigen Jahren die Praxis etabliert, für die Interprozess-Kommunikation zwischen Anwendungen nicht die aus SystemV bekannten IPC-Mechanismen, sondern eine auf Oracle-Tabellen basierende Queue-Implementierung einzusetzen. In der einfachsten Form werden die Queue-Daten satzweise in eine Tabelle geschrieben. Beim Einfügen der Daten bekommen sie eine ‘Sequenz’ zugewiesen, eine vom Datenbanksystem automatisch erhöhte Zahl. So ist das Auslesen der Daten in der gleichen Reihenfolge einfach durch eine Sortierung nach steigender Sequenznummer möglich. Für den Zugriff auf die Queues steht eine mit dem C-Präcompiler von Oracle erstellte C-API zur Verfügung.

Größter Nachteil dieser Lösung sind die Kosten für das Datenbanksystem. In einer professionellen Organisation ist jedoch ein RDBMS meist unabdingbar. Daher war sein Einsatz für die Realisierung der Queue-Metapher eher Voraussetzung als Zwang. Die Performance unserer Lösung ist zwar nicht zu vergleichen mit der nativer Betriebssystem-Queues, ihre Vorteile jedoch überwiegen die Nachteile: Das RDBMS sorgt für ein sicheres Aufbewahren der Queue-Daten. Man hat sozusagen permanente Queues von nahezu unbeschränkter Kapazität. Außerdem ist die Kommunikation zwischen Prozessen im Netz kein Thema mehr: Sie funktioniert überall, wo der C-Präcompiler verfügbar ist, das sind praktisch alle Client-Plattformen.

Unsere Lösung bietet aber mehr: Gestaltet man die Tabellenstruktur und die Implementierung entsprechend, kann man zum Beispiel die Sätze in der Queue erneut aktivieren, das zeitliche Geschehen in der Queue zurückverfolgen et cetera - alles mit komfortablen Werkzeugen.

Eine alternative Implementierung der Queue-API benutzt Unix-IPC-Mittel, so dass das RDBMS entfallen kann. Sie wird in Ausnahmefällen herangezogen, falls der Einsatz der Oracle-Queues und - damit verbunden - des kompletten RDBMS unpraktikabel erscheint. Allerdings können Prozesse, die diese Variante verwenden, natürlich nur untereinander kommunizieren.

COMKRNL sorgt für die Kontrolle der paarweise auftretenden LLK- und TG-Prozesse. Alle Queue-Daten landen in der Oracle-Datenbank (Abb. 2).

Abbildung 2 zeigt die Prozessarchitektur auf dem Kommunikationsserver. Die Prozesse sprechen untereinander über die erwähnten Queues. Je PLS-System gibt es ein Prozesspaar von LLK (Link-Level-Kommunikation) und TG-Daemon (Telegrammverarbeitungs-Daemon). Die LLK-Prozesse, abgeleitet aus den bisherigen Programmen, wickeln die serielle Kommunikation mit dem jeweiligen PLS-System gemäß den herstellerabhängigen Erfordernissen auf dem Link-Level ab. Sie schreiben ankommende Telegramme in die Empfangs-Queue des korrespondierenden TG-Daemon. Hier sieht man, dass die Quelle der Telegramme gleichgültig ist, sie können ebenso über TCP/IP oder eine andere Verbindung eintreffen. Wichtig ist nur, dass sie irgendwie in der Queue landen. Die LLK-Programme sind in reinem C geschrieben, da sie auf die serielle Schnittstelle zugreifen und die C-API der Queues einbinden müssen. Der TG-Daemon liest das Telegramm aus der Empfangs-Queue und wandelt es in ein oder mehrere Ausgabetelegramme um, für deren weitere Verarbeitung verschiedene Programme im Betrieb zuständig sind.

In der Datenbank sind zu jedem Typ von Eingabetelegramm ein oder mehrere Perl-Scripts abgelegt. Die passenden findet man durch Vergleich des Telegramms mit ebenfalls in der Datenbank gespeicherten regulären Ausdrücken. Das erledigt der Daemon mit einem eingebetteten Perl-Interpreter, weil SQL keine regulären Ausdrücke kennt. Die ausgewählten Konvertierungs-Scripts führt der Daemon wiederum mit embedded Perl aus, wobei ein Script immer genau ein Ausgabetelegramm erzeugt.

Jedes Perl-Script dient also als Programm für den universellen Automaten TG-Daemon. Durch diese Architektur ist es gelungen, die Umwandlungslogik flexibel und erweiterbar zu programmieren, ohne für die kleinsten Änderungen den Daemon anpassen beziehungsweise neu entwickeln zu müssen.

Zwar hätte ein einziger Daemon alle von den LLK-Programmen kommenden Telegramme verarbeiten können. Dies haben wir aber vermieden, um einerseits mehr Durchsatz, andererseits eine höhere Betriebssicherheit zu erreichen: Sollte sich einer der Daemons mal verabschieden, laufen die anderen trotzdem weiter.

Bild 2 zeigt unter anderem den Prozess COMKRNL, eine Art Überwachung. Er schwebt wie ein UFO über allen LLK und Daemons, kontrolliert sie und sorgt bei vorzeitiger Beendigung für den Neustart. Die maximale Anzahl der Neustarts ist konfigurierbar. Weiterhin kann COMKRNL die LLK- und Daemon-Prozesse auf Anfrage starten, stoppen und Informationen über ihren Status liefern. Die Steuerung erfolgt wahlweise von einem Kommandozeilentool unter AIX oder einem unter Windows NT laufenden Client. Letzterer bietet die gewohnte bunte Oberfläche für den Systemadministrator.

Bei der Implementierung des Daemon konnten C und Perl ihre besten Eigenschaften ausspielen: C erledigt die systemnahen Arbeiten und den Zugriff auf die Queue-API, während Perl für die Flexibilität der Programmlogik sorgt. Das Einbetten von Perl in C gestaltete sich relativ einfach, nachdem man die ersten Hürden genommen hatte. Hierfür bietet die mit dem Perl-Package gelieferte HTML-Dokumentation brauchbare Beispiele.

Das Einspeisen eines Telegramms in das Perl-Script ist recht simpel gelöst: Per Konvention steht es in der Variable $TG. Dazu wird das Script vor der Übergabe an den eingebetteten Interpreter durch eine Zeile mit der entsprechenden Zuweisung erweitert. Das Ausgabetelegramm steht wiederum per Konvention in der Variable $OTG, woraus es durch Aufruf der Perl-Funktion perl_get_sv() von C aus zu lesen ist. Ein Beispiel zeigt Listing 1.

Mehr Infos

LISTING 1

Diese C-Funktion reicht das übergebene script zusammen mit dem Eingabetelegramm inptg an den Perl-Interpreter weiter. Das Ausgabetelegramm outptg speichert script in OTG.

void myPerl_executeScript( const char *script, const char *inptg, 
char *outptg ) {
PerlInterpreter *my_perl = NULL;
char buff [1024*128];
char *embedding[] = { "", "-e", NULL };
char *otg;

sprintf (buff, "$TG = '%s';%s", inptg, (char*)script);
embedding[2] = buff;

my_perl = perl_alloc();

perl_construct(my_perl);
perl_parse(my_perl, xs_init, 3, embedding, NULL);
perl_run (my_perl);

otg = SvPV(perl_get_sv("OTG", FALSE), na) ;
strcpy( outptg, otg );

perl_destruct(my_perl);
perl_free(my_perl);

return;
}

Wie immer bei der Implementierung eines Daemon war zu entscheiden, wie er möglichst effektiv arbeitet. In dieser Hinsicht ist er zu vergleichen mit einem Web-Server, der Browser-Anfragen zu befriedigen hat. Im Interesse der maximalen Betriebssicherheit erzeugt fork() für jedes Telegramm einen neuen Prozess, der nach dem Abarbeiten des Telegramms aussteigt. Der Nachteil dieses Vorgehens ist seine geringe Effizienz, sein größter Vorteil, dass dadurch die Verarbeitung jedes Telegramms mit dem gleichen sauberen Zustand beginnt, weil sich etwaige Programmierfehler nicht im Laufe der Zeit summieren.

Wie gelangen die Perl-Scripts in die Datenbank? Für die Definition der verschiedenen Telegrammformate und deren Umsetzungslogik bietet ein Windows-NT-Programm die komfortable Umgebung (s. Aufmacher). Im Textfeld am oberen Rande der Maske legt der Anwender zunächst das Eingabetelegramm anhand eines konkreten Telegramms an, so wie es über die serielle Leitung kommt. Im nächsten Schritt gibt er das Erkennungsmuster als regulären Ausdruck an. Der ins Programm eingebettete Perl-Interpreter prüft es auf Übereinstimmung mit dem Telegramm. Im Erfolgsfall erscheint - wie im Bild - ein grüner Haken rechts vom Muster.

Nun kann der Anwender das Telegramm in Felder zerlegen. Dazu gibt er entweder die Anfangs- und Endpositionen der einzelnen Felder oder ein Trennzeichen ein. Die Tabelle auf der linken Seite der Maske enthält die Felder und dient als Aufnahmeplatz zusätzlicher Informationen wie Kommentar, Maßeinheit et cetera. All diese Werte speichert die Datenbank auf dem Kommunikationsserver zentral, sie können jederzeit wieder abgerufen, geändert oder ausgedruckt werden. So ist die komplette Dokumentation der Telegramme immer verfügbar und vor allem aktuell.

Auf der rechten Seite des Bildes ist zu sehen, wie Perl-Funktionen, die an anderer Stelle im Programm eingegeben werden, aus diesem Eingabe- ein Ausgabetelegramm erzeugen. Für Spezialfälle kann man statt einer vorhandenen Funktion direkt Perl-Code eingeben. Jede Funktion akzeptiert Argumente vom Typ String, Numerisch, Logisch oder Feldindex. Die Parametrierung der Funktionen kann in einem separaten Fenster erfolgen, das per Doppelklick auf den Namen erscheint. Sollte sich der Aufbau des Eingabetelegramms ändern, korrigiert das Programm die aktuellen Parameterwerte vom Typ Feldindex automatisch. Außerdem ist die rechte Tabellenhälfte anpassbar, das heißt, der Anwender wählt frei aus, welche Parameterwerte er sehen will.

Bei der Telegrammumwandlung werden die Funktionen der Reihe nach von oben nach unten durchlaufen. Jede erzeugt einen Teilstring des Ausgabetelegramms. Ein Druck auf ‘Test Program’ aktiviert den eingebetteten Perl-Interpreter: Das Programm generiert den Code, der die Funktionen mit ihren aktuellen Parametern sowie mit dem Eingabetelegramm als Eingabe ausführt, und übergibt ihn an den Interpreter. Das Umwandlungsergebnis erscheint sofort in der Spalte ‘Results’ als Teilstrings, die von den jeweiligen Funktionen erzeugt werden; zusammengefügt steht es in der Zeile am unteren Rande.

Ist man mit dem Ergebnis zufrieden, kann man die Definitionen in der Datenbank speichern. Das Programm fügt den Code der referenzierten Funktionen sowie die Aufrufe mit den aktuellen Parametern zu einem einzigen Perl-Script zusammen und stellt es ebenfalls in die Datenbank. Der TG-Daemon wird es zur Laufzeit anhand des Musters aus der Datenbank holen und ausführen. So ist gewährleistet, dass das gleiche Ergebnis herauskommt.

Ein zusätzliches Schmankerl: Es ist möglich, Funktionen zu definieren, die nicht einen Teilstring, sondern Code generieren, der im resultierenden Perl-Script erscheint. Das ist zum Beispiel brauchbar, um statt einer viel zu allgemeinen Funktion kompakten Code entsprechend den aktuellen Parameterwerten zu generieren, was das resultierende Script beschleunigt. Listing 2 zeigt ein Beispiel. Allerdings ist die Kodierung einer solchen Funktion keine einfache Aufgabe.

Mehr Infos

LISTING 2

Copy() erzeugt Perl-Code, der aus einem übergebenen Feldelement einen Teil-String extrahiert und per push in @OUT ablegt. Das Hauptprogramm illustriert die Funktionsweise anhand verschiedener Parameter und dem zweiten Element von @IN.

#! /usr/bin/perl

# Funktion Copy:
# Kopiere $Length Zeichen ab dem $FirstChar-sten Zeichen
# aus dem $Field-sten Feld des
# Eingabetelegramms ins Ausgabetelegramm.
# $Length ist optional, das heißt falls $Length gleich Null ist,
# nimm das ganze Feld.
# Der Formatstring ist optional.
#
sub Copy {
my( $Field, $FirstChar, $Length, $Format ) = @_;
my( $code );

if( $Length!=0 ) {
$code = "substr(\$IN[$Field],$FirstChar,$Length)" ;
} elsif ( $FirstChar!=0 ) {
$code = "substr(\$IN[$Field],$FirstChar)" ;
} else {
$code = "\$IN[$Field]" ;
}

if( $Format ) {
$code = "sprintf(\"$Format\",$code)" ;
}

$code = "push(\@OUT, $code );" ;

return $code;
}

# Laufzeitumgebung und Test dazu (beispielhalber)
# Globale Variablen:
# @IN beinhaltet die Felder des Eingabetelegramms.
# Die Felder des Ausgabetelegramms werden in den "echten" Scripts
# Schritt für Schritt in @OUT "gepusht" und am Ende mit einer "join"-Funktion
# zusammengefügt. Deshalb benutzen wir @OUT auch hier.

@IN = ("012", "1234.45", "786", "2399", "00", "00");

@OUT = ();
$progi = Copy(1,0,2)

print "progi=$progi\n";
eval "$progi";
# so entsteht aus den Teilergebnissen das Ausgabetelegramm:
$OTG = join( '', @OUT );
print "nach eval: OTG=$OTG\n" ;

@OUT = ();
$progi = Copy(1,2);

print "progi=$progi\n";
eval "$progi";
$OTG = join( '', @OUT );
print "nach eval: OTG=$OTG\n" ;

@OUT = ();
$progi = Copy(1,2,3);

print "progi=$progi\n";
eval "$progi";
$OTG = join( '', @OUT );
print "nach eval: OTG=$OTG\n" ;

@OUT = ();
$progi = Copy(1,2,3,"%15s");

print "progi=$progi\n";
eval "$progi";
$OTG = join( '', @OUT );
print "nach eval: OTG=$OTG\n" ;

Mit Hilfe von Perl ließ sich unsere Aufgabe, die man mit anderen Mitteln nur umständlich oder gar nicht hätte lösen können, elegant und flexibel bewältigen. Die Verfügbarkeit von Perl auf Windows-NT ermöglichte, eine unkonventionelle und einzigartige Client/Server-Architektur aufzubauen, in der jeder Teilnehmer das tut, wofür er prädestiniert ist: Unix läuft rund um die Uhr, während Windows-NT den Benutzer mit einer aufgabenorientierten Darstellung der Oberfläche verwöhnt. Die Ersparnis an Aufwand dank Perl ist nur schwer zu beziffern, aber eine herkömmliche Programmiersprache hätte sicherlich mehr Arbeit erfordert. Die Programme laufen zudem brav und stabil, was wiederum die Qualität dieses Paketes beweist.

LÁSZLÓ KOVÁCS
ist Dipl.-Elektroingenieur und arbeitet als EDV-Berater. Gleichzeitig ist er Geschäftsführer der DanuBit GmbH in Wien.

WOLFGANG WAGNER
ist bei der Bull AG Österreich im Bereich System Architecture und Design tätig.

Mehr Infos

iX-TRACT

  • Prozessleitsysteme übertragen Produktionsdaten per serieller Leitung an die Abteilungen, die für die Überwachung der Produktion zuständig sind.
  • Die Meldungen (Telegramme) unterscheiden sich je nach Hersteller, im Betrieb ist jedoch ein einheitliches Datenformat erforderlich.
  • Zur Umsetzung von rohen PLS-Telegrammen in verständlichen Klartext dienen Perl-Scripts, die der in C-Programme eingebettete Interpreter ausführt.

(ck)