mcp3421
126 строк · 6.4 Кб
1# micropython
2# MIT license
3# Copyright (c) 2024 Roman Shevchik goctaprog@gmail.com
4"""Представление битового поля"""
5from collections import namedtuple6from sensor_pack_2.base_sensor import check_value, get_error_str7
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_info27self._idx = 028# имя битового поля, которое будет параметром у методов get_value/set_value29self._active_field_name = fields_info[0].name30# значение, из которого будут извлекаться битовые поля31self._source_val = 032
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_val39
40@property41def source(self) -> int:42"""значение, из которого будут извлекаться/в котором будут изменятся битовые поля"""43return self._source_val44
45@source.setter46def source(self, value):47"""значение, из которого будут извлекаться/изменятся битовые поля"""48self._source_val = value49
50@property51def field_name(self) -> str:52"""имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их53параметр field is None"""
54return self._active_field_name55
56@field_name.setter57def field_name(self, value):58"""имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их59параметр field is None"""
60self._active_field_name = value61
62def _by_name(self, name: str) -> [bit_field_info, None]:63"""возвращает информацию о битовом поле по его имени (поле name именованного кортежа) или None"""64items = self._fields_info65for item in items:66if name == item.name:67return item68
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_info75if 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_values88if rng and validate:89check_value(value, rng, get_error_str("value", value, rng))90pos = item.position91bitmask = _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 = src97# print(f"DBG:set_field_value: self._source_val: {self._source_val}")98return src99
100def get_field_value(self, validate: bool = False) -> [int, bool]:101"""возвращает значение битового поля, по его имени(self.field_name), из self.source."""102f_name = self.field_name103item = self._get_field(f_name)104if item is None:105raise ValueError(f"get_field_value. Поле с именем {f_name} не существует!")106pos = item.position107bitmask = _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 # bool113return val # int114
115# протокол итератора116def __iter__(self):117return self118
119def __next__(self) -> bit_field_info:120ss = self._fields_info121try:122self._idx += 1123return ss[self._idx - 1]124except IndexError:125self._idx = 0 # для возможности выполнения повторной итерации!126raise StopIteration127