-
-
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)) |
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!
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.