Um mit einem Benutzer zu kommunizieren, also Eingaben zu empfangen oder Ausgaben zu machen, muß eine Task an ein Terminal gekoppelt sein. Ein Terminal im Sinne von L3 ist eine Tastatur und ein Bildschirm, gleichgültig, ob »fest eingebaut« wie die CONSOLE oder über serielle Schnittstelle angeschlossen. Im System wird ein Terminal durch eine Terminaltask repräsentiert, die drei Sohntasks hat. Eine dieser Sohntasks ist das Terminalmenü, die anderen beiden sind Manager für Tastatur und Bildschirm. Den Ausgangspunkt aller Zuordnungen eines Terminals an irgendwelche Tasks bildet die »Menütask«, die das Terminalmenü ausgibt. Diese Task, die ja aus Benutzersicht das Terminal darstellt, heißt für die CONSOLE CONSOLE.term, für weitere Terminals dementsprechend ??.term.
Tastatur- und Bildschirmmanager sind im Grundzustand des Systems mit »ihrer« Menütask gekoppelt. Von dieser Task wird das Terminal an andere Tasks gekoppelt. »Eine Task an ein Terminal koppeln« bedeutet demzufolge, daß eine Zuordnung vorgenommen wird, die zur Folge hat, daß eine Task Botschaften vom Tastaturmanager erhält und Botschaften an den Bildschirmmanager absetzt. Die Regelung der Zuordnung von Terminals zu Tasks obliegt der Task SYSIO.
Ein Beschreibung der Botschaften zwischen Tasks und SYSIO findet sich in Kap.13.7.
SYSIO........................... 0:08:04 1 wait - T 2......................... 0:00:02 1 wait - T 2.term................ 0:00:00 1 wait "T 2" T 2.kbd................. 0:00:00 1 wait - T 2.dsp................. 0:00:01 1 wait - CONSOLE..................... 0:04:39 1 wait - CONSOLE.term............ 0:01:32 1 wait - CONSOLE.kbd............. 0:01:32 1 wait - CONSOLE.dsp............. 0:15:48 1 wait -
SYSIO führt für jedes Terminal einen Terminalstack, auf dem zunächst nur die Terminaltask eingetragen ist. Von der Terminaltask CONSOLE kommt nun ein Auftrag an SYSIO das Terminal an die Task OPERATOR zu koppeln, von der Terminaltask T 2 der Auftrag Pascal 5.5 anzukoppeln, von dort der Auftrag die Task PRINTER anzukoppeln (link(printer)). Intern stellt sich der Zustand der Taskstacks in SYSIO dann so dar:
Offensichtlich ist nach Abkoppeln von OPERATOR wieder die Terminaltask CONSOLE am Bildschirm sichtbar, nach Abkoppeln von PRINTER die Task PASCAL 5.5.
Das Erzeugen und Ankoppeln von Task kann durch Passworte überwacht werden. Da Tasks autonom sind und das Passwort in der Task aufbewahrt wird, ist dieser Mechanismus hart. Wenn eine Task durch ein Passwort vor dem Ankoppeln geschützt ist, läßt sich diese Task so lange nicht zum Dialog ankoppeln, bis ihr das korrekte Passwort eingegeben wurde. Wenn das Passwort richtig eingegeben wurde, wird die Task angekoppelt. Solange sie im Terminalstack des zugeordneten Terminals steht, ist keine erneute Eingabe des Passworts nötig. Es ist also durchaus möglich per HotKey via Terminalmenü in eine andere Task zu wechseln und anschließend wieder in die geschützte Task zurückzukehren, ohne sich erneut zu identifizieren.
Die Zuordnung von Tasks und Terminals bleibt auch über Systemabschaltungen hinweg bestehen. Um zu verhindern, daß mit Passwort geschützte Terminals nach Neustart des Systems (noch) einem Terminal zugeordnet sind, kann durch die Prozedur auto unlink erzwungen werden, daß die Task beim Neustart des Systems auf jeden Fall abgekoppelt ist.
BOOL PROC on line (TASK CONST task)
TASK PROC terminal task (TASK CONST task)
PROC auto unlink (BOOL CONST op):
Die Voreinstellung für alle Tasks ist auto unlink (FALSE).
BOOL PROC auto unlink (TASK CONST task)
PROC terminal inquiery (INT CONST timeout)
PACKET inq beispiel DEFINES bsp: PROC bsp: INT VAR i; FOR i FROM 1 UPTO 10 REP create (hex(i), dir) PER; { Die Prozedur macht irgendwas } terminal inquiry (never) { wartet dann auf ein Terminal } FOR i FROM 10 UPTO 15 REP putline(hex(i)) PER; { und dann macht sie was im dialog } monitor; END PROC bsp; END PACKET inq beispiel; {.. so sieht das in der aufrufenden Task aus, } { nachdem 'bsp' insertiert wurde } TASK VAR t; putline ("jetzt das begin"); begin ("son task 1", PROC bsp, t); { start, Sohntask legt Verzeichnisse an } terminal inquiry ; { ich will erstmal am Terminal bleiben } putline ("Sohntask angelegt..."); TEXT VAR tx := "......................" ; editget (tx); { Die Vatertask bleibt am Terminal } link (/"son task 1"); { jetzt Sohntask ankoppeln und } unlink; { selbst aus Terminalstack raus }
begin password (TEXT CONST old, new)
task password (TEXT CONST old, new)
Gerade für Aufgaben der Systemverwaltung ist es zweckmäßig, allen oder einzelnen Benutzern des Systems Nachrichten zukommen lassen zu können. terminal broadcast bietet die Möglichkeit einzelnen Tasks oder allen Benutzern Texte zu schicken, die temporär in einem passenden Fenster über dem aktuellen Bildschirminhalt ausgegeben werden. Durch Drücken der ESC Taste kann der Benutzer die Nachricht nach Durchlesen verschwinden lassen. Der Empfang solcher Nachrichten läßt sich pro Terminal einstellen. In der Voreinstellung sind für alle Terminals »die Nachrichten eingeschaltet«. Durch die Hotkey Tastenkombination:
Shift STRG ALT b Broadcast Ein/Ausschalten
läßt sich für jeden Arbeitsplatz einstellen, ob Nachrichten angezeigt werden sollen oder nicht. Falls der Empfang abgeschaltet ist, wird das im Terminalmenü angezeigt.
PROC terminal broadcast (TEXT CONST message, TASK CONST recv, INT CONST timeout)
PROC terminal broadcast (TEXT CONST message)
PROC setup terminal (TEXT CONST terminal name)
Es kann auch von Interesse sein, innerhalb eigener Programme Informationen über eine Terminaltask zu erhalten. Falls es beispielsweise nötig ist, dem Benutzer eines bestimmten Terminals eine Mitteilung zu senden, so muß zunächst klar sein, welche Task an der Spitze des zu diesem Terminal gehörigen Terminalstacks steht, um dann per terminal broadcast die Mitteilung an genau diese Task zu senden.
Der Inhalt eines Terminalstacks wird in Datenobjekte des Typs TASKARRAY verwaltet. Ein TASKARRAY ist eine Folge von TaskIDs (interne Taskbezeichner), die dynamisch verwaltet wird. Zu diesem Zweck ist die interne Repräsentation nicht ROW xy TASK, sondern TEXT. Dieses ELAN Packet TASKARRAY ist auf der Diskette mit ELAN Beispielprogramme enthalten und sei jedem ELAN Interessenten wärmstens empfohlen. Es ist nichttrivial, kurz und zeigt bestens, wie man unter L3 mit ELAN programmiert.
PROC get terminal stack (TASK CONST terminal, TASKARRAY VAR tarray)
TASKARRAY VAR terminal stack; TASK PROC terminal top (TASK CONST terminal) : get terminal stack (terminal, terminal stack); terminal stack SUB LENGTH terminal stack END PROC terminal top; put (name (terminal top (task ("CONSOLE"))));
Das Terminalmenü stellt die äußerste Schicht des laufenden L3 Systems dar. Es wird durch die Menütask ausgegeben und kontrolliert. Diese Task ist im Normalfall Brudertask zu den Managern für Tastatur und Bildschirm. Um ein anderes Terminalmenü zu erhalten, muß das standardmäßig angebotene entweder ersetzt oder überlagert werden.
Eine vollständige Ersetzung des Menüs kann dadurch erreicht werden, daß in der Task configurator eine Prozedur terminal menu insertiert wird. Wenn im configurator eine modifizierte Prozedur terminal menu insertiert ist, werden anschließend alle neuinstallierten Terminals mit diesem neuen Menü gestartet. Dieses Vorgehen erfordert eine Ersetzung der Task configurator. Die dazu nötigen Aktionen sind am Ende dieses Abschnitts beschrieben.
Eine Alternative zu diesem Eingriff ins System ist die Erzeugung von Tasks mit modifiziertem Menü und die Zuordnung dieser Task als Menütask für ausgewählte Terminals. Zur Programmierung des Terminalmenüs sei an dieser Stelle auf das Standardmenü verwiesen, das auf der Diskette mit ELAN Beispielprogrammen enthalten ist.
Um den Terminaltasks neue Terminalmenüs zuzuordnen, sollte man im Systemzweig eine Task einrichten, unter der für alle Arbeitsplätze, denen ein neues Menü zugeordnet werden soll, eine neue Menütask erzeugt wird. Diese Tasks werden dann dem jeweiligen Arbeitsplatz als neue Menütask zugeordnet, indem ein dementsprechender Auftrag an die Terminaltask geschickt wird. Im nachfolgenden Beispiel sei die Ausgangslage, daß in einer eigens dafür erzeugten Task im Systemzweig eine passend gestaltete Prozedur special terminal menu insertiert wurde. Um die Terminaltask oli 1 mit diesem neuen Menü zu starten, sind folgende Aktionen nötig:
PACKET change terminal menu DEFINES change terminal menu : LET terminal menu code = 0x 06 00 00 11; PROC change terminal menu (TASK CONST terminal) : TASK VAR menu; IF exists (terminal) THEN begin new terminal menu; send new terminal menu; ELSE errorstop ("Terminal existiert nicht"); FI; . begin new terminal menu : IF exists task (menu name) THEN end (task (menu name), quiet) FI; begin (menu name, PROC special terminal menu, menu); . menu name : name (terminal) + ".menu" . send new terminal menu : INT VAR reply; new msg; put msg (terminal menu code); put msg (menu); call manager (terminal, reply); END PROC change terminal menu; PROC special terminal menu : new menue { eigenes Terminalmenü } END PROC special terminal menu; END PACKET change terminal menu; change terminal menu (task ("oli 1"));
set up terminal ("oli 1"); { starten } change terminal menu (task ("oli 1")); { eigenes Menü installieren }
Austausch der Task configurator
Um das Terminalmenü auszutauschen, kann auch eine passende Task configurator, in der ein selbstgestaltetes Terminalmenü insertiert ist, in das System gebracht werden. Dazu muß zunächst eine neue Task als Sohntask von UTILITIES begonnen werden. In dieser neuen Task wird dann das neue Terminalmenü insertiert. Bei dieser Vorgehensweise muß diese neue Prozedur terminal menu heißen. Anschließend müssen alle Datenräume (Tastaturtabellen etc.) aus der Task configurator in die neue Task geholt werden. Anschließend wird die alte Task configurator gelöscht un in der neuen Task die Prozedur configuration manager aufgerufen. Diese benennt die Task in configurator um und startet das gewohnte Menü. Bei Benutzung des ersten Menüpunkts terminal installieren wird für das neue Terminal das eigene, neue Terminalmenü gestartet.
begin ("conf", /"UTILITIES"); insert ("new terminal menu"); { die Datei enthält ein ELAN Packet, das eine neue Prozedur 'terminal menu' definiert } fetchall (/"configurator"); { Der configurator ist ein 'configuration manager'. Im Systemzweig kann man deshalb seine Managerfunktionen benutzen. } end (/"configurator"); { Da die neue Task 'conf' als Sohntask von UTILITIES Systemprivileg hat, kann man beliebige Tasks löschen. configuration manager; { Die Task, die nun alle benötigten Tabellen enthält, wird zum neuen configurator }
Zum Betrieb von Modems, Druckern oder anderen Geräten, die über parallele oder serielle Schnittstelle an das L3 System angeschlossen werden sollen, gibt es Prozeduren zur Entwicklung eigener Treiberroutinen. Die Benutzung dieser Routinen erspart die »Low Level Programmierung« auf GDP (General Driver Protocol) Ebene.
Eine solche Treiberroutine besteht aus:
Ein Schnittstellenbaustein mit V24 oder CENTRONICS Ausgang ist im Sinne von L3 ein Gerät. Wie ausführlicher in Kapitel 13 dargestellt ist, werden Geräte in einem L3 Systems durch besondere Tasks bedient. Diese Tasks sind Realprozesse, sie befinden sich permanent im Realspeicher und steuern die ihnen zugeordnete Hardware. Die Treibertasks finden sich im Taskbaum unterhalb der Task SYSHW. Aus historischen Gründen sind die Treiber für parallele Schnittstellen noch im Systemkern enthalten und werden erst in einer kommenden L3 Version als Tasks (= ladbare Treiber) realisiert. Es gibt jedoch Pseudonamen für die erste und zweite parallelen Schnittstelle, so daß sie wie Task angesprochen werden können. Sie lauten 'LPT1' bzw. 'LPT2'.
Für serielle Schnittstellen gibt es zwei ladbare Treiber im System. Für die integrierte serielle Schnittstelle und weitere auf »unintelligenten Karten« eingebauten Schnittstellen, gibt es die Task COM8250, für Schnittstellen auf »intelligenten Karten« von DIGIBOARD , gibt es die DIGI Treiber. Da serielle Schnittstellen bidirektional funktionieren, können mit einem Realprozess zwei Pseudonamen assoziiert werden: 'COMx.out' als Ausgabetreibername und 'COMx.in' als Eingabetreibername. 'x' ist dabei die Schnittstellennummer der angesprochenen seriellen Schnittstelle. Falls Ausgabe- und Eingabetreiber auf der ersten seriellen Schnittstelle (COM1) eingerichtet werden sollen, wird COM1 also durch die Pseudonamen 'COM1.out' für den Ausgabetreiber und 'COM1.in' für den Eingabetreiber angesprochen. Entsprechend heißen die Treiber für die Bausteine auf einer intelligenten Karte 'DIGI1.in', 'DIGI1.out' usw.
PROC out driver(TEXT CONST out driver name)
PROC in driver(TEXT CONST in driver name)
PROC stream transfer (INT CONST baud, data bits, stop bits, parity)
Baudrate : 75, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
Datenbits: 7 oder 8
Stopbits : 0 , 1 oder 2
Parity : Zur Einstellung der gewünschten Paritybits gibt es folgende Konstanten:
INT CONST no parity
INT CONST odd parity
INT CONST even parity
INT CONST high parity
INT CONST low parity
PROC dtr dsr flow control (BOOL CONST flag)
PROC rts cts flow control (BOOL CONST flag)
PROC xon xoff flow control (BOOL CONST flag)
PROC driver out (TEXT CONST string)
PROC driver out (TEXT CONST string, INT CONST from)
PROC driver out (TEXT CONST string, INT CONST from, to)
PROC driver out (TEXT CONST string address,string length)
PROC driver in (INT CONST timeout, TEXT VAR string)
PROC driver in (INT CONST timeout, string address, max string length, INT VAR string length)
FILE VAR file := sequential file (output, "protokoll"); in driver ("COM6.in"); out driver ("COM6.out"); stream transfer (9600, 8, 1, even parity); rts cts flow control (TRUE); open out driver ; TEXT VAR t := ""; REP driver in (1000, t) ; IF t <> "" THEN putline (t, file); t := ""; FI PER;