Что сказал датчик? Работа с байтами, расшифровка полученных данных. Python. Прошу помощи!

Что сказал датчик?

Датчики являются беспроводными, работают на батарейках до 7ми лет, поэтому они вынуждены передавать свои данные тщательно упакованными структурами.
Например четырьмя байтами информации датчик может передать информацию по 10 своим параметрам. Датчик отправляет пакет на хаб с данными (далее "пейлоад") "10FA0E00", хаб должен расшифровать этот набор байтов и выдать уже обработанные данные по всем 10 параметрам, в таком виде:

{'field1': 'Low',
 'field2': '00',
 'field3': '01',
 'field4': '00',
 'field5': '00',
 'field6': '01',
 'field7': '00',
 'field8': 'Very High',
 'field9': '00',
 'field10': '00'}
           

Структуру параметров в датчике можно представить следующим образом:

#Format settings - array [sett_byte1 as dict {bit: [size, 'field_name']}, sett_byte2, sett_byte3, sett_byte4]
device_settings = [{0: [3, 'field1'], 
                    3: [1, 'field2'],  
                    4: [1, 'field3'], 
                    5: [3, 'field4']}, 
                   {0: [1, 'field5'],  
                    1: [1, 'field6'], 
                    2: [1, 'field7'],  
                    3: [3, 'field8'], 
                   },
                   {0: [1, 'field9'], 
                    5: [1, 'field10']
                   },
                   {}
                  ]

Это список байтов (4), в которых каждому биту соответствует какой-то параметр (первый элемент в списке соответствующего бита). Некоторые параметры могут занимать больше одного бита, например field1, он занимает 3 бита, о чем говорит нулевой элемент списке соответствующего бита. Для параметров, которые в пейлоаде занимают больше 1 бита даны следующие переменные:

field1 = {'0': 'Low',
          '1': 'reserved',
          '2': 'reserved',
          '3': 'reserved',
          '4': 'Medium', 
          '5': 'reserved',
          '6': 'reserved',
          '7': 'High',  
          }
field4 = {'0': '00', 
          '1': '10',
          '2': '20',
          '3': '30',
          '4': '40',
          '5': '50',
          '6': '60',
          '7': '70',
          }
field8 = {'0': 'Very Low',
          '1': 'reserved',
          '2': 'Low',
          '3': 'reserved',
          '4': 'Medium',
          '5': 'High',
          '6': 'reserved',
          '7': 'Very High',
          }

Например, параметр field4, который занимает 3 бита, в битовой маске равен 011, что в десятичной системе измерения равно 3, из этого следует, что параметр field4 равен 30.

Задание:

Написать функцию которая будет парсить пейлоад команды от датчика и возвращать значения всех параметров. Проверочные данные: пейлоад = "10FA0E00", функция должна вернуть структуру:

{'field1': 'Low',
 'field2': '00',
 'field3': '01',
 'field4': '00',
 'field5': '00',
 'field6': '01',
 'field7': '00',
 'field8': 'Very High',
 'field9': '00',
 'field10': '00',
}


Прошу помощи с этим заданием

думал над его решением разными способами, например преобразовать полученные данные в виде 10FA0E00 шестнадцатеричного кода в двоичный и потом вытащить оттуда данные, никуда не продвинулся. Полученный двоичный вот:

0011000100110000010001100100000100110000010001010011000000110000

Собственно как должен работать датчик

Данное изображение есть частичным решением и объяснением того как должен работать датчик и расшифровывать полученные данные в виде 10FA0E00: Логика расшифровки 10FA0E00


Ответы (1 шт):

Автор решения: Сергей

Вот код с детальными комментариями - я поизучал некоторые возможности языка, которые не очень хорошо знал. Самое простое было решить "в лоб", но мне было интересно сделать для разнообразных структур, так что код достаточно адаптируемый.

Обратите внимание, что:

  1. Значения при объявлении вoutputя установил в test, чтобы было видно, что выход реально обновлён.
  2. 4 байта в коде записаны в правильной нотации (как они и должны прийти с устройства).
  3. Тестовый пример, что вам дали, содержит неверный выход относительно логики, описанной вами в задании. 10FA0E00 эквивалентно 32 битам:

байт 1:000 1 0 000(откуда у вас появилось "параметр field4, который занимает 3 бита, в битовой маске равен 011" я не понимаю, так как ваш ответ был "не могу сказать." Тут он - 000, как видите.)

байт 2:1 1 1 110 10 (последняя группа не используется)

байт 3:0 0001 1 10 (вторая и четвертая группы не используются)

байт 4: 00000000 (не используется).

Возможно, ваши работодатели задание, торопясь, писали, а, возможно, вашу внимательность проверяют. Код ниже работает относительно идеи, описанной выше. Если поймёте, что за ней стоит, и если это не ошибка, можете легко (надеюсь) модифицировать код. Есть у меня подозрение, что они начали считать с первого бита в двоичном представлении, не учтя, что три начальных 0 не отображаются. Впрочем, field4 это все равно не объясняет, разве что они ещё и обсчитались на один бит.

  1. Запускать на Python 3.7 и выше. Используется поддержка упорядоченности словарей.
device_settings = [{0: [3, 'field1'],
                    3: [1, 'field2'],
                    4: [1, 'field3'],
                    5: [3, 'field4']},
                   {0: [1, 'field5'],
                    1: [1, 'field6'],
                    2: [1, 'field7'],
                    3: [3, 'field8'],
                   },
                   {0: [1, 'field9'],
                    5: [1, 'field10']
                   },
                   {}
                  ]

field1 = {'0': 'Low',
          '1': 'reserved',
          '2': 'reserved',
          '3': 'reserved',
          '4': 'Medium',
          '5': 'reserved',
          '6': 'reserved',
          '7': 'High',
          }
field4 = {'0': '00',
          '1': '10',
          '2': '20',
          '3': '30',
          '4': '40',
          '5': '50',
          '6': '60',
          '7': '70',
          }
field8 = {'0': 'Very Low',
          '1': 'reserved',
          '2': 'Low',
          '3': 'reserved',
          '4': 'Medium',
          '5': 'High',
          '6': 'reserved',
          '7': 'Very High',
          }

output= {'field1': 'test',
 'field2': 'test',
 'field3': 'test',
 'field4': 'test',
 'field5': 'test',
 'field6': 'test',
 'field7': 'test',
 'field8': 'test',
 'field9': 'test',
 'field10': 'test'}


# Переданные 4 байта в правильной записи
bytes = b'\x10\xFA\x0E\x00'
# Количество бит в записи
bits = 32
# Переданные 4 байта в десятичной форме для удобства оперирования в коде
bytes_to_int = int.from_bytes(bytes, byteorder='big')

# Последовательный проход по каждому байту в словаре. Использовать Python 3.7 и старше! 
for i in range (len(device_settings)):
    # Последовательный проход по битам во вложенных словарях.
    for key, value in device_settings[i].items():
        # Отсекаем правую часть, сдвигая вправо
        pre_res = bytes_to_int >> (bits - (i * 8 + key + value[0]))
        # Отсекаем левую часть, деля по модулю
        pre_res = pre_res % (2 ** value[0])
        # Берем значение из полей для параметров с длиной более 3 бит
        if value[1] == 'field1':
            result = field1[str(int(pre_res))]
        elif value[1] == 'field4':
            result = field4[str(int(pre_res))]
        elif value[1] == 'field8':
            result = field8[str(int(pre_res))]
        else:
            result = pre_res
        # Помещаем результат в выходную структуру
        output[value[1]] = result
print (output)
→ Ссылка