前言
由於公司業務需要對接某公司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碼的數字太麻煩了,就沒做。
結尾
發現的問題就這些,還有問題的小夥伴可以留言問我。但是由於我們公司只需要位置數據,所以其他類型的消息我也沒接觸過,經驗有限,不一定能幫到你。