-
-
Save ashmigelski/21cdcb369f55444c4f26 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*- | |
import binascii | |
import struct | |
def parse(fmt, binary, offset=0): | |
''' | |
Unpack the string | |
fmt @see https://docs.python.org/2/library/struct.html#format-strings | |
value value to be formated | |
offset offset in bytes from begining | |
''' | |
parsed = struct.unpack_from(fmt, binary, offset) | |
return parsed[0] if len(parsed) == 1 else parsed | |
def parsePacket(packet): | |
''' | |
Parse Wialon Retranslator v1.0 packet w/o first 4 bytes (packet size) | |
''' | |
# parsed message | |
msg = { | |
'id': 0, | |
'time': 0, | |
'flags': 0, | |
'params': {}, | |
'blocks': [] | |
} | |
# parse packet info | |
controller_id_size = packet.find('\x00') | |
(msg['id'], msg['time'], msg['flags']) = parse('> %ds x i i' % (controller_id_size), packet) | |
# get data block | |
data_blocks = packet[controller_id_size + 1 + 4 + 4:] | |
while len(data_blocks): | |
# name offset in data block | |
offset = 2 + 4 + 1 + 1 | |
name_size = data_blocks.find('\x00', offset) - offset | |
(block_type, block_length, visible, data_type, name) = parse('> h i b b %ds' % (name_size), data_blocks) | |
# constuct block info | |
block = { | |
'type': block_type, | |
'length': block_length, | |
'visibility': visible, | |
'data_type': data_type, | |
'name': name | |
} | |
# get block data | |
block['data_block'] = data_blocks[offset + name_size + 1:block_length * 1 + 6] | |
v = '' | |
if data_type == 1: | |
# text | |
# TODO | |
pass | |
if data_type == 2: | |
# binary | |
if name == 'posinfo': | |
v = {'lat': 0, 'lon': 0, 'h': 0, 's': 0, 'c': 0, 'sc': 0} | |
(v['lon'], v['lat'], v['h']) = parse('d d d', block['data_block']) | |
(v['s'], v['c'], v['sc']) = parse('> h h b', block['data_block'], 24) | |
elif data_type == 3: | |
# integer | |
v = parse('> i', block['data_block']) | |
elif data_type == 4: | |
# float | |
v = parse('d', block['data_block']) | |
elif data_type == 5: | |
# long | |
v = parse('> q', block['data_block']) | |
# add param to message | |
msg['params'][name] = v | |
# data blocks parse information | |
msg['blocks'].append(block) | |
# delete parsed info | |
data_blocks = data_blocks[block_length + 6:] | |
return msg | |
if __name__ == '__main__': | |
# test data | |
data = [ | |
'333533393736303133343435343835004B0BFB70000000030BBB000000270102706F73696E666F00A027AFDF5D9848403AC7253383DD4B400000000000805A40003601460B0BBB0000001200047077725F657874002B8716D9CE973B400BBB00000011010361766C5F696E707574730000000001', | |
'73686d6900552d3f49000000070bbb000000270102706f73696e666f001f090e42538d3b40af8c20a82dfc4a400000000000000000006c0109ff0bbb0000000f000461646331000000000000ca21400bbb00000011010361766c5f696e7075747300000000240bbb00000012010361766c5f6f7574707574730000000037' | |
] | |
for i in range(len(data)): | |
print '\nParse packet: %s\n' % data[i] | |
print parsePacket(binascii.unhexlify(data[i])) |
# -*- coding: utf-8 -*- | |
import binascii | |
import socket | |
from parser import parse, parsePacket | |
CONNECTION = (socket.gethostname(), 12374) | |
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
# prevent 'ERROR: Address already in use' | |
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
server.bind((socket.gethostname(), 12374)) | |
server.listen(1) | |
print "Listen {0} on {1}".format(*CONNECTION) | |
# Accept connections | |
sck, addr = server.accept() | |
print "Connected {0}:{1}".format(*addr) | |
# packet queue | |
queue = '' | |
while 1: | |
data = sck.recv(1024) | |
if not data: | |
break | |
# append to queue | |
queue = queue + data | |
# get first packet size | |
packet_size = parse('<i', queue) | |
if packet_size + 4 <= len(queue): | |
# get packet | |
packet = queue[4:packet_size + 4] | |
# print binascii.hexlify(packet) | |
print parsePacket(packet) | |
# remove packet from queue | |
queue = queue[packet_size + 4:] | |
# packet was received successfully | |
sck.send(str(0x11)) |
hello, the code supports multiple tcp incoming connections?
Hi
This code works with single incoming TCP connection
Hi.
i have both files running in pycharm, but where is the information of the packets, i just run the files o i need to do something else? what is the best protocol to send the information, from wialon in the platform?
Hi there. WHen running server.py i get: ImportError: cannot import name parce. Both files are in same folder. Any ideea?
Update: it worked after renaming parse.py into something else
@DiegoArrietar
This sample uses Wialon Retranslator v1.0, this protocol is one of the best for data retransmission from Wialon to other server
You can find information about packets in docs http://extapi.wialon.com/hw/cfg/WialonRetranslator%201.0_en.pdf
Example from doc http://extapi.wialon.com/hw/cfg/WialonRetranslator%201.0_en.pdf:
74000000333533393736303133343435343835004B0BFB70000000030BBB000000270102706F73696E6
66F00A027AFDF5D9848403AC7253383DD4B400000000000805A40003601460B0BBB000000120004707
7725F657874002B8716D9CE973B400BBB00000011010361766C5F696E707574730000000001
But the null byte starts with 2 and parser not work
Profit :)
-controller_id_size = packet.find('\x00')
+controller_id_size = packet.find('\x00', 8)
Hi, I already started the server.py but I only have the message listen on 12374 port. But the screen have not shown anything else. Do I have to do something else or print something to look the packages. thanks.
The code works well. I had to open the port of centos. You could get the open ports in your linux with netstat -tulpn so you could look the ports. And you need to search in the firewall with the command. iptables -S. You need to have a rule to listen from that port.
Thanks for your code.
Hi Aleksey, thank you for this parsePacket function, I have found it very useful!
I noticed on some test data from a wialon local retranslator, that the latitude appeared to be in the 'lon' value and the longitude in the 'lat' value.
If I parse the following sample data using this function:
'333533393736303133343435343835004B0BFB70000000030BBB000000270102706F73696E666F00A027AFDF5D9848403AC7253383DD4B400000000000805A40003601460B0BBB0000001200047077725F657874002B8716D9CE973B400BBB00000011010361766C5F696E707574730000000001'
it yields: 'posinfo': {'c': 326, 'h': 106.0, 'lon': 55.7305664, 'sc': 11, 's': 54, 'lat': 49.1903648}
which locates to here: https://www.google.com/maps/search/+49.1903648+55.7305664
Is this correct? Or is it supposed to be here:
https://www.google.com/maps/search/+55.7305664+49.1903648
I modified lines 66-67 to the following, which appears to resolve for me:
v = {'lon': 0, 'lat': 0, 'h': 0, 's': 0, 'c': 0, 'sc': 0}
(v['lon'], v['lat'], v['h']) = parse('d d d', block['data_block'])
Could this be a mistake or could there be some other reason for this being jumbled up in my environment?
Hey @bshaw-au
WOW! What a bug) So many years it was in this code!
In spec 'posinfo' binary block has structure (lon-lat)
(bytes) Type Field description
8 Fractional value Lon - longitude
8 Fractional value Lat - latitude
So you're right, line 67 must be like this
-(v['lat'], v['lon'], v['h']) = parse('d d d', block['data_block'])
+(v['lon'], v['lat'], v['h']) = parse('d d d', block['data_block'])
I already updated code
Thanks for reporting!
To test parser run
$ python parser.py
Or run
$ python server.py
on your server and create Wialon Retranslator sending to yourserver:port