343 lines
18 KiB
Markdown
343 lines
18 KiB
Markdown
# SETProtocol — переносимое протокольное ядро
|
||
|
||
SETProtocol — общее C99-ядро для `SETGUI`, Android GUI, прошивок и утилит.
|
||
Оно объединяет основной SET protocol v2 и поддерживаемые форматы переходного
|
||
периода: ProtoCAN bridge и SETGUI transport v1. Windows, Linux, Android и
|
||
микроконтроллер используют одинаковые правила кадра, CRC, адресации,
|
||
телеметрии, обновления и потокового разбора.
|
||
|
||
Ядро **не открывает COM-порт, CAN-адаптер или сокет**. COM, SLCAN, SocketCAN,
|
||
USB CDC, TCP и аппаратный CAN относятся к портам. Они доставляют байты или
|
||
CAN-кадры, а SETProtocol проверяет и интерпретирует их одинаково на всех
|
||
платформах.
|
||
|
||
## 1. Граница ответственности
|
||
|
||
```text
|
||
SETGUI / Android GUI / CLI / firmware
|
||
│ прикладные команды и события
|
||
▼
|
||
Python facade / JNI / прямой C API
|
||
│ стабильный ABI или C99 API
|
||
▼
|
||
┌──────────────────────── SETProtocol ────────────────────────┐
|
||
│ SET v2 │ ProtoCAN ID │ v1 parsers │ CRC │ GAS │ telemetry │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│ байты или нормализованный CAN frame
|
||
▼
|
||
COM │ SLCAN │ SocketCAN │ USB CDC │ TCP │ STM32 UART/CAN
|
||
```
|
||
|
||
В ядре находятся:
|
||
|
||
- форматы проводных кадров и порядок байт;
|
||
- SET protocol v2: команды, адресация, подписки и firmware state machines;
|
||
- проверка длины, версии, DLC и контрольной суммы;
|
||
- восстановление синхронизации после мусора или оборванного кадра;
|
||
- упаковка и разбор ProtoCAN Extended ID;
|
||
- счётчики качества входного потока;
|
||
- общее адресное пространство регистров (GAS);
|
||
- стабильная C ABI-граница для `ctypes`, JNI и будущего Swift/FFI.
|
||
|
||
За пределами ядра остаются:
|
||
|
||
- поиск устройств и выбор `COM6`, `can0` или Bluetooth/USB endpoint;
|
||
- скорость UART и CAN bitrate;
|
||
- драйверы SLCAN, SocketCAN, PCAN, CANable и vendor SDK;
|
||
- разрешения Android USB и жизненный цикл приложения;
|
||
- виджеты, вкладки, таблицы, графики и хранение настроек;
|
||
- HAL, IRQ, DMA, RTOS, Flash и распиновка платы.
|
||
|
||
Отсюда следует важное правило: **500000 на экране COM — это baud rate
|
||
последовательного моста, а 500 kbit/s в CAN-настройках — bitrate самой CAN-шины.
|
||
Ядро не подменяет одно другим и не выбирает эти значения автоматически.**
|
||
|
||
## 2. Состав исходников
|
||
|
||
| Модуль | Роль | Платформенные зависимости |
|
||
|---|---|---|
|
||
| `set_protocol` | SET v2 frame, CRC32, stream/datagram parser | нет |
|
||
| `set_can` | CAN segmentation, flow control и reassembly | доставка CAN frame и время |
|
||
| `set_telemetry` | подписки и типизированные PUBLISH-пакеты | часы/callbacks приложения |
|
||
| `set_firmware` | BEGIN/DATA/END/STATUS и resume state machine | Flash/verify/reboot backend |
|
||
| `pcan_id` | Упаковка/разбор 29-битного ProtoCAN ID | нет |
|
||
| `pcan_crc` | CRC-16/CCITT-FALSE | нет |
|
||
| `pcan_frame` | Формат `AA 55`, encode и потоковый parser | нет |
|
||
| `gui_frame` | Формат `A5 5A`, CRC32, encode, parser и link | нет |
|
||
| `pcan_link` | Экземпляр канала, SEQ, RX/TX и статистика | два callback порта |
|
||
| `pcan_ring` | SPSC-кольцо и непрерывный участок для DMA | нет |
|
||
| `pcan_gas` | Карта 16-битных регистров и bridge к кадрам | callbacks региона |
|
||
| `gui_catalog` | C-каталог публикуемых GUI-полей | нет; входит в общий shared build |
|
||
| `pcan_abi` | Экспорт скалярного ABI для FFI | ABI компилятора C |
|
||
|
||
Общая точка включения для C-кода — `include/setprotocol.h`.
|
||
Иностранные runtimes должны использовать `include/setprotocol_abi.h`, а не
|
||
повторять внутреннюю раскладку `pcan_parser_t` или `gui_parser_t`.
|
||
Shared-библиотека `setprotocol` содержит SET v2 и совместимые legacy-модули.
|
||
ABI v1 пока экспортирует функции `pcan_abi_*`: имена намеренно сохранены для
|
||
бинарной совместимости SETGUI/Android. Расширение ABI для прямого SET v2 FFI
|
||
должно быть совместимым добавлением или новой версией ABI.
|
||
|
||
## 3. Три поддерживаемых wire format
|
||
|
||
SETProtocol поддерживает основной v2 и два legacy-формата. После первого
|
||
корректного ответа формат соединения фиксируется до отключения.
|
||
|
||
### 3.1. CAN bridge: `AA 55`
|
||
|
||
```text
|
||
AA 55 | LEN | SEQ | FLAGS | CAN_ID[4] LE | DATA[0..8] | CRC16 LE
|
||
```
|
||
|
||
`LEN = 6 + DLC`, поэтому допустимый диапазон — `6..14`. CRC-16/CCITT-FALSE
|
||
считается по участку от `LEN` до последнего байта `DATA`. Максимальный размер
|
||
кадра — 19 байт. Формат переносит один classic CAN 2.0 кадр через COM, USB CDC,
|
||
RS-232, RS-485 или TCP byte stream.
|
||
|
||
Флаги:
|
||
|
||
| Бит | Имя | Значение |
|
||
|---:|---|---|
|
||
| 0 | `IDE` | расширенный 29-битный CAN ID |
|
||
| 1 | `RTR` | remote frame |
|
||
| 2 | `DIR` | `0` из CAN в host, `1` из host в CAN |
|
||
| 3 | `ERR` | служебный кадр диагностики моста |
|
||
|
||
### 3.2. GUI transport: `A5 5A`
|
||
|
||
```text
|
||
A5 5A | VER | TYPE | SEQ[2] BE | SIZE[2] BE | PAYLOAD[0..512] | CRC32 LE
|
||
```
|
||
|
||
Версия сейчас равна `1`. Заголовочные `SEQ` и `SIZE` идут big-endian, CRC32
|
||
IEEE — little-endian. Payload до 512 байт нужен для каталога, чтения/записи
|
||
регистров, диагностики и потока значений. Это не CAN-кадр и у него нет DLC.
|
||
|
||
Оба parser принимают произвольные chunks: один вызов может содержать половину
|
||
кадра, несколько кадров или мусор между ними. Границы `read()` не считаются
|
||
границами протокольных сообщений.
|
||
|
||
### 3.3. SET protocol v2: `A5 5A 02`
|
||
|
||
```text
|
||
A5 5A | VER=02 | FLAGS | TYPE u16 LE | SOURCE u16 LE | DEST u16 LE |
|
||
SEQ u16 LE | SIZE u16 LE | PAYLOAD[0..512] | CRC32 LE
|
||
```
|
||
|
||
Это основной формат новых устройств. Он одинаков поверх RS-232/485, USB CDC,
|
||
TCP и UDP; CAN переносит байты полного v2-кадра через сегментацию. В v2
|
||
объединены запросы/ответы, события телеметрии и firmware flow. Нормативный
|
||
контракт находится в `PROTOCOL.md`.
|
||
|
||
## 4. ProtoCAN Extended ID
|
||
|
||
```text
|
||
28 27 26..24 23..20 19..16 15..0
|
||
Priority | Route | DeviceType | DeviceID | MsgType | MsgBody
|
||
```
|
||
|
||
Для переносимости используются маски и сдвиги, а не C bit-fields. ABI-функции
|
||
`pcan_abi_id_pack()` и `pcan_abi_id_unpack()` дают одинаковую раскладку при
|
||
MSVC, GCC и Clang.
|
||
|
||
## 5. Стабильный ABI v1
|
||
|
||
`pcan_abi.h` экспортирует простые числа, указатели и явно ограниченные буферы.
|
||
Текущая версия возвращается `pcan_abi_version()` и равна `1`.
|
||
|
||
### CAN bridge API
|
||
|
||
| Функция | Назначение |
|
||
|---|---|
|
||
| `pcan_abi_version` | Проверить совместимость загруженной библиотеки |
|
||
| `pcan_abi_id_pack/unpack` | Преобразовать поля ProtoCAN ID |
|
||
| `pcan_abi_crc16` | Рассчитать CRC-16/CCITT-FALSE |
|
||
| `pcan_abi_frame_encode` | Собрать целый `AA55` кадр |
|
||
| `pcan_abi_parser_size` | Узнать размер opaque parser context |
|
||
| `pcan_abi_parser_init` | Инициализировать память, принадлежащую вызывающему |
|
||
| `pcan_abi_parser_push` | Передать один байт; `1` означает готовый кадр |
|
||
| `pcan_abi_parser_stats` | Получить frames/CRC/bad length/stray bytes |
|
||
|
||
### GUI API
|
||
|
||
| Функция | Назначение |
|
||
|---|---|
|
||
| `pcan_abi_gui_crc32` | Рассчитать CRC32 IEEE |
|
||
| `pcan_abi_gui_frame_encode` | Собрать целый `A55A` кадр |
|
||
| `pcan_abi_gui_parser_size/init/push` | Управлять opaque GUI parser context |
|
||
| `pcan_abi_gui_parser_stats` | Получить frames/CRC/version/length/stray bytes |
|
||
|
||
Возврат `0` из encode означает неверные аргументы или недостаточный output
|
||
buffer. Parser API возвращает отрицательное значение при неверном context,
|
||
`0` пока кадр не собран и `1` при готовом кадре.
|
||
|
||
## 6. Память, состояние и многопоточность
|
||
|
||
В переносимом C-слое нет `malloc`, singleton и скрытого глобального parser.
|
||
Каждый канал имеет собственное состояние. В ABI вызывающий сначала спрашивает
|
||
его размер, выделяет байтовый блок и передаёт его в `init`.
|
||
|
||
```c
|
||
size_t size = pcan_abi_parser_size();
|
||
void *storage = /* память вызывающей стороны размером size */;
|
||
pcan_abi_parser_init(storage, size);
|
||
```
|
||
|
||
Это позволяет:
|
||
|
||
- держать память статически на MCU;
|
||
- использовать `ctypes.create_string_buffer()` в Python;
|
||
- выделять handle только в JNI-адаптере;
|
||
- одновременно разбирать несколько независимых линий.
|
||
|
||
Один parser context нельзя одновременно изменять из нескольких потоков.
|
||
Правильная модель — один владелец на канал или внешняя блокировка. Кольцевой
|
||
буфер рассчитан на одного писателя и одного читателя (SPSC). Для нескольких
|
||
писателей синхронизацию обеспечивает порт/приложение.
|
||
|
||
## 7. Порты и адаптеры
|
||
|
||
| Среда | Артефакт | Состояние |
|
||
|---|---|---|
|
||
| Windows desktop | `setprotocol.dll` + Python `ctypes` | используется SETGUI, проверено тестами |
|
||
| Android | `libsetprotocol.so` + JNI + Kotlin facade | сборка ABI `arm64-v8a`, `armeabi-v7a`, `x86`, `x86_64` проверяется Android build |
|
||
| Linux desktop | `libsetprotocol.so` + тот же ABI | ядро и сборщик готовы; нужен Linux CI/smoke-test приложения |
|
||
| macOS | `libsetprotocol.dylib` + тот же ABI | исходники совместимы; отдельная упаковка не проверена |
|
||
| STM32F4 | прямой C99 + UART/DMA port | готовый порт в `ports/stm32f4` |
|
||
| Другой MCU | прямой C99 | реализуются только callbacks I/O/времени/памяти |
|
||
| iOS/Swift | C ABI | ABI подходит, Swift wrapper пока не добавлен |
|
||
|
||
### Linux
|
||
|
||
Само ядро не содержит WinAPI, поэтому собирается GCC или Clang. Для SETGUI под
|
||
Linux остаются две отдельные задачи: упаковать `libsetprotocol.so` с приложением и
|
||
подключить нужный физический backend (`pyserial` для USB-COM или SocketCAN для
|
||
`can0`). Правила кадра, CRC и ID менять не потребуется.
|
||
|
||
SLCAN и SocketCAN — **порты снифера**, а не новая реализация протокола:
|
||
|
||
```text
|
||
SLCAN text / struct can_frame
|
||
│ adapter
|
||
▼
|
||
can_id + flags + data
|
||
│
|
||
▼
|
||
общий decoder/UI
|
||
```
|
||
|
||
## 8. Сборка
|
||
|
||
### CMake: Windows, Linux, macOS
|
||
|
||
```bash
|
||
cmake -S c/set-protocol -B build/setprotocol -DSETP_BUILD_TESTS=ON
|
||
cmake --build build/setprotocol --config Release
|
||
ctest --test-dir build/setprotocol -C Release --output-on-failure
|
||
```
|
||
|
||
Результат shared-сборки называется `setprotocol.dll`, `libsetprotocol.so` или
|
||
`libsetprotocol.dylib`. Статическая цель называется `setprotocol_static`;
|
||
совместимое имя CMake-цели SET v2 — `set_protocol`.
|
||
|
||
### Упрощённая host-сборка
|
||
|
||
```powershell
|
||
python c/set-protocol/tools/build_host.py --output native/setprotocol.dll
|
||
```
|
||
|
||
```bash
|
||
python3 c/set-protocol/tools/build_host.py --output native/libsetprotocol.so
|
||
```
|
||
|
||
На Windows tool использует MSVC, на Unix ищет `cc`, `clang` или `gcc`.
|
||
|
||
### Python
|
||
|
||
```python
|
||
from protocan.native import NativeProtocol
|
||
|
||
core = NativeProtocol()
|
||
raw = core.encode(sequence=1, flags=1, can_id=0x1234567, data=b"\xAA\xBB")
|
||
frames = core.parser().feed(raw)
|
||
```
|
||
|
||
Если библиотека лежит вне стандартного дерева:
|
||
|
||
```bash
|
||
export SETPROTOCOL_LIBRARY=/opt/set/lib/libsetprotocol.so
|
||
```
|
||
|
||
В PowerShell:
|
||
|
||
```powershell
|
||
$env:SETPROTOCOL_LIBRARY = 'C:\set\native\setprotocol.dll'
|
||
```
|
||
|
||
### Android
|
||
|
||
`ports/android/Android.mk` компилирует те же C-файлы. Kotlin-класс
|
||
`ru.setcorp.setprotocol.NativeSetProtocol` отвечает только за удобный API, а JNI — за
|
||
преобразование типов и время жизни parser handle.
|
||
|
||
### Микроконтроллер
|
||
|
||
Добавьте нужные `src/*.c` и каталог `include/` в проект. Порт STM32F4 не входит
|
||
автоматически в host CMake, потому что ему нужен CMSIS. Для другой платы
|
||
реализуйте `pcan_io_t.write` и `pcan_io_t.tx_space`; ISR/DMA лишь складывает
|
||
байты, а `pcan_link_feed()` вызывается в безопасном контексте приложения.
|
||
|
||
## 9. Пример прямого ABI
|
||
|
||
```c
|
||
#include "pcan_abi.h"
|
||
|
||
uint8_t output[19];
|
||
const uint8_t data[] = {0xAA, 0xBB};
|
||
const uint8_t flags = 0x01U; /* IDE */
|
||
uint32_t id = pcan_abi_id_pack(1, 0, 2, 3, 4, 0x1234);
|
||
size_t written = pcan_abi_frame_encode(
|
||
7, flags, id, data, sizeof(data), output, sizeof(output));
|
||
```
|
||
|
||
Для firmware удобнее полный C API из `protocan_transport.h`: он даёт link,
|
||
callbacks, ring и GAS без FFI-обёртки.
|
||
|
||
## 10. Диагностика
|
||
|
||
| Симптом | Что проверить |
|
||
|---|---|
|
||
| `crc_errors` растёт | bitrate/baud, ground, termination, порядок байт, потерю chunks |
|
||
| `bad_len`/`length_errors` | выбран ли правильный формат `AA55` или `A55A` |
|
||
| `version_errors` | версия GUI transport должна быть `1` |
|
||
| много `stray_bytes` | начало чтения посреди пакета допустимо; постоянный рост означает неверный порт |
|
||
| DLL/SO не найдена | путь, архитектуру процесса и `SETPROTOCOL_LIBRARY` |
|
||
| Android `UnsatisfiedLinkError` | имя `setprotocol`, ABI устройства и упаковку `jniLibs`/NDK |
|
||
| CAN пустой, но COM открыт | COM baud не равен CAN bitrate; проверьте настройку самого адаптера |
|
||
|
||
## 11. Совместимость и ограничения
|
||
|
||
- ABI v1 изменяется только совместимым добавлением функций. Ломающее изменение
|
||
требует нового значения `PCAN_ABI_VERSION`.
|
||
- Wire format нельзя менять без версии/миграционного документа и тестовых
|
||
векторов для C, Python и Android.
|
||
- CAN bridge сейчас рассчитан на classic CAN: `DLC <= 8`; CAN FD не включён.
|
||
- GUI payload ограничен 512 байтами; на MCU `GUI_RX_PAYLOAD_MAX` можно уменьшить.
|
||
- В ядре нет готового SocketCAN/SLCAN/vendor backend: это следующий слой портов.
|
||
- В ядро не входят виджеты GUI, настройки COM/CAN и обновление прошивки целиком.
|
||
Для firmware flow существует отдельная библиотека `protocan-boot`.
|
||
|
||
## 12. Проверка изменений
|
||
|
||
Минимальный quality gate:
|
||
|
||
1. CMake build и `ctest` для `test_transport` и `test_abi`.
|
||
2. Сверка машинных векторов `tests/vectors/test-vectors.json`.
|
||
3. Python-тесты с обязательной загрузкой native core.
|
||
4. Android unit tests и `assembleDebug`, если менялись ABI/JNI/Kotlin.
|
||
5. Smoke-test целевого порта на реальной линии.
|
||
|
||
Канонический код находится в `templates/c/set-protocol`. Проекты должны
|
||
получать его как Git submodule и фиксировать конкретный commit, а не хранить
|
||
разошедшиеся копии.
|