PHP项目数据协议如何解析设备报文

wen PHP项目 31

本文目录导读:

PHP项目数据协议如何解析设备报文

  1. 常见报文格式及解析方案
  2. 完整设备协议解析示例
  3. 完整使用示例
  4. 使用Socket接收和处理

我来详细解释PHP项目中解析设备报文的数据协议方法:

常见报文格式及解析方案

1 固定长度报文

class FixedLengthParser {
    public function parse($rawData) {
        // 假设报文格式:头(2字节) + 长度(2字节) + 数据 + 校验(1字节)
        $header = substr($rawData, 0, 2);
        $length = unpack('n', substr($rawData, 2, 2))[1];
        $data = substr($rawData, 4, $length);
        $checksum = ord(substr($rawData, -1));
        // 校验
        if ($this->verifyChecksum($rawData)) {
            return [
                'header' => bin2hex($header),
                'length' => $length,
                'data' => $data,
                'checksum' => $checksum
            ];
        }
        return false;
    }
}

2 可变长度报文(带分隔符)

class VariableLengthParser {
    public function parse($buffer) {
        $startMarker = 0xAA; // 起始标识
        $endMarker = 0xBB;   // 结束标识
        // 查找报文边界
        $startPos = strpos($buffer, chr($startMarker));
        if ($startPos === false) return false;
        $endPos = strpos($buffer, chr($endMarker), $startPos);
        if ($endPos === false) return false;
        // 提取完整报文
        $packet = substr($buffer, $startPos, $endPos - $startPos + 1);
        return $this->decodePacket($packet);
    }
}

完整设备协议解析示例

1 协议定义类

class DeviceProtocol {
    const HEADER = 0xAA55;
    const PROTOCOL_VERSION = 0x01;
    // 指令类型
    const CMD_HEARTBEAT = 0x01;
    const CMD_DATA_REPORT = 0x02;
    const CMD_CONFIG = 0x03;
    // 数据类型
    const DATA_TYPE_TEMPERATURE = 0x01;
    const DATA_TYPE_HUMIDITY = 0x02;
    const DATA_TYPE_PRESSURE = 0x03;
}

2 报文解析器

class PacketParser {
    private $buffer = '';
    private $deviceId;
    public function __construct($deviceId) {
        $this->deviceId = $deviceId;
    }
    /**
     * 解析设备报文
     * @param string $rawData 原始二进制数据
     * @return array|false
     */
    public function parse($rawData) {
        $this->buffer .= $rawData;
        while (strlen($this->buffer) >= 12) { // 最小报文长度
            $packet = $this->extractPacket();
            if ($packet === false) {
                $this->buffer = substr($this->buffer, 1);
                continue;
            }
            $result = $this->decodePacket($packet);
            if ($result !== false) {
                return $result;
            }
        }
        return false;
    }
    /**
     * 提取完整报文
     */
    private function extractPacket() {
        $pos = strpos($this->buffer, pack('n', DeviceProtocol::HEADER));
        if ($pos === false || $pos > 0) {
            return false;
        }
        // 检查最小长度
        if (strlen($this->buffer) < 6) return false;
        // 解析报文长度
        $length = unpack('n', substr($this->buffer, 4, 2))[1];
        // 检查是否收到完整报文
        if (strlen($this->buffer) < $length + 6) return false;
        $packet = substr($this->buffer, 0, $length + 6);
        $this->buffer = substr($this->buffer, $length + 6);
        return $packet;
    }
    /**
     * 解码报文
     */
    private function decodePacket($packet) {
        $header = unpack('n', substr($packet, 0, 2))[1];
        $version = ord(substr($packet, 2, 1));
        $cmd = ord(substr($packet, 3, 1));
        $length = unpack('n', substr($packet, 4, 2))[1];
        $payload = substr($packet, 6, $length);
        $checksum = ord(substr($packet, -1));
        // 验证校验和
        if (!$this->verifyChecksum($packet)) {
            return false;
        }
        return [
            'header' => dechex($header),
            'version' => $version,
            'command' => $cmd,
            'payload' => $this->parsePayload($cmd, $payload),
            'timestamp' => time()
        ];
    }
    /**
     * 解析负载数据
     */
    private function parsePayload($cmd, $payload) {
        switch ($cmd) {
            case DeviceProtocol::CMD_HEARTBEAT:
                return $this->parseHeartbeat($payload);
            case DeviceProtocol::CMD_DATA_REPORT:
                return $this->parseDataReport($payload);
            case DeviceProtocol::CMD_CONFIG:
                return $this->parseConfig($payload);
            default:
                return ['raw' => bin2hex($payload)];
        }
    }
    /**
     * 解析心跳包
     */
    private function parseHeartbeat($payload) {
        return [
            'device_status' => ord($payload[0]),
            'battery' => ord($payload[1]),
            'signal_strength' => ord($payload[2])
        ];
    }
    /**
     * 解析数据上报
     */
    private function parseDataReport($payload) {
        $data = [];
        $offset = 0;
        while ($offset < strlen($payload)) {
            $dataType = ord($payload[$offset]);
            $dataLength = ord($payload[$offset + 1]);
            $value = substr($payload, $offset + 2, $dataLength);
            switch ($dataType) {
                case DeviceProtocol::DATA_TYPE_TEMPERATURE:
                    $data['temperature'] = unpack('f', $value)[1];
                    break;
                case DeviceProtocol::DATA_TYPE_HUMIDITY:
                    $data['humidity'] = unpack('f', $value)[1];
                    break;
                case DeviceProtocol::DATA_TYPE_PRESSURE:
                    $data['pressure'] = unpack('f', $value)[1];
                    break;
            }
            $offset += 2 + $dataLength;
        }
        return $data;
    }
    /**
     * 验证校验和
     */
    private function verifyChecksum($packet) {
        $data = substr($packet, 0, -1);
        $checksum = ord(substr($packet, -1));
        $calculated = 0;
        for ($i = 0; $i < strlen($data); $i++) {
            $calculated ^= ord($data[$i]);
        }
        return $calculated === $checksum;
    }
}

