SHT4X

Форк
0
/
bitfield.py 
145 строк · 7.9 Кб
1
# micropython
2
# MIT license
3
# Copyright (c) 2024 Roman Shevchik   goctaprog@gmail.com
4
"""Представление битового поля"""
5
from collections import namedtuple
6
from 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
13
bit_field_info = namedtuple("bit_field_info", "name position valid_values description")
14

15

16
def _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}")
20
    return sum(map(lambda x: 2 ** x, bit_rng))
21

22

23
class BitFields:
24
    """Хранилище информации о битовых полях с доступом по индексу.
25
    _source - кортеж именованных кортежей, описывающих битовые поля;"""
26
    def _check(self, fields_info: tuple[bit_field_info, ...]):
27
        """Проверки на правильность информации!"""
28
        for field_info in fields_info:
29
            if 0 == len(field_info.name):
30
                raise ValueError(f"Нулевая длина строки имени битового поля!; position: {field_info.position}")
31
            if 0 == len(field_info.position):
32
                raise ValueError(f"Нулевая длина ('в битах') битового поля!; name: {field_info.name}")
33

34
    def __init__(self, fields_info: tuple[bit_field_info, ...]):
35
        self._check(fields_info)
36
        self._fields_info = fields_info
37
        self._idx = 0
38
        # имя битового поля, которое будет параметром у методов get_value/set_value
39
        self._active_field_name = fields_info[0].name
40
        # значение, из которого будут извлекаться битовые поля
41
        self._source_val = 0
42

43
    def _by_name(self, name: str) -> [bit_field_info, None]:
44
        """возвращает информацию о битовом поле по его имени (поле name именованного кортежа) или None"""
45
        items = self._fields_info
46
        for item in items:
47
            if name == item.name:
48
                return item
49

50
    def _get_field(self, key: [str, int, None]) -> [bit_field_info, None]:
51
        """для внутреннего использования"""
52
        fi = self._fields_info
53
        _itm = None
54
        if isinstance(key, int):
55
            _itm = fi[key]
56
        if isinstance(key, str):
57
            _itm = self._by_name(key)
58
        return _itm
59

60
    def get_field_value(self, field_name: str = None, validate: bool = False) -> [int, bool]:
61
        """возвращает значение битового поля, по его имени(self.field_name), из self.source."""
62
        f_name = self.field_name if field_name is None else field_name
63
        item = self._get_field(f_name)
64
        if item is None:
65
            raise ValueError(f"get_field_value. Поле с именем {f_name} не существует!")
66
        pos = item.position
67
        bitmask = _bitmask(pos)
68
        val = (self.source & bitmask) >> pos.start  # выделение маской битового диапазона и его сдвиг вправо
69
        if item.valid_values and validate:
70
            raise NotImplemented("Если вы решили проверить значение поля при его возвращении, то делайте это самостоятельно!!!")
71
        if 1 == len(pos):
72
            return 0 != val     # bool
73
        return val              # int
74

75
    def set_field_value(self, value: int, source: [int, None] = None, field: [str, int, None] = None,
76
                        validate: bool = True) -> int:
77
        """Записывает value в битовый диапазон, определяемый параметром field, в source.
78
        Возвращает значение с измененным битовым полем.
79
        Если field is None, то имя поля берется из свойства self._active_field_name.
80
        Если source is None, то значение поля, подлежащее изменению, изменяется в свойстве self._source_val"""
81
        item = self._get_field(key=field)     #   *
82
        rng = item.valid_values
83
        if rng and validate:
84
            # print(f"DBG: value: {value}; rng: {rng}")
85
            check_value(value, rng, get_error_str(self.field_name, value, rng))
86
        pos = item.position
87
        bitmask = _bitmask(pos)
88
        src = self._get_source(source) & ~bitmask  # чистка битового диапазона
89
        src |= (value << pos.start) & bitmask  # установка битов в заданном диапазоне
90
        # print(f"DBG:set_field_value: {value}; {source}; {field}")
91
        if source is None:
92
            self._source_val = src
93
            # print(f"DBG:set_field_value: self._source_val: {self._source_val}")
94
        return src
95

96
    def __getitem__(self, key: [int, str]) -> [int, bool]:
97
        """возвращает значение битового поля из значения в self.source по его имени/индексу"""
98
        _bfi = self._get_field(key)
99
        return self.get_field_value(_bfi.name)
100

101
    def __setitem__(self, field_name: str, value: [int, bool]):
102
        """Волшебный метод, вызывает set_field_value.
103
        До его вызова нужно установить свойства BitField source"""
104
        self.set_field_value(value=value, source=None, field=field_name, validate=True)     #   *
105

106
    def _get_source(self, source: [int, None]) -> int:
107
        return source if source else self._source_val
108

109
    @property
110
    def source(self) -> int:
111
        """значение, из которого будут извлекаться/в котором будут изменятся битовые поля"""
112
        return self._source_val
113

114
    @source.setter
115
    def source(self, value):
116
        """значение, из которого будут извлекаться/изменятся битовые поля"""
117
        self._source_val = value
118

119
    @property
120
    def field_name(self) -> str:
121
        """имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их
122
        параметр field is None"""
123
        return self._active_field_name
124

125
    @field_name.setter
126
    def field_name(self, value):
127
        """имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их
128
        параметр field is None"""
129
        self._active_field_name = value
130

131
    def __len__(self) -> int:
132
        return len(self._fields_info)
133

134
    # протокол итератора
135
    def __iter__(self):
136
        return self
137

138
    def __next__(self) -> bit_field_info:
139
        ss = self._fields_info
140
        try:
141
            self._idx += 1
142
            return ss[self._idx - 1]
143
        except IndexError:
144
            self._idx = 0   # для возможности выполнения повторной итерации!
145
            raise StopIteration
146

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.