Gezeigt wird das grundsätzliche Vorgehen zur Erstellung eines einfachen Device-Modules für IP-Symcon, die Implementierung der Basis und der der spezifischen Funktionalität, die Arbeit mit Properties und Statusvariablen sowie die Kommunikation über Interface
Das DemoModul implementiert ein Textinterface, bei dem Daten von PHP oder von einem passenden Modul (hier Serversocket) geloggt werden kann. So ist z.B. die Übertragung von Daten aus Unix-Systemen über den TCP-Port möglich
Download, für den Sourcecode bitte Disclaimer beachten
Der Sourcecode ist nach meinem besten Wissen fast Zeile für Zeile dokumentiert. Fragen sollten im IP-SymconForum besprochen. Die Screenshots beschreiben jedoch eine ältere Version. werden.
Einrichtung der Arbeitsumgebung
Zur Erstellung von IPSymcon-Modulen wird Delphi2006/2007 oder TurboDelphi benötigt. Alle anderen Versionen gehen leider nicht.
Zunächst ist das SDK (zur Zeit Version V2.3 oder 2.5beta) aus dem IP-Symcon Entwicklerbereich zu laden.

Der
Screenshot ist für Version 2.04, es ist jeweils die aktuelle
Version auszuwählen.
Das SDK ist dann auszupacken und die Verzeichnisstruktur anzulegen. Unterhalb des ~modules Ordner kann dann das DemoModul ausgepackt werden

Soll das Demo-Modul geklont werden, müssen die Files unter einen neuen Namen kopiert und ein paar manuelle Änderungen im Projektfile gemacht werden. Grundsätzlich ist es eine gute Prxis, für alle Units eindeutige Namen zu vergeben. Es sollten keinesfalls für alle verschiedenen Module immer wieder die gleichen Filenamen gewählt werden. Im bdsproj-File muss eine neue GUID vergeben, mindestens die existierende gelöscht werden. Außerdem ist der neue Name des Projektfiles zu hinterlegen. Alle weiteren Änderungen können dann in der Delphi-Oberfläche gemacht werden.

In Delphi sind nun die Projekteinstellungen zu überprüfen bzw. zu ändern. Als erstes sind die Pfade an die eigene Umgebung anzupassen

Nächster Punkt sind die Einträge in der Versionsverwaltung. Ich verwende diese Einstellung, bei der die Buildversion hoch zählt. Man kann das aber auch komplett deaktivieren.

Zum Debuggen muss man auch die Hostanwendung definieren. Die Pfade müssen angepasst werden. Zum Debuggen darf auf dem PC kein weiteres IPS laufen!

Wenn alles funktioniert, kann man jetzt Module erstellen. An Hand der Statistik sieht man, das trotz relativ wenig eigenem Quelltext eine beachtliche Zahl an zeilen verarbeitet werden muss.

