[Studienarbeiten: Kommunikation zwischen PDAs]

3.4 IrChat unter EPOC32 für Psion S5

3.4.1 Allgemeine Beschreibung von IrChat

Irchat wurde mit Hilfe von EIKON implementiert. EIKON ist das Standard GUI (Graphic User Interface) von EPOC32, und damit hat IrChat das typische Aussehen und 'Feeling' eines Psion Programms. Als Übertragungsprogramm für Texte und Dateien unterstützt IrChat auf dem Psion auch Point-To-Multipoint, mit dem es prinzipiell möglich ist, mit mehreren Geräten gleichzeitig verbunden zu sein.

3.4.2 Beschreibung der Benutzerschnittstelle

Die Schnittstelle zum Benutzer bietet hauptsächlich ein großes Textfenster zur Darstellung der empfangenen Meldungen, in dem der Benutzer auch selbst Eingaben oder Entfernungen von Texten vornehmen kann. Eine weiteres, kleineres Textfenster unterhalb davon dient zur Eingabe eigener Meldungen und spezieller Kommandos.
Ganz rechts befinden sich Schaltflächen für die wichtigsten Befehle, dazwischen eine Leiste mit der Anzeige der verbundenen Geräte.Diese Anzeige dient mit ihren Schaltflächen auch zum Aktivieren und Deaktivieren der Verbindungen (manchmal möchte der Benutzer einen Text oder eine Datei vielleicht nicht allen verbundenen Geräten schicken). Die Textfenster unterstützen natürlich das Kopieren, Ausschneiden und Einfügen von Clipboard-Textdaten, dies ist wie üblich über eine Standardschaltfläche auf der linken Seite erreichbar. Direkt darüber kann das Menü abgerufen werden; es erlaubt das Speichern und Laden der Texte des Anzeigefensters, sowie einige Einstellungen, unter anderem den Kurznamen, unter dem das Gerät bei der Kommunikation bekannt ist.
Die folgende Grafik zeigt diesen Sachverhalt noch einmal:

Das Menü bietet die folgenden Kommandos:
 

Menü Untermenü Beschreibung
File Create new file Erzeugt ein leeres Anzeigefenster
  Open file Öffnet ein gespeichertes Anzeigefenster
  More/Save As  &  More/Save Speichert das Anzeigefenster
  Close Beendet das Programm
Edit Cut Schneidet die Auswahl in die Zwischenablage
  Copy  Kopiert die Auswahl in die Zwischenablage
  Paste Fügt die Zwischenablage ein
  Select all Wählt den gesamten Text des Anzeigefensters
  Delete/Delete all Löscht die Auswahl bzw. das gesamte Anzeigefenster
View Show toolbar Zeigt oder versteckt die rechte Menüleiste
  Show top toolbar Zeigt oder versteckt die obere Menüleiste
Tools Preferences Erlaubt die Wahl des Namens und des Hintergrunds
  Start server Startet den Server mit aktuellem SLSAP-SEL
  Stop server Stopt den Server
  Status Zeigt Serverstatus und Verbindungen an
  Scan only Führt einen Scan nach anderen Geräten durch ohne zu verbinden
  Help on IRCHAT Zeigt eine kleine Hilfe an
  About IRCHAT Zeigt die About-Box an

 
 
 Wie schon erwähnt, lassen sich über die Eingabe weitere Kommandos abrufen, die nun kurz vorgestellt werden.
Jedes dieser Kommandos beginnt mit einem Ausrufezeichen und hat vier Buchstaben

!iasq Klassenname Attributname Nummer_der_Verbindung

Führt einen IAS-Query aus, d.h. die Datenbank eines anderen Gerätes wird nach einem Attribut einer angegebenen Klasse gefragt. Dies ist momentan im Programm nur mit schon verbundenen Geräten möglich, theoretisch gehts auch mit nicht verbundenen Geräten.
 

Klassenname gibt den Namen der Klasse an, in der gesucht werden soll
Attributname gibt den Namen des Attributs an, das gesucht werden soll
Nummer der Verbindung die nullbasierte Nummer des verbundenen Gerätes.

 

