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碼的數字太麻煩了,就沒做。

結尾

發現的問題就這些,還有問題的小夥伴可以留言問我。但是由於我們公司只需要位置數據,所以其他類型的消息我也沒接觸過,經驗有限,不一定能幫到你。

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