3 报文构造器(发送指令)

class PacketBuilder {
    /**
     * 构造配置指令
     */
    public static function buildConfigCommand($params) {
        $payload = '';
        foreach ($params as $key => $value) {
            $payload .= chr($key) . chr(strlen($value)) . $value;
        }
        return self::buildPacket(
            DeviceProtocol::CMD_CONFIG,
            $payload
        );
    }
    /**
     * 构造应答报文
     */
    public static function buildAck($cmd, $status = 0) {
        $payload = chr($status) . chr($cmd);
        return self::buildPacket(0x80 | $cmd, $payload);
    }
    /**
     * 构造完整报文
     */
    private static function buildPacket($cmd, $payload) {
        $header = pack('n', DeviceProtocol::HEADER);
        $version = chr(DeviceProtocol::PROTOCOL_VERSION);
        $command = chr($cmd);
        $length = pack('n', strlen($payload));
        $data = $header . $version . $command . $length . $payload;
        $checksum = self::calculateChecksum($data);
        return $data . chr($checksum);
    }
    /**
     * 计算校验和
     */
    private static function calculateChecksum($data) {
        $checksum = 0;
        for ($i = 0; $i < strlen($data); $i++) {
            $checksum ^= ord($data[$i]);
        }
        return $checksum;
    }
}

完整使用示例

class DeviceCommunication {
    private $parser;
    private $deviceId;
    public function __construct($deviceId) {
        $this->deviceId = $deviceId;
        $this->parser = new PacketParser($deviceId);
    }
    /**
     * 处理接收到的数据
     */
    public function handleReceivedData($rawData) {
        $result = $this->parser->parse($rawData);
        if ($result === false) {
            return false;
        }
        // 根据指令类型处理
        switch ($result['command']) {
            case DeviceProtocol::CMD_HEARTBEAT:
                return $this->handleHeartbeat($result['payload']);
            case DeviceProtocol::CMD_DATA_REPORT:
                return $this->handleDataReport($result['payload']);
            case DeviceProtocol::CMD_CONFIG:
                return $this->handleConfig($result['payload']);
        }
    }
    private function handleHeartbeat($payload) {
        // 更新设备在线状态
        $this->updateDeviceStatus($payload);
        // 返回应答
        return PacketBuilder::buildAck(DeviceProtocol::CMD_HEARTBEAT);
    }
    private function handleDataReport($payload) {
        // 存储传感器数据
        $this->saveSensorData($payload);
        // 检查是否需要下发配置
        if ($this->needConfigUpdate()) {
            return PacketBuilder::buildConfigCommand([
                0x01 => 'sample_interval',
                0x02 => 'upload_interval'
            ]);
        }
        return PacketBuilder::buildAck(DeviceProtocol::CMD_DATA_REPORT);
    }
}

使用Socket接收和处理

// TCP服务器示例
$server = new React\Socket\Server('0.0.0.0:8080', $loop);
$server->on('connection', function (React\Socket\ConnectionInterface $connection) {
    $deviceId = null;
    $parser = new PacketParser();
    $connection->on('data', function ($data) use ($connection, &$deviceId, $parser) {
        $result = $parser->parse($data);
        if ($result !== false) {
            echo "Received packet from device\n";
            print_r($result);
            // 发送应答
            $response = PacketBuilder::buildAck($result['command']);
            $connection->write($response);
        }
    });
    $connection->on('close', function () {
        echo "Connection closed\n";
    });
});

这个解决方案提供了完整的设备报文解析框架,可以根据实际协议格式进行调整,关键点包括:

  • 支持多种报文格式
  • 包含校验机制
  • 模块化设计,易于扩展
  • 提供报文构造和解析的完整流程

抱歉,评论功能暂时关闭!