Module für IPSymcon
Module für IPSymcon sind immer DLLs. Zu 90% kann die Basis-Funktionalität eines IPS-Moduls aus einer Vorlage (wie dem hier vorgestellten DemoModul) per Copy/Paste/Replace übernommen werden.
Ich teile den Sourcecode immer in mindestens 3 Files auf.
Projektfile(Endung .dpr)
Grundlegende
Moduldefinition ohne jegliche Funktionalität.
Beispiel
demologger.dpr
Interface-Unit (Uxyz_interface.pas).
Dort sind die
öffentlichen Interfacedefinitionen enthalten. (wie in
IPSModulTypes.pas).
Beispiel UModuleinterface.pas
pro Modul(Splitter bzw. Device) eine eigene Unit
(Uxyz_device.pas bzw Uxyz_splitter.pas).
Diese etwas
umfangreichere Unit beeinhaltet bis zum Abschnitt
"//---Implementation IIPSDevice Modulfunctionen (Actions)"
mit Ausnahme der Get/Set-Funktionen nur Basis-Funktionen, um ein
IPS-Modul zu definieren.
Beispiel UDevice.pas
optional Units mit Hilfsfunktionen bzw aus anderen Programmen übernommenen Logik (gemeinsame Codebasis)
Wichtig ist, das für jedes Modul/Library/Interface immer eigene GUIDs generiert werden (STRG-SHIFT-G), damit es keine Überschneidungen bei mehreren HomeMade Modulen gibt. Aus dem gleichen Grund sollten alle Namen, TypeDefinitionen und Bezeichnungen wie Hersteller, ModulName usw. mit eigenen Namen versehen werden. Sonst haben wir in einem Jahr 10 verschiedene „Logger“-Module :-)))
Funktionen mit komplizierter Logik sollten ersten in StandAlone-Programmen verprobt werden. Lagert man diese Logik in eigene Units aus, aknn man diese meist 1:1 im Modul einsetzen, indem man einfach nur die Funktionen durch Aufrufe der vorhandenen Logik implementiert.
Es können im Prinzip alle in IPS vorhandenen und bekannten Funktionen genauso wie im PHP oder der Konsole auch in Delphi verwendet werden (und umgekehrt) Eine entscheidende Hilfe ist die Befehlsreferenz und die Definitionen in IPSTypes und IPSModulTypes. Geht man in der Delphi-Oberfläche mit dem Cursor auf eine IPS-Funktion, kann man nach Drücken der STRG-Taste auf den entstandenen Link klicken und gelangt so zur Implementierung der Funktion. Das ist oft eine wertvolle Hilfestellung für das Verständnis, was warum passiert.
Projektfile
Kann komplett kopiert werden, nur die
eigenen Units,Namen und GUID sind auszutauschen.
const LibInfo: TIPSLibraryInfo = ( mUniqueID : '{30dec678-a21a-41f8-840b-cfd52a4a9436}'; //Für jedes Modul mit STRG-SHIFT-G neu erstellen !! //-------------------------- //nächste 3 Zeilen anpassen, Build muss leider Manuell hizugefügt werden mAuthor : 'Dein Name'; mURL : 'Deine.URL'; mName : 'DemoLogger Library'; mVersion : {CompileVersion}$0232{/CompileVersion}; { Hi - MajorV, Lo - MinorV $232=2.50} mBuild : {CompileBuild}3{/CompileBuild}; mDate : {CompileTime}0{/CompileTime};
...
//Register Classes ModuleRegistry.RegisterModule(TIPSDevice, TypeInfo(IIPSDevice), 'MyDevice');
Interface-Unit
Auch diese kleine Datei kann als
Vorlage kopiert werden. Für ein neues Modul sind dort ebenfalls
die GUIDs und der Interfacename auszutauschen. Zusätzlich
müssen die Deklarationen der Interfacefunktionen entsprechend
der eigenen Funktionen ausgetauscht werden
type IIPSDevice = interface(IInvokable) ['{2097D725-4B79-49CF-887A-1E84C38BEB92}'] //Für jedes Interface mit STRG-SHIFT-G neu erstellen !! //Diese Funktionen werden jetzt unter PHP sichtbar //setzt Filenamen für Logfile (relativ zu ips.exe) procedure setLogFile(FileName:string); stdcall; //Gibt einen String an das Modul und gibt die Anzahl der Zeichen zurück function setLine(Text:string):integer; stdcall; //gibt den letzten Eintrag zurück function getLine:string; stdcall; end;
Device-Unit
Die DeviceUnit besteht bis zum
Abschnitt "//---Implementation IIPSDevice Modulfunctionen
(Actions)" aus Definitionen, die für jedes Modul gelten.
So ist es ein guter Startpunkt, zunächst auch diese Datei
komplett zu kopieren. Allerdings muss hier etwas mehr Aufwand zur
Anpassung an eigene Module getrieben werden. So sollte jedes Modul
seinen eigenen Unit-Namen bekommen, das IPS-Objekt bekommt einen
eigenen Namen und anschliessend müssen deshalb alle
Funktionsköpfe den geänderten Namen bekommen. Letzteres
geht am Besten durch ein globales Suchen/Ersetzen auf TIPSDevice;
gemacht werden
Hier ist das IPS-Object definiert. Der Name TIPSDevice muss geändert ...
//Hauptobjekt des Moduls, implementiert die folgenden Interfaces. TIPSDevice = class(TIPSModuleObject, //standard IIPSModule, //standard IIPSReceiveString, //TextEmpfang aus anderen Modulen ermöglichen IIPSDevice) //eigenes Interface
... und dann global ersetzt werden
constructor TIPSDevice.Create(IKernel: IIPSKernel; InstanceID: TInstanceID);
beginDie Felder in TSettings sollten den Properties entsprechen
//alle persistenten Status-Informationen werden mit get/set hier zugewiesen TSettings = record Logfile:String; echo:boolean; end;
Properties und Status-Variablen werden üblicherweise schon im Constructor definiert
<RegisterStrProperty( 'LogFile',GetLogFileName,SetLogFileName); RegisterBoolProperty( 'Echo',GetEcho,SetEcho); //oder auch //RegisterIntProperty( 'IntVal',GetIntVal,SetIntVal); {Status-Variablen definieren (optional) Diese StatusVariablen werden dann in der Konsole und Frontend automatisch angezeigt Die StatusVariablen sollten auch ein profil bekommen, wenn sie in der Anzeige formatiert dargestellt werden sollen. Details bitte dem SDK (UIPSTypes ab Zeile 1925 für SDK 2.04) entnehmen. Im WebFrontend sind in 2.0.4 aber nur die DefaultProfile implementiert, so das man bei eigenen Profile diese dort selber nachbilden muss. } { In 2.10 gibt man den Namen eines existierenden Defaultprofiles oder eines eigenen Profiles mit, welches man vorher mit CreateXXXXProfile angelegt hat. Bei eigenen Profilen kann man auch ein eigenes Icon angeben oder man wählt ein existierendes aus, wie hier. } TIPSVarProfile.CreateStringProfile('DemoTextProfile', 'Information','', '-->logged'); RegisterVariable('LastLineVariable','LastText',vtString,'DemoTextProfile'); //Beispiel für einen Schalter mit default Profile //RegisterVariable('StatusVariable','Status',vtBoolean,GetDefaultProfile(dpSwitch));
Die TextDefinitionen wie Vendor(Herstellerauswahl), Aliases(unter all diesen Namen erscheint das Modul in der Instance-Auswahl) und Modulname müssen angepasst werden
//------------------------------------------------------------------------------ class function TIPSDevice.GetVendor(): String; //Das Modul erscheint unter diesem Herstellernamen in der Auswahl begin Result := 'MySelf'; end; //------------------------------------------------------------------------------ class function TIPSDevice.GetAliases(): TStringArray; //Das Modul erscheint unter allen diesen Namen in der Auswahl begin SetLength(Result, 2); //Anzahl der Namen entsprechend anpassen Result[0] := 'MyDevice'; Result[1] := 'MyLogger'; //optional end; //------------------------------------------------------------------------------ class function TIPSDevice.GetModuleName(): String; { gibt den Namen des Moduls für die Modulverwaltung zurück unter diesem Prefix z.B.die Einträge im Log vorgenommen } begin Result := 'MyDemo'; end;
letztendlich müssen Typ und Interface und ParentRequirements(if any) definiert werden.
//------------------------------------------------------------------------------ class function TIPSDevice.GetModuleType(): TIPSModuleType; //definiert den Modultyp. begin Result := mtDevice; //Typ ist i.d.R. mtDevice oder mtSplitter (siehe UIPSTypes) end; //------------------------------------------------------------------------------ class function TIPSDevice.GetModuleID(): TStrGUID; //gibt die GUID des Moduls für die Modulverwaltung zurück begin Result := GUIDToString(IIPSDevice); //Will return Interface GUID end;
...
//------------------------------------------------------------------------------ class function TIPSDevice.GetParentRequirements(): TStrGUIDs; //Abfrage, ob der Parent ein kompatibles Interface hat begin SetLength(Result, 1); Result[0] := GUIDToString(IIPSSendString); end; //------------------------------------------------------------------------------ class function TIPSDevice.GetImplemented(): TStrGUIDs; //Interface-Kompatibilität begin SetLength(Result, 1); Result[0] := GUIDToString(IIPSReceiveString); end;
...
//Check Parent
RequireParent(IIPSSendString,false);Letztendlich fehlen noch die GetSet-Funktionen für die Properties
// Implementation Get/Set-Funktionen //------------------------------------------------------------------------------ //Get Funktion für StringProperty Logfile function TIPSDevice.getLogFileName:string;stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'Read Property Logfile...'); result:=fchangedsettings.Logfile; end; //------------------------------------------------------------------------------ //Set Funktion für StringProperty Logfile procedure TIPSDevice.setLogFileName(FileName:string);stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'Set Property Logfile'); if (FileName=fchangedSettings.Logfile) then begin exit; //nichts zu tun, wenn wir das Gleiche schon haben end; fchangedsettings.Logfile:=FileName; //ablegen {setzt ein Flag zum Speichern und erzeugt eine SettingsChanged-Message} settingschanged; end;
/Get Funktion für BoolProperty echo function TIPSDevice.getEcho;stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'Read Property Echo...'); result:=fchangedsettings.echo; end; //------------------------------------------------------------------------------ //Set Funktion für BoolProperty Echo procedure TIPSDevice.setEcho(bEcho:boolean);stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'Set Property Echo'); if (bEcho=fchangedSettings.echo) then begin exit; //nichts zu tun, wenn wir das Gleiche schon haben end; fchangedsettings.echo:=bEcho; //ablegen {setzt ein Flag zum Speichern und erzeugt eine SettingsChanged-Message} settingschanged; end;
Die eigentliche Funktionalität muss hereingebracht
werden --)))
Diese habe ich der Funktionsbeschreibung kommentiert
Beschreibung des Demo-Moduls
Komponenten:
MyDevice Einfacher Text-Logger
Kompatibilität und Voraussetzungen
IP-Symcon Kernel/SDK Version 2.04+
Die Abweichungen zu höheren Versionen sind der SDK-Dokumentation zu entnehmen
Modul Funktionalität
Es sollen Texte aus PHP und von Einem ServerSocket-Modul geloggt werden. Texte von PHP sollen mit „PHP -->“+Text aufgezeichnet werden, Text über das ServerSocket-Modul dagegen mit dem Prefix „Interface -->“. Die letzte Aktion soll in einer StatusVariable abrufbarsein. Optional soll ein Logfile gescrieben werden. Optional soll zusätzlich der hereinkommende Text an den Parent zurück geschickt werden (echo).
Modul Umsetzung
Die Daten aus PHP und dem Socket-Modul werden getrennt entgegengenommen und dann über eine gemeinsame Funktion verarbeitet.
PHP-Funktion zur Entgegennahme des Textes. Als Rückgabe wird die Länge des Textes vereinbart
function TIPSDevice.setLine(Text:string):integer;stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'SetLine entered...'); //sendData erzeugt Eintrag im DebugFenster senddata('setLine','entered'); //Ausführung einer Logik-Funktion handleData('PHP',Text); //Rückgabe eines Wertes Result:=length(Text); end;
Die Daten aus dem ServerSocket-Modul werden durch die Implementation des Interfaces "ReceiveString" mit der Funktion ReceiveText entgegengenommen
procedure TIPSDevice.ReceiveText(Text:string); stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'External Data received'); //sendData erzeugt Eintrag im DebugFenster SendData('Received', ' Msg:'+Text); //Ausführung der Datenbearbeitung handleData('Interface',Text); end;
Zum Schluss noch die Funktion zum aktivieren des Echos, welche nichts weiter macht, als die entsprechende Property zu setzen.
//---PHP Stub zum setzen einer Property procedure TIPSDevice.EnableEcho(bEcho:boolean);stdcall; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'Enable Echo entered...'); //sendData erzeugt Eintrag im DebugFenster senddata('Enable Echo','entered'); //Einfach nur die Set Methode aufrufen setEcho(bEcho); end;Das ist wirklich alles !!
jetzt noch die gemeinsame Funktionen handleData, DoEcho und LogMe. In handleData wird die als Demo-Aufgabe geforderte Einstellung des Prefixes gemacht, die Variable gesetzt und die Loggingfunktion aufgerufen. Zusätzlich wird auch noch in die DoEcho funktion aufgerufen, welche selbst entscheidet, ob sie wirklich ein echo aussendet oder nicht.
Die Logfunktion nutzt die Property Logfile, in der ein Filename hinterlegt ist (oder auch nicht). Ist ein Filename da, wird auch versucht zu loggen. Das Try-finally-construct soll schreibfehler abfangen.
procedure TIPSDevice.HandleData(From,Text:string); //Verarbeitet die ankommenden Daten in einer gemeinasmen Methode für alle Quellen var s:string; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'Handle Data entered'); //sendData erzeugt Eintrag im DebugFenster senddata('HandleData','entered'); //Demo-Funktion=Text zusammensetzen s:=from+' -->'+Text; lastline:=s; //Text loggen logme(s); //Echo zurückgeben, wenn gefordert DoEcho(Text); //Statusvariable setzen fKernel.VariableManager.WriteVariableString(GetStatusVariableID('LastLineVariable'), s); senddata('HandleData','finished'); end;
procedure TIPSDevice.logMe(Text:string); var log:TextFile; fname:string; begin //Nur zur Demo!! Eintrag im Kernellog zum Nachvollzienen der Aufrufe LogMessage(KL_DEBUG,'LogMe entered...'); //sendData erzeugt Eintrag im DebugFenster senddata('LogMe','entered'); //property Logfile abfragen fname:=GetLogFileName; //kein Name=nichts loggen, also beenden if fname='' then exit; //fileöffenen( Anhängen oder neu) assignfile(log,fname); if fileexists(fname) then append(log) else rewrite(log); //schreiben´, dabei exceptions abfangen try writeln(log,Text); finally //file schliessen closefile(log); end; end;procedure TIPSDevice.DoEcho(Text:string); { implementiert PseudoEcho-Funktion gemäß Wunsch in http://www.ip-symcon.de/forum/f13/beispielprojekt-doku-source-veroeffentlicht-7605/index2.html#post66603 ->sendet den hereinkommenden Text an den angeschlossenen Parent zurück, soweit dieser das unterstützt. } var s:string; parent:IIPSModule; ifsend:IIPSSendString; begin s:=Text; senddata('DoEcho','entered'); //test, ob echo property gesetzt ist if getEcho() then begin senddata('DoEcho','Echo requested'); //Parent Instance holen parent:=getParent(); //Test ob Instance existiert if parent=NIL then begin //nein->Melden senddata('DoEcho','Kein Parent verbunden, skip'); end else begin //test ob IIPSSendString-Interface existiert if supports(parent,IIPSSendString,IfSend) then begin //existiert, kann senden senddata('DoEcho','Alles Ok'); ifsend.SendText(s); end else begin //Problem mitteilen senddata('DoEcho','Send String not supported'); end; end; end else begin //kein Echo, weil nicht aktiviert, mache Debug-Eintrag senddata('DoEcho','Echo not requested'); end; senddata('DoEcho','finished'); end;
Damit ist die komplette Modulfunktionalität implementiert!!
Der restliche Quellcode ist im wesentlichen nur noch formelle Modulimplementation
Modul Installation
Die Datei demologger.dll muss in das IP-Symcon Modules Verzeichnis kopiert werden. Anschließend muss der Dienst neu gestartet werden.
Modul Konfiguration und Inbetriebnahme:
Es muss zuerst eine Instanz des Modul angelegt werden. Dazu (entsprechend der jeweils gültigen IP-Symcon-Dokumentation ) in der Verwaltungskonsole "Objekt hinzufügen"->"Instanz-Hinzufügen" auswählen. Dort erst den Hersteller, dann kann das entsprechende Modul selektiert und der Dialog mit OK verlassen werden.

