Skip to content

Commit 76836f4

Browse files
committed
Implement measurement reception, define property encoding functions
1 parent 6a4ac32 commit 76836f4

File tree

10 files changed

+276
-67
lines changed

10 files changed

+276
-67
lines changed

zbnt/AxiDevice.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
class AxiDevice:
2222
_property_encoding = dict()
23+
_property_params = dict()
2324

2425
def __init__(self, parent, dev_id, initial_props):
2526
self.id = dev_id
@@ -39,33 +40,55 @@ def receive_measurement(self, data):
3940
if self.measurement_handler != None:
4041
self.measurement_handler(self.id, data)
4142

42-
async def set_property(self, prop_id, value):
43+
async def set_property(self, prop_id, value, params=dict()):
4344
prop_encoding = self._property_encoding.get(prop_id, None)
45+
_, prop_params = self._property_params.get(prop_id, (0, []))
46+
value_bytes = b""
4447

4548
if prop_encoding == None:
46-
raise Exception("Property {0} is invalid for {1}".format(prop_id, self.__class__.__name__))
49+
raise ValueError("Property {0} is invalid for {1}".format(prop_id, self.__class__.__name__))
4750

4851
encoder, _ = prop_encoding
4952

5053
if encoder == None:
51-
raise Exception("Property " + str(prop_id) + " is read-only")
54+
raise ValueError("Property {0} is read-only".format(prop_id))
5255

53-
return await self.client.set_raw_property(self.id, prop_id, encoder(value))
56+
for param_name, param_encoder in prop_params:
57+
param_value = params.get(param_name, None)
5458

55-
async def get_property(self, prop_id):
59+
if param_value == None:
60+
raise ValueError("Missing parameter: {0}".format(param_name))
61+
62+
value_bytes += param_encoder(param_value)
63+
64+
value_bytes += encoder(value)
65+
66+
return await self.client.set_raw_property(self.id, prop_id, value_bytes)
67+
68+
async def get_property(self, prop_id, params=dict()):
5669
prop_encoding = self._property_encoding.get(prop_id, None)
70+
params_size, prop_params = self._property_params.get(prop_id, (0, []))
71+
param_bytes = b""
5772

5873
if prop_encoding == None:
59-
raise Exception("Property {0} is invalid for {1}".format(prop_id, self.__class__.__name__))
74+
raise ValueError("Property {0} is invalid for {1}".format(prop_id, self.__class__.__name__))
6075

6176
_, decoder = prop_encoding
6277

6378
if decoder == None:
64-
raise Exception("Property " + str(prop_id) + " is write-only")
79+
raise ValueError("Property {0} is write-only".format(prop_id))
80+
81+
for param_name, param_encoder in prop_params:
82+
param_value = params.get(param_name, None)
83+
84+
if param_value == None:
85+
raise ValueError("Missing parameter: {0}".format(param_name))
86+
87+
param_bytes += param_encoder(param_value)
6588

66-
success, value = await self.client.get_raw_property(self.id, prop_id)
89+
success, value = await self.client.get_raw_property(self.id, prop_id, param_bytes)
6790

6891
if not success:
6992
return (False, None)
7093

71-
return (success, decoder(value))
94+
return (success, decoder(value[params_size:]))

zbnt/AxiMdio.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@
2020
from .AxiDevice import *
2121

2222
class AxiMdio(AxiDevice):
23-
def __init__(self, dev_id, initial_props):
24-
super().__init__(dev_id, initial_props)
23+
def __init__(self, parent, dev_id, initial_props):
24+
super().__init__(parent, dev_id, initial_props)
2525

2626
for prop_id, prop_bytes in initial_props:
2727
if prop_id == Properties.PROP_PHY_ADDR:
2828
self.phys = list(prop_bytes)
29-
30-
def receive_measurement(self, data):
31-
pass

zbnt/Encoding.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ def encode_s32(value):
4545
def encode_s64(value):
4646
return value.to_bytes(8, byteorder="little", signed=True)
4747

48+
def encode_str(value):
49+
return value.encode("UTF-8")
50+
51+
def encode_mac(value):
52+
return bytes.fromhex(value.replace(":", "").replace(" ", ""))
53+
54+
def encode_ip4(value):
55+
return b"".join(map(lambda x: encode_u8(int(x)), value.split(".")[::-1]))
56+
4857
# decode_x : bytes to number
4958

