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