Da bisher kein passender Parent (Modul-Instance mit SendString-Interface) existiert, sollte jetzt einer angelegt werden.

Zunächst wird eine Auswahl kompatibler Instancen angezeigt. Diese Funktionalität wird durch die Informationen aus RequireParent sowie GetParentRequirements und GetImplemented bereitgestellt. Wir wollen zur Demo eine Instance des ServerSocket-Modul erstellen. Das Modul wäre aber ohne irgendeine Änderung auch an den anderen Modulen betreibbar.

Diese Instance wird auch noch mit einem passenden TCP-Port konfiguriert. Dazu muss „Öffne Server“ aktiviert sein. Anschließend auf „übernehmen“ und „OK“ klicken

Zurück
in der Modulkonfiguration muss man erst auf „Übernehmen“
und dann auf „OK“ klicken.


Jetzt
sollte das Modul incl. Statusvariable in der Konsole sichtbar sein

Anschließend
ist mittels eines kleinen PHP-Scriptes der Filename für das
Logging einzugeben. Das ist ein guter Test um zu sehen, ob die
definierten PHP-Funktionen sauber implementiert worden sind. Dazu ist
ein neues Script anzulegen. Anschließend im Editor auf „Befehl
hinzufügen“ klicken und das neu erstellte Device
auswählen. Nach dem Klick auf „Weiter“ sollten die
definierten Funktionen auftauchen.
Dieser Assistent bietet schon (auch ohne eigene Programmierung!) einen passenden Eingabescreen an.

