JTT808协议解析数据相关问题 python

前言

由于公司业务需要对接某公司gps终端设备的定位信息,所以我走入了jtt808协议的大坑。而由于对这方面东西的不了解,导致我两眼一抹黑,完全不知道怎么办了。
也许你和我一样是一只新手,看了看jtt808协议之后还是觉得一个头有两个大,一无所获。那么,这篇文章也许可以帮到你。

什么是jtt808协议?

协议原文应该很好找到,我这里就不赘述了,我就简单说一下我遇到的问题。

关于通信方式

协议说:

“本协议采用的通信方式应符合JT/T 808 中的相关规定。通信协议采用TCP或UDP,平台作为服务器端,终端作为客户端。当数据通信链路异常时,终端可以采用SMS消息方式进行通信。”

我一看,这就是什么都没说嘛,通过这段话我只知道TCP或者UDP都能传,还有一种SMS消息的通信方式。具体到底是用的什么传的呢?抱歉,这个问题我跑去问我的合作公司,他们并没有告诉我,只是简单粗暴地让我提供一个ip地址和一个端口给他。于是我还是一头雾水。实际上一般的传输方法是TCP长连接,就是socket传输
对于python来说,可以使用asyncore这个库来实现。具体可以参照git上的这个项目:https://github.com/land-pack/jtt808
这个项目还有一个说明文档,点击how it work就能看到,但大部分人都忽视了,所以我把说明文档也列出来
https://blog.csdn.net/u011767611/article/details/50497709

关于传输的到底是什么玩意儿

这个问题是困扰了我最久的问题。看了协议之后我脑子里真的是一片空白,只知道终端传过来的玩意儿是一串的字节,相当于是二进制流那种东西。事实证明也确实是如此,我用上述的开源系统接收了对方公司发过来的数据,print出来的就是一串看不懂的乱码,就是二进制流。
我就觉得对方公司发过来的应该就是二进制流,然后我就完全不知道该怎么办了。
其实我走到了一个误区,虽然人家发过来的是二进制流,但是人家终端机器不可能直接写一堆0101什么的玩意儿就给平台发,人家的终端肯定也是有程序的,肯定不可能会直接写二进制流。
这个问题真的是困扰了我蛮久,我试图在网上找到终端机器发送数据的范例,完全没有头绪。直到某一天我看到了这个问题:https://bbs.csdn.net/topics/390852557 我才勉强算是找到了一个实例。当然也是算我蠢,其实git上的那个jtt808的包里面有一个测试的terminal.py,里面就有一个实例数据。
终端发送的数据是ascii码的字符串,只是通过binascii.unhexlify这个东西把ascii字符串转成了二进制流,想要还原终端发送的东西,把接收到的数据binascii.b2a_hex一下就行了。

git上的jtt808项目的bug

我由于自己比较懒,就没有动手写新的解析程序,直接用的git上的那个,但是这个程序有两个比较大的bug,第一个bug是接收不到发送过来的数据的bug。这个bug是由于EchoServer里面的handle_accept调用EchoHandler有问题(原文为handler = EchoHandler(sock)),加个self就行。修改过后的代码如下

import asyncore
import socket
import sys
import signal

import binascii

sys.path.append("..")
from core.dispatch import Dispatch
from visual.visual_decorator import info
from conf.settings import IP, PORT
from process_signal.payload import hello


class Adapt:
    """
    Just for make it's adapt suck_block_mode.py & conn.sendall()
    """

    def __init__(self, send_desc):
        self.sendall = send_desc  # For Adapt socket sendall() method


class EchoHandler(asyncore.dispatcher_with_send):
    data_len = 0

    def handle_read(self):
        data = self.recv(8192)
        self.data_len = len(data)
        print 'origin data: ' + binascii.b2a_hex(data)
        if data:
            conn = Adapt(self.send)  # Now your conn have method sendall()
            Dispatch(data, conn)


class EchoServer(asyncore.dispatcher):
    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)
        self.total_recv = 0
        self.current = 0
        self.counter = 0

    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            sock, addr = pair
            info_tips = 'Incoming connection from ' + repr(addr)
            info(info_tips)
            self.handler = EchoHandler(sock)
            self.current = self.handler.data_len
            self.total_recv += self.current
            self.counter += 1


if __name__ == '__main__':
    server = EchoServer(IP, PORT)
    signal.signal(signal.SIGTSTP, hello)
    asyncore.loop()

这个项目还有另外一个很严重的bug,就是有时候经纬度解析不正确,会出现那种一看不是我国座标的座标。经过我艰苦卓绝的努力排查,终于发现原来是tools.py里面的to_dword有问题,修改后如下。

def to_dword(val):
    """
    :param val: a tuple (2, 110, 226, 147)
    :return:40821395 but what we need is range(38.0000 ~ 42.00000)
    """
    temp_hex = []
    for item in val:
        temp_hex.append(hex(item))
    temp_str = ''
    for item in temp_hex:
        new_str = str(item).replace('0x', '')
        if len(new_str) < 2:
            new_str = '0' + new_str
        temp_str += new_str
    result = int(temp_str, 16)
    return result

还有一个问题,它没有提供设备号的解析。我的解决办法是直接接收后还原为ascii字符串,然后截取其中设备号的那一段直接作为设备号。毕竟设备号用的是BCD8421码,我觉得把二进制流转成8421码的数字太麻烦了,就没做。

结尾

发现的问题就这些,还有问题的小伙伴可以留言问我。但是由于我们公司只需要位置数据,所以其他类型的消息我也没接触过,经验有限,不一定能帮到你。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章