!iasr Klassenname Attributname Eintrag

Registriert einen Eintrag in die Datenbasis des eigenen Gerätes. Momentan sind nur Zahlen als Eintrag möglich.
 

Klassenname in welche Klasse soll eingetragen werden?
Attributname in welches Attribut der Klasse?
Eintrag eine Nummer, die eingetragen werden soll

!stop

Stopt den Server, d.h. läßt keine Verbindung von außen mehr zu.

!strt    LSAP-SEL

Startet den Server. Ist dieser schon am Laufen, wird er zuvor gestoppt. Der LSAP-SEL gibt den lokalen LSAP an, auf den ein Gerät von außen zugreifen kann.

!dsel   LSAP-SEL

Setzt für einen Verbindungsversuch den LSAP-SEL fest, der benutzt werden soll. Dieser muß mit dem LSAP-SEL der Maschine übereinstimmen, zu dem verbunden werden soll.
 

3.4.3 Beschreibung der Implementierung

IrChat unter EPOC32 läßt sich in zwei Teile spalten. Die Benutzeroberfläche, realisiert unter EIKON, und die Schnittstelle zu IrDA (das Programm verwendet im moment grundsätzlich IrTinyTP). Diese Schnittstelle, realisiert als Objekt CIrdaIF (in der Datei irif.cpp, Deklaration in irif.h) muß von einer Klasse der Applikation bzw. Oberfläche erzeugt werden. Bevor die Funktionen der Klasse genutzt werden können, muß per CIrdaIF::init() ein Zeiger auf ein CIrdaIFObserver Objekt übergeben werden.
Dieses erhält in Zukunft alle Meldungen und Informationen von der Schnittstelle. CIrdaIFObserver ist eine rein abstrakte Klasse, d.h. eine beliebige Klasse der Applikation muß (unter anderem) von ihr erben und ihre Funktionen überschreiben. Als dritte Klasse ist schließlich noch CConnection deklariert. Diese Klasse repräsentiert  eine einzelne Verbindung zu einem anderen Gerät, ist aber für einen Benutzer der Schnittstelle nicht wichtig.
 

Beschreibung der Klassen

Hinweise zu den in der Definition benutzten Klassen:
TBool ist unter EPOC eine Klasse für bool'sche Werte, TInt eine 32-bit Zahl. Eine TDes-Klasse steht für einen Speicherbereich (normalerweise einen Text), TDesC ist die konstante Version davon.

CIrdaIFObserver

Wie schon erwähnt, müssen alle Funktionen von der Schnittstellennutzerklasse überschrieben werden. Alle Texte haben am Schluß kein Zeilenende. Es ist Aufgabe des Observers, sich hierum zu kümmern.
Im Falle von IrChat gibt NewMessage() den Text in das Anzeigefenster, InfoMessage gibt ein kurzlebiges Infofeld im oberen rechten Eck des Psion aus, und
ErrorMessage() erzeugt ein Hinweisfenster mit der übergebenen Meldung, das aktiv geschlossen werden muß.
Eine etwas sauberere Lösung ist das Übergeben eines Fehler/Statuscodes bei InfoMessage und ErrorMessage. Darauf wurde im Falle von IrChat der Einfachheit halber verzichtet.
 

virtual void NewMessage(TInt devNum, const TDesC& aText) Wird aufgerufen, falls eine Nachricht (aText) vom Gerät mit der Kennziffer devNum erhalten wird. 
Ist devNum -1, so ist die Meldung allgemein. 
Ist devNum -2, so soll die Meldung noch nicht mit einem Zeilenende abgeschlossen werden.
virtual void InfoMessage(const TDesC& aText) Gibt eine Statusmeldung (Z.B. ein Verbindungsende) an
virtual void ErrorMessage(const TDesC& aText) Wird aufgerufen, wenn ein Fehler aufgetreten ist
virtual void ConnectionsChanged() Die Anzahl oder der Name von Verbindungen hat sich geändert, bspw. nach einer Suche

 

CIrdaIF

 

