feat: add unified SET protocol v2
This commit is contained in:
238
c/set-protocol/PROTOCOL.md
Normal file
238
c/set-protocol/PROTOCOL.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# 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 в одном потоке.
|
||||
Reference in New Issue
Block a user