Files
templates/c/protocan-transport/docs/GUI_CATALOG.md
Andrey Kruchinkin 3dc636e012 feat(protocan-transport): транспортный уровень ProtoCAN и каталог GUI
Перенесён из репозитория protocan-transport, который подключался
сабмодулем в CAN_to_RS485.

Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232,
USB CDC), разбор 29-битного идентификатора, общее адресное пространство
регистров и каталог с подпиской на поток значений для SETGUI. Состояние
живёт в структурах вызывающего, поэтому в одной прошивке поднимается
сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте.

Хостовые тесты test_transport и test_gui проходят.
2026-08-23 01:15:35 +03:00

8.1 KiB
Raw Blame History

Каталог общего адресного пространства и поток значений

Расширение GUI-протокола SETGUI (A5 5A, см. gui_desktop/core/protocol.py) для работы с общим адресным пространством: прибор сам объявляет, какие регистры у него есть и как они называются, а оператор выбирает, что показывать. Схема повторяет то, как устроен реестр регистров в ST Motor Control Workbench / Motor Pilot.

Транспорт не меняется: A5 5A | ver | type | seq(BE) | size(BE) | payload | CRC32(LE), payload до 512 байт. Добавлены только три типа сообщений.

     прибор                                   GUI
       │  ── GAS_CATALOG (seq = 0) ──────────► │  каталог приходит сам
       │  ── GAS_CATALOG (seq = 0) ──────────► │  при инициализации
       │  ── GAS_CATALOG (seq = 0) ──────────► │
       │                                       │  оператор отмечает нужное
       │  ◄──────────────── GAS_WATCH_SET ──── │
       │  ── GAS_WATCH_SET (эхо) ────────────► │
       │  ── GAS_WATCH_DATA (seq = 0) ───────► │  поток значений
       │  ── GAS_WATCH_DATA (seq = 0) ───────► │

Типы сообщений

Заняты из свободного диапазона 0x11..0x1F (между READ_LOGS = 0x10 и SENSOR_SCAN = 0x20). Существующие значения не перенумерованы.

Код Имя Направление
0x11 GAS_CATALOG запрос GUI → прибор; записи прибор → GUI
0x12 GAS_WATCH_SET GUI → прибор, прибор отвечает эхом
0x13 GAS_WATCH_DATA прибор → GUI, без запроса, sequence = 0

Незапрошенные кадры прибор публикует с sequence = 0 — так же, как уже устроены SENSOR_DATA и UI_STATE.

GAS_CATALOG (0x11)

Запрос (GUI → прибор), 4 байта

Смещение Тип Поле
0 u16 LE start_index — порядковый номер записи, не адрес
2 u16 LE max_count — сколько записей вернуть; 0 = сколько влезет

Ответ и автопубликация (прибор → GUI)

Смещение Тип Поле
0 u16 LE total — всего записей в каталоге
2 u16 LE start_index — индекс первой записи в пакете
4 u16 LE count — записей в пакете
6 count записей по 32 байта

Одна запись — 32 байта:

Смещение Тип Поле
0 u16 LE address — адрес в общем адресном пространстве
2 u8 type — формат значения
3 u8 flags — доступ
4 i8 scale_pow10 — значение = raw * 10^scale
5 u8 unit — код единицы измерения
6 u16 LE резерв, нули
8 24 байта name — UTF-8, дополнено нулями

При payload 512 байт в один пакет входит (512 - 6) / 32 = 15 записей.

Под имя отведено 24 байта — это 12 кириллических символов в UTF-8. На 16 байтах не помещалось даже «Температура», поэтому поле шире, чем кажется нужным для латиницы.

type

Код Значение Регистров
0 U16 — беззнаковое 1
1 I16 — знаковое 1
2 U32 — беззнаковое, младшее слово первым 2
3 I32 — знаковое, младшее слово первым 2
4 BITS — битовое поле 1

Многословные значения занимают подряд идущие адреса; в потоке они приходят отдельными словами, GUI собирает их сам.

flags

Бит Смысл
0 доступно на чтение
1 доступно на запись
2 включить в подписку по умолчанию

unit

Код Единица Код Единица
0 6 мс
1 В 7 с
2 А 8 кбит/с
3 °C 9 шт.
4 % 10 об/мин
5 Гц 11 Вт

GAS_WATCH_SET (0x12)

Запрос (GUI → прибор)

Смещение Тип Поле
0 u16 LE period_ms — период потока; 0 останавливает поток
2 u16 LE count — число адресов, не больше GAS_WATCH_MAX (64)
4 count × u16 LE — адреса в нужном порядке

Порядок адресов сохраняется: значения в GAS_WATCH_DATA приходят ровно в том же порядке, без повторной передачи адресов.

Ответ (прибор → GUI), 4 байта

Смещение Тип Поле
0 u16 LE period_ms — период, который прибор реально установил
2 u16 LE count — сколько адресов принято

Прибор может принять меньше, чем попросили: адрес вне карты в подписку не берётся. Расхождение count с запросом — сигнал GUI, что часть адресов отвергнута.

GAS_WATCH_DATA (0x13)

Прибор → GUI, sequence = 0, без запроса.

Смещение Тип Поле
0 u32 LE timestamp_ms — время прибора от старта
4 u16 LE count
6 count × u16 LE — значения в порядке подписки

timestamp_ms берётся у прибора, а не у хоста: по нему видно реальный период и провалы, которые иначе замаскировала бы буферизация UART.

Замечания по реализации

  • Каталог статичен. Он описывает прошивку, а не состояние, поэтому публикуется один раз при инициализации периферии. GUI может перечитать его запросом в любой момент.
  • Подписка живёт до переподключения. Прибор не сохраняет её в энергонезависимой памяти: после сброса поток молчит, пока GUI не пришлёт GAS_WATCH_SET снова.
  • Поток не должен забивать линию. При периоде 10 мс и 64 адресах выходит 134 байта на пакет и 13.4 кбайт/с — половина пропускной способности 256000 бод. Прибор пропускает такт, если в очереди передачи нет места для целого пакета, и это видно по разрыву timestamp_ms.
  • Значения передаются сырыми. Пересчёт в физические величины делает GUI по scale_pow10 и unit — прибор не тратит на это такты и не теряет точность на промежуточном округлении.