Sonstiges 4 Bytes vom Wiegemodul in einer Hochsprache in Real-Wert konvertieren

KlausMA

Level-1
Beiträge
14
Reaktionspunkte
2
Zuviel Werbung?
-> Hier kostenlos registrieren
Es geht um ein Wiegemodul WP231.
Über Netzwerk wurde auf einem PC von diesem Wiegemodul ein Datensatz empfangen. Darin ist auch ein RealWert enthalten, der die Länge von 4 Bytes umfasst. Ich habe also 4 Bytes und soll ihn in der Programmiersprache Delphi in ein Real-Wert umwandeln.
Also Vorzeichen, Exponent und Mantisse aus den 32 Bytes entnehmen, und daraus ein Real-Wert bilden.
Das ganze auch in umgekehrter Richtung.
Aber wie? Gibt es dazu ein Beispielprogramm? Damit wäre mir sehr geholfen.

Im vorraus Danke für ein passendes Beispielprogramm.
 
die 4 Bytes aus dem Datensatz sind dann bestimmt ein Single in Delphi (also ein 32 Bit Float = Single precision) - nicht zu verwechseln mit Real (ein 64 Bit Float = Double precision)
dann musst du nur noch wissen (oder ausprobieren) ob der Wert als Little oder Big-Endian vorliegt - ansonsten einfach alle Bytes vor dem casten in Single umdrehen
 
Das LibNoDave scheint ein erfolgversprechender Weg zu sein. Habe es mir mal runter geladen.
Mit Delphi5 klappt die Installation der Komponente aber nicht. Ich probier es mal mit Delphi6 oder 7.
Wenn ich dann die Library nutzen kann, kann das hoffentlich mein Problem lösen.
 
Wenn ich dann die Library nutzen kann, kann das hoffentlich mein Problem lösen.


Libnodave ist eine Kommunikationslösung für "PC <--> Simatic S7 SPS" - hilft dir das denn überhaupt?
oder meine Senator42 nur das du im Quelltext nach dem 4Byte Casten/Endianess-Swap für das Single schauen sollst?(in PC <-> S7 Kommunikation kommt sowas eben auch vor)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Libnodave ist eine Kommunikationslösung für "PC <--> Simatic S7 SPS" - hilft dir das denn überhaupt?
Ich glaub da wurde LibnoDave mit OSCAT verwechselt. Da wäre was drin gewesen, bin aber nicht sicher wie gut die OSCAT aktuell auf die 1200/1500 passt. Schon lang nicht mehr geschaut.

Da der TE mit dem WP231 anscheinend eine S7-1200 hat wäre eigentlich die Verwendung des Slice-Zugriffs über ein DWORD das einfachste...
Code:
FUNCTION "4ByteToReal" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      b0 : Byte;
      b1 : Byte;
      b2 : Byte;
      b3 : Byte;
   END_VAR

   VAR_OUTPUT 
      OUT : Real;
   END_VAR

   VAR_TEMP 
      iDWORD : DWord;
   END_VAR

BEGIN
      
    #iDWORD.%B0 := #b0;
    #iDWORD.%B1 := #b1;
    #iDWORD.%B2 := #b2;
    #iDWORD.%B3 := #b3;
    
    #OUT := DWORD_TO_REAL(#iDWORD);
    
    
END_FUNCTION
Die Bytes dann halt in gewünschter Reihenfolge übergeben.
 
Ahh... Sch****. Leseschwäche in der Tat. Hab mich zu serh vom WP231 ablenken lassen und bin automatisch davon ausgegangen das da ne CPU bei ist....
Sorry, Kommentar früh am Morgen ist halt nix. :oops:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Du mußt mal etwas mehr über dein Hochsprachen-Environment schreiben.
Hast du das .Net-Framework da mit drin ? Wenn ja dann gäbe es da den BitConverter, der dir ein Byte-Array (vorausgesetzt es liegt in der richtigen Reihenfolge vor) in einen Single umrechnen kann ...
 
