Für eine Auswertung eines Modbus-Gerätes benötigten wir eine Funktion, die uns das Byte-Array, das aus dem Modbus-TCP-Client zurückgeliefert wird, in Float- oder Double-Zahlen nach IEEE 754 konvertiert. Die Suchmaschine des geringsten Misstrauens lieferte keine brauchbaren Ergebnisse. Man sah den wenigen veröffentlichten Lösungen an, dass deren Autoren die Funktionalität von pack() und unpack() unter PHP nicht verstanden hatten. Zugegeben: Das kann auch ein wenig tricky sein.
Schauen wir uns einmal den einfachen Teil an: Eine PHP-Fließkommazahl in ein Byte-Array verwandeln. Der Code sieht wie folgt aus:
# double to byte array $test=47.11; print_r( $test ); echo "\n"; print_r( unpack("C*", pack("E", $test))); echo "\n";
Zunächst wandelt ein pack(„E“, $test) die Fließkommavariable $test im Big-Endian-Format in einen Bytestring um, der mit unpack( „C*“, …) in ein Array von einzelnen Bytes konvertiert wird. Die Ausgabe sieht wie folgt aus:
47.11 Array ( [1] => 64 [2] => 71 [3] => 142 [4] => 20 [5] => 122 [6] => 225 [7] => 71 [8] => 174 )
Mit einem Online-Formatkonverter wie https://www.binaryconvert.com/result_double.html lässt sich das Ergebnis schnell überprüfen. Dort wird 0x40478E147AE147AE als Lösung angezeigt. Die Umwandlung der Hex-Bytes in die Dezimaldarstellung überlasse ich dem Leser als kleine Hausaufgabe.
Wer nun denkt, die umgekehrte Richtung, also die Konvertierung eines Byte-Arrays in eine Fließkommazahl geht einfach, indem man die Reihenfolge von pack() und unpack() tauscht, wird jäh enttäuscht werden:
PHP Warning: unpack(): Type E: not enough input, need 8, have 1 in ...
Die richtige Lösung lautet nämlich so:
# byte array to double $recData = [ 0x40, 0x47, 0x8E, 0x14, 0x7A, 0xE1, 0x47, 0xAE ]; # 47.11 as double print_r($recData); echo "\n"; print_r( unpack( "E", pack( "C*", ...$recData ) )[ 1 ] ); echo "\n";
Man beachte die unscheinbaren Punkte vor $recData im Aufruf von pack()! Dieser Spread-Operator entpackt das Array in $recData als einzelne Argumente für pack() und behebt die obige Fehlermeldung. Die Ausgabe sieht nun so aus:
Array ( [0] => 64 [1] => 71 [2] => 142 [3] => 20 [4] => 122 [5] => 225 [6] => 71 [7] => 174 ) 47.11
Wir bekommen also wieder die erwarteten 47.11 als Ergebnis zurück.