# Каталог общего адресного пространства и поток значений Расширение 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` — прибор не тратит на это такты и не теряет точность на промежуточном округлении.