5059
def decode_bool(value):
@@ -73,3 +82,13 @@ def decode_s32(value):
7382

7483
def decode_s64(value):
7584
return int.from_bytes(value[:8], byteorder="little", signed=True)
85+
86+
def decode_str(value):
87+
return value.encode("UTF-8")
88+
89+
def decode_mac(value):
90+
hex_mac = value.hex().upper()
91+
return ":".join([hex_mac[i:i+1] for i in range(0, len(hex_mac), 2)])
92+
93+
def decode_ip4(value):
94+
return "{0}.{1}.{2}.{3}".format(value[3], value[2], value[1], value[0])

zbnt/FrameDetector.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,78 @@
1919
from enum import IntFlag
2020

2121
from .Enums import *
22+
from .Encoding import *
2223
from .AxiDevice import *
2324

2425
class FrameDetector(AxiDevice):
26+
_property_encoding = {
27+
Properties.PROP_ENABLE: (encode_bool, decode_bool),
28+
Properties.PROP_ENABLE_LOG: (encode_bool, decode_bool),
29+
Properties.PROP_ENABLE_SCRIPT: (encode_u32, decode_u32),
30+
Properties.PROP_OVERFLOW_COUNT: (None, lambda x: (decode_u64(x[:8]), decode_u64(x[8:16]))),
31+
Properties.PROP_FRAME_SCRIPT: (lambda x: x, lambda x: x),
32+
Properties.PROP_FRAME_SCRIPT_NAME: (encode_str, decode_str)
33+
}
34+
35+
_property_params = {
36+
Properties.PROP_FRAME_SCRIPT: (4, [
37+
("index", encode_u32)
38+
]),
39+
40+
Properties.PROP_FRAME_SCRIPT_NAME: (4, [
41+
("index", encode_u32)
42+
])
43+
}
44+
2545
class FeatureBits(IntFlag):
2646
HAS_CMP_UNIT = 1
2747
HAS_EDIT_UNIT = 2
2848
HAS_CSUM_UNIT = 4
2949
HAS_FPU = 8
3050

31-
def __init__(self, dev_id, initial_props):
32-
super().__init__(dev_id, initial_props)
51+
def __init__(self, parent, dev_id, initial_props):
52+
super().__init__(parent, dev_id, initial_props)
3353

3454
self.features = FrameDetector.FeatureBits(0)
3555

3656
for prop_id, prop_bytes in initial_props:
3757
if prop_id == Properties.PROP_FEATURE_BITS:
3858
if len(prop_bytes) >= 4:
39-
self.features = FrameDetector.FeatureBits(int.from_bytes(prop_bytes[:4], byteorder="little", signed=False))
59+
self.features = FrameDetector.FeatureBits(decode_u32(prop_bytes))
4060
elif prop_id == Properties.PROP_NUM_SCRIPTS:
4161
if len(prop_bytes) >= 4:
42-
self.num_scripts = int.from_bytes(prop_bytes[:4], byteorder="little", signed=False)
62+
self.num_scripts = decode_u32(prop_bytes)
4363
elif prop_id == Properties.PROP_MAX_SCRIPT_SIZE:
4464
if len(prop_bytes) >= 4:
45-
self.max_script_size = int.from_bytes(prop_bytes[:4], byteorder="little", signed=False)
65+
self.max_script_size = decode_u32(prop_bytes)
4666
elif prop_id == Properties.PROP_FIFO_SIZE:
4767
if len(prop_bytes) >= 8:
48-
self.tx_fifo_size = int.from_bytes(prop_bytes[:4], byteorder="little", signed=False)
49-
self.extr_fifo_size = int.from_bytes(prop_bytes[4:8], byteorder="little", signed=False)
68+
self.tx_fifo_size = decode_u32(prop_bytes[:4])
69+
self.extr_fifo_size = decode_u32(prop_bytes[4:8])
5070

