239 lines
12 KiB
Markdown
239 lines
12 KiB
Markdown
# 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 в одном потоке.
|