TBool Init(CIrdaIFObserver* pObserver,TBool startserver=ETrue); Muß zu Beginn aufgerufen werden. Übergibt den Observer und legt fest, ob der Server sofort starten soll.
TInt  Scan(TBool DoConnect=ETrue); Sucht nach anderen Geräten und versucht sich, falls DoConnect wahr ist, mit diesen zu verbinden.
TInt  GetDeviceCount(); Gibt die Anzahl der verbundenen Geräte aus
TBool GetDeviceName(TInt devNum, TDes& aName); Trägt den Namen des Gerätes in aName ein, devNum gibt die nullbasierte Kennziffer des Geräts an
 void  PrintStatus(); Gibt über CIrdaIFObserver::Newmessage() eine Reihe von Statusmeldungen aus.
void  IASQuery(TDesC8 &aClassName,TDesC8 &aAttributeName,TInt devNum); Führt eine IAS-Abfrage durch. Ist im Moment nur mit verbundenen Geräten möglich (wird über devNum angegeben)
void  IASRegistration(TDesC8 &aClassName,TDesC8 &aAttributeName,TInt value); Trägt einen Wert in die eigene Datenbasis ein. Momentan sind nur Ganzzahleneinträge möglich.
TBool SendToDevice(TInt devNum, const TDesC& aText); Sendet eine Nachricht an ein angegebenes Gerät
TBool SendToActiveDevices(const TDesC& aText); Sendet eine Nachricht an alle aktiven Geräte
void  ActivateDevice(TInt devNum,TBool bactivate = ETrue); Aktiviert oder deaktiviert ein angegebenes Gerät, je nach wert von bactivate.
TBool IsDeviceActive(TInt devNum); Gibt zurück, ob ein Gerät aktiv ist.
void  StopServer(TBool silent=EFalse); Hält den Server an. Ist silent wahr, so erfolgt dabei keine Statusmeldung
TBool StartServer(TInt homeport=0); Startet den Server. Ist homeport null, so wird der alte SLSAP-SEL benutzt. Ansonsten wird homeport als neuer SLSAP-SEL gewertet.
void SetDLSAPSEL(TInt dsel); Setzt den DLSAP-SEL, um einen Verbindungsaufbau bei abweichernder SLSAP-SEL des Zielgeräts zu ermöglichen
TInt GetNextFileReceive(TDes& Filename); Liefert die Kennziffer des nächsten Gerätes, das eine Datei zusenden will. Filename wird mit dem Namen der Datei gefüllt. Liefert -1, falls gerade kein Gerät eine Datei zusenden will.
void StartFileTransfer(TInt devNum,TDes& fileName,TBool Start); Startet einen Filetransfer. Ist devNum kleiner Null, so wird ein File (filename gibt den Ordner und die Datei an) an alle aktiven Verbindungen geschickt, ansonsten wird ein File von einem anderen Gerät empfangen und unter filename gespeichert, falls Start wahr ist. Im anderen Fall wird das File abgelehnt.
void SetNickName(TDesC& nickname); Setzt den eigenen Namen und informiert die anderen Geräte

 

3.4.4 Ausgewählte Stelle des Sourcecode

Zur Verdeutlichung einiger grundlegender Prinzipien unter EPOC werden nun ein paar Zeilen des Sourcecodes genauer beschrieben.
 

 m_log().iAddr.SetPort(dlsapsel);
 TRequestStatus stat,statTimer;
 RTimer tim;
 tim.CreateLocal();
 m_socket.Connect(m_log().iAddr,stat);
 tim.After(statTimer,5000000L);
 User::WaitForRequest(stat,statTimer);
 if (statTimer.Int()==KErrNone)...

