当前位置: 首页 > news >正文

用php实现jt808部标协议对接

背景

之前做的智慧校车/网约巴士系统(网约巴士旅游专线平台搭建历程),对接海康设备时需要对接交通部部标协议之前用的Java写的。突然想说能不能用PHP实现呢。说实话,肯定没有java顺手,不过也不是不可以,就是有点蹩脚!

以下是一个基于PHP实现JT808协议的简化示例代码,包含基础协议解析和封装逻辑:

<?php
class JT808Protocol {
    // 消息ID定义
    const MSG_ID_REGISTER = 0x0100;      // 终端注册
    const MSG_ID_REGISTER_RESP = 0x8100; // 注册响应
    const MSG_ID_LOCATION = 0x0200;      // 位置汇报

    // 协议版本
    const PROTOCOL_VERSION = 0x01;

    /**
     * 打包消息
     */
    public static function packMessage($msgId, $terminalId, $msgBody, $isSubPackage = false, $encryptType = 0) {
        // 消息头组装
        $header = self::packHeader($msgId, $terminalId, strlen($msgBody), $isSubPackage, $encryptType);
        
        // 合并头和消息体
        $message = $header . $msgBody;
        
        // 计算校验码
        $checkCode = self::calculateCheckCode($message);
        
        // 添加转义字符(示例简化处理)
        return "\x7e" . $message . $checkCode . "\x7e";
    }

    /**
     * 解包消息
     */
    public static function unpackMessage($data) {
        // 去除首尾的0x7e
        $data = substr($data, 1, -1);
        
        // 分割头和消息体
        $header = substr($data, 0, 16); // 头部固定16字节
        $msgBody = substr($data, 16, -1); // 去掉校验码
        
        // 解析头信息
        $headerInfo = self::unpackHeader($header);
        
        return [
            'header' => $headerInfo,
            'body' => self::unpackBody($msgBody, $headerInfo['msg_id'])
        ];
    }

    // 私有方法:打包消息头
    private static function packHeader($msgId, $terminalId, $bodyLen, $isSubPackage, $encryptType) {
        $msgProps = ($bodyLen & 0x3FFF) | ($encryptType << 10) | ($isSubPackage ? 0x2000 : 0);
        
        return pack('nnH12nn',
            $msgId,                // 消息ID
            $msgProps,             // 消息属性
            $terminalId,           // 终端手机号(BCD编码)
            self::getSerialNum(),  // 消息流水号
            $bodyLen               // 包总数(示例简化处理)
        );
    }

    // 私有方法:解析消息头
    private static function unpackHeader($header) {
        $headerData = unpack('nmsg_id/nmsg_props/H12terminal_id/nserial_num/ntotal_pkgs', $header);
        
        return [
            'msg_id'       => $headerData['msg_id'],
            'body_len'     => $headerData['msg_props'] & 0x3FFF,
            'encrypt_type' => ($headerData['msg_props'] >> 10) & 0x07,
            'is_sub_pkg'   => ($headerData['msg_props'] & 0x2000) > 0,
            'terminal_id'  => $headerData['terminal_id'],
            'serial_num'   => $headerData['serial_num'],
            'total_pkgs'   => $headerData['total_pkgs']
        ];
    }

    // 私有方法:解析消息体
    private static function unpackBody($body, $msgId) {
        switch ($msgId) {
            case self::MSG_ID_REGISTER:
                return self::unpackRegisterBody($body);
            case self::MSG_ID_LOCATION:
                return self::unpackLocationBody($body);
            default:
                return $body; // 返回原始二进制数据
        }
    }

    // 解析注册消息体
    private static function unpackRegisterBody($body) {
        $provinceId = unpack('n', substr($body, 0, 2))[1];
        $cityId = unpack('n', substr($body, 2, 2))[1];
        $manufacturer = substr($body, 4, 5);
        $model = substr($body, 9, 8);
        $terminalId = substr($body, 17, 7);
        $color = unpack('C', substr($body, 24, 1))[1];
        $plateNo = substr($body, 25);

        return [
            'province_id' => $provinceId,
            'city_id'     => $cityId,
            'manufacturer'=> trim($manufacturer),
            'model'       => trim($model),
            'terminal_id' => trim($terminalId),
            'color'       => $color,
            'plate_no'    => trim($plateNo)
        ];
    }

    // 解析位置消息体
    private static function unpackLocationBody($body) {
        $data = unpack('Nalarm/Nstatus/Nlat/Nlon/nspeed/Ndirection/Ntime', $body);
        
        return [
            'alarm'     => $data['alarm'],
            'status'    => $data['status'],
            'latitude'  => $data['lat'] / 1000000,
            'longitude' => $data['lon'] / 1000000,
            'speed'     => $data['speed'] / 10,
            'direction' => $data['direction'],
            'time'      => date('Y-m-d H:i:s', strtotime($data['time']))
        ];
    }

