-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsensor_details.py
More file actions
145 lines (114 loc) · 5.3 KB
/
sensor_details.py
File metadata and controls
145 lines (114 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import logging
from base_enum import BaseEnum
from log_manager import LogManager
from parse_utils import ParseUtils
from value_converter import ValueConverter
from socp_opcodes import SocpOpCode
class SensorDetailsFlags(BaseEnum):
SENSOR_DETAILS_ANNUNCIATION_PRESENT = 1<<0
MAXIMUM_CALIBRATION_INTERVAL_PRESENT = 1<<1
MAXIMUM_SENSOR_LIFE_PRESENT = 1<<2
SENSOR_FLEX_PACKAGE_VERSION_PRESENT = 1<<3
SENSOR_WARM_UP_PERIOD_PRESENT = 1<<4
class SensorDetailsAnnunciation(BaseEnum):
APPROVED_TREATMENT = 1<<0
DISPOSABLE = 1<<1
CAL_FREE = 1<<2
HAS_CALIBRATION_RECOMMENDED = 1<<3
HAS_ABNORMAL_SG_INCREASE_DETECTION = 1<<4
CALIBRATION_TRANSFER_SUPPORTED = 1<<5
class SensorDetails:
def __init__(self, data: bytes, use_e2e: bool = False):
"""
:param data: The raw data
:param use_e2e: Whether to assume an E2E-CRC is included in the data
"""
self.logger = LogManager.get_logger(self.__class__.__name__)
self.data = data
self.use_e2e = use_e2e
# parsed data
self.flags: int | None = None
self.annunciation: int | None = None
self.maximum_calibration_interval: int | None = None
self.maximum_sensor_life: int | None = None
self.sensor_flex_package_version: int | None = None
self.warmup_period: int | None = None
def parse(self) -> bool:
# minimal length is the size of the mandatory fields plus, optionally,
# 2 bytes for the E2E-CRC
min_length = 2
if self.use_e2e:
min_length += 2
data = self.data
length = len(data)
if length < min_length:
self.logger.error("Packet too short: wanted at least %d bytes, got %d"
% (min_length, length))
return False
# validate E2E-CRC
if self.use_e2e:
if not ValueConverter.check_crc(data):
self.logger.error("E2E-CRC mismatch")
return False
# snip CRC
data = data[:-2]
# mandatory fields
opcode, data = ParseUtils.consume_u8(data)
self.flags, data = ParseUtils.consume_u8(data)
expected_opcode = SocpOpCode.SENSOR_DETAILS_RESPONSE
if opcode != expected_opcode:
self.logger.error("Wrong response opcode: 0x%02x, wanted 0x%02x"
% (opcode, expected_opcode))
return False
# Annunciation (optional)
if self.flags & SensorDetailsFlags.SENSOR_DETAILS_ANNUNCIATION_PRESENT:
self.annunciation, data = ParseUtils.consume_u16(data)
# Maximum Calibration Interval (optional)
if self.flags & SensorDetailsFlags.MAXIMUM_CALIBRATION_INTERVAL_PRESENT:
self.maximum_calibration_interval, data = ParseUtils.consume_u16(data)
# Maximum Sensor Life (optional)
if self.flags & SensorDetailsFlags.MAXIMUM_SENSOR_LIFE_PRESENT:
self.maximum_sensor_life, data = ParseUtils.consume_u16(data)
# Sensor Flex Package Version (optional)
if self.flags & SensorDetailsFlags.SENSOR_FLEX_PACKAGE_VERSION_PRESENT:
self.sensor_flex_package_version, data = ParseUtils.consume_u16(data)
# Warm-Up Period (optional)
if self.flags & SensorDetailsFlags.SENSOR_WARM_UP_PERIOD_PRESENT:
self.warmup_period, data = ParseUtils.consume_u8(data)
# we are done, there must not be any data left
if len(data) > 0:
self.logger.error("Extra data in packet: %d byte(s) left, should be 0"
% len(data))
return False
return True
def __str__(self) -> str:
flags = ParseUtils.parse_flags(self.flags, SensorDetailsFlags)
flag_list = ", ".join([f.name for f in flags])
annunciation = ParseUtils.parse_flags(self.annunciation, SensorDetailsAnnunciation)
annunciation_list = ", ".join([f.name for f in annunciation])
return "\n ".join([
f"{self.__class__.__name__}(",
f"Flags: "
+ ("--" if (self.flags is None or not flag_list) else flag_list),
f"Annunciation: "
+ ("--" if (self.annunciation is None or not annunciation_list) else annunciation_list),
# TODO: add proper unit
f"Maximum Calibration Interval: "
+ ("--" if self.maximum_calibration_interval is None else f"{self.maximum_calibration_interval}"),
f"Maximum Sensor Life: "
+ ("--" if self.maximum_sensor_life is None else f"{self.maximum_sensor_life} min"),
# TODO: add proper unit
f"Sensor Flex Package Version: "
+ ("--" if self.sensor_flex_package_version is None else f"{self.sensor_flex_package_version}"),
# TODO: add proper unit
f"Warm-Up Period: "
+ ("--" if self.warmup_period is None else f"{self.warmup_period}"),
]) + "\n)"
if __name__ == "__main__":
LogManager.init(level=logging.DEBUG)
raw = bytes.fromhex("91071d00ffff60273420")
data = SensorDetails(raw, use_e2e=True)
if data.parse():
print(data)
else:
print("Failed to parse Sensor Details")