Danke für die Antworten.
Es handelt sich um mehrere Anforderungen. Eine ist die beschriebene Anwendung mit einem PC-Programm. Eine weitere mit einer CPU.

Mit PC-Programm: Für die Richtung Bytes zu Real habe ich nun folgenenden Code erarbeitet:
var y, Exponent, Mantisse : real;

procedure TForm1.Button1Click(Sender: TObject);
begin Exponent:=3; Mantisse:=2;
y:=exp((Exponent) * ln(Mantisse));
label1.caption:=IntToStr(round(y));
end;

Es wird zuhause das richtige Ergebnis 8 angezeigt. Innerhalb der virtuellen Maschine im Geschäft kommt aber mit der gleichen Formel etwas völlig anderes heraus! Ich probiere es am Dienstag nochmal mit dem Exe-File innerhalb und außerhalb der virtuellen Maschine.
Für die umgekehrte Richtung 'Real zu Bytes' habe ich noch keine Lösung.
Das NET-Framework kann nicht bei jedem PC als vorhanden vorausgesetzt werden.
LibNoDave: Die Installation der Komponente hat mit Delphi5 nicht funktioniert. Ich werde es mal einer Testversion von Delphi6 bzw. 7 probieren.

Anwendung mit CPU: Da wollte ich die Bytes in einen DB schreiben lassen und von dieser Position als Realwert auslesen. Müsste eigentlich funktionieren. Ansonsten werde ich auch mal den SCL-Code testen.
Wenn die WP231 die Telegramme übers Netzwerk vom CP343-1 verarbeiten würde. Auf die gleichen Telegramme vom PC reagiert das WP231. Die Inhalte sind die gleichen. Der Port auch. Scheinbar verhält sich der CP als Netzwerkteilnehmer anderes als ein PC. Ein neues Problem.
Als CPU wird eine 315-2 DP verwendet. Später soll es auch noch auf eine 1500 CPU umgesetzt werden.
 
Zuletzt bearbeitet:
Zuerstmal: dein Feedback ist verwirrend - hast du Ahnung von dem was du das machst oder ist das alles nur raten und probieren?

Mit PC-Programm: Für die Richtung Bytes zu Real habe ich nun folgenenden Code erarbeitet:
var y, Exponent, Mantisse : real;

procedure TForm1.Button1Click(Sender: TObject);
begin Exponent:=3; Mantisse:=2;
y:=exp((Exponent) * ln(Mantisse));
label1.caption:=IntToStr(round(y));
end;
Was auch immer du da machst - ich dachte du hast 4 Bytes von einem Float(aka Single)? die musst du nur in einen Float casten - sonst nix
- und gegebenenfalls noch auf die Endianess achten und verdrehen - Falls du nicht weisst was ist meine musst du Fragen - nicht einfach ignorieren
der Weg andersrum wäre exakt gleich

Es wird zuhause das richtige Ergebnis 8 angezeigt. Innerhalb der virtuellen Maschine im Geschäft kommt aber mit der gleichen Formel etwas völlig anderes heraus! Ich probiere es am Dienstag nochmal mit dem Exe-File innerhalb und außerhalb der virtuellen Maschine.
Für die umgekehrte Richtung 'Real zu Bytes' habe ich noch keine Lösung.
Das kann nicht sein und muss sich um einen Programmfehler auf deiner Seite handeln

Das NET-Framework kann nicht bei jedem PC als vorhanden vorausgesetzt werden.
und setzt auch vorraus das du mit Delphi.NET programmierst - für normales Delphi ist das unrelevant

LibNoDave: Die Installation der Komponente hat mit Delphi5 nicht funktioniert. Ich werde es mal einer Testversion von Delphi6 bzw. 7 probieren.
die brauchst du auch nur wenn du Daten direkt über Ethernet von einer S7 SPS lesen willst - willst du das?

