implemented unit look-up and improved payload helpers
This commit is contained in:
@@ -1,38 +1,61 @@
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
class PayloadBuilder:
|
class PayloadBuilder:
|
||||||
|
"""
|
||||||
|
Utility for building binary payloads
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, endian='little'):
|
def __init__(self, endian='little'):
|
||||||
self.buf = bytearray()
|
self.buf = bytearray()
|
||||||
self.endian = endian
|
self.endian = endian
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
""" Get the byte buffer """
|
||||||
return self.buf
|
return self.buf
|
||||||
|
|
||||||
def u8(self, num):
|
def u8(self, num):
|
||||||
|
""" Add a uint8_t """
|
||||||
self.buf.extend(num.to_bytes(length=1, byteorder=self.endian, signed=False))
|
self.buf.extend(num.to_bytes(length=1, byteorder=self.endian, signed=False))
|
||||||
|
|
||||||
def u16(self, num):
|
def u16(self, num):
|
||||||
|
""" Add a uint16_t """
|
||||||
self.buf.extend(num.to_bytes(length=2, byteorder=self.endian, signed=False))
|
self.buf.extend(num.to_bytes(length=2, byteorder=self.endian, signed=False))
|
||||||
|
|
||||||
def u32(self, num):
|
def u32(self, num):
|
||||||
|
""" Add a uint32_t """
|
||||||
self.buf.extend(num.to_bytes(length=4, byteorder=self.endian, signed=False))
|
self.buf.extend(num.to_bytes(length=4, byteorder=self.endian, signed=False))
|
||||||
|
|
||||||
def i8(self, num):
|
def i8(self, num):
|
||||||
|
""" Add a int8_t """
|
||||||
self.buf.extend(num.to_bytes(length=1, byteorder=self.endian, signed=True))
|
self.buf.extend(num.to_bytes(length=1, byteorder=self.endian, signed=True))
|
||||||
|
|
||||||
def i16(self, num):
|
def i16(self, num):
|
||||||
|
""" Add a int16_t """
|
||||||
self.buf.extend(num.to_bytes(length=2, byteorder=self.endian, signed=True))
|
self.buf.extend(num.to_bytes(length=2, byteorder=self.endian, signed=True))
|
||||||
|
|
||||||
def i32(self, num):
|
def i32(self, num):
|
||||||
|
""" Add a int32_t """
|
||||||
self.buf.extend(num.to_bytes(length=4, byteorder=self.endian, signed=True))
|
self.buf.extend(num.to_bytes(length=4, byteorder=self.endian, signed=True))
|
||||||
|
|
||||||
def float(self, num):
|
def float(self, num):
|
||||||
|
""" Add a float (4 bytes) """
|
||||||
fmt = '<f' if self.endian == 'little' else '>f'
|
fmt = '<f' if self.endian == 'little' else '>f'
|
||||||
self.buf.extend(struct.pack(fmt, num))
|
self.buf.extend(struct.pack(fmt, num))
|
||||||
|
|
||||||
|
def double(self, num):
|
||||||
|
""" Add a double (8 bytes) """
|
||||||
|
fmt = '<d' if self.endian == 'little' else '>d'
|
||||||
|
self.buf.extend(struct.pack(fmt, num))
|
||||||
|
|
||||||
def bool(self, num):
|
def bool(self, num):
|
||||||
|
""" Add a bool (0 or 1) """
|
||||||
self.buf.append(1 if num != False else 0)
|
self.buf.append(1 if num != False else 0)
|
||||||
|
|
||||||
def str(self, string):
|
def str(self, string):
|
||||||
|
""" Add a 0-terminated string """
|
||||||
self.buf.extend(string.encode('utf-8'))
|
self.buf.extend(string.encode('utf-8'))
|
||||||
self.buf.append(0)
|
self.buf.append(0)
|
||||||
|
|
||||||
|
def blob(self, blob):
|
||||||
|
""" Ad an arbitrary blob (bytearray or binary string) """
|
||||||
|
self.buf.extend(blob)
|
||||||
|
|||||||
+42
-15
@@ -1,12 +1,18 @@
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
class PayloadParser:
|
class PayloadParser:
|
||||||
|
"""
|
||||||
|
Utility for parsing a binary payload
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, buf, endian='little'):
|
def __init__(self, buf, endian='little'):
|
||||||
|
""" buf - buffer to parse (bytearray or binary string) """
|
||||||
self.buf = buf
|
self.buf = buf
|
||||||
self.ptr = 0
|
self.ptr = 0
|
||||||
self.endian = endian
|
self.endian = endian
|
||||||
|
|
||||||
def slice(self, n):
|
def _slice(self, n):
|
||||||
|
""" Extract a slice and advance the read pointer for the next slice """
|
||||||
if self.ptr + n > len(self.buf):
|
if self.ptr + n > len(self.buf):
|
||||||
raise Exception("Out of bounds")
|
raise Exception("Out of bounds")
|
||||||
|
|
||||||
@@ -14,49 +20,70 @@ class PayloadParser:
|
|||||||
self.ptr += n
|
self.ptr += n
|
||||||
return slice
|
return slice
|
||||||
|
|
||||||
|
def rewind(self):
|
||||||
|
""" Reset the slice pointer to the beginning """
|
||||||
|
self.ptr = 0
|
||||||
|
|
||||||
|
def tail(self):
|
||||||
|
""" Get all remaining bytes """
|
||||||
|
return self._slice(len(self.buf) - self.ptr)
|
||||||
|
|
||||||
def u8(self):
|
def u8(self):
|
||||||
slice = self.slice(1)
|
""" Read a uint8_t """
|
||||||
|
slice = self._slice(1)
|
||||||
return int.from_bytes(slice, byteorder=self.endian, signed=False)
|
return int.from_bytes(slice, byteorder=self.endian, signed=False)
|
||||||
|
|
||||||
def u16(self):
|
def u16(self):
|
||||||
slice = self.slice(2)
|
""" Read a uint16_t """
|
||||||
|
slice = self._slice(2)
|
||||||
return int.from_bytes(slice, byteorder=self.endian, signed=False)
|
return int.from_bytes(slice, byteorder=self.endian, signed=False)
|
||||||
|
|
||||||
def u32(self):
|
def u32(self):
|
||||||
slice = self.slice(4)
|
""" Read a uint32_t """
|
||||||
|
slice = self._slice(4)
|
||||||
return int.from_bytes(slice, byteorder=self.endian, signed=False)
|
return int.from_bytes(slice, byteorder=self.endian, signed=False)
|
||||||
|
|
||||||
def i8(self):
|
def i8(self):
|
||||||
slice = self.slice(1)
|
""" Read a int8_t """
|
||||||
|
slice = self._slice(1)
|
||||||
return int.from_bytes(slice, byteorder=self.endian, signed=True)
|
return int.from_bytes(slice, byteorder=self.endian, signed=True)
|
||||||
|
|
||||||
def i16(self):
|
def i16(self):
|
||||||
slice = self.slice(2)
|
""" Read a int16_t """
|
||||||
|
slice = self._slice(2)
|
||||||
return int.from_bytes(slice, byteorder=self.endian, signed=True)
|
return int.from_bytes(slice, byteorder=self.endian, signed=True)
|
||||||
|
|
||||||
def i32(self):
|
def i32(self):
|
||||||
slice = self.slice(4)
|
""" Read a int32_t """
|
||||||
|
slice = self._slice(4)
|
||||||
return int.from_bytes(slice, byteorder=self.endian, signed=True)
|
return int.from_bytes(slice, byteorder=self.endian, signed=True)
|
||||||
|
|
||||||
def float(self):
|
def float(self):
|
||||||
slice = self.slice(4)
|
""" Read a float (4 bytes) """
|
||||||
|
slice = self._slice(4)
|
||||||
fmt = '<f' if self.endian == 'little' else '>f'
|
fmt = '<f' if self.endian == 'little' else '>f'
|
||||||
return struct.unpack(fmt, slice)[0]
|
return struct.unpack(fmt, slice)[0]
|
||||||
|
|
||||||
|
def double(self):
|
||||||
|
""" Read a double (8 bytes) """
|
||||||
|
slice = self._slice(8)
|
||||||
|
fmt = '<d' if self.endian == 'little' else '>d'
|
||||||
|
return struct.unpack(fmt, slice)[0]
|
||||||
|
|
||||||
def bool(self):
|
def bool(self):
|
||||||
return 0 != self.slice(1)[0]
|
""" Read a bool (1 byte, True if != 0) """
|
||||||
|
return 0 != self._slice(1)[0]
|
||||||
|
|
||||||
def str(self):
|
def str(self):
|
||||||
|
""" Read a zero-terminated string """
|
||||||
p = self.ptr
|
p = self.ptr
|
||||||
while p < len(self.buf) and self.buf[p] != 0:
|
while p < len(self.buf) and self.buf[p] != 0:
|
||||||
p += 1
|
p += 1
|
||||||
|
|
||||||
bs = self.slice(p - self.ptr)
|
bs = self._slice(p - self.ptr)
|
||||||
self.ptr += 1
|
self.ptr += 1
|
||||||
return bs.decode('utf-8')
|
return bs.decode('utf-8')
|
||||||
|
|
||||||
def rewind(self):
|
def blob(self, length):
|
||||||
self.ptr = 0
|
""" Read a blob of given length """
|
||||||
|
return self._slice(length)
|
||||||
def tail(self):
|
|
||||||
return self.slice(len(self.buf) - self.ptr)
|
|
||||||
|
|||||||
+40
-10
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
|
from gex.PayloadParser import PayloadParser
|
||||||
from gex.TinyFrame import TinyFrame, TF_Msg
|
from gex.TinyFrame import TinyFrame, TF_Msg
|
||||||
|
|
||||||
# General, low level
|
# General, low level
|
||||||
@@ -29,24 +30,56 @@ MSG_PERSIST_SETTINGS = 0x23 # Write current settings to Flash
|
|||||||
|
|
||||||
|
|
||||||
class Gex:
|
class Gex:
|
||||||
def __init__(self, port='/dev/ttyACM0', timeout=0.2):
|
""" GEX client """
|
||||||
|
|
||||||
|
def __init__(self, port='/dev/ttyACM0', timeout=0.3):
|
||||||
|
""" Set up the client. timeout - timeout for waiting for a response. """
|
||||||
self.port = port
|
self.port = port
|
||||||
self.serial = serial.Serial(port=port, timeout=timeout)
|
self.serial = serial.Serial(port=port, timeout=timeout)
|
||||||
self.tf = TinyFrame()
|
self.tf = TinyFrame()
|
||||||
self.tf.write = self._write
|
self.tf.write = self._write
|
||||||
|
|
||||||
# test connection
|
# test connection
|
||||||
self.query_raw(type=MSG_PING)
|
resp = self.query_raw(type=MSG_PING)
|
||||||
|
print("GEX connected, version string: %s" % resp.data.decode('utf-8'))
|
||||||
|
|
||||||
self.load_units()
|
self.load_units()
|
||||||
|
|
||||||
|
def load_units(self):
|
||||||
|
""" Load a list of unit names and callsigns for look-up """
|
||||||
|
resp = self.query_raw(type=MSG_LIST_UNITS)
|
||||||
|
pp = PayloadParser(resp.data)
|
||||||
|
count = pp.u8()
|
||||||
|
|
||||||
|
self.unit_lu = {}
|
||||||
|
|
||||||
|
for n in range(0,count):
|
||||||
|
cs = pp.u8()
|
||||||
|
name = pp.str()
|
||||||
|
type = pp.str()
|
||||||
|
|
||||||
|
print("- Found unit \"%s\" (type %s) @ callsign %d" % (name, type, cs))
|
||||||
|
self.unit_lu[name] = {
|
||||||
|
'callsign': cs,
|
||||||
|
'type': type,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_callsign(self, name, type = None):
|
||||||
|
""" Find unit by name and type """
|
||||||
|
u = self.unit_lu[name]
|
||||||
|
|
||||||
|
if type is not None:
|
||||||
|
if u['type'] != type:
|
||||||
|
raise Exception("Unit %s is not type %s (is %s)" % (name, type, u['type']))
|
||||||
|
|
||||||
|
return u['callsign']
|
||||||
|
|
||||||
def _write(self, data):
|
def _write(self, data):
|
||||||
self.serial.write(data)
|
self.serial.write(data)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def poll(self):
|
def poll(self, attempts=10):
|
||||||
attempts = 10
|
""" Read messages sent by GEX """
|
||||||
|
|
||||||
first = True
|
first = True
|
||||||
while attempts > 0:
|
while attempts > 0:
|
||||||
@@ -76,6 +109,7 @@ class Gex:
|
|||||||
self.tf.query(type=type, pld=pld, id=id, listener=listener)
|
self.tf.query(type=type, pld=pld, id=id, listener=listener)
|
||||||
|
|
||||||
def send(self, cs, cmd, id=None, pld=None, listener=None):
|
def send(self, cs, cmd, id=None, pld=None, listener=None):
|
||||||
|
""" Send a command to a unit """
|
||||||
if cs is None:
|
if cs is None:
|
||||||
return self._send(type=cmd, id=id, pld=pld, listener=listener)
|
return self._send(type=cmd, id=id, pld=pld, listener=listener)
|
||||||
|
|
||||||
@@ -103,13 +137,9 @@ class Gex:
|
|||||||
return self._theframe
|
return self._theframe
|
||||||
|
|
||||||
def query_raw(self, type, id=None, pld=None):
|
def query_raw(self, type, id=None, pld=None):
|
||||||
""" Query without addressing a unit """
|
""" Query GEX, without addressing a unit """
|
||||||
return self.query(cs=None, cmd=type, id=id, pld=pld)
|
return self.query(cs=None, cmd=type, id=id, pld=pld)
|
||||||
|
|
||||||
def send_raw(self, type, id=None, pld=None):
|
def send_raw(self, type, id=None, pld=None):
|
||||||
""" Send without addressing a unit """
|
""" Send to GEX, without addressing a unit """
|
||||||
return self.send(cs=None, cmd=type, id=id, pld=pld)
|
return self.send(cs=None, cmd=type, id=id, pld=pld)
|
||||||
|
|
||||||
def load_units(self):
|
|
||||||
resp = self.query_raw(type=MSG_LIST_UNITS)
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,13 +30,9 @@ if False:
|
|||||||
print('>',pp.bool())
|
print('>',pp.bool())
|
||||||
print('>',pp.str())
|
print('>',pp.str())
|
||||||
|
|
||||||
if False:
|
if True:
|
||||||
client = gex.Gex()
|
client = gex.Gex()
|
||||||
|
|
||||||
# Check connection
|
|
||||||
resp = client.query_raw(type=gex.MSG_PING)
|
|
||||||
print("Ping resp = ", resp.data.decode("ascii"))
|
|
||||||
|
|
||||||
# Blink a LED at call-sign 1, command 0x02 = toggle
|
# Blink a LED at call-sign 1, command 0x02 = toggle
|
||||||
for i in range(0,10):
|
for i in range(0,10):
|
||||||
client.send(cs=1, cmd=0x02)
|
client.send(cs=1, cmd=0x02)
|
||||||
|
|||||||
Reference in New Issue
Block a user