SHT4X
145 строк · 7.9 Кб
1# micropython
2# MIT license
3# Copyright (c) 2024 Roman Shevchik goctaprog@gmail.com
4"""Представление битового поля"""
5from collections import namedtuple
6from sensor_pack_2.base_sensor import check_value, get_error_str
7
8# информация о битовом поле в виде именованного кортежа
9# name: str - имя
10# position: range - место в номерах битах. position.start = первый бит, position.stop-1 - последний бит
11# valid_values: [range, tuple] - диапазон допустимых значений, если проверка не требуется, следует передать None
12# description: str - читаемое описание значения, хранимого в битовом поле, если описания не требуется, следует передать None
13bit_field_info = namedtuple("bit_field_info", "name position valid_values description")
14
15
16def _bitmask(bit_rng: range) -> int:
17"""возвращает битовую маску по занимаемым битам"""
18# if bit_rng.step < 0 or bit_rng.start <= bit_rng.stop:
19# raise ValueError(f"_bitmask: {bit_rng.start}; {bit_rng.stop}; {bit_rng.step}")
20return sum(map(lambda x: 2 ** x, bit_rng))
21
22
23class BitFields:
24"""Хранилище информации о битовых полях с доступом по индексу.
25_source - кортеж именованных кортежей, описывающих битовые поля;"""
26def _check(self, fields_info: tuple[bit_field_info, ...]):
27"""Проверки на правильность информации!"""
28for field_info in fields_info:
29if 0 == len(field_info.name):
30raise ValueError(f"Нулевая длина строки имени битового поля!; position: {field_info.position}")
31if 0 == len(field_info.position):
32raise ValueError(f"Нулевая длина ('в битах') битового поля!; name: {field_info.name}")
33
34def __init__(self, fields_info: tuple[bit_field_info, ...]):
35self._check(fields_info)
36self._fields_info = fields_info
37self._idx = 0
38# имя битового поля, которое будет параметром у методов get_value/set_value
39self._active_field_name = fields_info[0].name
40# значение, из которого будут извлекаться битовые поля
41self._source_val = 0
42
43def _by_name(self, name: str) -> [bit_field_info, None]:
44"""возвращает информацию о битовом поле по его имени (поле name именованного кортежа) или None"""
45items = self._fields_info
46for item in items:
47if name == item.name:
48return item
49
50def _get_field(self, key: [str, int, None]) -> [bit_field_info, None]:
51"""для внутреннего использования"""
52fi = self._fields_info
53_itm = None
54if isinstance(key, int):
55_itm = fi[key]
56if isinstance(key, str):
57_itm = self._by_name(key)
58return _itm
59
60def get_field_value(self, field_name: str = None, validate: bool = False) -> [int, bool]:
61"""возвращает значение битового поля, по его имени(self.field_name), из self.source."""
62f_name = self.field_name if field_name is None else field_name
63item = self._get_field(f_name)
64if item is None:
65raise ValueError(f"get_field_value. Поле с именем {f_name} не существует!")
66pos = item.position
67bitmask = _bitmask(pos)
68val = (self.source & bitmask) >> pos.start # выделение маской битового диапазона и его сдвиг вправо
69if item.valid_values and validate:
70raise NotImplemented("Если вы решили проверить значение поля при его возвращении, то делайте это самостоятельно!!!")
71if 1 == len(pos):
72return 0 != val # bool
73return val # int
74
75def set_field_value(self, value: int, source: [int, None] = None, field: [str, int, None] = None,
76validate: bool = True) -> int:
77"""Записывает value в битовый диапазон, определяемый параметром field, в source.
78Возвращает значение с измененным битовым полем.
79Если field is None, то имя поля берется из свойства self._active_field_name.
80Если source is None, то значение поля, подлежащее изменению, изменяется в свойстве self._source_val"""
81item = self._get_field(key=field) # *
82rng = item.valid_values
83if rng and validate:
84# print(f"DBG: value: {value}; rng: {rng}")
85check_value(value, rng, get_error_str(self.field_name, value, rng))
86pos = item.position
87bitmask = _bitmask(pos)
88src = self._get_source(source) & ~bitmask # чистка битового диапазона
89src |= (value << pos.start) & bitmask # установка битов в заданном диапазоне
90# print(f"DBG:set_field_value: {value}; {source}; {field}")
91if source is None:
92self._source_val = src
93# print(f"DBG:set_field_value: self._source_val: {self._source_val}")
94return src
95
96def __getitem__(self, key: [int, str]) -> [int, bool]:
97"""возвращает значение битового поля из значения в self.source по его имени/индексу"""
98_bfi = self._get_field(key)
99return self.get_field_value(_bfi.name)
100
101def __setitem__(self, field_name: str, value: [int, bool]):
102"""Волшебный метод, вызывает set_field_value.
103До его вызова нужно установить свойства BitField source"""
104self.set_field_value(value=value, source=None, field=field_name, validate=True) # *
105
106def _get_source(self, source: [int, None]) -> int:
107return source if source else self._source_val
108
109@property
110def source(self) -> int:
111"""значение, из которого будут извлекаться/в котором будут изменятся битовые поля"""
112return self._source_val
113
114@source.setter
115def source(self, value):
116"""значение, из которого будут извлекаться/изменятся битовые поля"""
117self._source_val = value
118
119@property
120def field_name(self) -> str:
121"""имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их
122параметр field is None"""
123return self._active_field_name
124
125@field_name.setter
126def field_name(self, value):
127"""имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их
128параметр field is None"""
129self._active_field_name = value
130
131def __len__(self) -> int:
132return len(self._fields_info)
133
134# протокол итератора
135def __iter__(self):
136return self
137
138def __next__(self) -> bit_field_info:
139ss = self._fields_info
140try:
141self._idx += 1
142return ss[self._idx - 1]
143except IndexError:
144self._idx = 0 # для возможности выполнения повторной итерации!
145raise StopIteration
146