Skip to content

Instantly share code, notes, and snippets.

@ashmigelski
Last active March 23, 2023 11:51
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • 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))
@ashmigelski
Copy link
Author

To test parser run $ python parser.py
Or run $ python server.py on your server and create Wialon Retranslator sending to your server:port

@betomaxxx
Copy link

hello, the code supports multiple tcp incoming connections?

@ashmigelski
Copy link
Author

Hi
This code works with single incoming TCP connection

@DiegoArrietar
Copy link

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?

@bizkit1
Copy link

bizkit1 commented Apr 19, 2016

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

@ashmigelski
Copy link
Author

@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

@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