6 votes

Lire une structure dans une structure via la fonction Unpack de PHP

Je voudrais savoir comment lire une structure dans une structure via la fonction php déballer fonction. Lorsque je reçois un paquet IS_MCI, je vérifie son Type pour m'assurer qu'il est égal à ISP_MCI, puis je vérifie NumC pour savoir combien de structures CompCar il y a dans ce paquet. Le problème est d'essayer de décompresser ces contenus dans un tableau via une seule fonction. J'obtiens toujours un offset non défini. Je suis donc à la recherche d'un regard neuf sur la question.

Comment géreriez-vous ce paquet ?

La structure en question est la suivante :

struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[8];    // car info for each player, 1 to 8 of these (NumC)
};

struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

3voto

VolkerK Points 54118
$msg = 
   chr(0x20) // Size = 32 (4+1*28)
  . chr(0x1) // Type = 1
  . chr(0x0) // ReqI=0
  . chr(0x1) // NumC=1
    . chr(0x1) . chr(0x0) // node=1
    . chr(0x2) . chr(0x0)  // lap=2
    . chr(0x3) // puid=3
    . chr(0x5) // pos=5
    . chr(0x10) // info=16
    . chr(0x0) //sp3=0
    . chr(0x0) . chr(0x0) . chr(0x1) . chr(0x0) // x=65536
    . chr(0x0) . chr(0x0) . chr(0x2) . chr(0x0) // y=65536*2
    . chr(0x0) . chr(0x0) . chr(0x3) . chr(0x0)  // z=65536*3
    . chr(0x0) . chr(0x20) // speed=8192
    . chr(0x0) . chr(0x10) // dir=4096
    . chr(0x0) . chr(0x8) // heading=2048
    . chr(0x0) . chr(0x4) // AngVel=1024
;

$IS_MCI = unpack('CSize', $msg);
if ( strlen($msg) < $IS_MCI['Size'] ) {
  die("not enough data");
}
$IS_MCI += unpack('CType/CReqI/CNumC', substr($msg, 1));
$IS_MCI['Info'] = array();

for($i=0; $i<$IS_MCI['NumC']; $i++) {
  $data = substr($msg, 4+($i*28), 28);
  $IS_MCI['Info'][] = unpack('vNode/vLap/CPLID/CPosition/CInfo/CSp3/lX/lY/lZ/vSpeed/vDirection/vHeading/sAngVel', $data);
}
print_r($IS_MCI);

empreintes

Array
(
    [Size] => 32
    [Type] => 1
    [ReqI] => 0
    [NumC] => 1
    [Info] => Array
        (
            [0] => Array
                (
                    [Node] => 1
                    [Lap] => 2
                    [PLID] => 3
                    [Position] => 5
                    [Info] => 16
                    [Sp3] => 0
                    [X] => 65536
                    [Y] => 131072
                    [Z] => 196608
                    [Speed] => 8192
                    [Direction] => 4096
                    [Heading] => 2048
                    [AngVel] => 1024
                )

        )

)

Or, ce code repose sur des hypothèses que vous ne voudrez peut-être pas tenir pour acquises (c'est-à-dire ajouter beaucoup plus de gestion d'erreurs/de lecture de données).

  • Il suppose que le paquet ($msg) a été entièrement lu avant l'exécution du code. Vous pourriez vouloir ne lire que les parties dont vous avez besoin (pas besoin de substr() dans ce cas). Ou au moins être préparé au fait que le message peut arriver en plusieurs morceaux.
  • Il considère également les paramètres taille/num comme acquis, c'est-à-dire qu'il ne vérifie pas si les valeurs sont réalisables et si suffisamment de données sont disponibles. C'est certainement quelque chose que vous devez changer. Size doit être compris entre 0...228, NumC doit être compris entre 0...8 et les deux valeurs doivent s'emboîter, et ainsi de suite.
  • Regardez également de plus près les identifiants de format que j'ai utilisés dans unpack(). Pour les word J'ai utilisé v qui signifie "unsigned short (toujours 16 bits)", ordre des octets en petit endian ). Mais pour les int J'ai utilisé l : "signed long" (toujours 32 bits), commande d'octets de machine )". Cela fonctionne sur ma machine. Mais il faut chercher dans la documentation du protocole pour le endianness des données.

Les données de test contenues dans $msg ont été extraites du résultat de la commande

__declspec(align(1)) struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

__declspec(align(1)) struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[1];    // example: one element, fixed
};

