<?php

namespace Unit\models;

use app\models\Proto;
use Faker\Provider\Uuid;
use OS\PB\AccessAllowedEvent;
use OS\PB\AccessDeniedDeviceSpecialDaysOfWeekEvent;
use OS\PB\AccessDeniedDeviceTypeMismatchEvent;
use OS\PB\AccessDeniedDeviceUnknownEvent;
use OS\PB\AccessDeniedGroupDaysOfWeekEvent;
use OS\PB\AccessDeniedGroupGatewayEvent;
use OS\PB\AccessDeniedGroupTimeOfDayEvent;
use OS\PB\AccessDeniedNoEntriesLeftEvent;
use OS\PB\AccessDeniedNoExitsLeftEvent;
use OS\PB\AccessDevice;
use OS\PB\AccessDevicesEraseConfig;
use OS\PB\AccessDirectionTypes;
use OS\PB\AccessGatewayConfig;
use OS\PB\AccessGroup;
use OS\PB\AccessGroupsEraseConfig;
use OS\PB\AccessMetric;
use OS\PB\AccessUser;
use OS\PB\AccessUsersEraseConfig;
use OS\PB\ActionMessage;
use OS\PB\BleAssetTrackEvent;
use OS\PB\BlueToothPortConfig;
use OS\PB\ConfigMessage;
use OS\PB\ConfigUpdateFailedEvent;
use OS\PB\ConfigUpdateSuccessEvent;
use OS\PB\EventMessage;
use OS\PB\EventRule;
use OS\PB\LogMessage;
use OS\PB\LogMetric;
use OS\PB\Message;
use OS\PB\MetricMessage;
use OS\PB\NetworkAddressConfig;
use OS\PB\NetworkMetric;
use OS\PB\NetworkPort;
use OS\PB\NetworkQueueMetric;
use OS\PB\NetworkRouteConfig;
use OS\PB\Packet;
use OS\PB\Packet\PacketTypes;
use OS\PB\RelayActivateAction;
use OS\PB\RelayStateEvent;
use OS\PB\RelayStateEvent\RelayState;
use OS\PB\RequestMessage;
use OS\PB\RequestMessage\RequestIdTypes;
use OS\PB\RequestMessage\RequestMethodType;
use OS\PB\ResponseMessage;
use OS\PB\ResponseMessage\ResponseCode;
use OS\PB\RS485PortMetric;
use OS\PB\SystemFeaturesResponse;
use OS\PB\SystemHardwareInfoResponse;
use OS\PB\SystemPingResponse;
use OS\PB\SystemRestartAction;
use OS\PB\SystemRestartAction\RestartMethodType;
use OS\PB\SystemRestartEvent;
use OS\PB\SystemRestartEvent\RestartReasonType;
use OS\PB\WiegandCodeEvent;
use OS\PB\WiegandLineActivateAction;
use OS\PB\WiegandLineBlinkAction;
use OS\PB\WiegandLineTypes;
use OS\PB\WiegandPortMetric;
use UnitTester;

function timeMs(): int {
    return round(microtime(true) * 1000);
}

class ProtoTest extends \Codeception\Test\Unit {

    protected UnitTester $tester;

    private string $dest;
    private string $source;
    private int $SequenceNumber;
    private int $RedeliveryCounter;
    private int $CorrelationId;

    protected function _before() {
        parent::_before();

        /** @var Generator $eaker */
        $this->faker = \Faker\Factory::create('en_ZA');

        $this->dest = Uuid::uuid();
        $this->source = Uuid::uuid();
        $this->SequenceNumber = random_int(1, 10);
        $this->RedeliveryCounter = random_int(1, 5);
        $this->CorrelationId = random_int(1, 200);
    }

    private function randomMac() {
        return hexdec(bin2hex((random_bytes(6))));
    }

    // Test Serializing
    public function testBasicSerializingToArrayWithRawJSON() {
        $json = '{
            "PacketType": 64,
            "DestinationAddressEx": "fa2509d2-243c-32d1-a69f-9cd884a3a4ff",
            "SourceAddressEx": "0548ebbc-9a7a-3e02-8162-d1b4fd08bc47",
            "SequenceNumber": 6,
            "RedeliveryCounter": 1,
            "TimeStampMs": "1707300961094",
            "Messages": [
              {
                "TimeStampMs": "1707300961105",
                "TimeToLive": "3600",
                "CorrelationId": "6",
                "Event": {
                  "WiegandCode": {
                    "Code": "75f8ad82bd",
                    "BitCount": 3,
                    "ParityFailed": true
                  }
                }
              }
            ]
          }';


        $p = new Proto();
        $serialized = $p->jsonToSerializedArray($json);

