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