18 KiB
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. Граница ответственности
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
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
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
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
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.
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 — порты снифера, а не новая реализация протокола:
SLCAN text / struct can_frame
│ adapter
▼
can_id + flags + data
│
▼
общий decoder/UI
8. Сборка
CMake: Windows, Linux, macOS
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-сборка
python c/set-protocol/tools/build_host.py --output native/setprotocol.dll
python3 c/set-protocol/tools/build_host.py --output native/libsetprotocol.so
На Windows tool использует MSVC, на Unix ищет cc, clang или gcc.
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)
Если библиотека лежит вне стандартного дерева:
export SETPROTOCOL_LIBRARY=/opt/set/lib/libsetprotocol.so
В 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
#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:
- CMake build и
ctestдляtest_transportиtest_abi. - Сверка машинных векторов
tests/vectors/test-vectors.json. - Python-тесты с обязательной загрузкой native core.
- Android unit tests и
assembleDebug, если менялись ABI/JNI/Kotlin. - Smoke-test целевого порта на реальной линии.
Канонический код находится в templates/c/set-protocol. Проекты должны
получать его как Git submodule и фиксировать конкретный commit, а не хранить
разошедшиеся копии.