由于公司业务需要对接某公司gps终端设备的定位信息,所以我走入了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上的那个,但是这个程序有两个比较大的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码的数字太麻烦了,就没做。
发现的问题就这些,还有问题的小伙伴可以留言问我。但是由于我们公司只需要位置数据,所以其他类型的消息我也没接触过,经验有限,不一定能帮到你。