Files
templates/python/set_devices/ump_logger.py

133 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Протокол кольцевого логгера УМП v2.
Технологические регистры 0..127 не изменены. Расширение 0x1000 сначала
проверяется по сигнатуре и версии: старая прошивка игнорировала старший
байт адреса, поэтому диагностическое чтение безопасно попадёт в 0..15,
но управляющие записи до успешной проверки запрещены в панели.
CRC и проверка ответов остаются в общей библиотеке протокола ПМ35.
"""
from dataclasses import dataclass
from protocan import periph28335 as protocol
BASE = 0x1000
DATA = 0x1010
LIVE = 0x1100
EMU_CONTROL = 0x1200
FAST_CAPABILITY = 0x1220
FAST_DATA = 0x2000
WORDS = 40
CAPACITY = 64000 // (2 * WORDS)
FIELDS = (
('off_trigger', 'Событие отключения', 2, 8),
('post_truncated', 'Усечение постинтервала', 2, 9),
('start', 'Команда пуск УМП', 3, 7),
('reset', 'Команда сброс УМП', 3, 6),
('omega', 'Вход OMEGA', 4, 0),
('start_in', 'Вход Start_1', 4, 1),
('res_in', 'Вход Res_in', 4, 2),
('start_out', 'Выход START_DPTB', 5, 0),
('stop_out', 'Выход STOP_DPTB', 5, 1),
('res_out', 'Выход RES_OUT_2', 5, 2),
('impulse', 'Остаток импульса, мс', 6, None),
('wake', 'Инициализация ЦАП, тики ЦАП', 7, None),
('dac', 'Код ЦАП', 8, None),
('current', 'Расчётное задание тока, мА', 9, None),
('sensor0', 'Измерение 0', 10, None),
('sensor1', 'Измерение 1', 11, None),
('sensor2', 'Измерение 2', 12, None),
('sensor3', 'Измерение 3', 13, None),
('error0', 'Ошибки 0 (маска)', 14, None),
('error1', 'Ошибки 1 (маска)', 15, None),
('error2', 'Ошибки 2 (маска)', 16, None),
('error3', 'Ошибки 3 (маска)', 17, None),
('can_header', 'CAN адрес/маска', 18, None),
('can0', 'CAN слово 0', 19, None),
('can1', 'CAN слово 1', 20, None),
('can2', 'CAN слово 2', 21, None),
('can_count', 'Количество CAN RX (mod 65536)', 22, None),
('can_timeout', 'Таймауты CAN TX (mod 65536)', 23, None),
('commands', 'Команды (маска)', 3, None),
('go', 'Начальная уставка, мА', 24, None),
('stop', 'Конечная уставка, мА', 25, None),
('dac_min', 'Код калибровки 4 мА', 26, None),
('dac_max', 'Код калибровки 20 мА', 27, None),
('dac_cal', 'Калибровочный код ЦАП', 28, None),
('dac04', 'DAC_04 (регистр)', 29, None),
('dac20', 'DAC_20 (регистр)', 30, None),
('ramp', 'Счётчик разгона', 31, None),
('duration', 'Длительность разгона, тики ЦАП', 32, None),
('effective_go', 'Начальная уставка после ограничений, мА', 33, None),
('effective_stop', 'Конечная уставка после ограничений, мА', 34, None),
('position', 'Положение разгона (kod)', 35, None),
('adc0', 'АЦП канал 0', 36, None),
('adc1', 'АЦП канал 1', 37, None),
('adc2', 'АЦП канал 2', 38, None),
('adc3', 'АЦП канал 3', 39, None),
)
def request(controller: int, function: int, address: int, value: int) -> bytes:
if not 0 <= controller <= 255 or not 0 <= value <= 65535:
raise ValueError('Недопустимый адрес контроллера или значение')
if function == 3:
valid = ((address == BASE and value == 16) or
(address == DATA and 1 <= value <= 120 and value % WORDS == 0) or (address == LIVE and value == 124) or (address == EMU_CONTROL and value == 4)
or (address == FAST_CAPABILITY and value == 4)
or (FAST_DATA <= address < FAST_DATA + CAPACITY * WORDS
and (address - FAST_DATA) % WORDS == 0
and 1 <= value <= 120 and value % WORDS == 0
and address + value <= FAST_DATA + CAPACITY * WORDS))
else:
valid = function == 6 and (address in (BASE, BASE + 1, BASE + 2, BASE + 3) or
(address == EMU_CONTROL + 2 and value in (0, 1)))
if not valid:
raise ValueError('Недопустимый запрос логгера УМП')
return protocol.with_crc(bytes((controller, function)) + address.to_bytes(2, 'big') + value.to_bytes(2, 'big'))
@dataclass(frozen=True)
class Status:
state: int
count: int
period: int
mode: int
frequency: int
generation: int
archive_count: int
post_ms: int
live_state: int
@classmethod
def decode(cls, words):
if (len(words) != 16 or words[0:2] != (0x554D, 2) or
words[4] != WORDS or words[5] != CAPACITY):
raise ValueError('Прошивка не поддерживает логгер УМП v2')
if words[9] != 6:
raise ValueError('Логгер УМП доступен только на плате нагрузки dsk_LOAD')
if words[2] not in (0, 1, 2, 3) or words[3] > CAPACITY or not 1 <= words[6] <= 1000 or words[11] != 1000 or words[13] > CAPACITY or words[14] > 1000 or words[15] not in (0, 1, 3):
raise ValueError('Повреждён статус логгера')
return cls(words[2], words[3], words[6], words[10], words[11], *words[12:16])
def sample(words, frequency=1000):
if len(words) != WORDS or frequency <= 0:
raise ValueError('Неверная запись логгера')
result = {'time_ms': (words[0] | words[1] << 16) * 1000 / frequency, 'event': words[2]}
for key, _label, index, bit in FIELDS:
value = words[index]
if bit is not None:
value = (value >> bit) & 1
elif 10 <= index <= 13 and value >= 32768:
value -= 65536
# Физические единицы приводим здесь, одинаково для CSV и графика.
if key in ('current', 'effective_go', 'effective_stop'):
value /= 10.0
result[key] = value
return result
def emulator_enabled(words):
if len(words) != 4 or tuple(words[:2]) != (0x454D, 1) or words[2] not in (0, 1):
raise ValueError('Плата не поддерживает управление эмуляцией STM')
return bool(words[2])