Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt. Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor. Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов. Обновить справочники устройств, проекты CCS и регрессионные проверки.
57 lines
7.4 KiB
Python
57 lines
7.4 KiB
Python
"""Серверный Python-порт DEIF: протокольное состояние целиком принадлежит общему C99.""" # Пакет не импортирует gui_desktop и пригоден без Qt.
|
|
import ctypes as C # Структуры ABI содержат только готовые кадры и скаляры.
|
|
from time import monotonic # Порт поставляет монотонные часы, чтобы C оставался независим от ОС.
|
|
from .deif_display import DeifDisplay, FIELDS, STATUS, _milliseconds # Пассивный и активный режимы используют одинаковые снимки индикаторов.
|
|
from .deif_native import Frame, get_deif_native # Нет Python-копии адресации, NMT, сроков или SDO.
|
|
|
|
|
|
def validate_slave_transmit(frame, node): # Серверный маршрут имеет отдельный контракт по собственному Node-ID.
|
|
if type(node) is not int or not 1 <= node <= 127 or not get_deif_native().validate(frame.can_id, bytes(frame.data), len(frame.data), int(bool(frame.ide)) | (int(bool(frame.rtr)) << 1), node): # C разрешает только собственные heartbeat, EMCY и ответы SDO.
|
|
raise ValueError("Разрешены только heartbeat, EMCY и ответы SDO своего SLAVE") # Недопустимый кадр не попадёт в физический транспорт.
|
|
|
|
|
|
class DeifSlave(DeifDisplay): # Отличие от монитора состоит в включённых сервисах C и сохранении последнего числа при Data lost.
|
|
services, expiry_ms = True, 0 # Частоту heartbeat и срок Data lost определяет C, нулевой expiry сохраняет последнее показание.
|
|
|
|
def __init__(self, node=11, source_node=1, xdi_net=True, self_start=True, dam_pdo=2, bindings=None, dimmer_group=1): # Сохраняет существующий Python API модели SLAVE.
|
|
if any(type(value) is not int for value in (node, source_node, dam_pdo, dimmer_group)): # Порт проверяет типы до преобразования unsigned ABI.
|
|
raise ValueError("Параметры профиля DEIF должны быть целыми") # Логические и дробные адреса не превращаются в другой узел.
|
|
super().__init__(source="Локальная симуляция", device_node=node, bindings=bindings, source_node=source_node, xdi_net=xdi_net, self_start=self_start, dam_pdo=dam_pdo, dimmer_group=dimmer_group) # Все ограничения и исходные привязки создаются C-ядром.
|
|
|
|
def start(self, now=None, reset_values=True): # Явное включение не использует физический транспорт напрямую.
|
|
self._sync_bindings() # Последние параметры UI должны соответствовать создаваемому сеансу.
|
|
self._api.start(self._context, _milliseconds(monotonic() if now is None else now), bool(reset_values)) # Boot-up и self-start формируются общим автоматом C.
|
|
|
|
def stop(self): # Выключение прекращает в том числе невыданные служебные события.
|
|
self._api.stop(self._context) # Никакого отдельного Python-таймера heartbeat здесь нет.
|
|
|
|
def accept(self, source, identifier, data, extended=False, rtr=False, now=None): # Транспортное имя фильтруется базовым портом, байты принимает C.
|
|
return super().accept(source, identifier, data, extended, rtr, monotonic() if now is None else now) # Явные часы позволяют одинаково проверять границы Python и JNI.
|
|
|
|
def tick(self, now=None): # Порт регулярно выгружает готовые события и передаёт их выбранному транспорту.
|
|
from .can_transport import build_frame # Формат транспорта не смешивается с форматом DEIF.
|
|
self._sync_bindings() # Прямое изменение профиля не должно оставлять старые привязки.
|
|
output = (Frame * 16)() # Вместимость соответствует ограниченному пакету событий публичного ABI.
|
|
count = self._api.poll(self._context, _milliseconds(monotonic() if now is None else now), output, len(output)) # Все сроки и NMT-переходы обслуживаются C.
|
|
return [build_frame(frame.can_id, bytes(frame.data[:frame.dlc]), ide=False) for frame in output[:count]] # Python меняет только тип контейнера готовых кадров.
|
|
|
|
def lost_fields(self, now=None): # Локализованный список строится поверх готовой маски C.
|
|
self._sync_bindings() # Непривязанное поле не должно участвовать в предупреждении.
|
|
mask = self._api.lost(self._context, _milliseconds(monotonic() if now is None else now)) # Независимые сроки по 3000 мс определяются общим ядром.
|
|
return [key for index, key in enumerate(FIELDS) if mask & (1 << index)] # Соответствие field ID и имени не повторяет алгоритм свежести.
|
|
|
|
def _meta(self): # Набор скаляров не раскрывает внутреннюю структуру C.
|
|
output = (C.c_int64 * 10)() # Число элементов закреплено в deif_xdi.h.
|
|
self._api.meta(self._context, output) # Состояние хранится только в одном месте.
|
|
return list(output) # Python-порт не пересчитывает NMT или периоды.
|
|
|
|
active = property(lambda self: bool(self._meta()[0])) # Флаг нужен жизненному циклу страницы.
|
|
state = property(lambda self: self._meta()[1]) # Готовый CANopen NMT-код используется в локализованной подписи.
|
|
started = property(lambda self: self._meta()[2] / 1000) # Секунды сохраняют совместимость прежнего Python API.
|
|
last_heartbeat = property(lambda self: self._meta()[3] / 1000) # Преобразуется только единица системного времени.
|
|
heartbeat_ms = property(lambda self: self._meta()[4]) # Значение стандартного объекта 0x1017 выдаёт C.
|
|
accepted = property(lambda self: self._meta()[5]) # Счётчик принятия не имитируется количеством тиков GUI.
|
|
brightness = property(lambda self: self._meta()[7]) # Текущий процент подсветки из C-профиля.
|
|
palette = property(lambda self: self._meta()[8]) # Day/Night/Dusk хранится как код общего ядра.
|
|
invalid = property(lambda self: {key: STATUS[item[2]] for key, item in self._snapshots().items() if item[2]}) # Только текст статуса локализуется в Python.
|