Weil es so schön ist, fügen wir gleich noch einen zweiten Befehl hinzu

Im Editor kann man auch nach Tippen der Anfangsbuchstaben, gefolgt von STRG-Leertaste, ein Fenster mit den Funktionen bekommen.

Bei einer richtigen Konfiguration ist von nun an ein Datenempfang und Verarbeitung möglich.Wenn alles geklappt hat, liefert das Script auch die erwartete Ausgabe. Auch die Ersetzung durch das Modul (Funktion HandleData) wurde korrekt vorgenommen

Auch die Status-Variable hat nun den richtige Wert:

Als Krönung wollen wir jetzt noch sehen, ob die Daten vom ServerModul genauso angenommen werden. Man kann mit Telnet eine Verbindung herstellen, aber das macht wenig Sinn(jedes Zeichen eine Zeile). Ich habe es trotzdem mal gemacht->Siehe Textlog am Ende). Interessanter ist jedoch die Kommunikation mit anderen Rechnern. Dazu wird auf einem Linux-Rechner eine Abfrage auf den USV-Status gemacht und die Ausgabe über netcat(nc) eine TCP-Verbindung zum eigenen ServerSocket-Modul in IPSymcon hergestellt. Es sind aber auch beliebig andere Funktionen möglich.


Zum
Test benutzen wir erst ein Script, dann wird im Logfile nachgesehen.
Alles klappt wie gewünscht.

