"""Регистровый контракт аппаратного эмулятора DEIF из set_emu_deif.h.""" # Один контракт используется всеми MCU и настольными клиентами. from dataclasses import dataclass # Неизменяемый снимок отделяет подтверждённое состояние платы от редактируемой формы. DEIF_BASE = 0x1600 # Начало окна управления DEIF в set_emu_server.c, адрес измеряется в 16-битных словах. DEIF_WORDS = 52 # SET_DEIF_WORDS версии 1 включает профиль, RAW, привязки OD и диагностику. DEIF_FIELDS = ("Угол, RAW", "Обороты, RAW", "Обороты %, RAW", "Мощность, RAW", "Мощность %, RAW", "Задание угла, RAW") # Порядок соответствует raw[6] переносимой C-модели; физические единицы зависят от словаря прибора. DEIF_INDICES = (0x3001, 0x3081, 0x3091, 0x3421, 0x3431, 0x3001) # Начальные OD экземпляра 1 взяты из set_deif_init; последний объект имеет подиндекс 7. @dataclass(frozen=True) # Клиент публикует только целиком проверенный снимок без последующих изменений. class DeifSnapshot: # Тип не зависит от Qt, HAL, конкретного MCU и способа доставки регистров. words: tuple # Содержит 52 беззнаковых слова, прочитанных одной командой FC03. mode: int # Режим из конфигурации STM: 4 Master, 5 Slave либо иной остановленный режим. @property # Роль берётся из ответа платы, а не из ранее выбранного пункта GUI. def role(self): # Возвращает 1 Master либо 2 Slave согласно set_emu_deif.h. return self.words[3] # Слово 3 не совпадает с номерами режимов 4 и 5 блока STM. @property # Проверка запущенного состояния нужна для запрета изменения сетевого профиля. def running(self): # Возвращает логическое состояние модели без запуска либо остановки. return bool(self.words[2]) # Слово 2 равно 1 только после подтверждённой команды пуска. @property # Знаковое представление сохраняет отрицательные RAW и служебные коды DEIF. def raw(self): # Возвращает шесть значений I16 в порядке DEIF_FIELDS. return tuple(value if value < 0x8000 else value - 0x10000 for value in self.words[16:22]) # Старший бит задаёт знак 16-битного дополнения до двух. def decode_snapshot(words, mode): # Проверяет совместимость прошивки до разрешения любых записей DEIF. values = tuple(words) # Не сохраняет ссылку на изменяемый транспортный буфер. if len(values) != DEIF_WORDS or values[:2] != (0x4446, 1): # Сигнатура DF и версия 1 заданы set_deif_read, не совпадают с конфигурацией STM. raise ValueError("Нужна прошивка с сервисом DEIF Master/Slave версии 1") # Старая прошивка не получает команд в неизвестное окно. if values[2] not in (0, 1) or values[3] not in (1, 2): # Повреждённые running и role не должны превращаться в разрешение записи. raise ValueError("STM вернула неверное состояние DEIF") # Отказ сохраняет форму неподтверждённой. if mode in (4, 5) and values[3] != mode - 3: # Конфигурация и окно состояния должны описывать одну роль текущего экземпляра. raise ValueError("Роль DEIF не совпадает с режимом STM; повторите чтение") # Между чтениями другая сессия могла изменить режим платы. return DeifSnapshot(values, mode) # Единственная точка создания проверенного снимка для UI и тестов.