You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于CodeIgniter 4解析Teltonika FMB140二进制数据异常求助

解决Teltonika FMB140二进制数据解析问题(CodeIgniter 4)

问题根源

你当前的代码错误假设IMEI之后直接是timestamp等字段,但Teltonika FMB140的AVL数据协议有严格的二进制结构,并非简单的连续字节拼接。另外,直接用hexdec处理原始二进制字节是错误的——二进制数据需要按字节提取后转换为对应的数据类型(如64位时间戳、32位经纬度整数),还要注意小端字节序的要求。

正确解析逻辑

Teltonika设备通信分两个阶段:

  1. IMEI握手阶段:设备先发送「2字节长度码+IMEI字符串」,服务器回复0x01确认。
  2. AVL数据阶段:握手成功后,设备发送标准AVL数据包,结构为:
    • 4字节:数据包总长度(小端)
    • 1字节:AVL记录数量
    • N个AVL记录(每个含时间戳、优先级、GPS数据、IO数据等)
    • 1字节:结束标记(需与记录数量一致)

关键数据转换规则

  • 时间戳:8字节小端无符号整数,单位为毫秒
  • 经度/纬度:4字节小端有符号整数,需除以10^7转换为十进制坐标
  • 卫星数:1字节无符号整数
  • 所有多字节数据均采用小端字节序

CodeIgniter 4 修正代码示例

<?php
namespace App\Controllers;

use CodeIgniter\Controller;

class TeltonikaReceiver extends Controller
{
    public function index()
    {
        $port = xxxxx; // 替换为你的端口
        $serverIp = 'xxxxxxxx'; // 替换为你的服务器IP

        // 创建Socket
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if (!$socket) die("Socket创建失败: " . socket_strerror(socket_last_error()));

        // 绑定端口
        if (!socket_bind($socket, $serverIp, $port)) die("绑定失败: " . socket_strerror(socket_last_error($socket)));

        // 监听连接
        if (!socket_listen($socket, 5)) die("监听失败: " . socket_strerror(socket_last_error($socket)));

        echo "等待设备连接...\n";

        $clientSocket = socket_accept($socket);
        if (!$clientSocket) die("接受连接失败: " . socket_strerror(socket_last_error($socket)));

        socket_getpeername($clientSocket, $clientAddress, $clientPort);
        echo "设备连接: $clientAddress:$clientPort\n";

        // 设置超时
        socket_set_option($clientSocket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 10, 'usec' => 0]);

        $handshakeDone = false;

        while (true) {
            $data = socket_read($clientSocket, 4096); // 增大缓冲区适配AVL数据包
            if ($data === false || strlen($data) === 0) break;

            if (!$handshakeDone) {
                // 处理IMEI握手
                $imeiLenBytes = substr($data, 0, 2);
                $imeiLength = unpack('v', $imeiLenBytes)[1]; // 小端解析2字节长度
                $imei = substr($data, 2, $imeiLength);

                echo "收到IMEI: $imei\n";

                // 发送确认
                socket_write($clientSocket, "\x01", 1);
                $handshakeDone = true;
                continue;
            }

            // 处理AVL数据包
            $this->parseAvlData($clientSocket, $data);
        }

        socket_close($clientSocket);
        socket_close($socket);
    }

    private function parseAvlData($clientSocket, string $data)
    {
        // 解析数据包总长度(4字节小端)
        $packetLen = unpack('V', substr($data, 0, 4))[1];
        // 解析AVL记录数量(1字节)
        $recordCount = ord(substr($data, 4, 1));

        $offset = 5;
        for ($i = 0; $i < $recordCount; $i++) {
            // 解析时间戳(8字节小端,毫秒)
            $timestampBytes = substr($data, $offset, 8);
            $timestamp = unpack('P', $timestampBytes)[1]; // P表示小端64位无符号整数
            $offset += 8;

            // 解析优先级(1字节)
            $priority = ord(substr($data, $offset, 1));
            $offset += 1;

            // 解析GPS数据块(16字节)
            $gpsData = substr($data, $offset, 16);
            $offset += 16;

            // 经度:4字节小端有符号整数,转换为十进制
            $longitude = unpack('l', substr($gpsData, 0, 4))[1] / 10000000;
            // 纬度:4字节小端有符号整数,转换为十进制
            $latitude = unpack('l', substr($gpsData, 4, 4))[1] / 10000000;
            // 卫星数:1字节
            $satellites = ord(substr($gpsData, 8, 1));

            echo "=== AVL记录 $i ===\n";
            echo "时间戳(毫秒): $timestamp\n";
            echo "优先级: $priority\n";
            echo "经度: $longitude\n";
            echo "纬度: $latitude\n";
            echo "卫星数: $satellites\n";
        }

        // 验证结束标记并回复确认
        $endMarker = ord(substr($data, $offset, 1));
        if ($endMarker === $recordCount) {
            socket_write($clientSocket, pack('C', $recordCount), 1);
            echo "AVL数据解析完成,已回复确认\n";
        } else {
            echo "AVL数据包校验失败\n";
        }
    }
}
?>

关键注意事项

  1. 设备配置:确保FMB140的服务器设置为TCP协议,端口与代码一致,数据格式选择Teltonika AVL(不要选JSON、NMEA等其他格式)。
  2. 字节序匹配:PHP的unpack函数中,v对应小端16位无符号、V对应小端32位无符号、P对应小端64位无符号、l对应小端32位有符号,必须严格对应协议要求。
  3. 缓冲区大小:AVL数据包可能超过1024字节,需将socket_read的缓冲区设置为足够大的值(如4096)。
  4. 异常处理:实际部署时需添加数据包长度不匹配、解析失败等场景的异常捕获逻辑,避免服务崩溃。

参考建议

  • 直接查阅Teltonika官方AVL协议文档(设备手册中可获取),里面有完整的二进制结构定义。
  • 可封装pack/unpack相关操作成工具类,方便后续项目复用。

内容的提问来源于stack exchange,提问作者Ardita

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:24:56