Anwendung mit CPU: Da wollte ich die Bytes in einen DB schreiben lassen und von dieser Position als Realwert auslesen. Müsste eigentlich funktionieren. Ansonsten werde ich auch mal den SCL-Code testen.
genauso müsste es auch auf dem PC funktionieren

Wenn die WP231 die Telegramme übers Netzwerk vom CP343-1 verarbeiten würde. Auf die gleichen Telegramme vom PC reagiert das WP231. Die Inhalte sind die gleichen. Der Port auch.
Scheinbar verhält sich der CP als Netzwerkteilnehmer anderes als ein PC. Ein neues Problem.
Als CPU wird eine 315-2 DP verwendet. Später soll es auch noch auf eine 1500 CPU umgesetzt werden.
Es könnte sein das deine Bytes verdreht kommen (Little/Big Endianness) weil der PC die Bytes anders anordnet als die SPS

Fragen:
-weisst du GENAU in welchem Format der 4 Byte Float vorliegt?
-kannst du mal für einen Beispiel-Wert die 4 Byte-Werte posten?
-weisst du was Little/Big Endianess bedeutet? (https://de.wikipedia.org/wiki/Byte-Reihenfolge)
-wie kommuniziert dein PC mit dem WP231? (per Ethernet/Seriell?)

Hier mal ein Beispiel-Code für das 4-Bytes-Single cast/reverse (kompiliert mit FreePascal sollte aber auch unter Delphi gehen)
Code:
{
Es gibt Single(32Bit) und Double(64Bit) precision floats in https://de.wikipedia.org/wiki/IEEE_754

in C/C++:
float = 32Bit
double = 64Bit

in Simatic S7:
Real = 32Bit
LReal = 64Bit

in Delphi:
Single = 32Bit
Double = Real = 64Bit
}

Uses sysutils;
type
  TBytes4 = array[0..3] of Byte;
  
function bytes4_string(value_bytes: TBytes4): string;
var
  i: integer;
  result: string;
begin
  result := '';
  for i := 0 to sizeof(value_bytes)-1 do 
  begin
    result += Format('$%.2x',[value_bytes[i]]);
    if(i < sizeof(value_bytes)-1) then
    begin
      result += ' ';
    end;
  end;
  bytes4_string := result;
end;
  
function single_to_bytes4(value: Single): TBytes4;
var
  value_bytes: TBytes4 absolute value;
begin
  single_to_bytes4 := value_bytes;
end;

function bytes4_to_single(value_bytes: TBytes4): Single;
var
  result: Single absolute value_bytes;
begin
  bytes4_to_single := result;
end;

function reverse_bytes4(value_bytes: TBytes4): TBytes4;
var
  i: integer;
  result: TBytes4;
begin
  for i := 0 to sizeof(value_bytes)-1 do 
  begin
    result[i] := value_bytes[sizeof(value_bytes)-1-i];
  end;
  reverse_bytes4 := result;
end;
  
var
  value: Single;
  little_endian_value_bytes: TBytes4;
  big_endian_value_bytes: TBytes4;
begin
  // intern als Little Endian auf x86
  value := 123.4;
  writeln(Format('%.4f',[value])); // 123.4000
  little_endian_value_bytes := single_to_bytes4(value);
  writeln('{'+bytes4_string(little_endian_value_bytes)+'}'); // {$CD $CC $F6 $42} auf x86/Little Endian
  
  //so wuerde es (umgedreht) von einem Big Endian System kommen (z.B. die SPS oder ARM usw.)
  big_endian_value_bytes[0] := $42;
  big_endian_value_bytes[1] := $F6;
  big_endian_value_bytes[2] := $CC;
  big_endian_value_bytes[3] := $CD;
  writeln('{'+bytes4_string(big_endian_value_bytes)+'}'); // {$42 $F6 $CC $CD}
  little_endian_value_bytes := reverse_bytes4(big_endian_value_bytes); // aus Big wird Little Endian
  writeln('{'+bytes4_string(little_endian_value_bytes)+'}'); // {$CD $CC $F6 $42} Little Endian
  value := bytes4_to_single(little_endian_value_bytes);
  writeln(Format('%.4f',[value])); // 123.4000
end.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielen Dank für die umfangreiche Antwort. Ich kann momentan noch nicht auf alles antworten.

- Leider habe ich nicht von allem das nötige Wissen. Das ist ja mein Problem. Daher ist auch viel Probieren mit dabei.

- Inzwischen habe ich reproduzierbar heraus gefunden, daß manche Berechnungen von Delphi5 nicht durchgeführt werden! Daher verändert sich die Variable mit dem Endergebnis oftmals nicht und es bleibt ein total falscher Wert darin bestehen.
Daher probiere ich es mal mit Delphi6 statt mit Delphi5. Das muß aber erst noch installiert werden. Evtl. haben sich dann manche Probleme mit den Berechnungen erledigt.

- Ja ich will Daten direkt von der SPS auslesen. Daher ist LibNoDave interessant. Auch dafür ist ein Wechsel auf Delphi6 nötig.

- Das genaue Format von dem Realwert weiß ich nicht. Hier ein paar Beispielwerte:
1 dez = 00 00 80 3F Hex
2 dez = 00 00 00 40 Hex
5 dez = 00 00 A0 40 Hex
6 dez = 00 00 C0 40 Hex
10 dez = 00 00 20 41 Hex
20 dez = 00 00 A0 41 Hex
100 dez = 00 00 C8 42 Hex

So komme die Werte im Datenrecord von dem Wiegemodul.
Die Bytes links werden im Datensatz an den niedrigeren Adressen empfangen.
Mir sagt das jetzt nicht so viel. Möglicherweise kannst Du da mehr mit anfangen?
Ich wusste nicht was Little/Big Endianess bedeutet. Der Link auf Wikipedia hat weiter geholfen.

- Alle Kommunikationen erfolgen über TCP/IP.

- Am Mittwoch installiere ich Delphi6. Ich denke das bringt dann einiges an Verbesserungen.

- An meiner privaten Fritz-Box zeichne ich mal auf, was tatsächlich vom CP343 an das Wiegemodul gesendet wird. Evtl. ist das doch nicht das gleiche wie vom PC? Das Ergebnis bringt die Ursachensuche auf jeden Fall weiter.
 
1 dez = 00 00 80 3F Hex
2 dez = 00 00 00 40 Hex
5 dez = 00 00 A0 40 Hex
6 dez = 00 00 C0 40 Hex
10 dez = 00 00 20 41 Hex
20 dez = 00 00 A0 41 Hex
100 dez = 00 00 C8 42 Hex

Es sind IEEE754 Single(32Bit) Werte im Little-Endian Format

und da der Delphi Typ Single genau ein solcher ist kannst du das einfach casten

in diesem Beispiel-Code (kompiliert mit FreePascal 3.0) zeige ich dir die Hin- und Rueckwandlung deines Wertes "100 dez = 00 00 C8 42 Hex"

Code:
Uses sysutils;
type
  TBytes4 = array[0..3] of Byte;
  
function bytes4_string(value_bytes: TBytes4): string;
var
  i: integer;
  result: string;
begin
  result := '';
  for i := 0 to sizeof(value_bytes)-1 do 
  begin
    result += Format('$%.2x',[value_bytes[i]]);
    if(i < sizeof(value_bytes)-1) then
    begin
      result += ' ';
    end;
  end;
  bytes4_string := result;
end;
  
function single_to_bytes4(value: Single): TBytes4;
var
  value_bytes: TBytes4 absolute value;
begin
  single_to_bytes4 := value_bytes;
end;


function bytes4_to_single(value_bytes: TBytes4): Single;
var
  result: Single absolute value_bytes;
begin
  bytes4_to_single := result;
end;


function reverse_bytes4(value_bytes: TBytes4): TBytes4;
var
  i: integer;
  result: TBytes4;
begin
  for i := 0 to sizeof(value_bytes)-1 do 
  begin
    result[i] := value_bytes[sizeof(value_bytes)-1-i];
  end;
  reverse_bytes4 := result;
end;
  
var
  value: Single;
  little_endian_value_bytes: TBytes4;
  big_endian_value_bytes: TBytes4;
begin
  writeln('Bytes in Single umwandeln');
  little_endian_value_bytes[0] := $00;
  little_endian_value_bytes[1] := $00;
  little_endian_value_bytes[2] := $C8;
  little_endian_value_bytes[3] := $42;
  writeln('{'+bytes4_string(little_endian_value_bytes)+'}'); // {$00 $00 $C8 $42} auf x86/Little Endian
  value := bytes4_to_single(little_endian_value_bytes);
  writeln(Format('%.4f',[value])); // 100.0000
  
  writeln('Wert in 4 Bytes umwandeln');
  value := 100.0;
  writeln(Format('%.4f',[value])); // 100.0000
  little_endian_value_bytes := single_to_bytes4(value);
  writeln('{'+bytes4_string(little_endian_value_bytes)+'}'); // {$00 $00 $C8 $42} auf x86/Little Endian
end.
das ist wirklich alles - mehr Source-Beispiel brauchst du nicht - und das "rechnet" auch unter Delphi5 korrekt

und falls da doch Big-Endian im Spiel ist muss du eben noch VOR bytes4_to_single oder NACH single_to_bytes4 die reverse_bytes4-Funktion aufrufen
 
Zuletzt bearbeitet:
Vielen Dank für den Code. Auch wenn ich ihn nicht gleich verstanden habe, habe ich ihn mal getestet.

Delphi5 hatte da noch ein paar Einwände bei der Syntax, die ich anpassen musste:

- Wertzuweisungen erfolgen in Delphi immer mit ":="

- Statt der Ausgabe mit writeln habe ich die Texte in ein Memofeld eingetragen, damit sie ohne Fehlermeldung sichtbar werden.

- function bytes4_to_single(value_bytes : TBytes4) : Single;
var wert : Single absolute value_bytes;
begin bytes4_to_single:=wert;
end;
//"result" wird von Dephi bereits als Variable für den Rückgabewert verwendet. Daher die neue Variable "wert".

- function bytes4_string(value_bytes : TBytes4) : string;
var i : integer;
BytesOut : string;
begin BytesOut:='';
for i:=0 to sizeof(value_bytes) - 1 do
begin BytesOut:=BytesOut + Format('$%.2x', [value_bytes]);
if (i < sizeof(value_bytes) - 1) then BytesOut:=BytesOut + ' ';
end;
result:=BytesOut;
end;
//Damit alle 4 Bytes, statt nur dem letzten Byte angezeigt werden.

Die 4 Bytes mussten nicht in der Reihenfolge getauscht werden.

Ausgabe im Memo:
Bytes in Single umwandeln
{$00 $00 $C8 $42}
100,0000
Wert in 4 Bytes umwandeln
100,0000
{$00 $00 $C8 $42}

Und das alles ohne komplizierte Berechnungen mit Mantisse und Exponent! Ich bin echt begeistert!
Den Parameter "absolute" kannte ich auch noch nicht.

Inzwischen ist die Anwendung dazu so gut wie fertig. Alles funktioniert prima.

Ohne Dich wäre dies nicht möglich gewesen.
Vielen Vielen Dank. :D
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Und das alles ohne komplizierte Berechnungen mit Mantisse und Exponent! Ich bin echt begeistert!
Es muß ja auch nichts gerechnet werden, es werden einfach nur 32 Bits zwischen Variablen verschiedenen Datentyps kopiert. Die 4 Bytes liegen schon in der binären Entsprechung des Single-Formats vor.
IEEE 754 Umrechner

Harald
 
Zurück
Oben