    // 生成注册响应消息体
    public static function packRegisterResponse($serialNum, $terminalId, $resultCode) {
        $body = pack('nnH12', $serialNum, $resultCode, $terminalId);
        return self::packMessage(self::MSG_ID_REGISTER_RESP, $terminalId, $body);
    }

    // 计算校验码(异或校验)
    private static function calculateCheckCode($data) {
        $check = 0;
        for ($i = 0; $i < strlen($data); $i++) {
            $check ^= ord($data[$i]);
        }
        return pack('C', $check);
    }

    // 获取流水号(示例简单实现)
    private static $serialNum = 0;
    private static function getSerialNum() {
        return ++self::$serialNum % 65536;
    }
}

// ================= 使用示例 =================
// 接收到的示例数据(16进制字符串)
$sampleData = "7e010000261234567890123456780001000178313830313233343536373839303132333435367e";

// 转换二进制数据
$binaryData = hex2bin(str_replace(' ', '', $sampleData));

// 解析消息
$parsedMsg = JT808Protocol::unpackMessage($binaryData);

// 处理注册请求
if ($parsedMsg['header']['msg_id'] == JT808Protocol::MSG_ID_REGISTER) {
    // 生成响应消息
    $response = JT808Protocol::packRegisterResponse(
        $parsedMsg['header']['serial_num'],
        $parsedMsg['header']['terminal_id'],
        0 // 0表示成功
    );
    
    // 发送响应(示例输出)
    echo "Response HEX: " . bin2hex($response) . "\n";
}

// 打印解析结果
print_r($parsedMsg);

关键功能说明:

  1. 协议封装

    • packMessage() 方法用于组装符合JT808协议的消息

    • 支持消息头自动生成(包含消息ID、终端号、流水号等)

    • 自动计算校验码

  2. 协议解析

    • unpackMessage() 方法解析接收到的二进制数据

    • 支持解析注册消息和位置消息

    • 自动处理校验码验证

  3. 数据转换

    • 处理BCD编码的终端号

    • 转换经纬度坐标(1/1000000度)

    • 转换速度值(1/10 km/h)

  4. 校验处理

    • 使用异或校验算法

    • 自动处理转义字符(示例简化处理)

典型工作流程:

  1. 终端发送注册消息(0x0100)

  2. 服务端解析后返回注册响应(0x8100)

  3. 终端定期发送位置信息(0x0200)

  4. 服务端解析位置数据并存储

扩展建议:

  1. 根据实际需求添加更多消息类型处理

  2. 完善错误处理机制

  3. 添加TCP粘包处理逻辑

  4. 实现完整的分包处理功能

  5. 添加数据加密支持(根据协议规范)

注意:实际使用时需要根据具体设备厂商的协议扩展进行调整,不同厂商可能会在标准协议基础上进行扩展。

 

 

相关文章:

  • 开源模型应用落地-语音转文本-whisper模型-AIGC应用探索(五)
  • Day 1:Leetcode 两数相除
  • 云原生周刊:Ingress-NGINX 漏洞
  • C# 匿名方法讲解
  • Ubuntu Server版本Ubuntu 24.04.2 LTS下载与安装-详细教程,细致到每一步都有说明
  • 【常用的中间件】
  • c++ count方法
  • HTTP 核心知识点整理
  • C++进阶——哈希表的实现
  • 探秘Transformer系列之(18)--- FlashAttention
  • 如何避免测试环境不稳定导致的误报
  • 单纯形法之大M法
  • 解决 Ubuntu/Debian 中 `apt-get` 报错 “无法获得锁 /var/lib/dpkg/lock“
  • 歌曲缓存相关功能
  • Qt在IMX6ULL嵌入式系统中图片加载问题排查与解决
  • 多线程—锁策略
  • 去中心化金融
  • 漏洞挖掘---锐明Crocus系统Service.do接口任意文件读取
  • 《数字图像处理》第三章 3.8 基于模糊技术的图像强度变换与空间滤波学习笔记
  • 微积分小白入门:第二章 数列与极限——从困惑到顿悟的奇妙之旅
  • 微商做图王官方网站/域名注册服务商
  • 一个网站的建设方案/云搜索系统
  • 皋兰县城乡和住房建设局网站/公司网站开发费用
  • 适合手机的网站/推广宣传
  • 专业排名优化网站/游戏推广员平台
  • 课题组研究网站怎么做/百度免费资源网站