ads1115

Форк
0
/
bitfield.py 
126 строк · 6.4 Кб
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 - диапазон допустимых значений, если проверка не требуется, следует передать None
12
bit_field_info = namedtuple("bit_field_info", "name position valid_values")
13

14

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

21

22
class BitFields:
23
    """Хранилище информации о битовых полях с доступом по индексу.
24
    _source - кортеж именованных кортежей, описывающих битовые поля;"""
25
    def __init__(self, fields_info: tuple[bit_field_info, ...]):
26
        self._fields_info = fields_info
27
        self._idx = 0
28
        # имя битового поля, которое будет параметром у методов get_value/set_value
29
        self._active_field_name = fields_info[0].name
30
        # значение, из которого будут извлекаться битовые поля
31
        self._source_val = 0
32

33
    def _get_field(self, field: [str, int, None]) -> bit_field_info:
34
        """для внутреннего использования"""
35
        return self.__getitem__(field if field else self.field_name)
36

37
    def _get_source(self, source: [int, None]) -> int:
38
        return source if source else self._source_val
39

40
    @property
41
    def source(self) -> int:
42
        """значение, из которого будут извлекаться/в котором будут изменятся битовые поля"""
43
        return self._source_val
44

45
    @source.setter
46
    def source(self, value):
47
        """значение, из которого будут извлекаться/изменятся битовые поля"""
48
        self._source_val = value
49

50
    @property
51
    def field_name(self) -> str:
52
        """имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их
53
        параметр field is None"""
54
        return self._active_field_name
55

56
    @field_name.setter
57
    def field_name(self, value):
58
        """имя битового поля, значение которого извлекается/изменяется методами get_value/set_value, если их
59
        параметр field is None"""
60
        self._active_field_name = value
61

62
    def _by_name(self, name: str) -> [bit_field_info, None]:
63
        """возвращает информацию о битовом поле по его имени (поле name именованного кортежа) или None"""
64
        items = self._fields_info
65
        for item in items:
66
            if name == item.name:
67
                return item
68

69
    def __len__(self) -> int:
70
        return len(self._fields_info)
71

72
    def __getitem__(self, key: [int, str]) -> [bit_field_info, None]:
73
        """возвращает информацию о битовом поле по его имени/индексу или None"""
74
        fi = self._fields_info
75
        if isinstance(key, int):
76
            return fi[key]
77
        if isinstance(key, str):
78
            return self._by_name(key)
79

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

100
    def get_field_value(self, validate: bool = False) -> [int, bool]:
101
        """возвращает значение битового поля, по его имени(self.field_name), из self.source."""
102
        f_name = self.field_name
103
        item = self._get_field(f_name)
104
        if item is None:
105
            raise ValueError(f"get_field_value. Поле с именем {f_name} не существует!")
106
        pos = item.position
107
        bitmask = _bitmask(pos)
108
        val = (self.source & bitmask) >> pos.start  # выделение маской битового диапазона и его сдвиг вправо
109
        if item.valid_values and validate:
110
            raise NotImplemented("get_value validate")
111
        if 1 == len(pos):
112
            return 0 != val     # bool
113
        return val              # int
114

115
    # протокол итератора
116
    def __iter__(self):
117
        return self
118

119
    def __next__(self) -> bit_field_info:
120
        ss = self._fields_info
121
        try:
122
            self._idx += 1
123
            return ss[self._idx - 1]
124
        except IndexError:
125
            self._idx = 0   # для возможности выполнения повторной итерации!
126
            raise StopIteration
127

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

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

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

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