Obige Programmzeilen beschreiben den Verbindungsaufbau der CConnection Klassen. m_log enthält eine Addresssklasse iAddr, die das Ziel der Verbindung enthält.
Als erstes wird in dieser Addresse noch der Zielport (DLSAP-SEL) eingetragen. TRequestStatus repräsentiert einen Status für eine asynchrone Operation.
Diese sind im Beispiel RSocket::Connect() und RTimer::Afer(). Beide Funktionen blockieren den Programmablauf nicht, sind aber auch nach Ausführung noch nicht beendet. Connect() verbindet zu einem anderen Gerät, After() wartet eine bestimmte Zeit. Um zu überprüfen, ob ein asynchroner Aufruf beendet ist, muß der Funktion also ein Status übergeben werden. Dieser wird beim Aufruf auf den Wert KRequestPending gesetzt, d.h. Die Ausführung dauert noch an.
Connect() setzt in dem Status einen Fehler oder ein KErrNone (kein Fehler), sobald es durchgeführt ist. After() setzt das KErrNone nach den angegebenen Mikrosekunden. Die Funktion WaitForRequest, der die beiden Statusvariablen übergeben werden, wartet nun (blockierend) darauf, daß einer der beiden Befehle beendet wird. Später kann über den Wert der Variablen festgestellt werden, welcher Aufruf zuerst erfolgreich abgeschlossen wurde. Obiges Programmfragment implementiert also einen fünf Sekunden Time-Out für die Verbindung, d.h. nach fünf Sekunden wird der Verbindungsversuch automatisch abgebrochen.
Send() und Receive(), Funktionen von RSocket zum Datenaustausch, haben ebenfalls diese Statusvariablen. In einer Idlefunktion von CIrdaIF werden die Statusvariablen aller Verbindungen, die einen Receive ausführen, überprüft, ob in der Zwischenzeit eine Meldung angekommen ist. Ist dies der Fall, werden die Werte ausgelesen und der beendete Receive neu ausgeführt. Eine Aufspaltung in Threads wie unter Windows CE ist also trotz mehrfacher Verbindungsmöglichkeit nicht nötig.
 

3.4.4 Sourcecode

Deklaration in irif.h, Definition in irif.cpp
 

3.4.5 Compilieren des Programms

Die Konfigurationsdatei zur Erzeugung von IrChat ist IrChat.mmp. Falls als Verzeichnis nicht \EPOC32EX\IRCHAT gewählt wurde, muß dies in dieser Datei eingetragen werden. Danach lassen sich Projektdateien für Visual C++ unter Windows 95 oder NT erstellen. Dazu ist der Aufruf von makmake nötig.
Die Kommandozeile lautet: makmake irchat vc4 bzw makmake irchat vc5. Das Programm erzeugt dann eine Projektdatei (irchat.dsp) die in Visual C++ eingeladen werden kann. EPOC32 verfügt über einen Emulator unter Windows, der leider keine Programme ausführen kann, die auf die Infrarotschnittstelle zugreifen (Absturz). Dieser ist (unter Ausschließung der IrDA-Komponenten) also nur zum leichten Programmieren der Benutzeroberfläche nützlich.
Um die Applikation für den Psion zu erstellen, ist das Kommando makmake irchat marm notwendig. Dieses erzeugt das File irchat.marm, mit dem dann per nmake -f irchat.marm die Programmdatei (und die Resourcedatei) erstellt werden kann. Diese sind übrigens im \EPOC32\RELEASE\MARM\REL zu finden und müssen dann in ein Verzeichnis IrChat unter \System\Apps auf den Psion kopiert werden. (Die Namen der Dateien lauten irchat.app und irchat.rsc).
Wichtig für das Laufen des Programms auf dem Psion ist, falls noch nicht vorhanden, die Installation der Standard-C-Bibliothek (estlib.dll) ins Verzeichnis \SYSTEM\LIBS. Bleibt noch anzumerken, das zu Erstellung notwendige Verzeichnisse mit makemake -makework irchat marm bzw. makemake -makework irchat vc4 erstellt werden können.
 

3.4.5 Ausführen des Programms auf dem Psion S5

Das Programm reagiert mit einer Fehlermeldung, falls auf die Infrarotschnittstelle nicht zugegriffen kann. Der Psion unterstützt nicht gleichzeitig eine Kabel- und Infrarotverbindung. Über das Steuermenü bzw. CTRL-L läßt sich auf dem Psion die korrekte Einstellung vornehmen.
 

3.4.6 Weiterentwicklung des Programms

 

[Studienarbeiten: Kommunikation zwischen PDAs]