int _tmain(int argc, _TCHAR* argv[])
{
  struct IS_MCI mci = {
    32, 1, 0, 1,
    { 1, 2, 3, 5, 16, 0, 65536, 65536*2, 65536*3, 8192, 4096, 2048, 1024 }
  };

  WSADATA wsaData;
  WORD wVersionRequested = MAKEWORD( 2, 2 );
   int err = WSAStartup( wVersionRequested, &wsaData );
  if ( err != 0 ) {
      /* Tell the user that we could not find a usable */
      /* WinSock DLL.                                  */
      return 1;
  }

  SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  sockaddr_in addr; 
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
  addr.sin_port = htons( 8081 );
  if ( 0!=connect( s, (SOCKADDR*) &addr, sizeof(addr) ) ) {
    printf("%X ", WSAGetLastError());
    return 0;
  }
  send(s, (const char*)&mci, sizeof(mci), 0);
  shutdown(s, SD_BOTH);
  closesocket(s);
  return 0;
}

1voto

Misiek Points 57

J'utilise ceci :

class IS_MCI extends ISP {
        public $Size;
        public $Type = ISP_MCI;
        public $ReqI;
        public $NumC;

        public function IS_MCI($data, &$CompCar) {
                $up = unpack('CSize/CType/CReqI/CNumC', $data);
                $this->Size = $up['Size'];
                $this->ReqI = $up['ReqI'];
                $this->NumC = $up['NumC'];

                $temp = array();

                $p = 4;
                for ($i = 0; $i NumC; $i++) {
                        $up2 = unpack('SNode/SLap/CPLID/CPosition/CInfo/CSp3/IX/IY/IZ/SSpeed/SDirection/SHeading/sAngVel', substr($data, $p, 28));
                        $temp[] = new CompCar($up2['Node'],$up2['Lap'],$up2['PLID'],$up2['Position'],$up2['Info'],$up2['Sp3'],$up2['X'],$up2['Y'],$up2['Z'],$
                        $p += 28;
                }
                $CompCar = $temp;
        }
}

Et la classe CompCar :

class CompCar {
        public $xNode;          // current path node
        public $Lap;            // current lap
        public $PLID;           // player's unique id
        public $Position;       // current race position : 0 = unknown, 1 = leader, etc...
        public $Info;           // flags and other info - see below
        public $Sp3;
        public $X;              // X map (65536 = 1 metre)
        public $Y;              // Y map (65536 = 1 metre)
        public $Z;              // Z alt (65536 = 1 metre)
        public $Speed;          // speed (32768 = 100 m/s)
        public $Direction;      // direction of car's motion : 0 = world y direction, 32768 = 180 deg
        public $Heading;        // direction of forward axis : 0 = world y direction, 32768 = 180 deg
        public $AngVel;         // signed, rate of change of heading : (16384 = 360 deg/s)

        public $SpeedKPH;       // speed in kph
        public $SpeedMPH;       // speed in mph
        public $DirectionC;     // Direction calculated to degrees
        public $HeadingC;       // Heading calculated to degrees
        public $AngVelC;        // Calculated

        // ADDED:
        public $SpeedMS;                // speed in mps

        public function __construct($xNode,$Lap,$PLID,$Position,$Info,$Sp3,$X,$Y,$Z,$Speed,$Direction,$Heading,$AngVel) {
                $this->xNode = $xNode;
                $this->Lap = $Lap;
                $this->PLID = $PLID;
                $this->Position = $Position;
                $this->Info = $Info;
                $this->Sp3 = $Sp3;
                $this->X = $X;
                $this->Y = $Y;
                $this->Z = $Z;
                $this->Speed = $Speed;
                $this->Direction = $Direction;
                $this->Heading = $Heading;
                $this->AngVel = $AngVel;

                $this->doCalcs();
        }

        private function doCalcs() {
                // Speed Calc
                $old = $this->Speed;
                $this->SpeedKPH = ($old * (100 / 32768)) * 3.6;
                $this->SpeedMPH = $this->SpeedKPH * 0.6215;

                $this->SpeedKPH = floor($this->SpeedKPH);
                $this->SpeedMPH = floor($this->SpeedMPH);
                $this->SpeedMS = $this->SpeedKPH/3.6;

                // Direction
                $this->DirectionC = CompCar::degrees($this->Direction);

                // Heading
                $this->HeadingC = CompCar::degrees($this->Heading);

                // Angle Calcs
                $this->AngVelC = $this->AngVel * 180 / 8192;
        }

        public static function degrees($input) {
                $input = $input / 65535 * 360;
                //$input = 360 - floor($input);
                $input = floor(360 - $input);
                return $input;
        }

}

Et tout fonctionne bien !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X