Skip to content

Instantly share code, notes, and snippets.

@ashmigelski
Last active March 23, 2023 11:51
Show Gist options
  • Save ashmigelski/21cdcb369f55444c4f26 to your computer and use it in GitHub Desktop.
Save ashmigelski/21cdcb369f55444c4f26 to your computer and use it in GitHub Desktop.
Python server for Wialon Retranslator 1.0
# -*- 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))
@4khobta
Copy link

4khobta commented Aug 20, 2017

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)

@jddgn2000
Copy link

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.

@jddgn2000
Copy link

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.

@bshaw-au
Copy link

bshaw-au commented Jun 13, 2019

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?

@ashmigelski
Copy link
Author

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment