Единое ядро форматов
Windows, Android и MCU больше не реализуют CRC, порядок байт и ресинхронизацию каждый по-своему.
Одно C99-ядро для SETGUI, Android, Linux и микроконтроллеров. SET v2, ProtoCAN и GUI v1 собраны вместе; COM, SLCAN, SocketCAN и HAL подключаются снаружи.
SETProtocol — не драйвер адаптера и не GUI framework. Это детерминированная протокольная часть, одинаковая для каждой программы и платы.
Windows, Android и MCU больше не реализуют CRC, порядок байт и ресинхронизацию каждый по-своему.
Смена COM на SocketCAN заменяет адаптер ввода-вывода, но не декодер протокола и не тестовые векторы.
FFI использует публичный setprotocol_abi.h версии 1; внутренние C-структуры наружу не протекают.
500000 у COM — скорость последовательного интерфейса адаптера. CAN bitrate на линии задаётся отдельно в адаптере/драйвере. SETProtocol получает уже доставленные байты или CAN-кадры.can_id + flags + data. После этого таблица, фильтры и ProtoCAN-декодер общие.Нижний слой не знает, кто его вызвал. Верхний слой не должен знать внутреннюю раскладку parser context.
pcan_id упаковывает Priority, Route, DeviceType, DeviceID, MsgType и Body в Extended ID без непереносимых bit-fields.
pcan_frame кодирует и разбирает AA55; pcan_crc даёт CRC16; pcan_link ведёт SEQ и статистику.
gui_frame реализует отдельный A55A transport с payload до 512 байт и CRC32.
pcan_gas входит в shared core и отображает данные в 16-битное пространство. gui_catalog — опциональный C-модуль вне DLL.
pcan_ring — SPSC-кольцо с непрерывным участком, удобным для DMA. Размер буфера — степень двойки.
pcan_abi экспортирует только скалярные значения и буферы известных размеров для ctypes/JNI/Swift.
| Вне ядра | Почему | Где реализовать |
|---|---|---|
| COM/SLCAN/SocketCAN | ОС и конкретный адаптер | desktop/android port |
| CAN bitrate и UART baud | Настройка физического интерфейса | driver/config UI |
| HAL, IRQ, DMA | Зависят от MCU и SDK | ports/<platform> |
| Вкладки и графики | Представление, не протокол | SETGUI / Android GUI |
| Firmware A/B flow | Отдельная предметная библиотека | protocan-boot |
SET v2 — основной протокол новых устройств. ProtoCAN bridge и GUI v1 остаются для совместимости на время перехода.
Единый кадр для управления, телеметрии и firmware flow поверх serial, USB, Ethernet и сегментированного CAN.
schema u16 · class u16 · hardware u32 · firmware u32 · dictionary u32 · serial u64 · model_length u8 · model UTF-8
Модель ограничена 63 байтами. Числа little-endian; body идёт после обязательного status u16.
schema u16 · MTU u16 · interfaces u32 · features u32 · read/write/subscription/publish limits u16
Флаги объявляют READ, WRITE, CATALOG, SUBSCRIBE, LOG_READ, FIRMWARE и DIAGNOSTICS. GUI включает только реально доступные функции.
LEN = 6 + DLC, максимум 19 байт. CRC-16/CCITT-FALSE считается от LEN до DATA.
Версия 1. CRC32 IEEE как у zlib. Это самостоятельный протокол, не CAN DLC.
Parser принимает один байт, половину кадра или несколько кадров подряд. Граница read() не имеет протокольного смысла.
После шума parser снова ищет SOF, отбрасывает неверную длину/CRC и продолжает поток, сохраняя диагностические счётчики.
Биты 28…0 упаковываются масками и сдвигами. Это исключает зависимость от реализации C bit-fields.
Приложение сначала проверяет версию, затем работает через функции из pcan_abi.h. Внутренние структуры можно менять, сохраняя ABI.
| Группа | Функции | Контракт |
|---|---|---|
| Версия и ID | version, id_pack, id_unpack | Скалярные аргументы, 29-битный результат |
| CAN frame | crc16, frame_encode | Caller-owned input/output buffers |
| CAN parser | parser_size/init/push/stats | Opaque caller-owned context |
| GUI frame | gui_crc32, gui_frame_encode | Payload не более 512 байт |
| GUI parser | gui_parser_size/init/push/stats | Opaque caller-owned context |
Ядро не выделяет память. Python использует create_string_buffer, MCU — static/stack, JNI хранит allocation только в адаптере.
Каждая линия имеет собственный context. Можно одновременно держать COM bridge, GUI transport и несколько устройств.
Один context — один владелец потока. Если владельцев несколько, блокировку добавляет приложение.
size_t bytes = pcan_abi_parser_size();
void *context = allocate_on_host_or_static_storage(bytes);
pcan_abi_parser_init(context, bytes);
if (pcan_abi_parser_push(context, next_byte, &frame) == 1) {
/* frame полностью проверен */
}
Переносимость ядра и готовность полной упаковки приложения — разные вещи. Здесь они разделены честно.
| Цель | Библиотека | Адаптер | Статус |
|---|---|---|---|
| Windows | setprotocol.dll | Python ctypes / COM | проверено |
| Android | libsetprotocol.so | JNI + Kotlin | 4 ABI |
| Linux | libsetprotocol.so | ctypes; нужен pyserial/SocketCAN port | ядро готово |
| macOS | libsetprotocol.dylib | ctypes/FFI | исходники готовы |
| STM32F4 | статический C99 | UART + DMA | порт есть |
| Другой MCU | статический C99 | I/O callbacks | нужен порт |
| iOS | C ABI | Swift wrapper | не добавлен |
libsetprotocol.so, указать SETPROTOCOL_LIBRARY, затем добавить backend физической линии. Для прямого CAN логичен SocketCAN; для USB-COM моста — pyserial.CMake создаёт static core, shared ABI и тесты из одного набора C99-файлов.
Подключить репозиторий как Git submodule и зафиксировать проверенный commit.
На host — CMake или build_host.py; на Android — NDK; на MCU — добавить исходники в проект.
COM, SLCAN, SocketCAN, USB или HAL только доставляет данные через узкую границу.
C tests, ABI vectors, Python native tests, Android build и smoke-test реальной линии.
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
python3 c/set-protocol/tools/build_host.py \
--output native/libsetprotocol.so
export SETPROTOCOL_LIBRARY="$PWD/native/libsetprotocol.so"
from protocan.native import NativeProtocol
core = NativeProtocol()
wire = core.encode(1, 1, 0x1234567, b"\xAA\xBB")
frames = core.parser().feed(wire)
Этот раздел генерируется из канонического c/set-protocol/docs/SETPROTOCOL.md. Редактировать нужно Markdown, затем запускать doc/build-setprotocol-html.ps1.
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 проверяет и интерпретирует их одинаково на всех платформах.
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
В ядре находятся:
ctypes, JNI и будущего Swift/FFI.За пределами ядра остаются:
COM6, can0 или Bluetooth/USB endpoint;Отсюда следует важное правило: 500000 на экране COM — это baud rate последовательного моста, а 500 kbit/s в CAN-настройках — bitrate самой CAN-шины. Ядро не подменяет одно другим и не выбирает эти значения автоматически.
| Модуль | Роль | Платформенные зависимости |
|---|---|---|
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.
SETProtocol поддерживает основной v2 и два legacy-формата. После первого корректного ответа формат соединения фиксируется до отключения.
AA 55AA 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 |
служебный кадр диагностики моста |
A5 5AA5 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() не считаются
границами протокольных сообщений.
A5 5A 02A5 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.
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.
pcan_abi.h экспортирует простые числа, указатели и явно ограниченные буферы.
Текущая версия возвращается pcan_abi_version() и равна 1.
| Функция | Назначение |
|---|---|
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 |
| Функция | Назначение |
|---|---|
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 при готовом кадре.
В переносимом C-слое нет malloc, singleton и скрытого глобального parser.
Каждый канал имеет собственное состояние. В ABI вызывающий сначала спрашивает
его размер, выделяет байтовый блок и передаёт его в init.
size_t size = pcan_abi_parser_size();
void *storage = /* память вызывающей стороны размером size */;
pcan_abi_parser_init(storage, size);
Это позволяет:
ctypes.create_string_buffer() в Python;Один parser context нельзя одновременно изменять из нескольких потоков. Правильная модель — один владелец на канал или внешняя блокировка. Кольцевой буфер рассчитан на одного писателя и одного читателя (SPSC). Для нескольких писателей синхронизацию обеспечивает порт/приложение.
| Среда | Артефакт | Состояние |
|---|---|---|
| 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 пока не добавлен |
Само ядро не содержит 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
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.
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.
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'
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() вызывается в безопасном контексте приложения.
#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-обёртки.
| Симптом | Что проверить |
|---|---|
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; проверьте настройку самого адаптера |
PCAN_ABI_VERSION.DLC <= 8; CAN FD не включён.GUI_RX_PAYLOAD_MAX можно уменьшить.protocan-boot.Минимальный quality gate:
ctest для test_transport и test_abi.tests/vectors/test-vectors.json.assembleDebug, если менялись ABI/JNI/Kotlin.Канонический код находится в templates/c/set-protocol. Проекты должны
получать его как Git submodule и фиксировать конкретный commit, а не хранить
разошедшиеся копии.