![]() |
Delphi-Version: 10.2 Tokyo
Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delphi
Hallo Zusammen,
ich versuche, ein Array von Byte aus einer S7-SPS über eine OPC-UA-Verbindung aus meinem Delphi-Programm zu lesen. Dieses Byte-Array besteht aus allen möglichen Variablen wie Bool, Real, DateTime, Integer und ... aus der SPS (aber alle als ein kompaktes Byte-Array). Ich muss die Einträge dieses Arrays in interne Delphi-Variablen konvertieren. Das bedeutet, dass z.B. die ersten vier Bytes (0..3) des SPS Byte-Arrays zur ersten Integer-Variable in Delphi gehören und die nächsten acht Bytes (4..11) zur zweiten Variable in Delphi, nämlich TDateTime. Gibt es eine Idee, wie dieses Typecasting effizient durchgeführt werden kann? Ich kenne die Reihenfolge und den Typ der internen Variablen in delphi. Ich habe ein dynamisches Array in delphi "OPCServerItemArray" und habe alle Variablen mit den entsprechenden Namen und Typen darin abgelegt.
Delphi-Quellcode:
mfg,
Procedure TOPCClient.ReadAllItems;
var ReadVar_vonSPS : Array of Byte; AttributeSPSData : UAAttributeData; I: Integer; ItemRead : Boolean; Name : String; OPCUAResult: _UAAttributeDataResult; OPCUAResults: OleVariant; J,K: Cardinal; begin SetLength(ReadOPCUAArguments,Length(OPCServerItemArray)-NoV_DB100); try if (Connected) then begin AttributeSPSData := OPCUAClientSPS.Read(Url_String,'nsu=Siemens1' + ';s=' + S0); ReadVar_vonSPS := AttributeSPSData.Value; for I := 0 to NoV_DB100-1 do begin OPCServerItemArray[I].Value := //??? Typecasting --> ReadVar_vonSPS OPCServerItemArray[I].ItemQuality := AttributeSPSData.StatusCode; OPCServerItemArray[I].TimeStamp := AttributeSPSData.ServerTimestamplocal; end; if (FirstCylyle) then begin for J := NoV_DB100 to Length(OPCServerItemArray)-1 do begin Name := OPCServerItemArray[J].Source + '.' + OPCServerItemArray[J].ItemName; ReadOPCUAArguments[J-NoV_DB100] := CoUAReadArguments.Create; ReadOPCUAArguments[J-NoV_DB100].ReadParameters.MaximumAge := 100; ReadOPCUAArguments[J-NoV_DB100].EndpointDescriptor.UrlString := Url_String; ReadOPCUAArguments[J-NoV_DB100].NodeDescriptor.NodeId.ExpandedText := 'nsu='+ nsu + ';s=Local Items.' + Name; end; OPCUAArguments := VarArrayCreate([0, Length(OPCServerItemArray)-NoV_DB100-1], varVariant); for I := 0 to Length(OPCServerItemArray)-NoV_DB100-1 do begin OPCUAArguments[I] := ReadOPCUAArguments[I]; end; FirstCylyle := False; end; // Perform the operation TVarData(OPCUAResults).VType := varArray or varVariant; TVarData(OPCUAResults).VArray := PVarArray(OPCUAClientRead.ReadMultiple( //OPVLabs 2019.1 Version PSafeArray(TVarData(OPCUAArguments).VArray))); // Writing the results in Delphi internal Array for K := VarArrayLowBound(OPCUAResults, 1) to VarArrayHighBound(OPCUAResults, 1) do begin OPCUAResult := IInterface(OPCUAResults[K]) as _UAAttributeDataResult; OPCServerItemArray[NoV_DB100+K].Value := OPCUAResult.AttributeData.value; OPCServerItemArray[NoV_DB100+K].ItemQuality := OPCUAResult.AttributeData.StatusCode; OPCServerItemArray[NoV_DB100+K].TimeStamp := OPCUAResult.AttributeData.ServerTimestamplocal; end; end else begin Meldung(0,'TOPCClient.ReadAllItems: (Not connected to the OPC UA Server!)'); end; except on E: Exception do begin Meldung(0,'TOPCClient.ReadAllItems - Exception: ' + E.Message); end; end; end; Moien |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Ich würde als erstes versuchen, einen packed Record mit den passenden Feldern zu deklarieren und dann mittels Move die Bytes dort hinein zu kopieren. Ob das funktioniert, hängt davon ab, ob die Bytereihenfolge der Quelle mit der von Delphi / Intel übereinstimmt (Stichwort: Big Endian vs. Little Endian).
Wenn nicht, wird es knifflig. |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Also wenn du wirklich eine Struktur (packed record) hast die sich 1:1 auf die Bytes abbilden lässt die du bekommst dann gibt es das schon fertig mit
Delphi-Quellcode:
aus
TBitConverter
Delphi-Quellcode:
:
System.Types
Delphi-Quellcode:
uses
System.SysUtils, System.Types; type TAppData = packed record someInteger: Int32; someTimestamp: TDateTime; // usw. end; procedure p(); const data: TBytes = [103, 18, 0, 0] // 4711 + [0, 0, 0, 0, 160, 109, 229, 64]; // 2020, 02, 24 var appData: TAppData; begin appData := TBitConverter.InTo<TAppData>(data); end; Um ganz ehrlich zu sein habe ich das früher auch so gemacht, mir es aber mittlerweile abgewöhnt. Am besten gönnt man sich das Getippe von ein paar Zeilen für die Konvertierung zwischen den rohen Bytes und der Datenstruktur wie man sie in seinem Programm abbilden möchte. Da lässt sich auch wesentlich besser testen und auf Sonderfälle reagieren. |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Ich verlasse mich auch lieber nicht auf konstante Bytelängen. Spätestens mit Strings wars das nämlich. Schöner wartbar und zuverlässiger ist eine Analyse der Bytefolgen, wie Günther es bereits beschrieben hat.
Sherlock |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Vielen Dank für alle Antworten.
Ehrlich gesagt, ich habe nicht genau verstanden was ihr mit " eine Analyse der Bytefolgen" meint. Habt ihr ein Beispiel? LG; Moien |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Zitat:
Delphi-Quellcode:
program Project1;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, System.Types; type TAppData = packed record someInteger: Int32; someTimestamp: TDateTime; // usw. end; PAppData = ^TAppData; const data: TBytes = [103, 18, 0, 0] // 4711 + [0, 0, 0, 0, 160, 109, 229, 64]; // 2020, 02, 24 procedure p; var appData: TAppData; begin appData := TBitConverter.InTo<TAppData>(data); end; procedure p2; var appData2: TAppData; begin appData2 := (PAppData(@data[0]))^; end; procedure p3; var appData3: TAppData; begin appData3.someInteger := (PInteger(@data[0]))^; appData3.someTimestamp := (PDateTime(@data[4]))^; end; begin try p; p2; p3; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Der Unterschied bei
Delphi-Quellcode:
ist im Endeffekt dass die einzelnen Felder zugewiesen werden und nicht alles auf einen Rutsch.
p3()
Das ist natürlich schon einmal gut, damit muss der Record z.B. nicht mehr
Delphi-Quellcode:
sein und man kann sich die Reihenfolge der Felder selbst aussuchen.
packed
Ich würde noch einen Schritt weitergehen: Ob man die einzelnen Bytes da jetzt mit wilden Zeigerzugriffen, mit TBitConverter oder sonst womit rausholt ist ja im Endeffekt egal, aber z.B. bei einem
Delphi-Quellcode:
(das ja nur ein
TDateTime
Delphi-Quellcode:
ist) bieten sich noch Gültigkeitsprüfungen an. Akzeptiere ich nur Zeitstempel nach 1970? Was wenn ich eine ungültige Fließkommazahl erhalten habe?
Double
|
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Zitat:
Delphi-Quellcode:
doch ein Byte-Array. Dein
ReadVar_vonSPS
Delphi-Quellcode:
scheint ein Array von irgendwas zu sein, und du willst von deinem Byte-Array (oder "Bytefolge") aus die Daten in deinem
OPCServerItemArray
Delphi-Quellcode:
setzen. Mehr war nicht gemeint 😉
OPCServerItemArray
|
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Nochmal vielen Dank für eure Unterstützung :)
Ich habe schon ein Extra Unit definiert (UnitTypecasting). Und dann sortiere ich die Werte entwieder mit
Delphi-Quellcode:
oder
procedure Typecasting
Delphi-Quellcode:
. Ich erhalte von beiden die gleichen Werte. Aber irgendwie sind die Werte nicht die gleichen, wie sie in der SPS definiert sind. Ich glaube, es gibt einige Probleme mit der Byte-Ordnung (Big-Endian und Little-Endian). Habt ihr eine Idee, wie man die Byte-Reihenfolge ändern kann?
procedure P2
Delphi-Quellcode:
unit UnitTypecasting;
interface uses System.SysUtils, System.Types; type TAppData = packed record //[variable_0] S1_PIn_00_bar : Single; //[variable_1] S1_PIn_DateTime_00_time : UInt32; //[variable_2] S1_PIn_01_bar : Single; //[variable_3] S1_PIn_DateTime_01_time : UInt32; //[variable_4] S1_PIn_02_bar : Single; //[variable_5] S1_PIn_DateTime_02_time : UInt32; //[variable_6] S1_PIn_03_bar : Single; . . . //[variable_227] RDT_P1_Enabled : Boolean; //[variable_228] RDT_P2_Enabled : Boolean; //[variable_229] RDT_P3_Enabled : Boolean; end; PAppData = ^TAppData; procedure Typecasting(var data : TBytes); procedure p2(var data2 : TBytes); implementation procedure Typecasting(var data : TBytes); var appData: TAppData; begin appData := TBitConverter.InTo<TAppData>(data); end; procedure p2(var data2 : TBytes); var appData2: TAppData; begin appData2 := (PAppData(@data2[0]))^; end; end. |
AW: Typecasting eines Arrays von Byte und Schreiben in verschiedene Variablen in Delp
Wenn du dir sicher bist dass es ein Big/Little-Endian Problem ist:
Ich habe in der Delphi-Standard-Bibliothek komischerweise nie etwas gefunden wie man die Endianess von Dingen wie z.B. einem Word ändern kann. Ich habe es dann ganz billig von Hand gemacht. Hier ein Beispiel: ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:06 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz