Re: TLV Objekte aus Datei lesen



Hi,

zunächst mal vielen Dank für Deine ausführliche Antwort. Es geht, wie in Deinem letzten Satz erwähnt, auch um die Tachodaten.

Mein Code ist C++ und gehört mir auch nicht, sonst würde ich ihn Dir gerne einfach schicken.

Schätze Dich glücklich, daß Du das Zeug nicht auch noch von der Fahrerkarte oder aus der FE schaffen musst. Das ist Bitklopperei vom übelsten. Und so schön dieses Dokument aussieht, es steckt voller Lücken wenn man es wirklich runterprogrammieren will.

lesen sondern bekomme die erzeugte Datei beim Kartendownload geliefert und muß diese eben dann wieder in eine Struktur bringen um die Informationen darzustellen.

Wenn Du sie mal lesen kannst, ist es trivial das ganze zum Beispiel als XML auszugeben, oder bestimmte Daten zu finden.

Ich glaub irgendwie bin ich an dieser Stelle momentan noch ein bischen verwirrter als vorher.

Ich paraphrasiere meinen C++-Code:

type TSTRING=class(TObject)
private
FValue:string;
public
procedure Decode(aSource:TStream;aLen:integer);
property Value:string read FValue;
procedure SaveToXML(aNode:TXMLNode);
end;

Interessant ist nur Decode(). Das kriege ich sicher jetzt nicht mehr sauber in Delphi hin. Grob so:

procedure TSTRING.Decode(..);
begin
SetLength(FValue,aLen);
aSource.ReadBuffer(FValue[1],aLen);
end;

Simpel.

INTEGER analog, braucht auch eine LEN beim Decode, weil 1, 2, 3, und 4-Bytige Integers vorkommen, aber man beim Dekodieren ja einfach einen Value:integer haben will. An Grundtypen habe ich außerdem noch TIMESTAMP gebaut, ist eigentlich ein 4-Byte integer, soll aber einen Value als TDateTime haben, und sich anders wieder ins XML speichern, nämlich als yyyy-mm-dd...

Weil STRING als OCTET STRING und IA5STRING vorkommen, das aber piepegal ist, schere ich die alle über einen Kamm und komme mit meinem STRING aus. Unterschiede mache ich nur beim SaveToXML(). Die Methoden haben dann noch einen Flags-Parameter der steuert, ob der String wirklich ein String ist (die meisten der IA5STRINGs) oder ein binärer Blob, den ich dann als CDATA rauswerfe. Dekodiert werden sie alle als Delphi-String.

Dann baust Du daraus die anderen Typen zusammen. Zum Beispiel den Codepagestring:

type TCodePageString=class(TObject)
private
FCodepage:TINTEGER;
FValue:TSTRING;
public
constructor Create;
destructor Destroy;

procedure Decode(aSource:TStream;aLen:integer);
property Value:string read FValue;
property Codepage:integer read FCodepage;
procedure SaveToXML(aNode:TXMLNode);
end;

Der Konstruktor erzeugt die Intanzen für beiden Felder, der Destruktor räumt sie ab. Entfällt in C++. Decode() delegiert einfach:

procedure TCodePageString.Decode(..);
begin
Codepage.Decode(aSource,1);
Value.Decode(aSource,aLen); // Oder aLen-1, je nachdem, ob Du die Länge des Strings oder die Länge des ganzen Dings übergibst.
end;

Und so kannst Du die komplexeren Datentypen alle aufbauen.

Die Listen sind in C++ einfach, weil sie std::vector<TIrgendwas> sind. In Delphi kannst Du zum Beispiel ein dynamisches Array deklarieren wenn Du typsicher sein willst, oder halt eine TList mit castenden Zugriffsmethoden.

Ich habe auswendig keinen richtigen Namen aus der Spec, irgendwelche IWDingensEventData oder so:

type TIWEventData=class(TObject)
private
Records: array of TIWEventRecord;
public
procedure Decode(aSource:TObject);
..
end;

Wesentlich: Es gibt hier keinen Len-Parameter beim Decode. Die Länge in der Datei ist ja nicht vorher bekannt. Bei vielen der Record-Typen gibt es auch kein Len beim Decode(), weil die Länge aus der Record-Definition implizit ist.