1. MyModule
Funktion
DemoModul mit TextLogger-Funktion
Text über das Interface oder von PHP wird im Logfile und in der Statusvariable gespeichert.
Status-Variablen
LastText ->letzter eintrag
Konfiguration
Auswahl und Konfiguration Parent-Instance .mit SendString-Interface
Zuweisung LogfileName über PHP-Funktion
Freigabe Echo über PHP-Funktion (wenn gewünscht)
PHP-Funktionen
Alle Funktionen haben im PHP den Präfix "MyDevice_"
procedure SetLogFile(name:string);
setzt Filename zum Loggen der empfangenen Daten in eine Datei. Relative Pfadnamen beziehen sich auf die Position von ips.exe. Es werden alle zugewiesenen Strings geloggt. Ist kein Dateiname gesetzt oder ist dieser leer, wird keine Logdatei erzeugt(Standard). Die Daten werden an eine vorhandene Datei angehangen oder diese wird neu erzeugt..
Beispiel: MyDevice_SetLogFile(InstanceID,"mylog.txt"); //Log to %IP-Symcon%\mylog.txt
function SetLine(Text: string):integer;
sendet neue Zeile zum Loggen. Im Modul wird der Prefix „PHP -->“ vorangestellt. Text über das Interface erhält dagegen den Prefix „Interface-->“ Beispielhaft wird die Textlänge zurückgegeben.
Beispiel: echo MyDevice_SetLine(instanceID,"Hallo PHP"); //Ausgabe:8
function GetLine: string;
liest den letzten gespeicherten Texteintrag. Leerer String, wenn nicht gesetzt.
Beispiel: echo MyDevice_getLine(instanceID); //Ausgabe:PHP -->Hallo PHP
procedure EnableEcho(bEcho:boolean);
erlaubt Echo (true) oder nicht (default:kein echo)
Stand Dokumentation: Version 2.10 10.09.2009