        $this->assertEquals($serialized['PacketType'], 64);
        $this->assertEquals($serialized['DestinationAddressEx'], base64_encode("fa2509d2-243c-32d1-a69f-9cd884a3a4ff"));
        $this->assertEquals($serialized['SourceAddressEx'], base64_encode("0548ebbc-9a7a-3e02-8162-d1b4fd08bc47"));
        $this->assertEquals($serialized['SequenceNumber'], 6);
        $this->assertEquals($serialized['RedeliveryCounter'], 1);
        $this->assertEquals($serialized['TimeStampMs'], "1707300961094");
        $this->assertIsArray($serialized['Messages']);
    }


    // Test Reserializing
    private function getEncodedThenDecodedJson(string $json): array {
        $p = new Proto();
        $serialized = $p->jsonToSerializedArray($json);

        $packet = new Packet();
        $packet->mergeFromJsonString(json_encode($serialized));
        $proto = $packet->serializeToString();

        $packet2 = new Packet();
        $packet2->mergeFromString($proto);


        $q = new Proto();
        $deserialized = $q->protoPacketToUnserializedArray($packet2);

        return $deserialized;
    }


    public function testBasicEncodingAndDecodingWithRawJSON() {
        $json = '{
            "PacketType": 64,
            "DestinationAddressEx": "fa2509d2-243c-32d1-a69f-9cd884a3a4ff",
            "SourceAddressEx": "0548ebbc-9a7a-3e02-8162-d1b4fd08bc47",
            "SequenceNumber": 6,
            "RedeliveryCounter": 1,
            "TimeStampMs": "1707300961094",
            "Messages": [
              {
                "TimeStampMs": "1707300961105",
                "TimeToLive": "3600",
                "CorrelationId": "6",
                "Event": {
                  "WiegandCode": {
                    "Code": "75f8ad82bd",
                    "BitCount": 3,
                    "ParityFailed": true
                  }
                }
              }
            ]
          }';

        $deserialized = $this->getEncodedThenDecodedJson($json);

        $this->assertEquals($deserialized['PacketType'], 64);
        $this->assertEquals($deserialized['DestinationAddressEx'], "fa2509d2-243c-32d1-a69f-9cd884a3a4ff");
        $this->assertEquals($deserialized['SourceAddressEx'], "0548ebbc-9a7a-3e02-8162-d1b4fd08bc47");
        $this->assertEquals($deserialized['SequenceNumber'], 6);
        $this->assertEquals($deserialized['RedeliveryCounter'], 1);
        $this->assertEquals($deserialized['TimeStampMs'], "1707300961094");
        $this->assertIsArray($deserialized['Messages']);
    }


    public function testResponseEncodingAndDecodingWithRawJSON() {
        $json = '{
            "PacketType": 64,
            "DestinationAddressEx": "e2c3c51c-351c-365f-af62-6043a2d04d21",
            "SourceAddressEx": "3e810857-2a4d-3045-bec5-cc4bc6971696",
            "SequenceNumber": 2,
            "RedeliveryCounter": 2,
            "TimeStampMs": "1707374651249",
            "Messages": [
              {
                "TimeStampMs": "1707374651249",
                "TimeToLive": "3600",
                "CorrelationId": "71",
                "Response": {
                  "Code": 413,
                  "SystemHardwareInfo": {
                    "SerialNumber": 1968,
                    "BootFirmwareVersion": "2.8.16",
                    "AppFirmwareVersion": "3.2.8",
                    "HardwarePlatform": "AOH1000-007",
                    "HardwareVersion": 3,
                    "ManufacturedDate": "1707374651",
                    "InternalFileSystemSize": 4701,
                    "ExternalFileSystemSize": 5846,
                    "MCUUID": "6689065167",
                    "MCUSocRevision": 2375,
                    "MCUPartId": 2977,
                    "BLEMAC": "22946497157864",
                    "BLECodedPhyPresent": true,
                    "EthernetMAC": "177605987279305",
                    "GSMModemManufacturer": "Cheese Inc",
                    "GSMModemModel": "EC200U",
                    "GSMModemRevision": "EC200UCNAAR01A04M08",
                    "GSMModemIMEI": "3f63929b",
                    "GSMModemSerialNumber": "88969eba5fdf",
                    "GSMSIM1IMSI": "fca34c5ba0de06222cc207322e486a1f",
                    "GSMSIM1ICCID": "c3fe52052b0d9e123e7dd62be0bd5e11cf5971f0",
                    "GSMSIM1SubscriberNumber": "+27761234567",
                    "GSMSIM2IMSI": "7cd8aa6ac6f9214f026d754c89ee7b8c",
                    "GSMSIM2ICCID": "488638ddfdff6616326347a726951d786f92a1a1",
                    "GSMSIM2SubscriberNumber": "+27767654321"
                  }
                }
              }
            ]
          }';

        $deserialized = $this->getEncodedThenDecodedJson($json);

        $hw = $deserialized['Messages'][0]['Response']['SystemHardwareInfo'];

        $this->assertEquals($hw['GSMModemModel'], "EC200U");
        $this->assertEquals($hw['GSMModemRevision'], "EC200UCNAAR01A04M08");
        $this->assertEquals($hw['GSMModemIMEI'], "3f63929b");
        $this->assertEquals($hw['GSMModemSerialNumber'], "88969eba5fdf");
        $this->assertEquals($hw['GSMSIM1IMSI'], "fca34c5ba0de06222cc207322e486a1f");
        $this->assertEquals($hw['GSMSIM1ICCID'], "c3fe52052b0d9e123e7dd62be0bd5e11cf5971f0");
        $this->assertEquals($hw['GSMSIM2IMSI'], "7cd8aa6ac6f9214f026d754c89ee7b8c");
        $this->assertEquals($hw['GSMSIM2ICCID'], "488638ddfdff6616326347a726951d786f92a1a1");
    }


    public function testUnassociateArrayEncodingAndDecodingWithRawJSON() {
        $json = '{
            "PacketType": 64,
            "DestinationAddressEx": "df195a30-0588-3136-a04c-d55ed42c8a20",
            "SourceAddressEx": "d52bfcfe-d50f-36f8-8c50-eb87fd27f8f5",
            "SequenceNumber": 8,
            "RedeliveryCounter": 5,
            "TimeStampMs": "1707375216801",
            "Messages": [
              {
                "TimeStampMs": "1707375216802",
                "TimeToLive": "3600",
                "CorrelationId": "102",
                "Event": {
                  "ConfigUpdateFailed": {
                    "ConfigName": [
                      "Config Random A",
                      "The Rain in Spain"
                    ],
                    "FailedReasons": [
                      "The Rain in Spain",
                      "No internet"
                    ]
                  }
                }
              }
            ]
          }';

        $deserialized = $this->getEncodedThenDecodedJson($json);

        $hw = $deserialized['Messages'][0]['Event']['ConfigUpdateFailed'];

        $this->assertCount(2, $hw['ConfigName']);
        $this->assertCount(2, $hw['FailedReasons']);

        $this->assertEquals('Config Random A', $hw['ConfigName'][0]);
        $this->assertEquals('The Rain in Spain', $hw['FailedReasons'][0]);
    }


    public function testAccessDevicesEncodingAndDecodingWithRawJSON() {
        $json = '{
            "PacketType": 64,
            "DestinationAddressEx": "b5b626a5-6ff5-39de-be36-7531f7f4a2e2",
            "SourceAddressEx": "7c42019f-1d1f-39b2-b0d1-39ad2dd2c48a",
            "SequenceNumber": 7,
            "RedeliveryCounter": 3,
            "TimeStampMs": "1707376025429",
            "Messages": [
              {
                "TimeStampMs": "1707376025429",
                "TimeToLive": "3600",
                "CorrelationId": "176",
                "Event": {
                  "AccessAllowed": {
                    "AccessGatewayId": "221",
                    "AllowedAccessDevice": {
                      "NumberPlate": "NU 254234",
                      "AccessUserId": "118576",
                      "AccessGroupIds": [
                        "86917",
                        "682135",
                        "625620"
                      ],
                      "SpecialDaysOfWeekBits": 211297
                    },
                    "UserEntriesUsed": 3,
                    "UserExitsUsed": 3
                  }
                }
              }
            ]
          }';

        $deserialized = $this->getEncodedThenDecodedJson($json);

        $hw = $deserialized['Messages'][0]['Event']['AccessAllowed']['AllowedAccessDevice']['NumberPlate'];

        $this->assertEquals("NU 254234", $hw);
    }


    // Test Packet to Unserialized Array

    private function basicPacketPropertiesTest(Packet $p): array {
        $s = (new Proto())->protoPacketToUnserializedArray($p);

        $this->assertEquals(PacketTypes::PacketType_Response_NACK_Corrupt, $s['PacketType']);
        $this->assertEquals($this->dest, $s['DestinationAddressEx']);
        $this->assertEquals($this->source, $s['SourceAddressEx']);
        $this->assertEquals($this->SequenceNumber, $s['SequenceNumber']);
        $this->assertEquals($this->RedeliveryCounter, $s['RedeliveryCounter']);

        $m = $s['Messages'][0];

        $this->assertEquals($this->CorrelationId, $m['CorrelationId']);

        return $m;
    }


    public function testTopLevelProperties() {
        $DestinationSerial = random_int(1, 9999009);
        $DestinationAddress = random_int(1111111111, 9999999999);
        $DestinationPort = random_int(500, 2500);
        $SourceSerial = random_int(1, 9999009);
        $SourceAddress = random_int(1111111111, 9999999999);

        $EncryptedMessages = [
            bin2hex(random_bytes(30)),
            bin2hex(random_bytes(30)),
            bin2hex(random_bytes(30)),
        ];

        $networkPorts = [
            NetworkPort::NetworkPort_Any,
            NetworkPort::NetworkPort_RS485,
            NetworkPort::NetworkPort_BLE,
            NetworkPort::NetworkPort_BTMesh,
            NetworkPort::NetworkPort_Ethernet,
            NetworkPort::NetworkPort_WiFi,
        ];

        foreach ($networkPorts as $np) {
            $p = new Packet([
                "PacketType"         => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationSerial"  => $DestinationSerial,
                "DestinationAddress" => $DestinationAddress,
                "DestinationPort"    => $DestinationPort,
                "SourceSerial"       => $SourceSerial,
                "SourceAddress"      => $SourceAddress,
                "SourcePort"         => $np,
                "EncryptedMessages"  => $EncryptedMessages,
            ]);

            $s = (new Proto())->protoPacketToUnserializedArray($p);
            $this->assertEquals($DestinationSerial, $s['DestinationSerial']);
            $this->assertEquals($DestinationAddress, $s['DestinationAddress']);
            $this->assertEquals($DestinationPort, $s['DestinationPort']);
            $this->assertEquals($SourceSerial, $s['SourceSerial']);
            $this->assertEquals($SourceAddress, $s['SourceAddress']);
            $this->assertEquals($EncryptedMessages[0], $s['EncryptedMessages'][0]);
            $this->assertEquals($EncryptedMessages[1], $s['EncryptedMessages'][1]);
            $this->assertEquals($EncryptedMessages[2], $s['EncryptedMessages'][2]);
            if ($np == NetworkPort::NetworkPort_Any) {
                $this->assertArrayNotHasKey('SourcePort', $s);
            } else {
                $this->assertEquals($np, $s['SourcePort']);
            }
        }
    }


    public function testPacketEventMessageWiegandCode() {
        $Code = strval(bin2hex(random_bytes(5)));
        $BitCount = random_int(1, 3);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "WiegandCode" => new WiegandCodeEvent([
                            "Code" => $Code,
                            "BitCount" => $BitCount,
                            "ParityFailed" => true,
                            "SingleKeyBitBurstMode" => false,
                        ])
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $this->assertEquals($Code, $m['Event']['WiegandCode']['Code']);
        $this->assertEquals($BitCount, $m['Event']['WiegandCode']['BitCount']);
        $this->assertEquals(1, $m['Event']['WiegandCode']['ParityFailed']); // 1 == true
    }


    public function testPacketEventMessageRelayState() {
        $DurationMs = random_int(500, 2500);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "RelayState" => new RelayStateEvent([
                            'State' => RelayState::RelayActivated,
                            'DurationMs' => $DurationMs,
                        ])
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $this->assertEquals(RelayState::RelayActivated, $m['Event']['RelayState']['State']);
        $this->assertEquals($DurationMs, $m['Event']['RelayState']['DurationMs']);
    }


    public function testPacketEventMessageBleAssetTrack() {
        $nId1 = random_int(1, 999999);
        $nId2 = random_int(1, 999999);
        $nId3 = random_int(1, 999999);

        $m1 = $this->randomMac();
        $m2 = $this->randomMac();
        $m3 = $this->randomMac();

        $nRD1 = 'a' . bin2hex(random_bytes(10));
        $nRD2 = 'b' . bin2hex(random_bytes(10));
        $nRD3 = 'c' . bin2hex(random_bytes(10));

        $nRS1 = random_int(-2500, 2500);
        $nRS2 = random_int(-2500, 2500);
        $nRS3 = random_int(-2500, 2500);

        $nTm1 = random_int(1, 500);
        $nTm2 = random_int(1, 500);
        $nTm3 = random_int(1, 500);

        $lId1 = random_int(1, 25000);
        $lId2 = random_int(1, 25000);
        $lId3 = random_int(1, 25000);

        $lRS1 = random_int(1, 2500);
        $lRS2 = random_int(1, 2500);
        $lRS3 = random_int(1, 2500);

        $ltsM1 = random_int(1, 400);
        $ltsM2 = random_int(1, 400);
        $ltsM3 = random_int(1, 400);

        $cId1 = random_int(1, 250000);
        $cId2 = random_int(1, 250000);
        $cId3 = random_int(1, 250000);

        $cRS1 = random_int(-2500, 2500);
        $cRS2 = random_int(-2500, 2500);
        $cRS3 = random_int(-2500, 2500);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "BleAssetTrack" => new BleAssetTrackEvent([
                            "New_id"         => [$nId1,  $nId2,  $nId3],
                            "New_macAddress" => [$m1,    $m2,    $m3],
                            "New_rawData"    => [$nRD1,  $nRD2,  $nRD3],
                            "New_rssi"       => [$nRS1,  $nRS2,  $nRS3],
                            "New_tsMinus"    => [$nTm1,  $nTm2,  $nTm3],
                            "Lost_id"        => [$lId1,  $lId2,  $lId3],
                            "Lost_rssi"      => [$lRS1,  $lRS2,  $lRS3],
                            "Lost_tsMinus"   => [$ltsM1, $ltsM2, $ltsM3],
                            "Current_id"     => [$cId1,  $cId2,  $cId3],
                            "Current_rssi"   => [$cRS1,  $cRS2,  $cRS3]
                        ])
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $e = $m['Event']['BleAssetTrack'];
        $this->assertEquals($nId1, $e['NewId'][0]);
        $this->assertEquals($nId2, $e['NewId'][1]);
        $this->assertEquals($nId3, $e['NewId'][2]);
        $this->assertEquals($m1, $e['NewMacAddress'][0]);
        $this->assertEquals($m2, $e['NewMacAddress'][1]);
        $this->assertEquals($m3, $e['NewMacAddress'][2]);
        $this->assertEquals($nRD1, $e['NewRawData'][0]);
        $this->assertEquals($nRD2, $e['NewRawData'][1]);
        $this->assertEquals($nRD3, $e['NewRawData'][2]);
        $this->assertEquals($nRS1, $e['NewRssi'][0]);
        $this->assertEquals($nRS2, $e['NewRssi'][1]);
        $this->assertEquals($nRS3, $e['NewRssi'][2]);
        $this->assertEquals($nTm1, $e['NewTsMinus'][0]);
        $this->assertEquals($nTm2, $e['NewTsMinus'][1]);
        $this->assertEquals($nTm3, $e['NewTsMinus'][2]);
        $this->assertEquals($lId1, $e['LostId'][0]);
        $this->assertEquals($lId2, $e['LostId'][1]);
        $this->assertEquals($lId3, $e['LostId'][2]);
        $this->assertEquals($lRS1, $e['LostRssi'][0]);
        $this->assertEquals($lRS2, $e['LostRssi'][1]);
        $this->assertEquals($lRS3, $e['LostRssi'][2]);
        $this->assertEquals($ltsM1, $e['LostTsMinus'][0]);
        $this->assertEquals($ltsM2, $e['LostTsMinus'][1]);
        $this->assertEquals($ltsM3, $e['LostTsMinus'][2]);
        $this->assertEquals($cId1, $e['CurrentId'][0]);
        $this->assertEquals($cId2, $e['CurrentId'][1]);
        $this->assertEquals($cId3, $e['CurrentId'][2]);
        $this->assertEquals($cRS1, $e['CurrentRssi'][0]);
        $this->assertEquals($cRS2, $e['CurrentRssi'][1]);
        $this->assertEquals($cRS3, $e['CurrentRssi'][2]);
    }


    public function testPacketEventMessageConfigUpdateSuccess() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "ConfigUpdateSuccess" => new ConfigUpdateSuccessEvent([])
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $this->assertCount(0, $m['Event']['ConfigUpdateSuccess']);
    }


    public function testPacketEventMessageConfigUpdateFailed() {
        $config = [
            "Config Random A",
            "The Rain in Spain"
        ];
        $eailed = [
            "The Rain in Spain",
            "No internet"
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "ConfigUpdateFailed" => new ConfigUpdateFailedEvent([
                            "ConfigName" => $config,
                            "FailedReasons" => $eailed,
                        ])
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $e = $m['Event']['ConfigUpdateFailed'];
        $this->assertCount(2, $e['ConfigName']);
        $this->assertCount(2, $e['FailedReasons']);
        $this->assertEquals("Config Random A", $e['ConfigName'][0]);
        $this->assertEquals("The Rain in Spain", $e['ConfigName'][1]);
        $this->assertEquals("The Rain in Spain", $e['FailedReasons'][0]);
        $this->assertEquals("No internet", $e['FailedReasons'][1]);
    }


    public function testPacketEventMessageAccessAllowed() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessAllowed" => new AccessAllowedEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "AllowedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value
                                ]),
                                "UserEntriesUsed" => 3,
                                "UserExitsUsed" => 3,
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessAllowed'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['AllowedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['AllowedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['AllowedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['AllowedAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedDeviceUnknown() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedDeviceUnknown" => new AccessDeniedDeviceUnknownEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedDeviceUnknown'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedTypeMissmatch() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => [bin2hex(random_bytes(8)), bin2hex(random_bytes(8))],
            "RFIDTagUID"   => [random_int(1, 50), random_int(1, 50)],
            "BTPhoneUID"   => [random_int(1, 50), random_int(1, 50)],
            "RemoteUID"    => [random_int(1, 50), random_int(1, 50)],
            "BiometricUID" => [random_int(1, 50), random_int(1, 50)],
            "NumberPlate"  => ['NU 254234', 'NU 254235'],
            "PinCode"      => [random_int(1000, 9999), random_int(1000, 9999)],
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedDeviceTypeMismatch" => new AccessDeniedDeviceTypeMismatchEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value[0],
                                ]),
                                "MatchingAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value[1],
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedDeviceTypeMismatch'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);

            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value[0], $e['DeniedAccessDevice'][$key]);

            $this->assertEquals($AccessUserId, $e['MatchingAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['MatchingAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['MatchingAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value[1], $e['MatchingAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedDeviceSpecialDaysOfWeek() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedDeviceSpecialDaysOfWeek" => new AccessDeniedDeviceSpecialDaysOfWeekEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedDeviceSpecialDaysOfWeek'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedGroupDaysOfWeek() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedGroupGateway" => new AccessDeniedGroupGatewayEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedGroupGateway'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedGroupGateway() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedGroupDaysOfWeek" => new AccessDeniedGroupDaysOfWeekEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedGroupDaysOfWeek'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedGroupTimeOfDay() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedGroupTimeOfDay" => new AccessDeniedGroupTimeOfDayEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedGroupTimeOfDay'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);
        }
    }


    public function testPacketEventMessageAccessDeniedNoEntriesLeft() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);
        $Id =  random_int(1, 99999);
        $UAccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedNoEntriesLeft" => new AccessDeniedNoEntriesLeftEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "UserEntriesUsed" => 3,
                                "UserExitsUsed" => 3,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                                "DeniedUser" => new AccessUser([
                                    "Id" => $Id,
                                    "FirstExitsThenEnters" => true,
                                    "RollOverExitToNextDay" => true,
                                    "EntriesMax" => 5,
                                    "ExitsMax" => 5,
                                    "StartDateTime" => timeMs(),
                                    "EndDateTime" => timeMs(),
                                    "AccessGroupIds" => $UAccessGroupIds,
                                ]),
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedNoEntriesLeft'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);

            $this->assertEquals($Id, $e['DeniedUser']['Id']);
            $this->assertEquals($UAccessGroupIds[0], $e['DeniedUser']['AccessGroupIds'][0]);
            $this->assertEquals($UAccessGroupIds[1], $e['DeniedUser']['AccessGroupIds'][1]);
            $this->assertEquals($UAccessGroupIds[2], $e['DeniedUser']['AccessGroupIds'][2]);
        }
    }


    public function testPacketEventMessageAccessDeniedNoExitsLeft() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);
        $Id =  random_int(1, 99999);
        $UAccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedNoExitsLeft" => new AccessDeniedNoExitsLeftEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "UserEntriesUsed" => 3,
                                "UserExitsUsed" => 3,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                                "DeniedUser" => new AccessUser([
                                    "Id" => $Id,
                                    "FirstExitsThenEnters" => true,
                                    "RollOverExitToNextDay" => true,
                                    "EntriesMax" => 5,
                                    "ExitsMax" => 5,
                                    "StartDateTime" => timeMs(),
                                    "EndDateTime" => timeMs(),
                                    "AccessGroupIds" => $UAccessGroupIds,
                                ]),
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Event']['AccessDeniedNoExitsLeft'];
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals($AccessUserId, $e['DeniedAccessDevice']['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['DeniedAccessDevice']['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['DeniedAccessDevice']['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e['DeniedAccessDevice'][$key]);

            $this->assertEquals($Id, $e['DeniedUser']['Id']);
            $this->assertEquals($UAccessGroupIds[0], $e['DeniedUser']['AccessGroupIds'][0]);
            $this->assertEquals($UAccessGroupIds[1], $e['DeniedUser']['AccessGroupIds'][1]);
            $this->assertEquals($UAccessGroupIds[2], $e['DeniedUser']['AccessGroupIds'][2]);
        }
    }


    public function testPacketEventMessageSystemRestart() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "SystemRestart" => new SystemRestartEvent([
                            // "RestartReason" => RestartReasonType::RestartReasonNotSet,
                            // "RestartReason" => RestartReasonType::RestartReasonAction,
                            // "RestartReason" => RestartReasonType::RestartDFURollback,
                            // "RestartReason" => RestartReasonType::RestartReasonEthernetDriverUnresponsive,
                            "RestartReason" => RestartReasonType::RestartReasonModemDriverUnresponsive,
                            "ActionReceived" => new SystemRestartAction([
                                // "RestartMethod" => RestartMethodType::RestartMethodNotSet,
                                // "RestartMethod" => RestartMethodType::RestartMethodGraceful,
                                "RestartMethod" => RestartMethodType::RestartMethodForceful,
                            ])
                        ])
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $e = $m['Event']['SystemRestart'];

        $this->assertEquals(RestartReasonType::RestartReasonModemDriverUnresponsive, $e['RestartReason']);
        $this->assertEquals(RestartMethodType::RestartMethodForceful, $e['ActionReceived']['RestartMethod']);
    }


    public function testPacketActionsMessageWiegandLineActivate() {
        $DurationMs = random_int(500, 2500);

        $types = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        foreach ($types as $type) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Action" => new ActionMessage([
                            "WiegandLineActivate" => new WiegandLineActivateAction([
                                "LineOutput" => $type,
                                "DurationMs" => $DurationMs,
                            ]),
                        ])
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Action']['WiegandLineActivate'];
            $this->assertEquals($DurationMs, $e['DurationMs']);
            if ($type == WiegandLineTypes::Wiegand_LineNotSet) {
                $this->assertArrayNotHasKey('LineOutput', $e);
            } else {
                $this->assertEquals($type, $e['LineOutput']);
            }
        }
    }


    public function testPacketActionsMessageWiegandLineBlink() {
        $DurationMs = random_int(500, 2500);
        $PeriodOnMs = random_int(100, 3500);
        $PeriodOffMs = random_int(100, 3500);

        $types = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        foreach ($types as $type) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Action" => new ActionMessage([
                            "WiegandLineBlink" => new WiegandLineBlinkAction([
                                "LineOutput" => $type,
                                "DurationMs" => $DurationMs,
                                "PeriodOnMs" => $PeriodOnMs,
                                "PeriodOffMs" => $PeriodOffMs,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Action']['WiegandLineBlink'];
            $this->assertEquals($DurationMs, $e['DurationMs']);
            $this->assertEquals($PeriodOnMs, $e['PeriodOnMs']);
            $this->assertEquals($PeriodOffMs, $e['PeriodOffMs']);
            if ($type == WiegandLineTypes::Wiegand_LineNotSet) {
                $this->assertArrayNotHasKey('LineOutput', $e);
            } else {
                $this->assertEquals($type, $e['LineOutput']);
            }
        }
    }


    public function testPacketActionsMessageRelayActivate() {
        $DurationMs = random_int(500, 2500);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Action" => new ActionMessage([
                        "RelayActivate" => new RelayActivateAction([
                            "DurationMs" => $DurationMs,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Action']['RelayActivate'];
        $this->assertEquals($DurationMs, $e['DurationMs']);
    }


    public function testPacketActionsMessageSystemRestart() {
        $types = [
            RestartMethodType::RestartMethodNotSet,
            RestartMethodType::RestartMethodGraceful,
            RestartMethodType::RestartMethodForceful,
        ];

        foreach ($types as $type) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Action" => new ActionMessage([
                            "SystemRestart" => new SystemRestartAction([
                                "RestartMethod" => $type,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Action']['SystemRestart'];

            if ($type == RestartMethodType::RestartMethodNotSet) {
                $this->assertArrayNotHasKey('RestartMethod', $e);
            } else {
                $this->assertEquals($type, $e['RestartMethod']);
            }
        }
    }


    public function testPacketConfigMessageAccessDeviceUpdate() {
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "AccessDeviceUpdate" => new AccessDevice([
                                "AccessUserId" => $AccessUserId,
                                "AccessGroupIds" => $AccessGroupIds,
                                "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                $key => $value,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Config']['AccessDeviceUpdate'];
            $this->assertEquals($AccessUserId, $e['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e[$key]);
        }
    }


    public function testPacketConfigMessageAccessDeviceDelete() {
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "AccessDeviceDelete" => new AccessDevice([
                                "AccessUserId" => $AccessUserId,
                                "AccessGroupIds" => $AccessGroupIds,
                                "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                $key => $value,
                            ])
                        ])
                    ]),
                ],
            ]);

            $m = $this->basicPacketPropertiesTest($p);

            $e = $m['Config']['AccessDeviceDelete'];
            $this->assertEquals($AccessUserId, $e['AccessUserId']);
            $this->assertEquals($AccessGroupIds, $e['AccessGroupIds']);
            $this->assertEquals($SpecialDaysOfWeekBits, $e['SpecialDaysOfWeekBits']);
            $this->assertEquals($value, $e[$key]);
        }
    }


    public function testPacketConfigMessageAccessUserUpdate() {
        $Id = random_int(1, 99999);
        $AccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessUserUpdate" => new AccessUser([
                            "Id" => $Id,
                            "FirstExitsThenEnters" => true,
                            "RollOverExitToNextDay" => true,
                            "EntriesMax" => 5,
                            "ExitsMax" => 5,
                            "StartDateTime" => timeMs(),
                            "EndDateTime" => timeMs(),
                            "AccessGroupIds" => $AccessGroupIds,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessUserUpdate'];
        $this->assertEquals($Id, $e['Id']);
        $this->assertEquals($AccessGroupIds[0], $e['AccessGroupIds'][0]);
        $this->assertEquals($AccessGroupIds[1], $e['AccessGroupIds'][1]);
        $this->assertEquals($AccessGroupIds[2], $e['AccessGroupIds'][2]);
        $this->assertEquals(1, $e['FirstExitsThenEnters']); // 1 == true
        $this->assertEquals(1, $e['RollOverExitToNextDay']); // 1 == true
    }


    public function testPacketConfigMessageAccessUserDelete() {
        $Id = random_int(1, 99999);
        $AccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessUserDelete" => new AccessUser([
                            "Id" => $Id,
                            "FirstExitsThenEnters" => true,
                            "RollOverExitToNextDay" => true,
                            "EntriesMax" => 5,
                            "ExitsMax" => 5,
                            "StartDateTime" => timeMs(),
                            "EndDateTime" => timeMs(),
                            "AccessGroupIds" => $AccessGroupIds,
                        ])
                    ])
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessUserDelete'];
        $this->assertEquals($Id, $e['Id']);
        $this->assertEquals($AccessGroupIds[0], $e['AccessGroupIds'][0]);
        $this->assertEquals($AccessGroupIds[1], $e['AccessGroupIds'][1]);
        $this->assertEquals($AccessGroupIds[2], $e['AccessGroupIds'][2]);
        $this->assertEquals(1, $e['FirstExitsThenEnters']); // 1 == true
        $this->assertEquals(1, $e['RollOverExitToNextDay']); // 1 == true
    }


    public function testPacketConfigMessageAccessGroupUpdate() {
        $Id = random_int(1, 99999);
        $AccessPeripheralIds = [
            random_int(1, 9999),
            random_int(1, 9999),
            random_int(1, 9999),
        ];

        $DaysOfWeekBits = random_int(0, 7);
        $TimesOfDayBits = random_int(0, 47);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessGroupUpdate" => new AccessGroup([
                            "Id" => $Id,
                            "DaysOfWeekBits" => $DaysOfWeekBits,
                            "TimesOfDayBits" => $TimesOfDayBits,
                            "AccessPeripheralIds" => $AccessPeripheralIds,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessGroupUpdate'];
        $this->assertEquals($Id, $e['Id']);
        if ($DaysOfWeekBits == 0) {
            $this->assertArrayNotHasKey('DaysOfWeekBits', $e);
        } else {
            $this->assertEquals($DaysOfWeekBits, $e['DaysOfWeekBits']);
        }
        if ($TimesOfDayBits == 0) {
            $this->assertArrayNotHasKey('TimesOfDayBits', $e);
        } else {
            $this->assertEquals($TimesOfDayBits, $e['TimesOfDayBits']);
        }
        $this->assertEquals($AccessPeripheralIds[0], $e['AccessPeripheralIds'][0]);
        $this->assertEquals($AccessPeripheralIds[1], $e['AccessPeripheralIds'][1]);
        $this->assertEquals($AccessPeripheralIds[2], $e['AccessPeripheralIds'][2]);
    }


    public function testPacketConfigMessageAccessGroupDelete() {
        $Id = random_int(1, 99999);
        $AccessPeripheralIds = [
            random_int(1, 9999),
            random_int(1, 9999),
            random_int(1, 9999),
        ];

        $DaysOfWeekBits = random_int(0, 7);
        $TimesOfDayBits = random_int(0, 47);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessGroupDelete" => new AccessGroup([
                            "Id" => $Id,
                            "DaysOfWeekBits" => $DaysOfWeekBits,
                            "TimesOfDayBits" => $TimesOfDayBits,
                            "AccessPeripheralIds" => $AccessPeripheralIds,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessGroupDelete'];
        $this->assertEquals($Id, $e['Id']);
        if ($DaysOfWeekBits == 0) {
            $this->assertArrayNotHasKey('DaysOfWeekBits', $e);
        } else {
            $this->assertEquals($DaysOfWeekBits, $e['DaysOfWeekBits']);
        }
        if ($TimesOfDayBits == 0) {
            $this->assertArrayNotHasKey('TimesOfDayBits', $e);
        } else {
            $this->assertEquals($TimesOfDayBits, $e['TimesOfDayBits']);
        }
        $this->assertEquals($AccessPeripheralIds[0], $e['AccessPeripheralIds'][0]);
        $this->assertEquals($AccessPeripheralIds[1], $e['AccessPeripheralIds'][1]);
        $this->assertEquals($AccessPeripheralIds[2], $e['AccessPeripheralIds'][2]);
    }


    public function testPacketConfigMessageAccessGateway() {
        $AccessDevicesHashModulo = random_int(1, 64);
        $AccessGroupsHashModulo = random_int(1, 10);
        $AccessUsersHashModulo = random_int(1, 10);
        $AccessGatewayId = random_int(1, 19999);

        $directions = [
            AccessDirectionTypes::AccessDirection_NotSet,
            AccessDirectionTypes::AccessDirection_Both,
            AccessDirectionTypes::AccessDirection_Entry,
            AccessDirectionTypes::AccessDirection_Exit,
        ];

        foreach ($directions as $direction) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "AccessGateway" => new AccessGatewayConfig([
                                "AccessDevicesHashModulo" => $AccessDevicesHashModulo,
                                "AccessGroupsHashModulo" => $AccessGroupsHashModulo,
                                "AccessUsersHashModulo" => $AccessUsersHashModulo,
                                "AccessGatewayId" => $AccessGatewayId,
                                "AccessGatewayDirection" => $direction,
                                "AccessEnabled" => true,
                            ]),
                        ]),
                    ]),
                ],
            ]);
            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Config']['AccessGateway'];
            $this->assertEquals($AccessDevicesHashModulo, $e['AccessDevicesHashModulo']);
            $this->assertEquals($AccessGroupsHashModulo, $e['AccessGroupsHashModulo']);
            $this->assertEquals($AccessUsersHashModulo, $e['AccessUsersHashModulo']);
            $this->assertEquals($AccessGatewayId, $e['AccessGatewayId']);
            $this->assertEquals(1, $e['AccessEnabled']); // 1 == true

            if ($direction == AccessDirectionTypes::AccessDirection_NotSet) {
                $this->assertArrayNotHasKey('AccessGatewayDirection', $e);
            } else {
                $this->assertEquals($direction, $e['AccessGatewayDirection']);
            }
        }
    }


    public function testPacketConfigMessageAccessDevicesErase() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessDevicesErase" => new AccessDevicesEraseConfig([
                            "AreYouSure" => true,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessDevicesErase'];
        $this->assertEquals(1, $e['AreYouSure']); // 1 == true
    }


    public function testPacketConfigMessageAccessUsersErase() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessUsersErase" => new AccessUsersEraseConfig([
                            "AreYouSure" => true,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessUsersErase'];
        $this->assertEquals(1, $e['AreYouSure']); // 1 == true
    }


    public function testPacketConfigMessageAccessGroupsErase() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessGroupsErase" => new AccessGroupsEraseConfig([
                            "AreYouSure" => true,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['AccessGroupsErase'];
        $this->assertEquals(1, $e['AreYouSure']); // 1 == true
    }


    public function testPacketConfigMessagEventRuleUpdate() {
        $Id = random_int(1, 99999);
        $DestinationSerial = random_int(1, 9999);
        $DestinationAddress = [
            random_int(1, 9999999),
            random_int(1, 9999999),
            random_int(1, 9999999),
        ];

        $nId1 = random_int(1, 999999);
        $nId2 = random_int(1, 999999);
        $nId3 = random_int(1, 999999);

        $m1 = $this->randomMac();
        $m2 = $this->randomMac();
        $m3 = $this->randomMac();

        $nRD1 = 'a' . bin2hex(random_bytes(10));
        $nRD2 = 'b' . bin2hex(random_bytes(10));
        $nRD3 = 'c' . bin2hex(random_bytes(10));

        $nRS1 = random_int(-2500, 2500);
        $nRS2 = random_int(-2500, 2500);
        $nRS3 = random_int(-2500, 2500);

        $nTm1 = random_int(1, 500);
        $nTm2 = random_int(1, 500);
        $nTm3 = random_int(1, 500);

        $lId1 = random_int(1, 25000);
        $lId2 = random_int(1, 25000);
        $lId3 = random_int(1, 25000);

        $lRS1 = random_int(1, 2500);
        $lRS2 = random_int(1, 2500);
        $lRS3 = random_int(1, 2500);

        $ltsM1 = random_int(1, 400);
        $ltsM2 = random_int(1, 400);
        $ltsM3 = random_int(1, 400);

        $cId1 = random_int(1, 250000);
        $cId2 = random_int(1, 250000);
        $cId3 = random_int(1, 250000);

        $cRS1 = random_int(-2500, 2500);
        $cRS2 = random_int(-2500, 2500);
        $cRS3 = random_int(-2500, 2500);

        $aDurationMs = random_int(500, 2500);

        $bDurationMs = random_int(100, 3500);
        $bPeriodOnMs = random_int(100, 3500);
        $bPeriodOffMs = random_int(100, 3500);

        $cDurationMs = random_int(100, 3500);


        $lineTypes = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        $restartMethods = [
            RestartMethodType::RestartMethodNotSet,
            RestartMethodType::RestartMethodGraceful,
            RestartMethodType::RestartMethodForceful,
        ];

        foreach ($lineTypes as $b => $lt) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "EventRuleUpdate" => new EventRule([
                                "Id" => $Id,
                                "OnEvent" => new EventMessage([
                                    "BleAssetTrack" => new BleAssetTrackEvent([
                                        "New_id"         => [$nId1,  $nId2,  $nId3],
                                        "New_macAddress" => [$m1,    $m2,    $m3],
                                        "New_rawData"    => [$nRD1,  $nRD2,  $nRD3],
                                        "New_rssi"       => [$nRS1,  $nRS2,  $nRS3],
                                        "New_tsMinus"    => [$nTm1,  $nTm2,  $nTm3],
                                        "Lost_id"        => [$lId1,  $lId2,  $lId3],
                                        "Lost_rssi"      => [$lRS1,  $lRS2,  $lRS3],
                                        "Lost_tsMinus"   => [$ltsM1, $ltsM2, $ltsM3],
                                        "Current_id"     => [$cId1,  $cId2,  $cId3],
                                        "Current_rssi"   => [$cRS1,  $cRS2,  $cRS3],
                                    ]),
                                ]),
                                "PerformTheseActions" => [
                                    new ActionMessage([
                                        "WiegandLineActivate" => new WiegandLineActivateAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $aDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "WiegandLineBlink" => new WiegandLineBlinkAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $bDurationMs,
                                            "PeriodOnMs" => $bPeriodOnMs,
                                            "PeriodOffMs" => $bPeriodOffMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "RelayActivate" => new RelayActivateAction([
                                            "DurationMs" => $cDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "SystemRestart" => new SystemRestartAction([
                                            "RestartMethod" => $restartMethods[$b] ?? RestartMethodType::RestartMethodForceful,
                                        ]),
                                    ]),
                                ],
                                "DestinationSerial" => $DestinationSerial,
                                "DestinationAddress" => $DestinationAddress,
                            ]),
                        ]),
                    ]),
                ],
            ]);


            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Config']['EventRuleUpdate'];

            $this->assertEquals($Id, $e['Id']);
            $this->assertEquals($DestinationSerial, $e['DestinationSerial']);
            $this->assertEquals($DestinationAddress, $e['DestinationAddress']);

            $f = $e['OnEvent']['BleAssetTrack'];
            $this->assertEquals($nId1, $f['NewId'][0]);
            $this->assertEquals($nId2, $f['NewId'][1]);
            $this->assertEquals($nId3, $f['NewId'][2]);
            $this->assertEquals($m1, $f['NewMacAddress'][0]);
            $this->assertEquals($m2, $f['NewMacAddress'][1]);
            $this->assertEquals($m3, $f['NewMacAddress'][2]);
            $this->assertEquals($nRD1, $f['NewRawData'][0]);
            $this->assertEquals($nRD2, $f['NewRawData'][1]);
            $this->assertEquals($nRD3, $f['NewRawData'][2]);
            $this->assertEquals($nRS1, $f['NewRssi'][0]);
            $this->assertEquals($nRS2, $f['NewRssi'][1]);
            $this->assertEquals($nRS3, $f['NewRssi'][2]);
            $this->assertEquals($nTm1, $f['NewTsMinus'][0]);
            $this->assertEquals($nTm2, $f['NewTsMinus'][1]);
            $this->assertEquals($nTm3, $f['NewTsMinus'][2]);
            $this->assertEquals($lId1, $f['LostId'][0]);
            $this->assertEquals($lId2, $f['LostId'][1]);
            $this->assertEquals($lId3, $f['LostId'][2]);
            $this->assertEquals($lRS1, $f['LostRssi'][0]);
            $this->assertEquals($lRS2, $f['LostRssi'][1]);
            $this->assertEquals($lRS3, $f['LostRssi'][2]);
            $this->assertEquals($ltsM1, $f['LostTsMinus'][0]);
            $this->assertEquals($ltsM2, $f['LostTsMinus'][1]);
            $this->assertEquals($ltsM3, $f['LostTsMinus'][2]);
            $this->assertEquals($cId1, $f['CurrentId'][0]);
            $this->assertEquals($cId2, $f['CurrentId'][1]);
            $this->assertEquals($cId3, $f['CurrentId'][2]);
            $this->assertEquals($cRS1, $f['CurrentRssi'][0]);
            $this->assertEquals($cRS2, $f['CurrentRssi'][1]);
            $this->assertEquals($cRS3, $f['CurrentRssi'][2]);

            $g = $e['PerformTheseActions'][0]['WiegandLineActivate'];
            if ($lt == WiegandLineTypes::Wiegand_LineNotSet) {
                $this->assertArrayNotHasKey('LineOutput', $g);
            } else {
                $this->assertEquals($lt, $g['LineOutput']);
            }
            $this->assertEquals($aDurationMs, $g['DurationMs']);

            $h = $e['PerformTheseActions'][1]['WiegandLineBlink'];
            if ($lt == WiegandLineTypes::Wiegand_LineNotSet) {
                $this->assertArrayNotHasKey('LineOutput', $h);
            } else {
                $this->assertEquals($lt, $h['LineOutput']);
            }
            $this->assertEquals($bDurationMs, $h['DurationMs']);
            $this->assertEquals($bPeriodOnMs, $h['PeriodOnMs']);
            $this->assertEquals($bPeriodOffMs, $h['PeriodOffMs']);

            $i = $e['PerformTheseActions'][2]['RelayActivate'];
            $this->assertEquals($cDurationMs, $i['DurationMs']);

            $k = $e['PerformTheseActions'][3]['SystemRestart'];
            if (($restartMethods[$b] ?? RestartMethodType::RestartMethodForceful) == RestartMethodType::RestartMethodNotSet) {
                $this->assertArrayNotHasKey('RestartMethod', $k);
            } else {
                $this->assertEquals(
                    ($restartMethods[$b] ?? RestartMethodType::RestartMethodForceful),
                    $k['RestartMethod'],
                );
            }
        }
    }


    public function testPacketConfigMessagEventRuleDelete() {
        $Id = random_int(1, 99999);
        $DestinationSerial = random_int(1, 9999);
        $DestinationAddress = [
            random_int(1, 9999999),
            random_int(1, 9999999),
            random_int(1, 9999999),
        ];

        $nId1 = random_int(1, 999999);
        $nId2 = random_int(1, 999999);
        $nId3 = random_int(1, 999999);

        $m1 = $this->randomMac();
        $m2 = $this->randomMac();
        $m3 = $this->randomMac();

        $nRD1 = 'a' . bin2hex(random_bytes(10));
        $nRD2 = 'b' . bin2hex(random_bytes(10));
        $nRD3 = 'c' . bin2hex(random_bytes(10));

        $nRS1 = random_int(-2500, 2500);
        $nRS2 = random_int(-2500, 2500);
        $nRS3 = random_int(-2500, 2500);

        $nTm1 = random_int(1, 500);
        $nTm2 = random_int(1, 500);
        $nTm3 = random_int(1, 500);

        $lId1 = random_int(1, 25000);
        $lId2 = random_int(1, 25000);
        $lId3 = random_int(1, 25000);

        $lRS1 = random_int(1, 2500);
        $lRS2 = random_int(1, 2500);
        $lRS3 = random_int(1, 2500);

        $ltsM1 = random_int(1, 400);
        $ltsM2 = random_int(1, 400);
        $ltsM3 = random_int(1, 400);

        $cId1 = random_int(1, 250000);
        $cId2 = random_int(1, 250000);
        $cId3 = random_int(1, 250000);

        $cRS1 = random_int(-2500, 2500);
        $cRS2 = random_int(-2500, 2500);
        $cRS3 = random_int(-2500, 2500);

        $aDurationMs = random_int(500, 2500);

        $bDurationMs = random_int(100, 3500);
        $bPeriodOnMs = random_int(100, 3500);
        $bPeriodOffMs = random_int(100, 3500);

        $cDurationMs = random_int(100, 3500);


        $lineTypes = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        $restartMethods = [
            RestartMethodType::RestartMethodNotSet,
            RestartMethodType::RestartMethodGraceful,
            RestartMethodType::RestartMethodForceful,
        ];

        foreach ($lineTypes as $b => $lt) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "EventRuleDelete" => new EventRule([
                                "Id" => $Id,
                                "OnEvent" => new EventMessage([
                                    "BleAssetTrack" => new BleAssetTrackEvent([
                                        "New_id"         => [$nId1,  $nId2,  $nId3],
                                        "New_macAddress" => [$m1,    $m2,    $m3],
                                        "New_rawData"    => [$nRD1,  $nRD2,  $nRD3],
                                        "New_rssi"       => [$nRS1,  $nRS2,  $nRS3],
                                        "New_tsMinus"    => [$nTm1,  $nTm2,  $nTm3],
                                        "Lost_id"        => [$lId1,  $lId2,  $lId3],
                                        "Lost_rssi"      => [$lRS1,  $lRS2,  $lRS3],
                                        "Lost_tsMinus"   => [$ltsM1, $ltsM2, $ltsM3],
                                        "Current_id"     => [$cId1,  $cId2,  $cId3],
                                        "Current_rssi"   => [$cRS1,  $cRS2,  $cRS3],
                                    ]),
                                ]),
                                "PerformTheseActions" => [
                                    new ActionMessage([
                                        "WiegandLineActivate" => new WiegandLineActivateAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $aDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "WiegandLineBlink" => new WiegandLineBlinkAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $bDurationMs,
                                            "PeriodOnMs" => $bPeriodOnMs,
                                            "PeriodOffMs" => $bPeriodOffMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "RelayActivate" => new RelayActivateAction([
                                            "DurationMs" => $cDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "SystemRestart" => new SystemRestartAction([
                                            "RestartMethod" => $restartMethods[$b] ?? RestartMethodType::RestartMethodForceful,
                                            // "RestartMethod" => RestartMethodType::RestartMethodNotSet,
                                            // "RestartMethod" => RestartMethodType::RestartMethodGraceful,
                                            // "RestartMethod" =>    RestartMethodType::RestartMethodForceful,
                                        ]),
                                    ]),
                                ],
                                "DestinationSerial" => $DestinationSerial,
                                "DestinationAddress" => $DestinationAddress,
                            ]),
                        ]),
                    ]),
                ],
            ]);


            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Config']['EventRuleDelete'];

            $this->assertEquals($Id, $e['Id']);
            $this->assertEquals($DestinationSerial, $e['DestinationSerial']);
            $this->assertEquals($DestinationAddress, $e['DestinationAddress']);

            $f = $e['OnEvent']['BleAssetTrack'];
            $this->assertEquals($nId1, $f['NewId'][0]);
            $this->assertEquals($nId2, $f['NewId'][1]);
            $this->assertEquals($nId3, $f['NewId'][2]);
            $this->assertEquals($m1, $f['NewMacAddress'][0]);
            $this->assertEquals($m2, $f['NewMacAddress'][1]);
            $this->assertEquals($m3, $f['NewMacAddress'][2]);
            $this->assertEquals($nRD1, $f['NewRawData'][0]);
            $this->assertEquals($nRD2, $f['NewRawData'][1]);
            $this->assertEquals($nRD3, $f['NewRawData'][2]);
            $this->assertEquals($nRS1, $f['NewRssi'][0]);
            $this->assertEquals($nRS2, $f['NewRssi'][1]);
            $this->assertEquals($nRS3, $f['NewRssi'][2]);
            $this->assertEquals($nTm1, $f['NewTsMinus'][0]);
            $this->assertEquals($nTm2, $f['NewTsMinus'][1]);
            $this->assertEquals($nTm3, $f['NewTsMinus'][2]);
            $this->assertEquals($lId1, $f['LostId'][0]);
            $this->assertEquals($lId2, $f['LostId'][1]);
            $this->assertEquals($lId3, $f['LostId'][2]);
            $this->assertEquals($lRS1, $f['LostRssi'][0]);
            $this->assertEquals($lRS2, $f['LostRssi'][1]);
            $this->assertEquals($lRS3, $f['LostRssi'][2]);
            $this->assertEquals($ltsM1, $f['LostTsMinus'][0]);
            $this->assertEquals($ltsM2, $f['LostTsMinus'][1]);
            $this->assertEquals($ltsM3, $f['LostTsMinus'][2]);
            $this->assertEquals($cId1, $f['CurrentId'][0]);
            $this->assertEquals($cId2, $f['CurrentId'][1]);
            $this->assertEquals($cId3, $f['CurrentId'][2]);
            $this->assertEquals($cRS1, $f['CurrentRssi'][0]);
            $this->assertEquals($cRS2, $f['CurrentRssi'][1]);
            $this->assertEquals($cRS3, $f['CurrentRssi'][2]);

            $g = $e['PerformTheseActions'][0]['WiegandLineActivate'];
            if ($lt == WiegandLineTypes::Wiegand_LineNotSet) {
                $this->assertArrayNotHasKey('LineOutput', $g);
            } else {
                $this->assertEquals($lt, $g['LineOutput']);
            }
            $this->assertEquals($aDurationMs, $g['DurationMs']);

            $h = $e['PerformTheseActions'][1]['WiegandLineBlink'];
            if ($lt == WiegandLineTypes::Wiegand_LineNotSet) {
                $this->assertArrayNotHasKey('LineOutput', $h);
            } else {
                $this->assertEquals($lt, $h['LineOutput']);
            }
            $this->assertEquals($bDurationMs, $h['DurationMs']);
            $this->assertEquals($bPeriodOnMs, $h['PeriodOnMs']);
            $this->assertEquals($bPeriodOffMs, $h['PeriodOffMs']);

            $i = $e['PerformTheseActions'][2]['RelayActivate'];
            $this->assertEquals($cDurationMs, $i['DurationMs']);

            $k = $e['PerformTheseActions'][3]['SystemRestart'];
            if (($restartMethods[$b] ?? RestartMethodType::RestartMethodForceful) == RestartMethodType::RestartMethodNotSet) {
                $this->assertArrayNotHasKey('RestartMethod', $k);
            } else {
                $this->assertEquals(
                    ($restartMethods[$b] ?? RestartMethodType::RestartMethodForceful),
                    $k['RestartMethod'],
                );
            }
        }
    }


    public function testPacketConfigMessageNetworkAddress() {
        $Address = random_int(268435456, 2576980377);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "NetworkAddress" => new NetworkAddressConfig([
                            "Address" => $Address,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['NetworkAddress'];
        $this->assertEquals($Address, $e['Address']);
    }


    public function testPacketConfigMessageNetworkRouteUpdate() {
        $DestinationAddress = random_int(268435456, 2576980377);
        $DestinationPort = random_int(8000, 140000);
        $AddressRoute = [
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "NetworkRouteUpdate" => new NetworkRouteConfig([
                            "DestinationAddress" => $DestinationAddress,
                            "DestinationPort" => $DestinationPort,
                            "AddressRoute" => $AddressRoute,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['NetworkRouteUpdate'];
        $this->assertEquals($DestinationAddress, $e['DestinationAddress']);
        $this->assertEquals($DestinationPort, $e['DestinationPort']);
        $this->assertEquals($AddressRoute[0], $e['AddressRoute'][0]);
        $this->assertEquals($AddressRoute[1], $e['AddressRoute'][1]);
        $this->assertEquals($AddressRoute[2], $e['AddressRoute'][2]);
    }


    public function testPacketConfigMessageNetworkRouteDelete() {
        $DestinationAddress = random_int(268435456, 2576980377);
        $DestinationPort = random_int(8000, 140000);
        $AddressRoute = [
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "NetworkRouteDelete" => new NetworkRouteConfig([
                            "DestinationAddress" => $DestinationAddress,
                            "DestinationPort" => $DestinationPort,
                            "AddressRoute" => $AddressRoute,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['NetworkRouteDelete'];
        $this->assertEquals($DestinationAddress, $e['DestinationAddress']);
        $this->assertEquals($DestinationPort, $e['DestinationPort']);
        $this->assertEquals($AddressRoute[0], $e['AddressRoute'][0]);
        $this->assertEquals($AddressRoute[1], $e['AddressRoute'][1]);
        $this->assertEquals($AddressRoute[2], $e['AddressRoute'][2]);
    }


    public function testPacketConfigMessageBlueToothPort() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "BlueToothPort" => new BlueToothPortConfig([
                            "BeSecureService" => true
                        ]),
                    ]),
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Config']['BlueToothPort'];
        $this->assertEquals(1, $e['BeSecureService']); // 1 == true
    }


    public function testPacketLogMessageDebug() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Debug" => "Debug: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Log'];
        $this->assertEquals($CoreDumpBin, $e['CoreDumpBin']);
        $this->assertEquals($CoreDumpLog, $e['CoreDumpLog']);
        $this->assertEquals("Debug: The message has been sent successfully", $e['Debug']);
    }


    public function testPacketLogMessageInfo() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Info" => "Info: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Log'];
        $this->assertEquals($CoreDumpBin, $e['CoreDumpBin']);
        $this->assertEquals($CoreDumpLog, $e['CoreDumpLog']);
        $this->assertEquals("Info: The message has been sent successfully", $e['Info']);
    }


    public function testPacketLogMessageWarning() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Warning" => "Warning: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Log'];
        $this->assertEquals($CoreDumpBin, $e['CoreDumpBin']);
        $this->assertEquals($CoreDumpLog, $e['CoreDumpLog']);
        $this->assertEquals("Warning: The message has been sent successfully", $e['Warning']);
    }


    public function testPacketLogMessageError() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Error" => "Error: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Log'];
        $this->assertEquals($CoreDumpBin, $e['CoreDumpBin']);
        $this->assertEquals($CoreDumpLog, $e['CoreDumpLog']);
        $this->assertEquals("Error: The message has been sent successfully", $e['Error']);
    }


    public function testPacketLogMessageFatal() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Fatal" => "Fatal: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Log'];
        $this->assertEquals($CoreDumpBin, $e['CoreDumpBin']);
        $this->assertEquals($CoreDumpLog, $e['CoreDumpLog']);
        $this->assertEquals("Fatal: The message has been sent successfully", $e['Fatal']);
    }


    public function testPacketMetricMessage() {
        $Interval = random_int(500, 2500);

        $InfoLogCount = random_int(1, 4000);
        $WarningLogCount = random_int(1, 4000);
        $ErrorLogCount = random_int(1, 4000);

        $BytesTransmitted = random_int(1, 5000);
        $BytesReceived = random_int(1, 5000);
        $BytesReceivedExclusive = random_int(1, 5000);
        $ReceiveOverrunErrors = random_int(1, 5000);
        $ReceiveFramingErrors = random_int(1, 5000);
        $ReceiveBreakErrors = random_int(1, 5000);
        $PacketsTransmitted = random_int(1, 5000);
        $PacketsReceived = random_int(1, 5000);
        $PacketsReceivedExclusive = random_int(1, 5000);
        $PacketsACKed = random_int(1, 5000);
        $PacketsNACKedCorrupted = random_int(1, 5000);
        $PacketsCorruptedDiscarded = random_int(1, 5000);
        $PacketsTimedOut = random_int(1, 5000);
        $PacketsDecodingFailed = random_int(1, 5000);

        $FailedParityChecks = random_int(1, 5000);

        $AccessDeviceLookupTimeMin = random_int(1, 5000);
        $AccessDeviceLookupTimeMax = random_int(5000, 10000);
        $AccessUserLookupTimeMin = random_int(1, 5000);
        $AccessUserLookupTimeMax = random_int(5000, 10000);
        $AcesssGroupLookupTimeMin = random_int(1, 5000);
        $AccessGroupLookupTimeMax = random_int(5000, 10000);

        $LargestQueueMessageCount = random_int(1, 5000);
        $Address = hexdec(bin2hex(random_bytes(8)));
        $MessageCount = random_int(1, 500);
        $QueueSize = random_int(1, 500);
        $ExpiredMessages = random_int(1, 5000);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Metric" => new MetricMessage([
                        "Interval" => $Interval,
                        "Log" => new LogMetric([
                            "InfoLogCount" => $InfoLogCount,
                            "WarningLogCount" => $WarningLogCount,
                            "ErrorLogCount" => $ErrorLogCount,
                        ]),
                        "RS485MainPort" => new RS485PortMetric([
                            "BytesTransmitted"          => $BytesTransmitted,
                            "BytesReceived"             => $BytesReceived,
                            "BytesReceivedExclusive"    => $BytesReceivedExclusive,
                            "ReceiveOverrunErrors"      => $ReceiveOverrunErrors,
                            "ReceiveFramingErrors"      => $ReceiveFramingErrors,
                            "ReceiveBreakErrors"        => $ReceiveBreakErrors,
                            "PacketsTransmitted"        => $PacketsTransmitted,
                            "PacketsReceived"           => $PacketsReceived,
                            "PacketsReceivedExclusive"  => $PacketsReceivedExclusive,
                            "PacketsACKed"              => $PacketsACKed,
                            "PacketsNACKedCorrupted"    => $PacketsNACKedCorrupted,
                            "PacketsCorruptedDiscarded" => $PacketsCorruptedDiscarded,
                            "PacketsTimedOut"           => $PacketsTimedOut,
                            "PacketsDecodingFailed"     => $PacketsDecodingFailed,
                        ]),
                        "WiegandPort1" => new WiegandPortMetric([
                            "FailedParityChecks" => $FailedParityChecks,
                        ]),
                        "Access" => new AccessMetric([
                            "AccessDeviceLookupTimeMin" => $AccessDeviceLookupTimeMin,
                            "AccessDeviceLookupTimeMax" => $AccessDeviceLookupTimeMax,
                            "AccessUserLookupTimeMin"   => $AccessUserLookupTimeMin,
                            "AccessUserLookupTimeMax"   => $AccessUserLookupTimeMax,
                            "AcesssGroupLookupTimeMin"  => $AcesssGroupLookupTimeMin,
                            "AccessGroupLookupTimeMax"  => $AccessGroupLookupTimeMax,
                        ]),
                        "Network" => new NetworkMetric([
                            "LargestQueueMessageCount" => $LargestQueueMessageCount,
                            "NetworkQueues" => [
                                new NetworkQueueMetric([
                                    "Address" => $Address,
                                    "MessageCount" => $MessageCount,
                                    "QueueSize" => $QueueSize,
                                    "ExpiredMessages" => $ExpiredMessages,
                                ]),
                            ],
                        ]),
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);

        $e = $m['Metric'];
        $this->assertEquals($Interval, $e['Interval']);

        $l = $e['Log'];
        $this->assertEquals($InfoLogCount, $l['InfoLogCount']);
        $this->assertEquals($WarningLogCount, $l['WarningLogCount']);
        $this->assertEquals($ErrorLogCount, $l['ErrorLogCount']);

        $m = $e['RS485MainPort'];
        $this->assertEquals($BytesTransmitted, $m['BytesTransmitted']);
        $this->assertEquals($BytesReceived, $m['BytesReceived']);
        $this->assertEquals($BytesReceivedExclusive, $m['BytesReceivedExclusive']);
        $this->assertEquals($ReceiveOverrunErrors, $m['ReceiveOverrunErrors']);
        $this->assertEquals($ReceiveFramingErrors, $m['ReceiveFramingErrors']);
        $this->assertEquals($ReceiveBreakErrors, $m['ReceiveBreakErrors']);
        $this->assertEquals($PacketsTransmitted, $m['PacketsTransmitted']);
        $this->assertEquals($PacketsReceived, $m['PacketsReceived']);
        $this->assertEquals($PacketsReceivedExclusive, $m['PacketsReceivedExclusive']);
        $this->assertEquals($PacketsACKed, $m['PacketsACKed']);
        $this->assertEquals($PacketsNACKedCorrupted, $m['PacketsNACKedCorrupted']);
        $this->assertEquals($PacketsCorruptedDiscarded, $m['PacketsCorruptedDiscarded']);
        $this->assertEquals($PacketsTimedOut, $m['PacketsTimedOut']);
        $this->assertEquals($PacketsDecodingFailed, $m['PacketsDecodingFailed']);

        $n = $e['WiegandPort1'];
        $this->assertEquals($FailedParityChecks, $n['FailedParityChecks']);

        $o = $e['Access'];
        $this->assertEquals($AccessDeviceLookupTimeMin, $o['AccessDeviceLookupTimeMin']);
        $this->assertEquals($AccessDeviceLookupTimeMax, $o['AccessDeviceLookupTimeMax']);
        $this->assertEquals($AccessUserLookupTimeMin, $o['AccessUserLookupTimeMin']);
        $this->assertEquals($AccessUserLookupTimeMax, $o['AccessUserLookupTimeMax']);
        $this->assertEquals($AcesssGroupLookupTimeMin, $o['AcesssGroupLookupTimeMin']);
        $this->assertEquals($AccessGroupLookupTimeMax, $o['AccessGroupLookupTimeMax']);

        $p = $e['Network'];
        $this->assertEquals($LargestQueueMessageCount, $p['LargestQueueMessageCount']);
        $this->assertEquals($Address, $p['NetworkQueues'][0]['Address']);
        $this->assertEquals($MessageCount, $p['NetworkQueues'][0]['MessageCount']);
        $this->assertEquals($QueueSize, $p['NetworkQueues'][0]['QueueSize']);
        $this->assertEquals($ExpiredMessages, $p['NetworkQueues'][0]['ExpiredMessages']);
    }


    public function testPacketRequestMessage() {
        $methods = [
            RequestMethodType::NotSet,
            RequestMethodType::Get,
            RequestMethodType::Post,
            RequestMethodType::Put,
            RequestMethodType::Patch,
            RequestMethodType::Delete,
            RequestMethodType::Head,
            RequestMethodType::Options,
        ];

        $Raw = bin2hex(random_bytes(10));
        $Binary = bin2hex(random_bytes(10));
        $Json = json_encode([
            'cheese' => 'cake',
            'abc' => true,
            'def' => random_int(1, 25)
        ]);

        $oneOf = [
            ['Raw', $Raw],
            ['Binary', $Binary],
            ['Json', $Json],
            ['RequestId', RequestIdTypes::Request_SystemFeatures],
        ];

        foreach ($methods as $i => $method) {
            $pos = $i % 4;
            list($key, $value) = $oneOf[$pos];

            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Request" => new RequestMessage([
                            "Method" => $method,
                            $key => $value,
                        ])
                    ]),
                ],
            ]);
            $m = $this->basicPacketPropertiesTest($p);
            $e = $m['Request'];

            if ($method == RequestMethodType::NotSet) {
                $this->assertArrayNotHasKey('Method', $e);
            } else {
                $this->assertEquals($method, $e['Method']);
            }
            $this->assertEquals($value, $e[$key]);
        }
    }


    public function testPacketResponseMessageRaw() {
        $Raw = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "Raw" => $Raw,
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Response'];

        $this->assertEquals(ResponseCode::Payload_Too_large, $e['Code']);
        $this->assertEquals($Raw, $e['Raw']);
    }


    public function testPacketResponseMessageBinary() {
        $Binary = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "Binary" => $Binary,
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Response'];

        $this->assertEquals(ResponseCode::Payload_Too_large, $e['Code']);
        $this->assertEquals($Binary, $e['Binary']);
    }


    public function testPacketResponseMessageJson() {
        $Json = json_encode([
            'cheese' => 'cake',
            'abc' => true,
            'def' => random_int(1, 25)
        ]);
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "Json" => $Json,
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Response'];

        $this->assertEquals(ResponseCode::Payload_Too_large, $e['Code']);
        $this->assertEquals($Json, $e['Json']);
    }


    public function testPacketResponseMessageSystemPing() {
        $ReceivedTimeStampMs = random_int(500, 2500);
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "SystemPing" => new SystemPingResponse([
                            "ReceivedTimeStampMs" => $ReceivedTimeStampMs,
                        ]),
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Response'];

        $this->assertEquals(ResponseCode::Payload_Too_large, $e['Code']);
        $this->assertEquals($ReceivedTimeStampMs, $e['SystemPing']['ReceivedTimeStampMs']);
    }


    public function testPacketResponseMessageSystemHardwareInfo() {
        $SerialNumber = random_int(1, 3000);
        $BootFirmwareVersion = implode('.', [
            random_int(1, 3),
            random_int(1, 18),
            random_int(1, 25),
        ]);
        $AppFirmwareVersion = implode('.', [
            random_int(1, 3),
            random_int(1, 18),
            random_int(1, 25),
        ]);
        $HardwarePlatform = 'AOH1000-007';
        $HardwareVersion = random_int(1, 5);
        $ManufacturedDate = time();
        $InternalFileSystemSize = random_int(50, 6000);
        $ExternalFileSystemSize = random_int(300, 7000);
        $MCUUID = random_int(1111111111, 9999999999);
        $MCUSocRevision = random_int(1, 5000);
        $MCUPartId = random_int(1, 5000);
        $BLEMAC = $this->randomMac();
        $BLECodedPhyPresent = true;
        $EthernetMAC = $this->randomMac();
        $GSM_Modem_Manufacturer = "Cheese Inc";
        $GSM_Modem_Model = 'EC200U';
        $GSM_Modem_Revision = 'EC200UCNAAR01A04M08';
        $GSM_Modem_IMEI = bin2hex(random_bytes(4));
        $GSM_Modem_SerialNumber = bin2hex(random_bytes(6));
        $GSM_SIM1_IMSI = bin2hex(random_bytes(16));
        $GSM_SIM1_ICCID = bin2hex(random_bytes(20));
        $GSM_SIM1_SubscriberNumber = '+27761234567';
        $GSM_SIM2_IMSI = bin2hex(random_bytes(16));
        $GSM_SIM2_ICCID = bin2hex(random_bytes(20));
        $GSM_SIM2_SubscriberNumber = '+27767654321';
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "SystemHardwareInfo" => new SystemHardwareInfoResponse([
                            "SerialNumber"              => $SerialNumber,
                            "BootFirmwareVersion"       => $BootFirmwareVersion,
                            "AppFirmwareVersion"        => $AppFirmwareVersion,
                            "HardwarePlatform"          => $HardwarePlatform,
                            "HardwareVersion"           => $HardwareVersion,
                            "ManufacturedDate"          => $ManufacturedDate,
                            "InternalFileSystemSize"    => $InternalFileSystemSize,
                            "ExternalFileSystemSize"    => $ExternalFileSystemSize,
                            "MCUUID"                    => $MCUUID,
                            "MCUSocRevision"            => $MCUSocRevision,
                            "MCUPartId"                 => $MCUPartId,
                            "BLEMAC"                    => $BLEMAC,
                            "BLECodedPhyPresent"        => $BLECodedPhyPresent,
                            "EthernetMAC"               => $EthernetMAC,
                            "GSM_Modem_Manufacturer"    => $GSM_Modem_Manufacturer,
                            "GSM_Modem_Model"           => $GSM_Modem_Model,
                            "GSM_Modem_Revision"        => $GSM_Modem_Revision,
                            "GSM_Modem_IMEI"            => $GSM_Modem_IMEI,
                            "GSM_Modem_SerialNumber"    => $GSM_Modem_SerialNumber,
                            "GSM_SIM1_IMSI"             => $GSM_SIM1_IMSI,
                            "GSM_SIM1_ICCID"            => $GSM_SIM1_ICCID,
                            "GSM_SIM1_SubscriberNumber" => $GSM_SIM1_SubscriberNumber,
                            "GSM_SIM2_IMSI"             => $GSM_SIM2_IMSI,
                            "GSM_SIM2_ICCID"            => $GSM_SIM2_ICCID,
                            "GSM_SIM2_SubscriberNumber" => $GSM_SIM2_SubscriberNumber,
                        ]),
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Response'];

        $this->assertEquals(ResponseCode::Payload_Too_large, $e['Code']);
        $this->assertEquals($SerialNumber, $e['SystemHardwareInfo']['SerialNumber']);
        $this->assertEquals($BootFirmwareVersion, $e['SystemHardwareInfo']['BootFirmwareVersion']);
        $this->assertEquals($AppFirmwareVersion, $e['SystemHardwareInfo']['AppFirmwareVersion']);
        $this->assertEquals($HardwarePlatform, $e['SystemHardwareInfo']['HardwarePlatform']);
        $this->assertEquals($HardwareVersion, $e['SystemHardwareInfo']['HardwareVersion']);
        $this->assertEquals($ManufacturedDate, $e['SystemHardwareInfo']['ManufacturedDate']);
        $this->assertEquals($InternalFileSystemSize, $e['SystemHardwareInfo']['InternalFileSystemSize']);
        $this->assertEquals($ExternalFileSystemSize, $e['SystemHardwareInfo']['ExternalFileSystemSize']);
        $this->assertEquals($MCUUID, $e['SystemHardwareInfo']['MCUUID']);
        $this->assertEquals($MCUSocRevision, $e['SystemHardwareInfo']['MCUSocRevision']);
        $this->assertEquals($MCUPartId, $e['SystemHardwareInfo']['MCUPartId']);
        $this->assertEquals($BLEMAC, $e['SystemHardwareInfo']['BLEMAC']);
        $this->assertEquals($BLECodedPhyPresent, $e['SystemHardwareInfo']['BLECodedPhyPresent']);
        $this->assertEquals($EthernetMAC, $e['SystemHardwareInfo']['EthernetMAC']);
        $this->assertEquals($GSM_Modem_Manufacturer, $e['SystemHardwareInfo']['GSMModemManufacturer']);
        $this->assertEquals($GSM_Modem_Model, $e['SystemHardwareInfo']['GSMModemModel']);
        $this->assertEquals($GSM_Modem_Revision, $e['SystemHardwareInfo']['GSMModemRevision']);
        $this->assertEquals($GSM_Modem_IMEI, $e['SystemHardwareInfo']['GSMModemIMEI']);
        $this->assertEquals($GSM_Modem_SerialNumber, $e['SystemHardwareInfo']['GSMModemSerialNumber']);
        $this->assertEquals($GSM_SIM1_IMSI, $e['SystemHardwareInfo']['GSMSIM1IMSI']);
        $this->assertEquals($GSM_SIM1_ICCID, $e['SystemHardwareInfo']['GSMSIM1ICCID']);
        $this->assertEquals($GSM_SIM1_SubscriberNumber, $e['SystemHardwareInfo']['GSMSIM1SubscriberNumber']);
        $this->assertEquals($GSM_SIM2_IMSI, $e['SystemHardwareInfo']['GSMSIM2IMSI']);
        $this->assertEquals($GSM_SIM2_ICCID, $e['SystemHardwareInfo']['GSMSIM2ICCID']);
        $this->assertEquals($GSM_SIM2_SubscriberNumber, $e['SystemHardwareInfo']['GSMSIM2SubscriberNumber']);
    }


    public function testPacketResponseMessageSystemFeatures() {
        $BLEPorts = random_int(1, 3);
        $RS485Ports = random_int(1, 3);
        $ModemPorts = random_int(1, 3);
        $EthernetPorts = random_int(1, 3);
        $WiegandPorts = random_int(1, 3);
        $RelayPorts = random_int(1, 3);
        $InputPorts = random_int(1, 3);
        $ExpansionPorts = random_int(1, 3);
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "SystemFeatures" => new SystemFeaturesResponse([
                            "BLEPorts"       => $BLEPorts,
                            "RS485Ports"     => $RS485Ports,
                            "ModemPorts"     => $ModemPorts,
                            "EthernetPorts"  => $EthernetPorts,
                            "WiegandPorts"   => $WiegandPorts,
                            "RelayPorts"     => $RelayPorts,
                            "InputPorts"     => $InputPorts,
                            "ExpansionPorts" => $ExpansionPorts,
                        ]),
                    ]),
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);
        $e = $m['Response'];

        $this->assertEquals(ResponseCode::Payload_Too_large, $e['Code']);
        $this->assertEquals($BLEPorts, $e['SystemFeatures']['BLEPorts']);
        $this->assertEquals($RS485Ports, $e['SystemFeatures']['RS485Ports']);
        $this->assertEquals($ModemPorts, $e['SystemFeatures']['ModemPorts']);
        $this->assertEquals($EthernetPorts, $e['SystemFeatures']['EthernetPorts']);
        $this->assertEquals($WiegandPorts, $e['SystemFeatures']['WiegandPorts']);
        $this->assertEquals($RelayPorts, $e['SystemFeatures']['RelayPorts']);
        $this->assertEquals($InputPorts, $e['SystemFeatures']['InputPorts']);
        $this->assertEquals($ExpansionPorts, $e['SystemFeatures']['ExpansionPorts']);
    }


    public function testPacketDeadLetterMessage() {
        $DeadLetterMessage = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "DeadLetterMessage" => $DeadLetterMessage,
                ]),
            ],
        ]);
        $m = $this->basicPacketPropertiesTest($p);

        $this->assertEquals($m['DeadLetterMessage'], $DeadLetterMessage);
    }


    public function testPacketRoutedPacketMessage() {
        $RoutedPacket = bin2hex(
            (new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => random_int(1, 10),
                'RedeliveryCounter' => random_int(1, 5),
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => random_int(1, 200),
                        "Config" => new ConfigMessage([
                            "AccessGroupDelete" => new AccessGroup([
                                "Id" => random_int(1, 99999),
                                "DaysOfWeekBits" => random_int(0, 7),
                                "TimesOfDayBits" => random_int(0, 47),
                                "AccessPeripheralIds" => [
                                    random_int(1, 9999),
                                    random_int(1, 9999),
                                    random_int(1, 9999),
                                ],
                            ]),
                        ]),
                    ]),
                ],
            ]))->serializeToString()
        );
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "RoutedPacket" => $RoutedPacket,
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $this->assertEquals($m['RoutedPacket'], $RoutedPacket);
    }


    public function testPacketBesecurePacketMessage() {
        $BeSecurePacket = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "BeSecurePacket" => $BeSecurePacket,
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $this->assertEquals($m['BeSecurePacket'], $BeSecurePacket);
    }


    public function testPacketSystemAuditPacketMessage() {
        $SystemAuditPacket = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "SystemAuditPacket" => $SystemAuditPacket,
                ]),
            ],
        ]);

        $m = $this->basicPacketPropertiesTest($p);

        $this->assertEquals($m['SystemAuditPacket'], $SystemAuditPacket);
    }


    /***************************************************************************/
    /********************************RESERIALIZE********************************/
    /***************************************************************************/


    private function performDeserializeReserializeTest(Packet $p): void {
        $testProto = $p->serializeToString();
        $proto = new Proto;
        $s = $proto->protoPacketToUnserializedArray($p);
        $recoded = $proto->jsonToSerializedArray(json_encode($s));

        $newPacket = new Packet();
        // try {
        $newPacket->mergeFromJsonString(json_encode($recoded));
        $this->assertEquals($testProto, $newPacket->serializeToString());
        // } catch (\Throwable $th) {
        //     echo "\n\n";

        //     print_r($p->serializeToJsonString());
        //     echo "\n\n";
        //     print_r($newPacket->serializeToJsonString());

        //     //     print_r($th->getMessage());
        //     //     echo "\n";
        //     //     // print_r($th->getTrace());
        //     //     // echo "\n";
        //     //     print_r($recoded);
        //     echo "\n";
        //     die;
        // }
    }


    public function testReserializePacketEventMessageWiegandCode() {
        $Code = strval(bin2hex(random_bytes(5)));
        $BitCount = random_int(1, 3);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "WiegandCode" => new WiegandCodeEvent([
                            "Code" => $Code,
                            "BitCount" => $BitCount,
                            "ParityFailed" => true,
                            "SingleKeyBitBurstMode" => false,
                        ])
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketEventMessageRelayState() {
        $DurationMs = random_int(500, 2500);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "RelayState" => new RelayStateEvent([
                            'State' => RelayState::RelayActivated,
                            'DurationMs' => $DurationMs,
                        ])
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketEventMessageBleAssetTrack() {
        $nId1 = random_int(1, 999999);
        $nId2 = random_int(1, 999999);
        $nId3 = random_int(1, 999999);

        $m1 = $this->randomMac();
        $m2 = $this->randomMac();
        $m3 = $this->randomMac();

        $nRD1 = 'a' . bin2hex(random_bytes(10));
        $nRD2 = 'b' . bin2hex(random_bytes(10));
        $nRD3 = 'c' . bin2hex(random_bytes(10));

        $nRS1 = random_int(-2500, 2500);
        $nRS2 = random_int(-2500, 2500);
        $nRS3 = random_int(-2500, 2500);

        $nTm1 = random_int(1, 500);
        $nTm2 = random_int(1, 500);
        $nTm3 = random_int(1, 500);

        $lId1 = random_int(1, 25000);
        $lId2 = random_int(1, 25000);
        $lId3 = random_int(1, 25000);

        $lRS1 = random_int(1, 2500);
        $lRS2 = random_int(1, 2500);
        $lRS3 = random_int(1, 2500);

        $ltsM1 = random_int(1, 400);
        $ltsM2 = random_int(1, 400);
        $ltsM3 = random_int(1, 400);

        $cId1 = random_int(1, 250000);
        $cId2 = random_int(1, 250000);
        $cId3 = random_int(1, 250000);

        $cRS1 = random_int(-2500, 2500);
        $cRS2 = random_int(-2500, 2500);
        $cRS3 = random_int(-2500, 2500);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "BleAssetTrack" => new BleAssetTrackEvent([
                            "New_id"         => [$nId1,  $nId2,  $nId3],
                            "New_macAddress" => [$m1,    $m2,    $m3],
                            "New_rawData"    => [$nRD1,  $nRD2,  $nRD3],
                            "New_rssi"       => [$nRS1,  $nRS2,  $nRS3],
                            "New_tsMinus"    => [$nTm1,  $nTm2,  $nTm3],
                            "Lost_id"        => [$lId1,  $lId2,  $lId3],
                            "Lost_rssi"      => [$lRS1,  $lRS2,  $lRS3],
                            "Lost_tsMinus"   => [$ltsM1, $ltsM2, $ltsM3],
                            "Current_id"     => [$cId1,  $cId2,  $cId3],
                            "Current_rssi"   => [$cRS1,  $cRS2,  $cRS3]
                        ])
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketEventMessageConfigUpdateSuccess() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "ConfigUpdateSuccess" => new ConfigUpdateSuccessEvent([])
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketEventMessageConfigUpdateFailed() {
        $config = [
            "Config Random A",
            "The Rain in Spain"
        ];
        $eailed = [
            "The Rain in Spain",
            "No internet"
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "ConfigUpdateFailed" => new ConfigUpdateFailedEvent([
                            "ConfigName" => $config,
                            "FailedReasons" => $eailed,
                        ])
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketEventMessageAccessAllowed() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessAllowed" => new AccessAllowedEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "AllowedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value
                                ]),
                                "UserEntriesUsed" => 3,
                                "UserExitsUsed" => 3,
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedDeviceUnknown() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedDeviceUnknown" => new AccessDeniedDeviceUnknownEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedTypeMissmatch() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => [bin2hex(random_bytes(8)), bin2hex(random_bytes(8))],
            "RFIDTagUID"   => [random_int(1, 50), random_int(1, 50)],
            "BTPhoneUID"   => [random_int(1, 50), random_int(1, 50)],
            "RemoteUID"    => [random_int(1, 50), random_int(1, 50)],
            "BiometricUID" => [random_int(1, 50), random_int(1, 50)],
            "NumberPlate"  => ['NU 254234', 'NU 254235'],
            "PinCode"      => [random_int(1000, 9999), random_int(1000, 9999)],
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedDeviceTypeMismatch" => new AccessDeniedDeviceTypeMismatchEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value[0],
                                ]),
                                "MatchingAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value[1],
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedDeviceSpecialDaysOfWeek() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedDeviceSpecialDaysOfWeek" => new AccessDeniedDeviceSpecialDaysOfWeekEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedGroupDaysOfWeek() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedGroupGateway" => new AccessDeniedGroupGatewayEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedGroupGateway() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedGroupDaysOfWeek" => new AccessDeniedGroupDaysOfWeekEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedGroupTimeOfDay() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedGroupTimeOfDay" => new AccessDeniedGroupTimeOfDayEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                            ])
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedNoEntriesLeft() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);
        $Id =  random_int(1, 99999);
        $UAccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedNoEntriesLeft" => new AccessDeniedNoEntriesLeftEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "UserEntriesUsed" => 3,
                                "UserExitsUsed" => 3,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                                "DeniedUser" => new AccessUser([
                                    "Id" => $Id,
                                    "FirstExitsThenEnters" => true,
                                    "RollOverExitToNextDay" => true,
                                    "EntriesMax" => 5,
                                    "ExitsMax" => 5,
                                    "StartDateTime" => timeMs(),
                                    "EndDateTime" => timeMs(),
                                    "AccessGroupIds" => $UAccessGroupIds,
                                ]),
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageAccessDeniedNoExitsLeft() {
        $AccessGatewayId =  random_int(1, 250);
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);
        $Id =  random_int(1, 99999);
        $UAccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Event" => new EventMessage([
                            "AccessDeniedNoExitsLeft" => new AccessDeniedNoExitsLeftEvent([
                                "AccessGatewayId" => $AccessGatewayId,
                                "UserEntriesUsed" => 3,
                                "UserExitsUsed" => 3,
                                "DeniedAccessDevice" => new AccessDevice([
                                    "AccessUserId" => $AccessUserId,
                                    "AccessGroupIds" => $AccessGroupIds,
                                    "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                    $key => $value,
                                ]),
                                "DeniedUser" => new AccessUser([
                                    "Id" => $Id,
                                    "FirstExitsThenEnters" => true,
                                    "RollOverExitToNextDay" => true,
                                    "EntriesMax" => 5,
                                    "ExitsMax" => 5,
                                    "StartDateTime" => timeMs(),
                                    "EndDateTime" => timeMs(),
                                    "AccessGroupIds" => $UAccessGroupIds,
                                ]),
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketEventMessageSystemRestart() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Event" => new EventMessage([
                        "SystemRestart" => new SystemRestartEvent([
                            // "RestartReason" => RestartReasonType::RestartReasonNotSet,
                            // "RestartReason" => RestartReasonType::RestartReasonAction,
                            // "RestartReason" => RestartReasonType::RestartDFURollback,
                            // "RestartReason" => RestartReasonType::RestartReasonEthernetDriverUnresponsive,
                            "RestartReason" => RestartReasonType::RestartReasonModemDriverUnresponsive,
                            "ActionReceived" => new SystemRestartAction([
                                // "RestartMethod" => RestartMethodType::RestartMethodNotSet,
                                // "RestartMethod" => RestartMethodType::RestartMethodGraceful,
                                "RestartMethod" => RestartMethodType::RestartMethodForceful,
                            ])
                        ])
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketActionsMessageWiegandLineActivate() {
        $DurationMs = random_int(500, 2500);

        $types = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        foreach ($types as $type) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Action" => new ActionMessage([
                            "WiegandLineActivate" => new WiegandLineActivateAction([
                                "LineOutput" => $type,
                                "DurationMs" => $DurationMs,
                            ]),
                        ])
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketActionsMessageWiegandLineBlink() {
        $DurationMs = random_int(500, 2500);
        $PeriodOnMs = random_int(100, 3500);
        $PeriodOffMs = random_int(100, 3500);

        $types = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        foreach ($types as $type) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Action" => new ActionMessage([
                            "WiegandLineBlink" => new WiegandLineBlinkAction([
                                "LineOutput" => $type,
                                "DurationMs" => $DurationMs,
                                "PeriodOnMs" => $PeriodOnMs,
                                "PeriodOffMs" => $PeriodOffMs,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketActionsMessageRelayActivate() {
        $DurationMs = random_int(500, 2500);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Action" => new ActionMessage([
                        "RelayActivate" => new RelayActivateAction([
                            "DurationMs" => $DurationMs,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketActionsMessageSystemRestart() {
        $types = [
            RestartMethodType::RestartMethodNotSet,
            RestartMethodType::RestartMethodGraceful,
            RestartMethodType::RestartMethodForceful,
        ];

        foreach ($types as $type) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Action" => new ActionMessage([
                            "SystemRestart" => new SystemRestartAction([
                                "RestartMethod" => $type,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketConfigMessageAccessDeviceUpdate() {
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "AccessDeviceUpdate" => new AccessDevice([
                                "AccessUserId" => $AccessUserId,
                                "AccessGroupIds" => $AccessGroupIds,
                                "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                $key => $value,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketConfigMessageAccessDeviceDelete() {
        $AccessUserId = random_int(1, 250000);
        $AccessGroupIds = [
            random_int(1, 750000),
            random_int(1, 750000),
            random_int(1, 750000),
        ];
        $SpecialDaysOfWeekBits = random_int(1, 250000);

        $accessDevices = [
            "WiegandCode"  => bin2hex(random_bytes(8)),
            "RFIDTagUID"   => random_int(1, 50),
            "BTPhoneUID"   => random_int(1, 50),
            "RemoteUID"    => random_int(1, 50),
            "BiometricUID" => random_int(1, 50),
            "NumberPlate"  => 'NU 254234',
            "PinCode"      => random_int(1000, 9999),
        ];

        foreach ($accessDevices as $key => $value) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "AccessDeviceDelete" => new AccessDevice([
                                "AccessUserId" => $AccessUserId,
                                "AccessGroupIds" => $AccessGroupIds,
                                "SpecialDaysOfWeekBits" => $SpecialDaysOfWeekBits,
                                $key => $value,
                            ])
                        ])
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketConfigMessageAccessUserUpdate() {
        $Id = random_int(1, 99999);
        $AccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessUserUpdate" => new AccessUser([
                            "Id" => $Id,
                            "FirstExitsThenEnters" => true,
                            "RollOverExitToNextDay" => true,
                            "EntriesMax" => 5,
                            "ExitsMax" => 5,
                            "StartDateTime" => timeMs(),
                            "EndDateTime" => timeMs(),
                            "AccessGroupIds" => $AccessGroupIds,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageAccessUserDelete() {
        $Id = random_int(1, 99999);
        $AccessGroupIds = [
            random_int(1, 50000),
            random_int(1, 50000),
            random_int(1, 50000),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessUserDelete" => new AccessUser([
                            "Id" => $Id,
                            "FirstExitsThenEnters" => true,
                            "RollOverExitToNextDay" => true,
                            "EntriesMax" => 5,
                            "ExitsMax" => 5,
                            "StartDateTime" => timeMs(),
                            "EndDateTime" => timeMs(),
                            "AccessGroupIds" => $AccessGroupIds,
                        ])
                    ])
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageAccessGroupUpdate() {
        $Id = random_int(1, 99999);
        $AccessPeripheralIds = [
            random_int(1, 9999),
            random_int(1, 9999),
            random_int(1, 9999),
        ];

        $DaysOfWeekBits = random_int(0, 7);
        $TimesOfDayBits = random_int(0, 47);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessGroupUpdate" => new AccessGroup([
                            "Id" => $Id,
                            "DaysOfWeekBits" => $DaysOfWeekBits,
                            "TimesOfDayBits" => $TimesOfDayBits,
                            "AccessPeripheralIds" => $AccessPeripheralIds,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageAccessGroupDelete() {
        $Id = random_int(1, 99999);
        $AccessPeripheralIds = [
            random_int(1, 9999),
            random_int(1, 9999),
            random_int(1, 9999),
        ];

        $DaysOfWeekBits = random_int(0, 7);
        $TimesOfDayBits = random_int(0, 47);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessGroupDelete" => new AccessGroup([
                            "Id" => $Id,
                            "DaysOfWeekBits" => $DaysOfWeekBits,
                            "TimesOfDayBits" => $TimesOfDayBits,
                            "AccessPeripheralIds" => $AccessPeripheralIds,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageAccessGateway() {
        $AccessDevicesHashModulo = random_int(1, 64);
        $AccessGroupsHashModulo = random_int(1, 10);
        $AccessUsersHashModulo = random_int(1, 10);
        $AccessGatewayId = random_int(1, 19999);

        $directions = [
            AccessDirectionTypes::AccessDirection_NotSet,
            AccessDirectionTypes::AccessDirection_Both,
            AccessDirectionTypes::AccessDirection_Entry,
            AccessDirectionTypes::AccessDirection_Exit,
        ];

        foreach ($directions as $direction) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "AccessGateway" => new AccessGatewayConfig([
                                "AccessDevicesHashModulo" => $AccessDevicesHashModulo,
                                "AccessGroupsHashModulo" => $AccessGroupsHashModulo,
                                "AccessUsersHashModulo" => $AccessUsersHashModulo,
                                "AccessGatewayId" => $AccessGatewayId,
                                "AccessGatewayDirection" => $direction,
                                "AccessEnabled" => true,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketConfigMessageAccessDevicesErase() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessDevicesErase" => new AccessDevicesEraseConfig([
                            "AreYouSure" => true,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageAccessUsersErase() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessUsersErase" => new AccessUsersEraseConfig([
                            "AreYouSure" => true,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageAccessGroupsErase() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "AccessGroupsErase" => new AccessGroupsEraseConfig([
                            "AreYouSure" => true,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessagEventRuleUpdate() {
        $Id = random_int(1, 99999);
        $DestinationSerial = random_int(1, 9999);
        $DestinationAddress = [
            random_int(1, 9999999),
            random_int(1, 9999999),
            random_int(1, 9999999),
        ];

        $nId1 = random_int(1, 999999);
        $nId2 = random_int(1, 999999);
        $nId3 = random_int(1, 999999);

        $m1 = $this->randomMac();
        $m2 = $this->randomMac();
        $m3 = $this->randomMac();

        $nRD1 = 'a' . bin2hex(random_bytes(10));
        $nRD2 = 'b' . bin2hex(random_bytes(10));
        $nRD3 = 'c' . bin2hex(random_bytes(10));

        $nRS1 = random_int(-2500, 2500);
        $nRS2 = random_int(-2500, 2500);
        $nRS3 = random_int(-2500, 2500);

        $nTm1 = random_int(1, 500);
        $nTm2 = random_int(1, 500);
        $nTm3 = random_int(1, 500);

        $lId1 = random_int(1, 25000);
        $lId2 = random_int(1, 25000);
        $lId3 = random_int(1, 25000);

        $lRS1 = random_int(1, 2500);
        $lRS2 = random_int(1, 2500);
        $lRS3 = random_int(1, 2500);

        $ltsM1 = random_int(1, 400);
        $ltsM2 = random_int(1, 400);
        $ltsM3 = random_int(1, 400);

        $cId1 = random_int(1, 250000);
        $cId2 = random_int(1, 250000);
        $cId3 = random_int(1, 250000);

        $cRS1 = random_int(-2500, 2500);
        $cRS2 = random_int(-2500, 2500);
        $cRS3 = random_int(-2500, 2500);

        $aDurationMs = random_int(500, 2500);

        $bDurationMs = random_int(100, 3500);
        $bPeriodOnMs = random_int(100, 3500);
        $bPeriodOffMs = random_int(100, 3500);

        $cDurationMs = random_int(100, 3500);


        $lineTypes = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        $restartMethods = [
            RestartMethodType::RestartMethodNotSet,
            RestartMethodType::RestartMethodGraceful,
            RestartMethodType::RestartMethodForceful,
        ];

        foreach ($lineTypes as $b => $lt) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "EventRuleUpdate" => new EventRule([
                                "Id" => $Id,
                                "OnEvent" => new EventMessage([
                                    "BleAssetTrack" => new BleAssetTrackEvent([
                                        "New_id"         => [$nId1,  $nId2,  $nId3],
                                        "New_macAddress" => [$m1,    $m2,    $m3],
                                        "New_rawData"    => [$nRD1,  $nRD2,  $nRD3],
                                        "New_rssi"       => [$nRS1,  $nRS2,  $nRS3],
                                        "New_tsMinus"    => [$nTm1,  $nTm2,  $nTm3],
                                        "Lost_id"        => [$lId1,  $lId2,  $lId3],
                                        "Lost_rssi"      => [$lRS1,  $lRS2,  $lRS3],
                                        "Lost_tsMinus"   => [$ltsM1, $ltsM2, $ltsM3],
                                        "Current_id"     => [$cId1,  $cId2,  $cId3],
                                        "Current_rssi"   => [$cRS1,  $cRS2,  $cRS3],
                                    ]),
                                ]),
                                "PerformTheseActions" => [
                                    new ActionMessage([
                                        "WiegandLineActivate" => new WiegandLineActivateAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $aDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "WiegandLineBlink" => new WiegandLineBlinkAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $bDurationMs,
                                            "PeriodOnMs" => $bPeriodOnMs,
                                            "PeriodOffMs" => $bPeriodOffMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "RelayActivate" => new RelayActivateAction([
                                            "DurationMs" => $cDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "SystemRestart" => new SystemRestartAction([
                                            "RestartMethod" => $restartMethods[$b] ?? RestartMethodType::RestartMethodForceful,
                                        ]),
                                    ]),
                                ],
                                "DestinationSerial" => $DestinationSerial,
                                "DestinationAddress" => $DestinationAddress,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketConfigMessagEventRuleDelete() {
        $Id = random_int(1, 99999);
        $DestinationSerial = random_int(1, 9999);
        $DestinationAddress = [
            random_int(1, 9999999),
            random_int(1, 9999999),
            random_int(1, 9999999),
        ];

        $nId1 = random_int(1, 999999);
        $nId2 = random_int(1, 999999);
        $nId3 = random_int(1, 999999);

        $m1 = $this->randomMac();
        $m2 = $this->randomMac();
        $m3 = $this->randomMac();

        $nRD1 = 'a' . bin2hex(random_bytes(10));
        $nRD2 = 'b' . bin2hex(random_bytes(10));
        $nRD3 = 'c' . bin2hex(random_bytes(10));

        $nRS1 = random_int(-2500, 2500);
        $nRS2 = random_int(-2500, 2500);
        $nRS3 = random_int(-2500, 2500);

        $nTm1 = random_int(1, 500);
        $nTm2 = random_int(1, 500);
        $nTm3 = random_int(1, 500);

        $lId1 = random_int(1, 25000);
        $lId2 = random_int(1, 25000);
        $lId3 = random_int(1, 25000);

        $lRS1 = random_int(1, 2500);
        $lRS2 = random_int(1, 2500);
        $lRS3 = random_int(1, 2500);

        $ltsM1 = random_int(1, 400);
        $ltsM2 = random_int(1, 400);
        $ltsM3 = random_int(1, 400);

        $cId1 = random_int(1, 250000);
        $cId2 = random_int(1, 250000);
        $cId3 = random_int(1, 250000);

        $cRS1 = random_int(-2500, 2500);
        $cRS2 = random_int(-2500, 2500);
        $cRS3 = random_int(-2500, 2500);

        $aDurationMs = random_int(500, 2500);

        $bDurationMs = random_int(100, 3500);
        $bPeriodOnMs = random_int(100, 3500);
        $bPeriodOffMs = random_int(100, 3500);

        $cDurationMs = random_int(100, 3500);


        $lineTypes = [
            WiegandLineTypes::Wiegand_LineNotSet,
            WiegandLineTypes::Wiegand_D0,
            WiegandLineTypes::Wiegand_D1,
            WiegandLineTypes::Wiegand_LED_Green,
            WiegandLineTypes::Wiegand_LED_Red,
            WiegandLineTypes::Wiegand_Buzzer,
        ];

        $restartMethods = [
            RestartMethodType::RestartMethodNotSet,
            RestartMethodType::RestartMethodGraceful,
            RestartMethodType::RestartMethodForceful,
        ];

        foreach ($lineTypes as $b => $lt) {
            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Config" => new ConfigMessage([
                            "EventRuleDelete" => new EventRule([
                                "Id" => $Id,
                                "OnEvent" => new EventMessage([
                                    "BleAssetTrack" => new BleAssetTrackEvent([
                                        "New_id"         => [$nId1,  $nId2,  $nId3],
                                        "New_macAddress" => [$m1,    $m2,    $m3],
                                        "New_rawData"    => [$nRD1,  $nRD2,  $nRD3],
                                        "New_rssi"       => [$nRS1,  $nRS2,  $nRS3],
                                        "New_tsMinus"    => [$nTm1,  $nTm2,  $nTm3],
                                        "Lost_id"        => [$lId1,  $lId2,  $lId3],
                                        "Lost_rssi"      => [$lRS1,  $lRS2,  $lRS3],
                                        "Lost_tsMinus"   => [$ltsM1, $ltsM2, $ltsM3],
                                        "Current_id"     => [$cId1,  $cId2,  $cId3],
                                        "Current_rssi"   => [$cRS1,  $cRS2,  $cRS3],
                                    ]),
                                ]),
                                "PerformTheseActions" => [
                                    new ActionMessage([
                                        "WiegandLineActivate" => new WiegandLineActivateAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $aDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "WiegandLineBlink" => new WiegandLineBlinkAction([
                                            "LineOutput" => $lt,
                                            "DurationMs" => $bDurationMs,
                                            "PeriodOnMs" => $bPeriodOnMs,
                                            "PeriodOffMs" => $bPeriodOffMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "RelayActivate" => new RelayActivateAction([
                                            "DurationMs" => $cDurationMs,
                                        ]),
                                    ]),
                                    new ActionMessage([
                                        "SystemRestart" => new SystemRestartAction([
                                            "RestartMethod" => $restartMethods[$b] ?? RestartMethodType::RestartMethodForceful,
                                            // "RestartMethod" => RestartMethodType::RestartMethodNotSet,
                                            // "RestartMethod" => RestartMethodType::RestartMethodGraceful,
                                            // "RestartMethod" =>    RestartMethodType::RestartMethodForceful,
                                        ]),
                                    ]),
                                ],
                                "DestinationSerial" => $DestinationSerial,
                                "DestinationAddress" => $DestinationAddress,
                            ]),
                        ]),
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketConfigMessageNetworkAddress() {
        $Address = random_int(268435456, 2576980377);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "NetworkAddress" => new NetworkAddressConfig([
                            "Address" => $Address,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageNetworkRouteUpdate() {
        $DestinationAddress = random_int(268435456, 2576980377);
        $DestinationPort = random_int(8000, 140000);
        $AddressRoute = [
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "NetworkRouteUpdate" => new NetworkRouteConfig([
                            "DestinationAddress" => $DestinationAddress,
                            "DestinationPort" => $DestinationPort,
                            "AddressRoute" => $AddressRoute,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageNetworkRouteDelete() {
        $DestinationAddress = random_int(268435456, 2576980377);
        $DestinationPort = random_int(8000, 140000);
        $AddressRoute = [
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
            random_int(268435456, 2576980377),
        ];

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "NetworkRouteDelete" => new NetworkRouteConfig([
                            "DestinationAddress" => $DestinationAddress,
                            "DestinationPort" => $DestinationPort,
                            "AddressRoute" => $AddressRoute,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketConfigMessageBlueToothPort() {
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Config" => new ConfigMessage([
                        "BlueToothPort" => new BlueToothPortConfig([
                            "BeSecureService" => true
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketLogMessageDebug() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Debug" => "Debug: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketLogMessageInfo() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Info" => "Info: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketLogMessageWarning() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Warning" => "Warning: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketLogMessageError() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Error" => "Error: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketLogMessageFatal() {
        $CoreDumpBin = bin2hex(random_bytes(50));
        $CoreDumpLog = bin2hex(random_bytes(50));

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Log" => new LogMessage([
                        "CoreDumpBin" => $CoreDumpBin,
                        "CoreDumpLog" => $CoreDumpLog,
                        "Fatal" => "Fatal: The message has been sent successfully"
                    ])
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketMetricMessage() {
        $Interval = random_int(500, 2500);

        $InfoLogCount = random_int(1, 4000);
        $WarningLogCount = random_int(1, 4000);
        $ErrorLogCount = random_int(1, 4000);

        $BytesTransmitted = random_int(1, 5000);
        $BytesReceived = random_int(1, 5000);
        $BytesReceivedExclusive = random_int(1, 5000);
        $ReceiveOverrunErrors = random_int(1, 5000);
        $ReceiveFramingErrors = random_int(1, 5000);
        $ReceiveBreakErrors = random_int(1, 5000);
        $PacketsTransmitted = random_int(1, 5000);
        $PacketsReceived = random_int(1, 5000);
        $PacketsReceivedExclusive = random_int(1, 5000);
        $PacketsACKed = random_int(1, 5000);
        $PacketsNACKedCorrupted = random_int(1, 5000);
        $PacketsCorruptedDiscarded = random_int(1, 5000);
        $PacketsTimedOut = random_int(1, 5000);
        $PacketsDecodingFailed = random_int(1, 5000);

        $FailedParityChecks = random_int(1, 5000);

        $AccessDeviceLookupTimeMin = random_int(1, 5000);
        $AccessDeviceLookupTimeMax = random_int(5000, 10000);
        $AccessUserLookupTimeMin = random_int(1, 5000);
        $AccessUserLookupTimeMax = random_int(5000, 10000);
        $AcesssGroupLookupTimeMin = random_int(1, 5000);
        $AccessGroupLookupTimeMax = random_int(5000, 10000);

        $LargestQueueMessageCount = random_int(1, 5000);
        $Address = hexdec(bin2hex(random_bytes(8)));
        $MessageCount = random_int(1, 500);
        $QueueSize = random_int(1, 500);
        $ExpiredMessages = random_int(1, 5000);

        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Metric" => new MetricMessage([
                        "Interval" => $Interval,
                        "Log" => new LogMetric([
                            "InfoLogCount" => $InfoLogCount,
                            "WarningLogCount" => $WarningLogCount,
                            "ErrorLogCount" => $ErrorLogCount,
                        ]),
                        "RS485MainPort" => new RS485PortMetric([
                            "BytesTransmitted"          => $BytesTransmitted,
                            "BytesReceived"             => $BytesReceived,
                            "BytesReceivedExclusive"    => $BytesReceivedExclusive,
                            "ReceiveOverrunErrors"      => $ReceiveOverrunErrors,
                            "ReceiveFramingErrors"      => $ReceiveFramingErrors,
                            "ReceiveBreakErrors"        => $ReceiveBreakErrors,
                            "PacketsTransmitted"        => $PacketsTransmitted,
                            "PacketsReceived"           => $PacketsReceived,
                            "PacketsReceivedExclusive"  => $PacketsReceivedExclusive,
                            "PacketsACKed"              => $PacketsACKed,
                            "PacketsNACKedCorrupted"    => $PacketsNACKedCorrupted,
                            "PacketsCorruptedDiscarded" => $PacketsCorruptedDiscarded,
                            "PacketsTimedOut"           => $PacketsTimedOut,
                            "PacketsDecodingFailed"     => $PacketsDecodingFailed,
                        ]),
                        "WiegandPort1" => new WiegandPortMetric([
                            "FailedParityChecks" => $FailedParityChecks,
                        ]),
                        "Access" => new AccessMetric([
                            "AccessDeviceLookupTimeMin" => $AccessDeviceLookupTimeMin,
                            "AccessDeviceLookupTimeMax" => $AccessDeviceLookupTimeMax,
                            "AccessUserLookupTimeMin"   => $AccessUserLookupTimeMin,
                            "AccessUserLookupTimeMax"   => $AccessUserLookupTimeMax,
                            "AcesssGroupLookupTimeMin"  => $AcesssGroupLookupTimeMin,
                            "AccessGroupLookupTimeMax"  => $AccessGroupLookupTimeMax,
                        ]),
                        "Network" => new NetworkMetric([
                            "LargestQueueMessageCount" => $LargestQueueMessageCount,
                            "NetworkQueues" => [
                                new NetworkQueueMetric([
                                    "Address" => $Address,
                                    "MessageCount" => $MessageCount,
                                    "QueueSize" => $QueueSize,
                                    "ExpiredMessages" => $ExpiredMessages,
                                ]),
                            ],
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketRequestMessage() {
        $methods = [
            RequestMethodType::NotSet,
            RequestMethodType::Get,
            RequestMethodType::Post,
            RequestMethodType::Put,
            RequestMethodType::Patch,
            RequestMethodType::Delete,
            RequestMethodType::Head,
            RequestMethodType::Options,
        ];

        $Raw = bin2hex(random_bytes(10));
        $Binary = bin2hex(random_bytes(10));
        $Json = json_encode([
            'cheese' => 'cake',
            'abc' => true,
            'def' => random_int(1, 25)
        ]);

        $oneOf = [
            ['Raw', $Raw],
            ['Binary', $Binary],
            ['Json', $Json],
            ['RequestId', RequestIdTypes::Request_SystemFeatures],
        ];

        foreach ($methods as $i => $method) {
            $pos = $i % 4;
            list($key, $value) = $oneOf[$pos];

            $p = new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => $this->SequenceNumber,
                'RedeliveryCounter' => $this->RedeliveryCounter,
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => $this->CorrelationId,
                        "Request" => new RequestMessage([
                            "Method" => $method,
                            $key => $value,
                        ])
                    ]),
                ],
            ]);

            $this->performDeserializeReserializeTest($p);
        }
    }


    public function testReserializePacketResponseMessageRaw() {
        $Raw = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "Raw" => $Raw,
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketResponseMessageBinary() {
        $Binary = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "Binary" => $Binary,
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketResponseMessageJson() {
        $Json = json_encode([
            'cheese' => 'cake',
            'abc' => true,
            'def' => random_int(1, 25)
        ]);
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "Json" => $Json,
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketResponseMessageSystemPing() {
        $ReceivedTimeStampMs = random_int(500, 2500);
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "SystemPing" => new SystemPingResponse([
                            "ReceivedTimeStampMs" => $ReceivedTimeStampMs,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketResponseMessageSystemHardwareInfo() {
        $SerialNumber = random_int(1, 3000);
        $BootFirmwareVersion = implode('.', [
            random_int(1, 3),
            random_int(1, 18),
            random_int(1, 25),
        ]);
        $AppFirmwareVersion = implode('.', [
            random_int(1, 3),
            random_int(1, 18),
            random_int(1, 25),
        ]);
        $HardwarePlatform = 'AOH1000-007';
        $HardwareVersion = random_int(1, 5);
        $ManufacturedDate = time();
        $InternalFileSystemSize = random_int(50, 6000);
        $ExternalFileSystemSize = random_int(300, 7000);
        $MCUUID = random_int(1111111111, 9999999999);
        $MCUSocRevision = random_int(1, 5000);
        $MCUPartId = random_int(1, 5000);
        $BLEMAC = $this->randomMac();
        $BLECodedPhyPresent = true;
        $EthernetMAC = $this->randomMac();
        $GSM_Modem_Manufacturer = "Cheese Inc";
        $GSM_Modem_Model = 'EC200U';
        $GSM_Modem_Revision = 'EC200UCNAAR01A04M08';
        $GSM_Modem_IMEI = bin2hex(random_bytes(4));
        $GSM_Modem_SerialNumber = bin2hex(random_bytes(6));
        $GSM_SIM1_IMSI = bin2hex(random_bytes(16));
        $GSM_SIM1_ICCID = bin2hex(random_bytes(20));
        $GSM_SIM1_SubscriberNumber = '+27761234567';
        $GSM_SIM2_IMSI = bin2hex(random_bytes(16));
        $GSM_SIM2_ICCID = bin2hex(random_bytes(20));
        $GSM_SIM2_SubscriberNumber = '+27767654321';
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "SystemHardwareInfo" => new SystemHardwareInfoResponse([
                            "SerialNumber"              => $SerialNumber,
                            "BootFirmwareVersion"       => $BootFirmwareVersion,
                            "AppFirmwareVersion"        => $AppFirmwareVersion,
                            "HardwarePlatform"          => $HardwarePlatform,
                            "HardwareVersion"           => $HardwareVersion,
                            "ManufacturedDate"          => $ManufacturedDate,
                            "InternalFileSystemSize"    => $InternalFileSystemSize,
                            "ExternalFileSystemSize"    => $ExternalFileSystemSize,
                            "MCUUID"                    => $MCUUID,
                            "MCUSocRevision"            => $MCUSocRevision,
                            "MCUPartId"                 => $MCUPartId,
                            "BLEMAC"                    => $BLEMAC,
                            "BLECodedPhyPresent"        => $BLECodedPhyPresent,
                            "EthernetMAC"               => $EthernetMAC,
                            "GSM_Modem_Manufacturer"    => $GSM_Modem_Manufacturer,
                            "GSM_Modem_Model"           => $GSM_Modem_Model,
                            "GSM_Modem_Revision"        => $GSM_Modem_Revision,
                            "GSM_Modem_IMEI"            => $GSM_Modem_IMEI,
                            "GSM_Modem_SerialNumber"    => $GSM_Modem_SerialNumber,
                            "GSM_SIM1_IMSI"             => $GSM_SIM1_IMSI,
                            "GSM_SIM1_ICCID"            => $GSM_SIM1_ICCID,
                            "GSM_SIM1_SubscriberNumber" => $GSM_SIM1_SubscriberNumber,
                            "GSM_SIM2_IMSI"             => $GSM_SIM2_IMSI,
                            "GSM_SIM2_ICCID"            => $GSM_SIM2_ICCID,
                            "GSM_SIM2_SubscriberNumber" => $GSM_SIM2_SubscriberNumber,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketResponseMessageSystemFeatures() {
        $BLEPorts = random_int(1, 3);
        $RS485Ports = random_int(1, 3);
        $ModemPorts = random_int(1, 3);
        $EthernetPorts = random_int(1, 3);
        $WiegandPorts = random_int(1, 3);
        $RelayPorts = random_int(1, 3);
        $InputPorts = random_int(1, 3);
        $ExpansionPorts = random_int(1, 3);
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "Response" => new ResponseMessage([
                        "Code" => ResponseCode::Payload_Too_large,
                        "SystemFeatures" => new SystemFeaturesResponse([
                            "BLEPorts"       => $BLEPorts,
                            "RS485Ports"     => $RS485Ports,
                            "ModemPorts"     => $ModemPorts,
                            "EthernetPorts"  => $EthernetPorts,
                            "WiegandPorts"   => $WiegandPorts,
                            "RelayPorts"     => $RelayPorts,
                            "InputPorts"     => $InputPorts,
                            "ExpansionPorts" => $ExpansionPorts,
                        ]),
                    ]),
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketDeadLetterMessage() {
        $DeadLetterMessage = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "DeadLetterMessage" => $DeadLetterMessage,
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketRoutedPacketMessage() {
        $RoutedPacket = bin2hex(
            (new Packet([
                "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
                "DestinationAddressEx" => $this->dest,
                'SourceAddressEx' => $this->source,
                'SequenceNumber' => random_int(1, 10),
                'RedeliveryCounter' => random_int(1, 5),
                'TimeStampMs' => timeMs(),
                "Messages" => [
                    new Message([
                        "TimeStampMs" => timeMs(),
                        "TimeToLive" => 3600,
                        "CorrelationId" => random_int(1, 200),
                        "Config" => new ConfigMessage([
                            "AccessGroupDelete" => new AccessGroup([
                                "Id" => random_int(1, 99999),
                                "DaysOfWeekBits" => random_int(0, 7),
                                "TimesOfDayBits" => random_int(0, 47),
                                "AccessPeripheralIds" => [
                                    random_int(1, 9999),
                                    random_int(1, 9999),
                                    random_int(1, 9999),
                                ],
                            ]),
                        ]),
                    ]),
                ],
            ]))->serializeToString()
        );
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "RoutedPacket" => $RoutedPacket,
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketBesecurePacketMessage() {
        $BeSecurePacket = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "BeSecurePacket" => $BeSecurePacket,
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }


    public function testReserializePacketSystemAuditPacketMessage() {
        $SystemAuditPacket = bin2hex(random_bytes(10));
        $p = new Packet([
            "PacketType" => PacketTypes::PacketType_Response_NACK_Corrupt,
            "DestinationAddressEx" => $this->dest,
            'SourceAddressEx' => $this->source,
            'SequenceNumber' => $this->SequenceNumber,
            'RedeliveryCounter' => $this->RedeliveryCounter,
            'TimeStampMs' => timeMs(),
            "Messages" => [
                new Message([
                    "TimeStampMs" => timeMs(),
                    "TimeToLive" => 3600,
                    "CorrelationId" => $this->CorrelationId,
                    "SystemAuditPacket" => $SystemAuditPacket,
                ]),
            ],
        ]);

        $this->performDeserializeReserializeTest($p);
    }
}
