Files
templates/c/set-protocol/PROTOCOL.md

239 lines
12 KiB
Markdown
Raw Permalink 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.
# SET protocol v2 — wire contract
## 1. Назначение
SETP v2 — единственный прикладной протокол новых устройств SET. Он решает три
задачи одним контрактом:
1. запросы и ответы: конфигурация, диагностика, журналы и общая карта данных;
2. события: значения для отрисовки в реальном времени;
3. обновление прошивки с продолжением после разрыва соединения.
Носитель не меняет типы сообщений или payload. Меняется только способ доставки
целого SETP-кадра.
## 2. Общий кадр
```text
offset size field
0 2 SOF = A5 5A
2 1 version = 02
3 1 flags
4 2 message_type u16 LE
6 2 source u16 LE
8 2 destination u16 LE
10 2 sequence u16 LE
12 2 payload_length u16 LE
14 N payload
14+N 4 CRC32 IEEE u32 LE
```
CRC32 считается от `version` (offset 2) до последнего байта payload. Полином
`0xEDB88320`, init/final XOR `0xFFFFFFFF`; проверочное значение строки
`123456789``0xCBF43926`.
Максимальный payload базового профиля — 512 байт. Реализация может объявить
меньший предел через `CAPABILITIES`, но не может молча принять начало большого
кадра и отбросить его конец.
Эталонный PING к узлу `0x002A`, sequence `0x1234`, с `ACK_REQUIRED`:
```text
A5 5A 02 08 01 00 00 00 2A 00 34 12 00 00 33 EC 33 04
```
## 3. Флаги и транзакции
| Бит | Имя | Смысл |
|---:|---|---|
| 0 | `RESPONSE` | ответ; тип и sequence повторяют запрос |
| 1 | `EVENT` | самостоятельная публикация, не ответ |
| 2 | `ERROR` | status ответа не равен `OK` |
| 3 | `ACK_REQUIRED` | отправитель требует явный ответ |
| 4 | `MORE` | за этим логическим куском последуют другие |
| 5 | `PRIORITY` | приоритет над обычной телеметрией |
| 7..6 | — | передавать нулями |
Запрос содержит `RESPONSE=0`, `EVENT=0`. Ответ содержит `RESPONSE=1`, тот же
`message_type` и `sequence`, а первые два байта payload всегда являются
`status u16`. Push-телеметрия содержит `EVENT=1`; её `sequence` — счётчик
кадров источника и позволяет заметить потерю.
`source/destination = 0` означает локальный узел в точке-точке. `0xFFFF`
broadcast; на broadcast-запрос отвечать нельзя, если прикладная команда явно
не задаёт безопасное окно ответа.
## 4. Стабильные типы сообщений
| Диапазон | Назначение |
|---|---|
| `0x0000..0x00FF` | системные команды и карта данных |
| `0x0100..0x01FF` | обновление прошивки |
| `0x0200..0x0FFF` | зарезервировано общей спецификацией |
| `0x1000..0x7FFF` | команды конкретного изделия |
| `0x8000..0xFFFF` | зарезервировано |
Общие команды:
| Код | Имя | Назначение |
|---:|---|---|
| `0x0001` | `PING` | доступность и uptime |
| `0x0002` | `DEVICE_INFO` | модель, версии, серийный номер |
| `0x0003` | `CAPABILITIES` | интерфейсы, MTU, функции, лимиты |
| `0x0008` | `DIAGNOSTICS` | счётчики транспорта и приложения |
| `0x0009` | `READ` | чтение 32-битно адресуемой карты |
| `0x000A` | `WRITE` | транзакционная запись карты |
| `0x0010` | `LOG_READ` | чтение журналов блоками |
| `0x0011` | `CATALOG` | метаданные общей карты |
| `0x0012` | `SUBSCRIBE` | создать/изменить поток данных |
| `0x0013` | `PUBLISH` | пакет значений для отрисовки |
| `0x0014` | `UNSUBSCRIBE` | удалить подписку |
| `0x0100..0105` | `FW_*` | обновление и активация прошивки |
Неизвестный тип не является ошибкой кадрирования. Устройство отвечает
`UNSUPPORTED`, если запрос был адресован ему и требовал ответа.
## 5. Телеметрия реального времени
### SUBSCRIBE
```text
subscription_id u16
period_ms u32 (0 = по изменению)
address_count u16
addresses u32[address_count]
```
Ответ сообщает status и фактически принятый период. Устройство вправе увеличить
слишком короткий период. Подписка принадлежит соединению/источнику и удаляется
при его закрытии либо командой `UNSUBSCRIBE`.
### PUBLISH
```text
subscription_id u16
sample_sequence u16
timestamp_ms u32
item_count u16
repeat item_count times:
address u32
encoding u8 (U16/I16/U32/I32/F32/BYTES)
element_count u8
data_length u16
data u8[data_length]
```
`timestamp_ms` — монотонное время устройства. GUI строит графики по нему, а не
по моменту прихода в Windows. Большой массив, например спектр, разбивается на
несколько `PUBLISH` с `MORE`; адрес и `sample_sequence` остаются теми же.
Телеметрия имеет меньший приоритет, чем ответы и прошивка. При переполнении
очереди разрешено отбросить старый `PUBLISH`, но нельзя частично передать кадр.
## 6. Прошивка
Типы:
| Код | Команда |
|---:|---|
| `0x0100` | `FW_BEGIN` |
| `0x0101` | `FW_DATA` |
| `0x0102` | `FW_END` |
| `0x0103` | `FW_ABORT` |
| `0x0104` | `FW_STATUS` |
| `0x0105` | `FW_ACTIVATE` |
`FW_BEGIN` содержит размер, CRC32 и SHA-256 образа, версию, базовый адрес,
целевой слот, желаемый размер блока, ID ключа и необязательную подпись. Подпись
проверяется над каноническим manifest, а не над полученными по частям данными:
```text
ASCII "SETPFW2\0" || image_size || image_crc32 || image_version ||
base_address || slot || sha256
```
Числа manifest также little-endian. Рекомендуемая подпись — Ed25519 (64 байта).
Конкретный загрузчик может потребовать подписанный образ и вернуть `AUTH_FAILED`
для неподписанного.
`FW_DATA`:
```text
offset u32 | data_length u16 | flags u16 | data_crc32 u32 | data[]
```
Ответ возвращает status и `next_offset u32`. Повтор уже записанного блока с теми
же данными обязан быть идемпотентным. После потери связи GUI запрашивает
`FW_STATUS` и продолжает с `next_offset`.
`FW_END` повторяет размер, CRC32 и SHA-256. Устройство проверяет весь образ и
только затем переводит слот в `READY`. `FW_ACTIVATE` меняет загрузочный слот;
операция обновления не должна перезаписывать единственный рабочий образ. Для
серийных устройств требуется A/B или эквивалентный механизм rollback.
CRC32 защищает линию, SHA-256 — целостность образа, подпись — происхождение.
Один CRC не является защитой от подмены прошивки.
## 7. Привязки к физическим интерфейсам
### RS-232, RS-485, USB CDC
Кадры передаются подряд как поток байтов. Parser обязан восстанавливаться после
мусора и битого CRC. На RS-485 используются `source/destination`; передача
broadcast не должна запускать прошивку или запись конфигурации.
### Ethernet TCP
TCP несёт тот же поток кадров без дополнительной длины: она уже есть в header.
Один `recv()` может вернуть часть кадра или несколько кадров. Порт по умолчанию
задаётся приложением; рекомендуемое значение проекта — `25060`, оно не считается
зарегистрированным IANA. Для внешних сетей используется TLS, SETP внутри TLS не
меняется.
### Ethernet UDP
Одна UDP-датаграмма содержит ровно один полный SETP-кадр. Датаграммы с хвостом,
двумя кадрами или несовпадающей длиной отбрасываются. Базовый payload 512 байт
не превышает безопасный IPv4 MTU. Прошивка по UDP допустима только в режиме
stop-and-wait с `ACK_REQUIRED`; предпочтителен TCP.
### CAN
Через классический CAN передаются байты того же полного SETP-кадра. Повторно
кодировать команды в поля CAN ID нельзя.
Extended CAN ID:
```text
28..24 prefix = 0x12
23..16 destination (младшие 8 бит SETP destination)
15..8 source (младшие 8 бит SETP source)
7 priority
6..0 channel
```
CAN-профиль использует node `1..254`; `0` и `255` сохраняют смысл local и
broadcast. Поля полного SETP-заголовка остаются обязательными и должны совпасть
с CAN ID после reassembly.
PCI классического CAN:
```text
FIRST: data[0]=0x10, data[1..2]=total_length u16 LE, data[3..7]=5 байт
CONSECUTIVE: data[0]=0x20|SN, data[1..7]=до 7 байт, SN начинается с 1
FLOW_CONTROL:data[0]=0x30|status, data[1]=block_size, data[2]=st_min_ms
```
`status`: 0 continue, 1 wait, 2 overflow. Номер сегмента идёт по модулю 16.
Timeout сборки по умолчанию 500 мс. Новый FIRST заменяет незавершённую сборку
того же канала. CAN-FD может увеличить данные сегмента в следующей версии
binding, не меняя SETP frame/message/payload.
## 8. Совместимость
GUI protocol v1 и SETP v2 несовместимы. Автоопределение допускается только во
время миграции: клиент посылает v2 PING, затем при полном тайм-ауте пробует v1.
После первого корректного ответа формат соединения фиксируется до отключения.
Новое устройство не должно одновременно публиковать v1 и v2 в одном потоке.