5171
def receive_measurement(self, data):
52-
pass
72+
if len(data) < 14:
73+
return
74+
75+
if self.measurement_handler == None:
76+
return
77+
78+
time = decode_u64(data[0:8])
79+
match_dir = data[8] - 65
80+
log_width = data[9]
81+
ext_count = decode_u16(data[10:12])
82+
match_mask = decode_u16(data[12:14])
83+
ext_offset = ((log_width + 13) // log_width) * log_width
84+
85+
if match_dir < 0 or match_dir > 1:
86+
return
87+
88+
if ext_count > len(data) - ext_offset:
89+
return
90+
91+
ext_data = data[ext_offset:]
92+
93+
if ext_count % log_width != 0:
94+
ext_data = ext_data[:-log_width] + ext_data[-log_width + ext_count % log_width:]
95+
96+
self.measurement_handler(self.id, (time, match_dir, match_mask, ext_data))

zbnt/LatencyMeasurer.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,52 @@
1717
"""
1818

1919
from .Enums import *
20+
from .Encoding import *
2021
from .AxiDevice import *
2122

2223
class LatencyMeasurer(AxiDevice):
23-
def __init__(self, dev_id, initial_props):
24-
super().__init__(dev_id, initial_props)
24+
_property_encoding = {
25+
Properties.PROP_ENABLE: (encode_bool, decode_bool),
26+
Properties.PROP_ENABLE_LOG: (encode_bool, decode_bool),
27+
Properties.PROP_ENABLE_BROADCAST: (encode_bool, decode_bool),
28+
Properties.PROP_MAC_ADDR: (encode_mac, decode_mac),
29+
Properties.PROP_IP_ADDR: (encode_ip4, decode_ip4),
30+
Properties.PROP_FRAME_PADDING: (encode_u16, decode_u16),
31+
Properties.PROP_FRAME_GAP: (encode_u32, decode_u32),
32+
Properties.PROP_TIMEOUT: (encode_u32, decode_u32),
33+
Properties.PROP_OVERFLOW_COUNT: (None, decode_u64)
34+
}
35+
36+
_property_params = {
37+
Properties.PROP_MAC_ADDR: (1, [
38+
("index", encode_u8)
39+
]),
40+
41+
Properties.PROP_IP_ADDR: (1, [
42+
("index", encode_u8)
43+
])
44+
}
45+
46+
def __init__(self, parent, dev_id, initial_props):
47+
print(self._property_encoding)
48+
print(LatencyMeasurer._property_encoding)
49+
50+
super().__init__(parent, dev_id, initial_props)
2551

2652
def receive_measurement(self, data):
27-
pass
53+
if len(data) < 40:
54+
return
55+
56+
if self.measurement_handler == None:
57+
return
58+
59+
time = decode_u64(data[0:8])
60+
61+
last_ping = decode_u32(data[8:12])
62+
last_pong = decode_u32(data[12:16])
63+
64+
num_ping_pongs = decode_u64(data[16:24])
65+
num_lost_pings = decode_u64(data[24:32])
66+
num_lost_pongs = decode_u64(data[32:40])
67+
68+
self.measurement_handler(self.id, (time, last_ping, last_pong, num_ping_pongs, num_lost_pings, num_lost_pongs))

zbnt/MessageReceiver.py

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
along with this program. If not, see <https://www.gnu.org/licenses/>.
1717
"""
1818

19-
import socket
2019
import asyncio
21-
import netifaces
2220

2321
from enum import Enum, auto
2422

2523
from .Enums import *
24+
from .Encoding import *
2625

2726
class MsgRxStatus(Enum):
2827
MSG_RX_MAGIC = auto()
@@ -33,14 +32,7 @@ class MsgRxStatus(Enum):
3332
class MessageReceiver(asyncio.Protocol):
3433
MSG_MAGIC_IDENTIFIER = b"\xFFZB\x02"
3534

36-
def __init__(self, on_connection_made, on_message, on_error):
37-
loop = asyncio.get_running_loop()
38-
39-
self.on_connection_lost = loop.create_future()
40-
self.on_connection_made = on_connection_made
41-
self.on_message = on_message
42-
self.on_error = on_error
43-
35+
def __init__(self):
4436
self.status = MsgRxStatus.MSG_RX_MAGIC
4537
self.buffer = b"\x00\x00\x00\x00"
4638
self.count = 0
@@ -50,9 +42,6 @@ def __init__(self, on_connection_made, on_message, on_error):
5042
def connection_made(self, transport):
5143
self.transport = transport
5244

53-
if self.on_connection_made != None:
54-
self.on_connection_made()
55-
5645
def bytes_received(self, data):
5746
for b in data:
5847
b = b.to_bytes(1, "little")
@@ -69,17 +58,15 @@ def bytes_received(self, data):
6958
self.buffer += b
7059

7160
if len(self.buffer) == 4:
72-
self.id = int.from_bytes(self.buffer[0:2], byteorder='little', signed=False)
73-
self.size = int.from_bytes(self.buffer[2:4], byteorder='little', signed=False)
61+
self.id = decode_u16(self.buffer[0:2])
62+
self.size = decode_u16(self.buffer[2:4])
7463
self.buffer = b""
7564

7665
if self.id == Messages.MSG_ID_EXTENDED:
7766
self.status = MsgRxStatus.MSG_RX_EXTENDED_HEADER
7867
self.id = self.size
7968
elif self.size == 0:
80-
if self.on_message != None:
81-
self.on_message(self.id, self.buffer)
82-
69+
self.message_received(self.id, self.buffer)
8370
self.status = MsgRxStatus.MSG_RX_MAGIC
8471
self.buffer = b"\x00\x00\x00\x00"
8572
else:
@@ -94,30 +81,29 @@ def bytes_received(self, data):
9481
self.buffer += b
9582

9683
if len(self.buffer) == 4:
97-
self.size = int.from_bytes(self.buffer, byteorder='little', signed=False)
84+
self.size = decode_u32(self.buffer)
9885
self.buffer = b""
9986

10087
if self.size == 0:
10188
self.status = MsgRxStatus.MSG_RX_DATA
10289
else:
103-
if self.on_message != None:
104-
self.on_message(self.id, self.buffer)
105-
90+
self.message_received(self.id, self.buffer)
10691
self.status = MsgRxStatus.MSG_RX_MAGIC
10792
self.buffer = b"\x00\x00\x00\x00"
10893
else:
10994
self.buffer += b
11095

11196
if len(self.buffer) == self.size:
112-
if self.on_message != None:
113-
self.on_message(self.id, self.buffer)
114-
97+
self.message_received(self.id, self.buffer)
11598
self.status = MsgRxStatus.MSG_RX_MAGIC
11699
self.buffer = b"\x00\x00\x00\x00"
117100

118-
def error_received(self, exc):
119-
if self.on_error != None:
120-
self.on_error(exc)
101+
def message_received(self, msg_id, msg_payload):
102+
pass
103+
104+
def error_received(self, err):
105+
raise ConnectionError(err)
121106

122107
def connection_lost(self, exc):
123-
self.on_connection_lost.set_result(True)
108+
if exc != None:
109+
raise ConnectionError(exc)

zbnt/SimpleTimer.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,18 @@
1717
"""
1818

1919
from .Enums import *
20+
from .Encoding import *
2021
from .AxiDevice import *
2122

2223
class SimpleTimer(AxiDevice):
23-
def __init__(self, dev_id, initial_props):
24-
super().__init__(dev_id, initial_props)
24+
_property_encoding = {
25+
Properties.PROP_ENABLE: (encode_bool, decode_bool),
26+
Properties.PROP_TIMER_TIME: (None, decode_u64),
27+
Properties.PROP_TIMER_LIMIT: (encode_u64, decode_u64)
28+
}
29+
30+
def __init__(self, parent, dev_id, initial_props):
31+
super().__init__(parent, dev_id, initial_props)
2532

2633
self.freq = 125000000
2734

@@ -30,5 +37,5 @@ def __init__(self, dev_id, initial_props):
3037
if len(prop_bytes) >= 4:
3138
self.freq = int.from_bytes(prop_bytes[:4], byteorder="little", signed=False)
3239

33-
def receive_measurement(self, data):
34-
pass
40+
def __repr__(self):
41+
return "zbnt.{0}(dev_id={1}, freq={2})".format(self.__class__.__name__, self.id, self.freq)

0 commit comments

Comments
 (0)