In meinen Daten haben die meisten der Arrays eine ein- oder zweibyteige Angabe der Anzahl der Records am Anfang. Also erst die lesen, dann die Records.

procedure TIWEventData.Decode;
var Count:byte;
begin
aStream.ReadBuffer(Count,Sizeof(Count));
SetLength(Records,Count);
for i:=Low(Records) to High(Records) do
begin
Records[i]:=TIWEventRecord.Create;
Records[i].Decode(aSource);
end;
end;

Der Destruktor von TIWEventData muss natürlich auch über das Array iterieren und die Records freigeben.

Also ich erzeuge eine Objektinstanz eines Containers (z.B. für die technischen Daten der Karte sofern sie in der Datei stehn).

Die Technical Data sind ein Record. Den deklarierst Du wie oben als Lego aus den Basis-Typen. Ich habe längst nicht alle einzeln als Klassen definiert, weil viele davon einfach Synonyme sind.

Und dann liest Du so eine Datei:

F:=TFileStream.Create(aFileName,fmRead);
try
TechnicalData:=TTechnicalData.Create;
try
TechnicalData.Decode(F);
// Verwenden. Zum Beispiel:
TechnicalData.SaveToXML(XMLDocument.RootNode);
XMLDocument.SaveToFile(aFileName+'.xml');
finally
FreeAndNIL(TechnicalData);
end;
finally
F.Free;
end;

Der Rest ist Fleißarbeit.

In meiner Version verwende ich nicht direkt einen Stream, sondern wrappe noch eine Klasse um std::istream, die so Zeug macht wie mitschreiben wie weit man gekommen ist, eof() und bad() abfangen wenn man versucht zu weit zu lesen oder was kaputt ist (macht in Delphi TStream.ReadBuffer alleine, C++-iostreams Unzulänglichkeit), und ein Skip() hat, damit man eine Struktur, die man noch nicht vollständig deklariert hat, überspringen kann. Wenn man die Größe kennt.

In den BER http://telecom.htwm.de/asn1/ber.htm steht ja das man das T Byte auswerten muß. Hab ich bisher nicht gewußt. Scheiße ich glaub das wird komplizierter als ich dachte. Immer das gleiche.

Meine Daten sind nicht BER. Aber das kann je nach Downloadquelle natürlich anders aussehen falls das unterwegs irgendwo schon umkodiert wurde. Meine sind im wesentlichen PER, aber nicht richtig. Es gibt keine Tags. Automatisch aus der ASN.1 Syntax compilierte Decoderklassen fliegen auf die Schnauze.

Die Tags werden eigentlich nur interessant, wenn Du CHOICEs hast. Die sind in den Tachodaten aber alle äquivalent, heißen nur anders. Das soll heißen, sie enthalten dieselben Datenfeldern mit den selben Längen, nur haben sie semantisch eine andere Bedeutung. Overengineered, und noch dazu in den PER-Daten nicht mehr zu unterscheiden, also wahrscheinlich gar nicht implementiert.

Und sie sind natürlich interessant, falls Du kompatibel mit alten und neuen Versionen der Datenstruktur sein müsstest. Kommt ein Feld dazu, kann man das mit Tags kompatibel erreichen. PER und mein simples Decoder-Modell können das nicht. Ist aber auch alles andere als wahrscheinlich, und ich lasse das brav auf mich zukommen und würde mir gegebenenfalls ganz entspannt anschauen, wie das dann tatsächlich enkodiert wurde und auf keinen Standard schwören.

Auf volles BER lässt sich das natürlich auch erweitern. Die Decode() der komplexen Typen müssten halt nur das Tag-Feld lesen, und dann erst entscheiden an welches Ihrer eigenen Felder sie nun das weitere Decode() als nächstes delegieren anstatt einfach der Reihe nach vorzugehen. Auch keine Hexerei.

> Nicht klar ist mir warum ich eigene Datentypen ableiten
> soll. Da versteh ich irgendwas noch nicht fürchte ich.

Jetzt besser?

Ciao, MM
--
Marian Aldenhövel, Rosenhain 23, 53123 Bonn
http://www.marian-aldenhoevel.de
"Success is the happy feeling you get between the time you
do something and the time you tell a woman what you did."
.