|
... aber ich werde alles geben,
daß er Wirklichkeit wird!
Der Traum ist aus
aus: "Keine Macht für Niemand"
Ton Steine Scherben (1972)
|
|
Aufgrund der Systematisierung und der Bestandsanalyse wurden im
vorangegangenen Abschnitt Überlegungen angestellt, wie das
Kommunikationsproblem in verteilten virtuellen Umgebungen gelöst
werden kann. Einige der Überlegungen führten zur Entwicklung
von Mechanismen. Diese Mechanismen wurden in einer Prototyplösung
implementiert, um ihre Funktionsfähigkeit praktisch zu prüfen.
Wesentliche Ziele bei der Entwicklung des Systems waren Skalierbarkeit
und eine flexible, erweiterbare Struktur.
Im Einzelnen realisiert der Prototyp den Eventtransport über
das Netzwerk, die Verwaltung der OI, den Abonnementmechanismus,
den Übergang der Objektkontrolle, Objektverhalten sowie minimale
Visualisierung. Mechanismen wie automatische (positionsabhängige)
Abonnierung, Interaktionserkennung und Interaktionsauflösung
sowie Objektcodetransfer wurden noch nicht implementiert.
Der Prototyp stellt einen ersten Ansatz für eine verteilte
virtuelle Umgebung zur Verfügung. In einem solchen System
müssen neben der Implementation der vorgestellten kommunikationsrelevanten
Mechanismen noch andere Probleme gelöst werden. Diese Probleme
wurden im Abschnitt 3 (ab Seite 32) dargestellt.
|
|
Der Voodie Prototyp stellt die Prototypimplementation für
die Mechanismen dar. Er besteht aus einer Applikation, die die
eigentlichen Mechanismen und die notwendigen Verwaltungsmodule
implementiert und einer Visualisierungsanwendung. Die derzeitige
Version des Prototypen besteht aus ca. 4500 Zeilen C++ Code und
wurde auf einer Indigo2 Maximum Impact Grafikworkstation
mit TRAM Option der Firma Silicon Graphics Inc. unter Irix 6.2
entwickelt. Der vollständige Quellcode liegt der Arbeit auf
einem digitalen Speichermedium bei.
Die Implementierung der Verwaltungsmodule und der Voodie Objekte
ist objektorientiert und greift auf Funktionalitäten der
Standard C++ Bibliothek (/JOS96/, /STL96/) zurück.
Die Applikation besteht aus einer Reihe eigenständiger Module,
die in separaten Threads ablaufen. Dabei wurde zur Implementation
die POSIX 1003.1c Threads API (pthreads, /NIC96/) benutzt. Die
einzelnen Module kommunizieren dabei über den Austausch von
Ereignissen (Events). Dieser Austausch erfolgt über globale,
modulspezifische Eventqueues. Bei der Entscheidung, für die
Implementierung der einzelnen Module Threads zu verwenden, stand
die mögliche Benutzung von Mehrprozessormaschinen an zweiter
Stelle. Hauptgrund für den Einsatz von Threads waren softwaretechnologische
Überlegungen. Durch den Einsatz von separaten, getrennt ausgeführten
Modulen für die Verwaltung, ist eine sehr gute Trennung der
einzelnen Module möglich.
Der Prototyp hat verschiedene Aufgaben zu lösen. Eine Aufgabe
ist die Verwaltung der Objekte, eine weitere Aufgabe stellt die
Kommunikation der Systeme verschiedener Teilnehmer über das
Netzwerk dar. Die Visualisierung und die Verarbeitung von Eingaben
sind weitere Aufgaben, die durch den Prototyp gelöst werden
müssen. Da die einzelnen Module des Systems miteinander kommunizieren
müssen, ist auch das Management dieser Kommunikation eine
Aufgabe des Prototypen. Ausgehend von diesen Aufgaben wurde das
System in folgende Module unterteilt:
Interaktionsdämon
Der Interaktionsdämon (ID) verwaltet Objekte
des Voodie Systems. Über ihn werden Objekte generiert und
gelöscht.
Net_listener / Net_sender
Diese Module ermöglichen den Transport von Ereignissen
von einer Station zu einer anderen.
Renderer / console_listener
Das Renderer Modul verarbeitet Visualisierungsereignisse,
console_listener setzt Eingaben in entsprechende Ereignisse um.
Im Prototyp fungieren diese Module als Interface zu einer separaten
Visualisierungsanwendung. Diese Anwendung verarbeitet die durch
das Renderer Modul ausgegebenen Ereignisse weiter und liefert
in Abhängigkeit von den Eingaben des Benutzers Daten an das
Console_listener Modul.
Distributor
Ein weiteres Modul, der Distributor, verteilt die
Ereignisse an die entsprechenden Module und ermöglicht damit
die Kommunikation zwischen den einzelnen Modulen.
Voodie Objekte
Objekte im Voodie System werden wie die Verwaltungsmodule
als separate Threads implementiert. Dies ermöglicht zum einen
eine flexible und abgeschlossene Implementierung der Objekte und
zum anderen auch die Implementation von zusätzlichen Verwaltungsmodulen.
Im Prototyp wurde z.B. der OI-Verwalter als spezielles Voodie
Objekt implementiert.
Abbildung 9 verdeutlicht den Zusammenhang und die Aufgaben der
Module.
|
|
Die Implementierung der Mechanismen beinhaltet keine Funktionalität
zur Visualisierung der Voodie Objekte. Durch das Renderer Modul
wird lediglich der Inhalt der RENDER
Events auf den Standardausgabekanal ausgegeben. Derzeit werden
diese Ausgaben von einer eigenständigen Renderapplikation
weiterverarbeitet. Für die Implementierung der Renderapplikation
wurde das OpenGL API (/NEI93/) und die GLUT Bibliothek (/KIL96/)
benutzt. Die Renderapplikation läuft als Koprozeß parallel
zur Hauptanwendung (Voodie Applikation).
Die Kommunikation der beiden Prozesse erfolgt über eine
Duplex Pipe. Das bedeutet, daß auf den Standardausgabekanal
(stdout) der Voodie Applikation von der Renderapplikation
aus zugegriffen werden kann. Der Standardeingabekanal
(stdin) der Voodie Applikation wird von der Renderapplikation
mit Daten versorgt. Abbildung 10 zeigt die Kommunikation der beiden Prozesse.
|
|
Die gesamte Kommunikation im Voodie System ist ereignisorientiert.
Dabei besitzen die Ereignisse (Events) die Struktur
TYPE
FROM_HOST
FROM_MODULE
TO_HOST
TO_MODULE
MESSAGE.
Diese Struktur ist die derzeitig implementierte Struktur für
Events. Sie orientiert sich sehr stark an der internen Implementation
des Voodie Prototyps.
|
mögliche Verbesserungen |
In der bisherigen Eventdatenstruktur sind keine Objektidentifikatoren
enthalten. Sollen Voodie Objekte identifiziert werden, so geschieht
dies derzeit über den Messageteil des Events. Diese Identifikatoren
können aber fest in die Datenstruktur eingebaut werden, indem
die Module auch als Voodie Objekte behandelt werden. Diese Objekte
besitzen dann reservierte ObjektIDs und sind nicht abonnierbar.
Aufgrund bisheriger Erfahrungen und Tests sollten die Events in
der Form:
|
|
TYPE
SENDER
RECEIVER
MESSAGE
vorliegen.
Dabei sind SENDER und RECEIVER jeweils ObjektIDs.
Dadurch könnten sich z.B.: folgende (vorbelegte und damit
reservierte) ObjektIDs ergeben:
DISTRIBUTOR ... Objekt 0
NET_SENDER ... Objekt 1
NET_LISTENER ... Objekt 2
RENDERER ... Objekt 3
CONSOLE ... Objekt 4
ID ... Objekt 5
OI_OBJECT ... Objekt 6
Eine ObjektID kann dann - unter Hinzunahme einer Portnummer -
in der Form: HOSTNAME:PORTNUMMER/OBJEKTNUMMER
dargestellt werden. Beispielhaft sähen ObjektIDs folgendermaßen
aus:
parrot.informatik.hab-weimar.de:3490/2
parrot.informatik.hab-weimar.de:3490/ID
141.54.12.13/7 (default Port)
Wenn diese Darstellung um den Namen des Protokolls erweitert wird,
kann eine URL (/LEE94a/, /LEE94b/) konforme Schreibweise für
die Identifizierung eines Voodie Objektes benutzt werden:
voodie://parrot.informatik.hab-weimar.de:3490/ID
|
4.4 |
Netzwerkmodule / Eventtransfer |
|
Der Netzwerkverkehr im Voodie System basiert auf UNIX Stream Sockets
(/BRO94/, /STE92/, /WRI95/, /RAG93/, /HAL96/).
Stream Sockets stellen ein Interface für die Netzwerkkommunikation
zur Verfügung, welches verindungsorientierte Kommunikation
ermöglicht. Dadurch können Datenströme beliebiger
Länge von einer Station zu einer anderen übertragen
werden. Die Verwendung von Stream Sockets bringt aber auch einen
nicht unerheblichen Aufwand für das Verbindungsmangement
mit sich, da der Datentransport Methoden zur Fehlerkorrektur beinhaltet.
Durch die Benutzung von verbindungsloser, paketorientierter Kommunikation
(realisierbar z.B. durch den Einsatz von UDP) wäre eine Performancesteigerung
beim Datentransport möglich. In diesem Fall müßte
aber die Notwendigkeit einer Fehlerkontrolle und Empfangsbestätigung
geprüft werden.
Da die Übertragung der Ereignisse noch optimiert werden muß,
diese Aufgabe aber für das Funktionieren des Prototypen nicht
relevant ist, wurden im Rahmen der Lehrveranstaltung Rechnernetze
II des Studienganges Informatik der Bauhaus-Universität Weimar
im Sommersemester 1997 Belegaufgaben zur Optimierung der Ereignisübertragung
ausgegeben. Die Ergebnisse dieser Arbeiten werden erst nach Abschluß
dieser Arbeit vorliegen. Die Ergebnisse sollten auf jeden Fall
auf eine Verwendbarkeit in zukünftigen Voodie Implementationen
hin untersucht werden.
Die Netzwerkmodule im Voodie System (Net_Listener,
Net_Sender), haben die Aufgabe, Events von einer
Station zu einer anderen zu übertragen.
Das Modul Net_Sender hat
die Aufgabe, Events an eine entfernte Station zu schicken. Das
Modul Net_Listener empfängt
Events, die von anderen Stationen abgeschickt wurden.
Bei der Übertragung der Events wird folgendes "low-level"
Eventtransferprotokoll benutzt (Eventlänge unbegrenzt, Streamvariante):
LISTENER : erwarte Verbindung
SENDER : stelle Verbindung her
LISTENER : sende Begrüßung
This is VOODIE <Version>
accepting Events:
(TYPE FROM_HOST FROM_MODULE
TO_HOST TO_MODULE MESSAGE end of message)
SENDER : lese Begrüßung
wiederhole
SENDER : sende ein Paket (einen Teil, einige Byte
der Zeichenkette)
LISTENER : lese ein Paket
LISTENER : sende Empfangsbestätigung
received <num> Bytes. ok!
SENDER : erwarte und lese Empfangsbestätigung
LISTENER : hänge das Paket an die bereits
empfangene Zeichenkette an
bis die gesamte Zeichenkette übertragen wurde.
SENDER : sende
end of message
LISTENER : sende Bestätigung, Verbindung abbrechen
SENDER : lese Bestätigung, Verbindung abbrechen
LISTENER : empfangene Zeichenkette ist Rückgabewert
Da die Events uncodiert übertragen werden, ist es möglich,
ein Event von Hand über das Netzwerk an eine Voodie Station
zu übertragen. Dies könnte mit telnet
z.B. so aussehen:
parrot 102 /usr/people/meister1>telnet 141.54.24.13 3490
Trying 141.54.24.13...
Connected to 141.54.24.13.
Escape character is '^]'.
This is Voodie v0.002, feb,mar 97,(p) by mm
accepting Events: (TYPE FROM_HOST FROM_MODULE TO_HOST TO_MODULE
MESSAGE end of message) ...
DATA LOCAL INPUT 141.54.24.13 ID CREATE_OBJECT CLASS = voodie_first_object_c
end of message
received 77 Bytes. ok!
Connection closed by foreign host.
parrot 103 /usr/people/meister1>
|
zeitliche Ordnung von Ereignissen |
Für die Mechanismen, die auf dem Austausch von Ereignissen
basieren, ist es nicht notwendig, die Ereignisse zeitlich zu ordnen.
Die derzeitig implementierten Mechanismen benötigen eine
solche Ordnung nicht. Für in Zukunft zu entwickelnde Mechanismen
ist es aber durchaus denkbar, daß eine zeitliche Ordnung
der Ereignisse hergestellt werden muß. Lamport schlägt
in /LAM78/ einen verteilten Algorithmus vor, der zu diesem Zweck
eingesetzt werden könnte.
|
4.4 |
Wichtige Ereignisse (Events) in Voodie |
|
Im Voodie System sind sehr viele Events möglich. Einige dieser
Events wurden in den vorangegangenen Ausführungen bereits
erwähnt. Die meisten dieser Events werden automatisch als
Reaktion auf andere Events generiert. Prinzipiell können
alle diese Events auch explizit von außen durch einen Operator
an das jeweilige Modul bzw. Voodie Objekt geschickt werden. Für
die Demonstration der Mechanismen werden aber lediglich die folgenden
Events benötigt:
|
Global Exit |
Typ:
|
EXIT |
Name: |
GLOBAL_EXIT |
From Host |
LOCAL |
To Host |
LOCAL |
From Module |
INPUT |
To Module |
DISTRIBUTOR |
Message |
<error_string> |
Beschreibung |
Alle Module beenden, Objekte zerstören, Programm beenden. |
|
Create Object |
Typ: |
DATA |
Name: |
CREATE_OBJECT |
From Host |
LOCAL |
To Host |
LOCAL |
From Module |
INPUT |
To Module |
ID |
Message |
CREATE_OBJECT CLASS = <a_valid_class_name> |
Beschreibung |
Generiere auf der lokalen Station ein Objekt der Klasse
<a_valid_class_name> (CREATE_OBJECT Event).
|
|
Send All OI |
Typ: |
DATA |
Name: |
SEND_ALL_OI |
From Host |
LOCAL |
To Host |
<TO HOST> |
From Module |
INPUT |
To Module |
ID |
Message |
SEND_ALL_OI |
Beschreibung |
Bitte um Sendung aller OI der Station <to_host>
(SEND_ALL_OI Event). |
|
Typ: |
DATA |
|
|
From Host |
LOCAL |
To Host |
LOCAL |
From Module |
INPUT |
To Module |
OBJECTS |
Message |
id = <num> STATE = EXIT |
Beschreibung |
Weise das Voodie Objekt mit der Identifikation
<num> der lokalen Station an, seinen Status auf
EXIT zu setzen. |
|
Subscribe OI |
Typ: |
DATA |
Name: |
SUBSCRIBE OI |
From Host |
LOCAL |
To Host |
LOCAL |
From Module |
INPUT |
To Module |
OBJECTS |
Message |
id = 1 SUBSCRIBE OI = <num> |
Beschreibung |
Anweisung an das OI-Objekt, den OI, der sich an der
Position <num> der aktuellen OI-Liste befindet, zu
abonnieren. (SUBSCRIBE OI Event, nicht zu verwechseln mit
SUBSCRIBEEvent).
|
Die derzeitige Implementation des graphischen Interfaces erlaubt
lediglich das Senden dieser Events. Eine Aufstellung aller möglichen
Ereignisse findet sich im Anhang B "Mögliche Events in Voodie")
|
|
Ein Objekt kann nur dann abonniert werden, wenn zumindest sein
OI bekannt ist. Im OI sind Informationen zur Klasse des Objektes
und die eindeutige Identifizierung (ObjektID)
enthalten. Das Abonnement eines Objektes geschieht in drei Phasen.
In Abbildung 11 sind die Phasen beim Objektabonnement zum besseren
Verständnis graphisch dargestellt.
- Phase 1:
- Die OI werden in der ersten Phase des Objektabonnements
übertragen.
Station 2 sendet zu einem Zeitpunkt t1 ein SEND_ALL_OI
Ereignis an eine andere Station (Station 1). Diese Station reagiert
auf dieses Ereignis, indem sie alle Objekte anweist, einen OI
an die erste Station zu senden. Diese OI kommen irgendwann zwischen
den Zeitpunkten t2 und t3 bei Station 2
an.
Soll ein Objekt (Masterobjekt) der Station 1 von
Station 2 abonniert werden, so ergibt sich in einer zweiten Phase
folgender Ablauf:
- Phase 2:
- Auf Station 2 wird ein Slaveobjekt generiert (anhand
der Daten im OI).
Das Slaveobjekt sendet dann ein SUBSCRIBE
Event an das Masterobjekt auf Station 1 (ObjektID stand im OI)
und wird somit zum Abonnent des Masterobjektes.
- Phase 3:
- Das Masterobjekt (auf Station 1) sendet nun während
der dritten Phase UPDATE
Events an das neu generierte Objekt Slaveobjekt (auf Station 2),
bis es ein UNSUBSCRIBE
Event vom Slaveobjekt erhält.
Die OI werden durch das OI-Objekt verwaltet. Das Abonnement eines
Objektes einer anderen Station kann von außen durch Senden
eines SUBSCRIBE OI = <oi_num>
Events an das OI-Objekt eingeleitet werden. Das OI-Objekt veranlaßt
dann die Erzeugung des Slaveobjektes
|
4.7 |
Übergabe der ObjektkontrolleObjektabonnement |
|
Es sind zwei Ausgangssituationen für den Übergang der
Objektkontrolle denkbar:
|
[A1] |
Ein Slaveobjekt will die Objektkontrolle übernehmen.
Das Slaveobjekt sendet ein REQ_OWNER
Event an das Masterobjekt. Das Masterobjekt entscheidet nun, ob
es die Kontrolle abgeben will.
Wenn nein, passiert nichts.
Wenn ja, sendet das Masterobjekt ein SET_OWNER
Event an das Slaveobjekt und wird selbst zu einem Slaveobjekt
indem es seinen Status auf SLAVE setzt. Das
Objekt wird dann automatisch Abonnent seines ehemaligen
Slaveobjektes.
Wenn das Slaveobjekt das SET_OWNER
Event erhält, setzt es seinen Status auf MASTER
und nimmt das ehemaligen Masterobjekt in seine Aboliste auf.
|
[A2] |
Ein Masterobjekt will die Kontrolle abgeben.
In diesem Fall sendet das Masterobjekt an einen seiner Slaves
ein REQ_REQ_OWNER Event und bittet damit darum,
ein REQ_OWNER Event zu schicken. Der weitere Ablauf
erfolgt wie unter [A1] beschrieben.
Durch Senden eines STATE = EXIT Events an ein
abonniertes Objekt wird das entsprechende Slaveobjekt gelöscht
und das Abonnement aufgehoben. Dabei wird durch das Slaveobjekt
folgendes sichergestellt:
|
[B1] |
Das Slaveobjekt sendet ein UNSUBSCRIBE
Event an seinen Abomaster.
|
[B2] |
Wenn das Slaveobjekt selbst Abonnenten besitzt, dann
müssen die Abonnenten von der bevorstehenden Löschung
des Objektes informiert werden. Deshalb sendet das Objekt seine
master_id (die ObjektID
des Abomasterobjektes) als MASTER_ID = <id>
Event an alle seine Abonnenten. Die Abonnenten senden dann ein
SUBSCRIBE Event an den neuen Abomaster. Damit wird
die Unterbrechung evtl. bestehender Aboketten verhindert.
|
[B3] |
Ein Masterobjekt kann nur dann gelöscht werden,
wenn es keine Abonnenten besitzt. Ein Masterobjekt mit Abonnenten
wandelt sich nach dem Empfang eines STATE = EXIT Events
selbständig in ein Slaveobjekt um.
Dabei geht die Objektkontrolle vom Masterobjekt an ein Slaveobjekt
der gleichen Abonnementgruppe über.
|
4.8 |
Objektimplementation in Voodie |
|
Da das Voodie System in C++ implementiert wurde, sind systemintern
prinzipiell alle Module und die meisten Datenstrukturen C++ Klassen.
In der Nutzersicht auf das Voodie System sind Objekte dagegen
eigenständig agierende Einheiten, die sich durch Senden von
Events an das Rendermodul darstellen können. Diese Einheiten,
im folgenden Voodie Objekte genannt, können als Objektgruppen
auf verschiedenen Stationen residieren. Die Voodie Objekte einer
Gruppe synchronisieren sich dabei über den vorgestellten
Mechanismus (Abonnement, Updates etc.).
Implementationsseitig besitzen Voodie Objekte eine Funktion, die
Events behandeln kann. Dabei kann nötigenfalls die Eventbehandlungsfunktion
der jeweils übergeordneten Basisklasse aufgerufen werden,
um allgemeinere Events automatisch behandeln zu lassen.
In der vorliegenden Implementation können die meisten Events
durch die Eventbehandlungsroutine der Basisklasse voodie_base_object_c
behandelt werden. Dies betrifft insbesondere alle Events, die
für die Mechanismen (Objektsubscription, Übergabe der
Objektkontrolle usw.) notwendig sind. Objekte abgeleiteter Klassen
behandeln nur die Events, die in den Basisklassen nicht behandelt
werden können, bzw. die nur für das jeweilige Objekt
relevant sind.
Die Klasse voodie_threaded_object_c
und davon abgeleitete Klassen definieren Objekte, die eine Funktion
zur Steuerung des Objektverhaltens besitzen. Diese Funktion läuft
in einem separaten Thread (also parallel zum Rest der Applikation
und anderen Thread-Funktionen) ab. In AVIARY (ab Seite 25 und
/SNO94/) werden lightweight objects vorgeschlagen. Objekte mit
Thread-Funktion können ebenfalls als leichtgewichtige autonome
Objekte in diesem Sinne angesehen werden.
Die Implementation eines Voodie Objektes kann dann so erfolgen,
als ob keine anderen Aktionen ausgeführt werden müßten.
Dies vereinfacht die Implementation der Voodie Objekte sehr stark.
Die Eventbehandlungsroutine läuft asynchron zur Thread-Funktion.
Konkurrierende Datenzugriffe müssen dabei durch den Einsatz
einer (standardmäßig vorhandenen) Mutexvariable
synchronisiert werden.
Soll z.B. ein Voodie Objekt implementiert werden, welches
einen Ball simuliert, so könnte die Implementation im Pseudocode
etwa so aussehen:
Simulationsschleife [thread_function()]:
durchlaufe, solange du lebst:
wenn du Master bist:
berechne Position,
Flugrichtung,
Geschwindigkeit,
Beschleunigung
mit Hilfe der vorherigen Werte,
beachte dabei Umwelteinflüsse (Gravitation etc.) und
andere Objekte (Kollisionen)
benachrichtige alle Deine Abonnenten von den neuen Werten
[update_slaves("<all_the_values>");]
wenn du Slave bist:
mache gar nichts
wenn dein Status EXIT ist,
begehe Selbstmord
[suicide()]
generiere in jedem Fall ein Event für das Ausgabemodul
[enqueue_a_render_event ("Display me and use
<all_the_values>");]
Diese Art der Implementation bedeutet, daß einzig das Masterobjekt
Simulationsberechnungen ausführt. Das Verhalten der Slaveobjekte
wird nur durch UPDATE
Events vom Masterobjekt beeinflußt.
Es ist leicht einzusehen, daß dadurch ein sehr starker
Kommunikationsaufwand durch die Master-Slave Synchronisation entsteht.
Eine etwas andere Implementation löst dieses Problem dadurch,
daß auch die Slaveobjekte ihr Verhalten berechnen. Im Pseudocode
also so:
durchlaufe solange du lebst:
wenn du Master bist:
berechne Position,
Flugrichtung,
Geschwindigkeit,
Beschleunigung
mit Hilfe der vorherigen Werte,
beachte dabei Umwelteinflüsse (Gravitation etc.) und
andere Objekte (Kollisionen)
wenn Interaktionen mit anderen Objekten aufgetreten sind.
benachrichtige alle Deine Abonnenten von den neuen
Werten,
[update_slaves("<all_the_values>");]
wenn du Slave bist:
berechne Position,
Flugrichtung,
Geschwindigkeit,
Beschleunigung
mit Hilfe der vorherigen Werte,
beachte dabei Umwelteinflüsse (Gravitation etc.) und
andere Objekte (Kollisionen)
wenn dein Status EXIT ist,
begehe Selbstmord
[suicide()]
generiere in jedem Fall ein Event für das Ausgabemodul
[enqueue_a_render_event ("Display me and use
<all_the_values>");]
Eventbehandlungsroutine [handle_event(event)]:
durchlaufe, wenn ein Event vorliegt:
lasse die Basisklasse(n) alle Events behandeln,
die dort behandelt werden können
behandle unbehandelte Ereignisse wie folgt:
Event ist Update-event und du bist Slave:
setze Deine Werte auf die Werte aus
dem Update Event
benachrichtige auch alle Deine Abonnenten
von den neuen Werten
[update_slaves("<all_the_values>");]
ansonsten:
das Event kann nicht behandelt werden.
|
4.8.1 |
Klassenbeschreibung |
|
Bei der praktischen Implementierung der Voodie Objekte bietet
sich die Aufstellung einer Klassenhierarchie an. Basis dieser
Hierarchie bildet eine Klasse, deren Instanzen alle grundlegenden
Eigenschaften eines Voodie Objektes besitzen. Von dieser Klasse
werden weitere, spezialisierte Klassen abgeleitet. Abbildung 12
stellt den Zusammenhang der Klassen in der Hierarchie dar.
Im folgenden werden die einzelnen Klassen genauer beschrieben.
|
voodie_ base_ object_c |
Voodie_base_object_c
ist die Basisklasse für alle Objekte. Objekte dieser Klasse
besitzen folgende öffentliche Attribute:
|
|
C++ Code |
Beschreibung |
object_id_c _my_id; |
Objektidentifizierung des Objektes |
char _class_name[100]; |
Name der Klasse des Objektes |
bool _subscribable; |
Wenn diese Variable auf FALSE
gesetzt wird, kann das Objekt nicht abonniert werden |
object_id_c _master_id; |
Diese ObjektID ist die Identifizierung des
Abomasterobjektes |
object_state_c _state; |
Der Status des Objektes kann MASTER,
SLAVE oder EXIT sein. |
id_list_t _slave_objects; |
Eine Liste, in die ObjektIDs der Abonnenten stehen. |
Alle Objekte dieser und abgeleiteter Klassen besitzen folgende
öffentliche Funktionen:
C++ Code |
Beschreibung |
voodie_base_object_c (int id); |
Konstruktor für das Objekt. |
virtual ~voodie_base_object_c; |
Destruktor für das Objekt. |
virtual int handle_event (event_c event); |
Diese Routine besorgt die Eventbehandlung. |
virtual void update_slaves
(const char* update_string); |
... sendet den übergebenen String als UPDATE
Event an alle Abonnenten. |
Objekte der Klasse voodie_base_object_c
(und Objekte aller abgeleiteten Klassen) sind in der Lage, auf
folgende Events zu reagieren:
STATE = EXIT
SEND_OI
REQ_UPDATE ID = <slave_id>
SUBSCRIBE ID = <slave_id>
UNSUBSCRIBE ID = <slave_id>
als Masterobjekt: als Slaveobjekt:
STATE = SLAVE STATE= MASTER
REQ_OWNER ID = <slave_id> MASTER_ID = <new_master_id>
SET_OWNER ID = <old_master_id> REQ_REQ_OWNER
|
voodie_ threaded_ object_c |
Die Klasse voodie_threaded_object_c
implementiert die Funktionalitäten, die ein Objekt mindestens
besitzen muß, wenn seine thread_function()
in einem separaten, unabhängigen Thread laufen soll. Dieser
separate Thread dient der Implementation des Objektverhaltens
und wird mit Objekt-Thread bezeichnet.
Diese Klasse ist von voodie_base_object_c
abgeleitet. Folgende Attribute sind zusätzlich vorhanden:
C++ Code |
Beschreibung |
pthread_mutex_t *_mutex; |
diese Mutex wird vom Objekt benutzt, um
konkurrierende Zugriffe auf interne Variablen durch die
Thread Funktion und die Eventbehandlungsfunktion zu
synchronisieren |
pthread_t _object_thread; |
Datenstruktur für Threaddaten |
voodie_threaded_object_c
besitzt folgende öffentliche Funktionen:
C++ Code |
Beschreibung |
virtual void run (void); |
generiert Thread und startet
thread_function() |
virtual void thread_function (void); |
Diese Funktion kann von abgeleiteten Klassen
überladen werden, um das Verhalten des Objektes zu implementieren. |
|
voodie_ oi_ object_c |
Die Klasse, deren Instanzen OI-Objekte sind, heißt voodie_oi_object_c.
Diese Klasse ist von voodie_threaded_object_c
abgeleitet. Auf jeder Station muß ein solches OI-Objekte
vorhanden sein. Das OI-Objekt besitzt in der jetzigen Implementation
die ObjektID 1. Das OI-Objekt hat die Aufgabe, eingehende OI Events
in eine Liste einzutragen und regelmäßig zu prüfen,
ob die Objekte noch immer existieren. Zusätzlich muß
das OI-Objekt Events der FormSUBSCRIBE OI = <num>
verarbeiten können
(also entsprechende Abonnentenobjekte generieren).
OI-Objekte selbst können nicht abonniert werden, d.h. die
Membervariable subscribable
wird bei der Erzeugung des OI-Objektes explizit auf FALSE
gesetzt.
Neben den Attributen der Basisklassen verfügt dieses Objekt
zusätzlich über eine OI-Liste oi_list_t _oi_list.
Die Klasse redefiniert die Thread-Funktion und die Eventbehandlungsfunktion
der Basisklasse voodie_threaded_object_c.
Objekte der Klasse voodie_oi_object_c
sind in der Lage, auf folgende Events zu reagieren:
OI ID = <id> CLASS = <class_name> STATE = <state_val>
SUBSCRIBE OI = <num>
SHOW_LIST
Alle anderen Events werden durch die Basisklassen bearbeitet.
Dazu genügt es, voodie_threaded_object_c::handle_event()vor
der eigentlichen Eventbehandlung durch voodie_oi_object_c
aufzurufen.
|
voodie_ nonthreaded_ object_c |
Objekte dieser Klasse besitzen lediglich eine Funktion zur Ereignisbehandlung.
Ein Thread-Funktion ist nicht vorhanden. voodie_nonthreaded_object_c
kann als Basisklasse für Objekte dienen, die kein eigenes
Verhalten besitzen (z.B. statische Umgebungsobjekte). Es ist denkbar,
daß sehr viele Objekte einer virtuellen Umgebung solche
statischen Objekte sind, da die meisten Gegenstände ihr Verhalten
nicht eigenständig ändern müssen. Verhaltensänderungen
können bei Objekten dieser Klasse über Ereignisse ausgelöst
werden.
|
voodie_ first_ object_c |
Voodie_first_object_c
bildet die erste Beispielimplementation für eine Klasse,
deren Instanzen echte Voodie Objekte sind. Diese Klasse ist von
voodie_threaded_object_c
abgeleitet.
Mit voodie_first_object_c wurden Mechanismen, die der
Reduktion des Synchronisationsaufwandes
dienen können, implementiert. Objekte dieser Klasse besitzen
als Stellvertreter für echtes Verhalten eine Variable, die
bei jedem Simulationsschritt um eins erhöht wird. Im einfachsten
Fall erhöht lediglich das Masterobjekt diese Variable und
benachrichtigt den Rest der Objektgruppe davon. Die jetzige Implementierung
sieht allerdings vor, daß auch die Slaveobjekte diese Variable
verändern können. Das Masterobjekt sendet nicht nach
jeder Veränderung eine Synchronisationsnachricht, sondern
nur in bestimmten Abständen. Die Slaveobjekte verändern
die Variable eigenständig und gleichen den Wert der Variable
mit dem Masterobjekt ab, wenn eine Synchronisationsnachricht ankommt.
Durch diese Vorgehensweise kann auch Dead Reckoning implementiert
werden.
Die Thread-Funktion dieser Klasse sieht im Pseudocode so aus:
while (!suiceded) {
lock_mutex()
enqueue_a_render_event ("message for RENDERER:
Display me and my Attributes");
if (_state == MASTER)
i++
release_mutex()
if (0 == (i%25))
update_slaves("i is <value_of_i>");
if (_state == SLAVE)
i++
if (_state == EXIT)
suicide()
wait_a_few_moments()
}
Die Eventbehandlungsfunktion sieht so aus:
// Alle Events, die die Basisklassen behandeln
// können, behandeln
lock_mutex()
handle_status = voodie_threaded_object_c::handle_event(event);
if (EVENT_UNHANDLED == handle_status) {
if (event == REQ_UPDATE ID = <slave_id>) {
send_update_to_one_slave(slave_id, "i is <value_of_i>")
handle_status = EVENT_HANDLED
}
if (_state == SLAVE) &&
(event == "UPDATE i is <value_of_i>") {
i = <value_of_i>
set_attributes_on_update_event()
update_slaves("i is <value_of_i>")
handle_status = EVENT_HANDLED
}
}
release_mutex()
return (handle_status)
voodie_first_object_c
ist also in der Lage, die Events
REQ_UPDATE ID = <slave_id>
UPDATE i is <value_of_i>
zu behandeln. Alle anderen Events werden durch die Basisklassen
bearbeitet.
|
4.9 |
Funktionsbeschreibung des Prototypen |
|
Nachdem der Prototyp gestartet wurde, ist es möglich, verschiedene
Ereignisse an die Verwaltungsmodule bzw. Objekte des Prototyp
zu senden, und damit die Mechanismen zu testen. Die Ereignisse
werden durch die Auswahl aus einem Menü erzeugt. Abbildung 13
zeigt den Prototypen mit aufgeklapptem Ereignismenü.
Es können Objekte der Klassen
voodie_first_object_c
voodie_threaded_object_c
voodie_nonthreaded_object_c
generiert werden.
Es ist weiterhin möglich, von einer anderen Station,
auf der auch der Prototyp läuft, die OI der dort vorhandenen
Objekte anzufordern. Nach der Auslösung eines entsprechenden
Ereignisses (SEND_ALL_OI
Event) durch die Anwahl des Menüpunktes
Events | Request All OI from ...
erhält das OI-Verwaltungsobjekt (ObjektID = 1) die OI
der entsprechenden Objekte und stellt diese in einer Liste dar
(Abbildung 14).
Das Abonnement dieser Objekte erfolgt dadurch, daß dem OI-Objekt
entsprechende SUBSCRIBE OI Events zugesandt werden. Die
Übergabe der Objektkontrolle
von einem Objekt einer Objektgruppe zu einem anderen, kann getestet
werden, indem eine Objektgruppe erzeugt wird und dem Masterobjekt
ein STATE = EXIT
Event geschickt wird. Das Masterobjekt gibt dann die Kontrolle
ab, weil es davon ausgeht, demnächst gelöscht zu werden.
Der Prototyp generiert eine Protokolldatei. In dieser Protokolldatei
werden die Aktivitäten des Programmes und Debug Informationen
festgehalten. Der Umfang der zu protokollierenden Daten ist dabei
über die Kommandozeile einstellbar.
|
|