refactor: merge protocol cores as SETProtocol
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
build/
|
||||
cmake-build-*/
|
||||
*.o
|
||||
*.obj
|
||||
*.d
|
||||
*.a
|
||||
*.elf
|
||||
|
||||
13
README.md
13
README.md
@@ -30,10 +30,9 @@ templates/
|
||||
| [`c/eeprom-ft24c256`](c/eeprom-ft24c256) | EEPROM 24Cxx по I²C с нарезкой записи по страницам | `stdint.h` | две I²C-транзакции, задержка |
|
||||
| [`c/can-sensor`](c/can-sensor) | передача 64-битных ROM датчиков парой CAN-кадров | `stdint.h` | отправка и приём CAN-кадра |
|
||||
| [`c/ds18b20`](c/ds18b20) | термометры DS18B20 поверх программной 1-Wire | `stdint.h` | Init, DelayUs, Reset, WriteBit, ReadBit — **порты STM32F103, STM32G431 и STM32G474 в комплекте** |
|
||||
| [`c/protocan-transport`](c/protocan-transport) | транспорт ProtoCAN: кадр, канал, CRC, общее адресное пространство, каталог GUI и стабильный host ABI | `stdint.h` | запись в поток и запрос свободного места — **порты STM32F4, Windows/Linux/macOS FFI и Android JNI в комплекте** |
|
||||
| [`c/set-protocol`](c/set-protocol) | единое ядро SETProtocol: SET v2, совместимые ProtoCAN/GUI v1, GAS, телеметрия, firmware flow и стабильный host ABI | C99 | COM/SLCAN/SocketCAN/USB/Ethernet или callbacks — **Windows, Android и STM32F4-порты в комплекте** |
|
||||
| [`c/protocan-boot`](c/protocan-boot) | адресная прошивка по ProtoCAN: A/B-слоты, сессия, CRC32, verify и rollback-контракт | C99 | CAN TX, erase/write Flash, boot metadata, проверка образа и reboot |
|
||||
| [`c/rs485-boot`](c/rs485-boot) | прошивка по RS-485 в формате SETGUI v1: потоковый parser, CRC32 и resume | C99 | UART TX/RX, DE, Flash — **порты STM32F103 и STM32G474VET в комплекте** |
|
||||
| [`c/set-protocol`](c/set-protocol) | единый SET protocol v2: управление, real-time телеметрия и обновление прошивки через UART/CAN/USB/Ethernet | C99 | доставка целого stream/datagram-кадра, часы, backend карты и загрузчика |
|
||||
| [`c/rtc-service`](c/rtc-service) | RTC с резервированным backup-томом | `stdint.h` | доступ к RTC и backup-памяти — **порт K1921VK028 в комплекте** |
|
||||
| [`c/firmware-info`](c/firmware-info) | SemVer, build stamp и git build ID работающего образа | C99 | config-порты STM32F1/F4/G4 и К1921ВК028; упаковка для Modbus/SETGUI |
|
||||
|
||||
@@ -43,9 +42,13 @@ templates/
|
||||
|---|---|---|
|
||||
| [`python/protocan`](python/protocan) | разбор ProtoCAN, транспортный кадр моста, кадр SETGUI, кодеки каталога | stdlib, Python 3.9+ |
|
||||
|
||||
Кодировщики `c/protocan-transport` и `python/protocan` дают побайтово
|
||||
Кодировщики `c/set-protocol` и `python/protocan` дают побайтово
|
||||
одинаковый результат — это зафиксировано эталонами в тестах на C.
|
||||
|
||||
Интерактивная документация общего ядра: [`doc/setprotocol.html`](doc/setprotocol.html).
|
||||
В ней отдельно описаны граница ядра, ABI, память, три wire format и состояние
|
||||
портов Windows, Android, Linux и MCU.
|
||||
|
||||
## Как подключить к проекту
|
||||
|
||||
**Сабмодуль** — когда нужна одна конкретная версия и обновление по команде:
|
||||
@@ -78,10 +81,10 @@ git subtree pull --prefix lib/templates https://git.rd12.ru/Andrey/templates.git
|
||||
|---|---|
|
||||
| `KONOR_ds18b20` | st7789, keypad, menu, eeprom-ft24c256, can-sensor, ds18b20, firmware-info |
|
||||
| `OpticalTester` | st7789, keypad, menu, eeprom-ft24c256 |
|
||||
| `CAN_to_RS485` | protocan-transport, python/protocan |
|
||||
| `CAN_to_RS485` | set-protocol legacy transport, python/protocan |
|
||||
| `candleLight_fw` | эталон разделения общей логики и G431/G474 FDCAN-порта; общий ProtoCAN Boot переносится по контракту `protocan-boot` |
|
||||
| `SETGUI` | python/protocan |
|
||||
| новые устройства SET и `SETGUI` v2 | set-protocol |
|
||||
| `SETGUI`, Android GUI и новые устройства SET | set-protocol |
|
||||
| `k1921vk028` | rtc-service |
|
||||
|
||||
## Что сюда не попало и почему
|
||||
|
||||
@@ -43,7 +43,7 @@ STM32 HAL / другой MCU / host-тест
|
||||
| `tests/test_pcan_boot.c` | хостовые тесты с RAM вместо Flash | стандартная библиотека C |
|
||||
| `CMakeLists.txt` | сборка библиотеки и теста | CMake 3.13+ |
|
||||
|
||||
Модуль не зависит от `protocan-transport`: разбор пяти полей 29-битного ID
|
||||
Модуль не зависит от legacy-модулей `set-protocol`: разбор пяти полей 29-битного ID
|
||||
сделан переносимыми сдвигами, без непереносимых C-битовых полей.
|
||||
|
||||
## Карта `MsgType`
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(protocan_transport C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
# Ядро библиотеки: платформенных зависимостей нет, собирается под что угодно.
|
||||
set(PCAN_CORE_SOURCES
|
||||
src/gui_frame.c
|
||||
src/pcan_abi.c
|
||||
src/pcan_crc.c
|
||||
src/pcan_frame.c
|
||||
src/pcan_id.c
|
||||
src/pcan_link.c
|
||||
src/pcan_ring.c
|
||||
src/pcan_gas.c
|
||||
)
|
||||
|
||||
add_library(protocan_transport STATIC ${PCAN_CORE_SOURCES})
|
||||
|
||||
target_include_directories(protocan_transport PUBLIC include)
|
||||
|
||||
option(PCAN_BUILD_SHARED "Build stable host ABI shared library" ON)
|
||||
if(PCAN_BUILD_SHARED)
|
||||
add_library(setcore SHARED ${PCAN_CORE_SOURCES})
|
||||
target_include_directories(setcore PUBLIC include)
|
||||
target_compile_definitions(setcore PRIVATE PCAN_ABI_BUILD_DLL)
|
||||
set_target_properties(setcore PROPERTIES
|
||||
C_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN YES
|
||||
OUTPUT_NAME setcore
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(protocan_transport PRIVATE /W4)
|
||||
else()
|
||||
target_compile_options(protocan_transport PRIVATE -Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
if(TARGET setcore)
|
||||
if(MSVC)
|
||||
target_compile_options(setcore PRIVATE /W4)
|
||||
else()
|
||||
target_compile_options(setcore PRIVATE -Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Порт ports/stm32f4 сюда не входит: ему нужен CMSIS-заголовок stm32f4xx.h,
|
||||
# он подключается напрямую в проект прошивки.
|
||||
|
||||
option(PCAN_BUILD_TESTS "Собирать тесты" ON)
|
||||
|
||||
if(PCAN_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_executable(test_transport tests/test_transport.c)
|
||||
target_link_libraries(test_transport PRIVATE protocan_transport)
|
||||
add_test(NAME transport COMMAND test_transport)
|
||||
add_executable(test_abi tests/test_abi.c)
|
||||
target_link_libraries(test_abi PRIVATE protocan_transport)
|
||||
add_test(NAME abi COMMAND test_abi)
|
||||
endif()
|
||||
@@ -1,149 +0,0 @@
|
||||
# protocan-transport
|
||||
|
||||
Переносимая библиотека транспортного уровня для протокола **ProtoCAN**:
|
||||
кадрирование поверх любого байтового потока (RS485, RS232, USB CDC),
|
||||
разбор идентификатора и общее адресное пространство регистров.
|
||||
|
||||
Написана на C99, без динамической памяти, без ОС, без зависимостей от HAL
|
||||
и от конкретного микроконтроллера. Состояние живёт в структурах вызывающего,
|
||||
поэтому в одной прошивке поднимается сколько угодно независимых каналов.
|
||||
|
||||
```
|
||||
ваш код protocan-transport платформа
|
||||
┌────────┐ ┌────────────────────┐ ┌──────────────┐
|
||||
│ кадры │─────►│ pcan_link_send() │─────►│ io.write() │──► UART/DMA
|
||||
│ │◄─────│ on_frame() │◄─────│ pcan_link_feed()
|
||||
└────────┘ └────────────────────┘ └──────────────┘
|
||||
pcan_gas_* pcan_id_*
|
||||
```
|
||||
|
||||
## Состав
|
||||
|
||||
| Модуль | Назначение |
|
||||
|---|---|
|
||||
| `pcan_frame` | кадр `AA 55 … CRC16` и потоковый разборщик с ресинхронизацией |
|
||||
| `pcan_crc` | CRC-16/CCITT-FALSE, побитовый или табличный |
|
||||
| `pcan_link` | экземпляр канала: приём, передача, SEQ, счётчики |
|
||||
| `pcan_ring` | кольцевой буфер, отдаёт непрерывный участок для DMA |
|
||||
| `pcan_id` | упаковка и разбор 29-битного идентификатора ProtoCAN |
|
||||
| `pcan_gas` | общее адресное пространство: карта регионов, чтение/запись, мост к кадрам |
|
||||
| `pcan_abi` | стабильный C ABI для Python, JNI, Swift и других FFI |
|
||||
| `ports/stm32f4` | готовый порт USART + DMA (пакетная передача, кольцевой приём) |
|
||||
| `ports/android` | JNI и Kotlin-фасад; собирает `libsetcore.so` из этого же C99-кода |
|
||||
|
||||
## Кадр
|
||||
|
||||
```
|
||||
AA 55 | LEN | SEQ | FLAGS | ID0 ID1 ID2 ID3 | DATA[0..8] | CRC_L CRC_H
|
||||
```
|
||||
|
||||
`LEN = 6 + DLC` (6..14), CRC-16/CCITT-FALSE по байтам `LEN..DATA`,
|
||||
little-endian. Подробности — [docs/FRAME.md](docs/FRAME.md).
|
||||
|
||||
Полное описание прикладного ProtoCAN и общего адресного пространства теперь
|
||||
также хранится здесь: [docs/PROTOCOL.md](docs/PROTOCOL.md) и
|
||||
[docs/OAP.md](docs/OAP.md). Эталонные данные находятся в
|
||||
`tests/vectors/test-vectors.json`. Это канонические документы; копии в SETCAN
|
||||
считаются историческим снимком legacy HAL-адаптера.
|
||||
|
||||
## Общее адресное пространство
|
||||
|
||||
Плоское пространство 16-битных регистров `0x0000..0xFFFF`, собранное из
|
||||
регионов. Регион ссылается либо на массив в памяти, либо на пару колбэков —
|
||||
так в карту попадают и переменные, и вычисляемые значения, и регистры
|
||||
периферии. Подробности — [docs/GAS.md](docs/GAS.md).
|
||||
|
||||
```c
|
||||
static uint16_t holding[8];
|
||||
|
||||
static const pcan_gas_region_t regions[] = {
|
||||
{ 0x0000, 8, holding, NULL, NULL, 0, NULL, "holding" },
|
||||
{ 0xFF00, 4, NULL, diag_read, NULL, PCAN_GAS_RDONLY, NULL, "diag" },
|
||||
};
|
||||
static const pcan_gas_map_t map = { regions, 2 };
|
||||
```
|
||||
|
||||
## Использование
|
||||
|
||||
```c
|
||||
#include "protocan_transport.h"
|
||||
|
||||
static void on_frame(const pcan_frame_t *f, void *user)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
pcan_io_t io;
|
||||
pcan_uart_io(&uart, &io); /* или свой io */
|
||||
|
||||
pcan_link_t link;
|
||||
pcan_link_init(&link, &io, on_frame, NULL);
|
||||
|
||||
pcan_frame_t f = { .flags = PCAN_FLAG_IDE, .id = 0x1234567, .dlc = 2 };
|
||||
f.data[0] = 0xAA; f.data[1] = 0xBB;
|
||||
pcan_link_send(&link, &f); /* SEQ проставится сам */
|
||||
```
|
||||
|
||||
Для своей платформы достаточно реализовать две функции:
|
||||
|
||||
```c
|
||||
size_t my_write(void *ctx, const uint8_t *data, size_t len); /* всё-или-ничего */
|
||||
size_t my_space(void *ctx);
|
||||
```
|
||||
|
||||
## Сборка
|
||||
|
||||
Библиотека — шесть файлов в `src/` и заголовки в `include/`. Добавьте их
|
||||
в проект и укажите `include/` в путях поиска. CMake для тестов и хостовых
|
||||
сборок:
|
||||
|
||||
```bash
|
||||
cmake -B build && cmake --build build && ctest --test-dir build
|
||||
```
|
||||
|
||||
Для desktop CMake дополнительно собирает shared library `setcore`
|
||||
(`setcore.dll`, `libsetcore.so` или `libsetcore.dylib`). Публичная граница
|
||||
для приложений описана в `include/pcan_abi.h`; внутренние структуры ядра через
|
||||
FFI не экспортируются. Python-обёртка находится в `python/protocan/native.py`.
|
||||
|
||||
Android-проект подключает `ports/android/Android.mk` и добавляет
|
||||
`ports/android/kotlin` в `sourceSets`. JNI-код занимается только преобразованием
|
||||
типов и временем жизни parser context, а правила кадра и CAN ID остаются в C.
|
||||
|
||||
Если на Windows нет CMake, host-библиотеку тем же MSVC можно собрать так:
|
||||
|
||||
```powershell
|
||||
python tools/build_host.py --output native/setcore.dll
|
||||
```
|
||||
|
||||
Либо напрямую:
|
||||
|
||||
```bash
|
||||
clang -std=c99 -Wall -Wextra -Iinclude tests/test_transport.c src/pcan_*.c -o test && ./test
|
||||
```
|
||||
|
||||
Порт `ports/stm32f4` в тесты не входит: ему нужен CMSIS-заголовок
|
||||
`stm32f4xx.h`, подключайте его в проект прошивки отдельно.
|
||||
|
||||
## Настройки
|
||||
|
||||
Переопределяются через `-D` либо через свой `pcan_config_user.h`
|
||||
(с `-DPCAN_USE_USER_CONFIG`):
|
||||
|
||||
| Макрос | По умолчанию | Смысл |
|
||||
|---|---|---|
|
||||
| `PCAN_DATA_MAX` | 8 | длина поля данных CAN |
|
||||
| `PCAN_CRC_TABLE` | 0 | 1 — таблица на 512 байт вместо побитового расчёта |
|
||||
| `PCAN_GAS_MAX_REGIONS` | 16 | предел числа регионов в карте |
|
||||
| `PCAN_BARRIER()` | барьер компилятора | для очередей, разделяемых с прерыванием |
|
||||
|
||||
## Ограничения
|
||||
|
||||
- Очереди рассчитаны на схему «один писатель + один читатель». Если
|
||||
писателей несколько, оборачивайте вызовы своей блокировкой.
|
||||
- Размер кольцевого буфера обязан быть степенью двойки; `pcan_ring_init()`
|
||||
вернёт `false`, а не станет молча портить индексы.
|
||||
- На STM32F407 DMA не видит CCM RAM (`0x10000000`) — буферы держите
|
||||
в основном SRAM.
|
||||
- Порядок байт на линии фиксирован (little-endian) и не зависит от порядка
|
||||
байт хоста.
|
||||
@@ -1,28 +1,84 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(set_protocol C)
|
||||
project(setprotocol C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
add_library(set_protocol STATIC
|
||||
# SET protocol v2: управление, телеметрия, CAN segmentation и firmware flow.
|
||||
set(SETPROTOCOL_V2_SOURCES
|
||||
src/set_protocol.c
|
||||
src/set_can.c
|
||||
src/set_firmware.c
|
||||
src/set_telemetry.c
|
||||
)
|
||||
|
||||
target_include_directories(set_protocol PUBLIC include)
|
||||
# Совместимые ProtoCAN/SETGUI v1 форматы переходного периода.
|
||||
set(SETPROTOCOL_LEGACY_SOURCES
|
||||
src/gui_catalog.c
|
||||
src/gui_frame.c
|
||||
src/pcan_abi.c
|
||||
src/pcan_crc.c
|
||||
src/pcan_frame.c
|
||||
src/pcan_gas.c
|
||||
src/pcan_id.c
|
||||
src/pcan_link.c
|
||||
src/pcan_ring.c
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(set_protocol PRIVATE /W4 /WX)
|
||||
else()
|
||||
target_compile_options(set_protocol PRIVATE -Wall -Wextra -Wpedantic -Werror)
|
||||
set(SETPROTOCOL_SOURCES
|
||||
${SETPROTOCOL_V2_SOURCES}
|
||||
${SETPROTOCOL_LEGACY_SOURCES}
|
||||
)
|
||||
|
||||
add_library(setprotocol_static STATIC ${SETPROTOCOL_SOURCES})
|
||||
target_include_directories(setprotocol_static PUBLIC include)
|
||||
|
||||
# Совместимое имя цели для проектов, уже использовавших SET protocol v2.
|
||||
add_library(set_protocol ALIAS setprotocol_static)
|
||||
|
||||
option(SETP_BUILD_SHARED "Build stable host ABI shared library" ON)
|
||||
if(SETP_BUILD_SHARED)
|
||||
add_library(setprotocol SHARED ${SETPROTOCOL_SOURCES})
|
||||
target_include_directories(setprotocol PUBLIC include)
|
||||
target_compile_definitions(setprotocol PRIVATE PCAN_ABI_BUILD_DLL)
|
||||
set_target_properties(setprotocol PROPERTIES
|
||||
C_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN YES
|
||||
OUTPUT_NAME setprotocol
|
||||
)
|
||||
endif()
|
||||
|
||||
function(setprotocol_warnings target)
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE /W4)
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
setprotocol_warnings(setprotocol_static)
|
||||
if(TARGET setprotocol)
|
||||
setprotocol_warnings(setprotocol)
|
||||
endif()
|
||||
|
||||
# JNI/CMSIS-порты подключаются целевым проектом и в host CMake не входят.
|
||||
option(SETP_BUILD_TESTS "Build host tests" ON)
|
||||
if(SETP_BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
add_executable(test_set_protocol tests/test_set_protocol.c)
|
||||
target_link_libraries(test_set_protocol PRIVATE set_protocol)
|
||||
add_test(NAME set_protocol COMMAND test_set_protocol)
|
||||
target_link_libraries(test_set_protocol PRIVATE setprotocol_static)
|
||||
add_test(NAME set_protocol_v2 COMMAND test_set_protocol)
|
||||
|
||||
add_executable(test_transport tests/test_transport.c)
|
||||
target_link_libraries(test_transport PRIVATE setprotocol_static)
|
||||
add_test(NAME legacy_transport COMMAND test_transport)
|
||||
|
||||
add_executable(test_gui tests/test_gui.c)
|
||||
target_link_libraries(test_gui PRIVATE setprotocol_static)
|
||||
add_test(NAME legacy_gui COMMAND test_gui)
|
||||
|
||||
add_executable(test_abi tests/test_abi.c)
|
||||
target_link_libraries(test_abi PRIVATE setprotocol_static)
|
||||
add_test(NAME stable_abi COMMAND test_abi)
|
||||
endif()
|
||||
|
||||
@@ -1,75 +1,81 @@
|
||||
# set-protocol
|
||||
# SETProtocol
|
||||
|
||||
Единый переносимый протокол SET для новых устройств и SETGUI. Один и тот же
|
||||
прикладной кадр используется для управления, чтения общей карты, потоковой
|
||||
телеметрии и безопасной передачи прошивки через:
|
||||
Единое переносимое протокольное ядро SET для `SETGUI`, Android GUI, устройств
|
||||
и сервисных утилит. В одном C99-проекте собраны:
|
||||
|
||||
- RS-232 и RS-485;
|
||||
- USB CDC;
|
||||
- CAN с сегментацией;
|
||||
- Ethernet TCP;
|
||||
- Ethernet UDP, когда нужен обмен без соединения.
|
||||
- SET protocol v2 для управления, карты данных, телеметрии и firmware flow;
|
||||
- совместимый ProtoCAN transport для существующих CAN-мостов;
|
||||
- SETGUI transport v1 для приборов переходного периода;
|
||||
- стабильный host ABI для Python/JNI/других FFI;
|
||||
- GAS, кольцевые буферы, CRC и потоковые parser.
|
||||
|
||||
Протокол не привязан к HAL, ОС или микроконтроллеру. Реализация на C99 не
|
||||
использует динамическую память. Все многобайтные значения little-endian.
|
||||
Ядро не зависит от HAL, ОС или конкретного адаптера, не использует
|
||||
динамическую память и не открывает COM/CAN само. SLCAN, SocketCAN, USB CDC,
|
||||
TCP, UART/DMA и аппаратный CAN подключаются портами.
|
||||
|
||||
## Что уже реализовано
|
||||
## Структура
|
||||
|
||||
| Файл | Назначение |
|
||||
| Каталог | Назначение |
|
||||
|---|---|
|
||||
| `set_protocol.*` | кадр v2, CRC32, потоковый parser, статусы ответов |
|
||||
| `set_can.*` | extended CAN ID, сегментация, flow-control и reassembly timeout |
|
||||
| `set_firmware.*` | BEGIN/DATA/END/STATUS, resume, CRC блока, SHA-256 и подпись |
|
||||
| `set_telemetry.*` | подписки и типизированные push-пакеты с timestamp |
|
||||
| `PROTOCOL.md` | обязательный wire-контракт и привязки к носителям |
|
||||
| `MIGRATION.md` | порядок перехода SETGUI и существующих прошивок с v1 |
|
||||
| `PORTING.md` | подключение UART, CAN, USB и Ethernet |
|
||||
| `tests/` | host-тесты и фиксированный эталонный кадр |
|
||||
|
||||
Файл [`PROTOCOL.md`](PROTOCOL.md) является источником истины. Числа из него
|
||||
нельзя менять без выпуска следующей версии протокола.
|
||||
|
||||
## Минимальный stream-приёмник
|
||||
|
||||
```c
|
||||
#include "set_protocol.h"
|
||||
|
||||
static setp_parser_t parser;
|
||||
|
||||
static void on_frame(const setp_frame_t *frame, void *user)
|
||||
{
|
||||
(void)user;
|
||||
/* frame->payload действует только до возврата из callback. */
|
||||
}
|
||||
|
||||
void protocol_init(void)
|
||||
{
|
||||
setp_parser_init(&parser);
|
||||
}
|
||||
|
||||
void protocol_feed(const uint8_t *data, uint16_t length)
|
||||
{
|
||||
(void)setp_parser_feed(&parser, data, length, on_frame, NULL);
|
||||
}
|
||||
```
|
||||
|
||||
Для RS-232, RS-485, USB CDC и TCP в parser передаются любые принятые chunks.
|
||||
Для UDP один UDP payload должен содержать ровно один полный SETP-кадр.
|
||||
| `include/set_*.h` | публичный SET protocol v2 |
|
||||
| `include/pcan_*.h`, `gui_*.h` | совместимые ProtoCAN/GUI v1 модули |
|
||||
| `include/setprotocol.h` | единая C99-точка включения |
|
||||
| `include/setprotocol_abi.h` | стабильная FFI-точка включения ABI v1 |
|
||||
| `src/` | общая реализация всех протокольных модулей |
|
||||
| `ports/android` | JNI и Kotlin facade |
|
||||
| `ports/stm32f4` | UART/DMA port для legacy byte stream |
|
||||
| `docs/SETPROTOCOL.md` | архитектура, ABI, память и переносимость |
|
||||
| `docs/legacy` | нормативные документы ProtoCAN/SETGUI v1 |
|
||||
| `PROTOCOL.md` | нормативный wire contract SET protocol v2 |
|
||||
| `MIGRATION.md` | переход с v1/ProtoCAN на v2 |
|
||||
| `PORTING.md` | подключение новых транспортов и платформ |
|
||||
|
||||
## Сборка
|
||||
|
||||
```text
|
||||
cmake -B build
|
||||
cmake --build build
|
||||
ctest --test-dir build
|
||||
```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
|
||||
```
|
||||
|
||||
Либо добавьте три файла из `src/` и каталог `include/` непосредственно в
|
||||
проект прошивки.
|
||||
CMake создаёт:
|
||||
|
||||
## Версии
|
||||
- `setprotocol_static` — статическое C99-ядро;
|
||||
- совместимую CMake-цель `set_protocol`;
|
||||
- `setprotocol.dll`, `libsetprotocol.so` или `libsetprotocol.dylib`;
|
||||
- тесты SET v2, ProtoCAN transport, GUI v1 и ABI.
|
||||
|
||||
- GUI protocol v1 остаётся только переходным форматом старых устройств.
|
||||
- Все новые устройства используют SET protocol v2 (`SETP_VERSION = 2`).
|
||||
- v1 и v2 имеют одинаковый SOF `A5 5A`, поэтому номер версии проверяется до
|
||||
разбора остальных полей.
|
||||
Упрощённая 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
|
||||
```
|
||||
|
||||
## Подключение в C
|
||||
|
||||
Для всего ядра:
|
||||
|
||||
```c
|
||||
#include "setprotocol.h"
|
||||
```
|
||||
|
||||
Для минимальной прошивки можно подключать только нужные заголовки и исходники.
|
||||
Например, новый v2 stream parser использует `set_protocol.*`, а legacy
|
||||
CAN-мост — `pcan_frame.*`, `pcan_crc.*` и `pcan_id.*`.
|
||||
|
||||
FFI-клиенты подключают `setprotocol_abi.h`. Имена функций `pcan_abi_*`
|
||||
сохраняются в ABI v1 для бинарной совместимости; переименование символов без
|
||||
повышения версии ABI запрещено.
|
||||
|
||||
## Версии wire format
|
||||
|
||||
- SET protocol v2 — основной формат новых устройств.
|
||||
- SETGUI v1 и ProtoCAN bridge остаются поддерживаемыми на время миграции.
|
||||
- Одинаковый SOF `A5 5A` у GUI v1 и SET v2 различается полем версии.
|
||||
- Изменение wire contract требует новой версии и тестовых векторов.
|
||||
|
||||
Полная интерактивная документация: [`../../doc/setprotocol.html`](../../doc/setprotocol.html).
|
||||
|
||||
342
c/set-protocol/docs/SETPROTOCOL.md
Normal file
342
c/set-protocol/docs/SETPROTOCOL.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# 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, а не хранить
|
||||
разошедшиеся копии.
|
||||
@@ -6,7 +6,7 @@
|
||||
* оператор отмечает нужное, и прибор шлёт только отмеченное пакетами.
|
||||
* Схема повторяет реестр регистров ST Motor Control Workbench.
|
||||
*
|
||||
* Двоичный контракт: docs/GUI_CATALOG.md.
|
||||
* Двоичный контракт: docs/legacy/GUI_CATALOG.md.
|
||||
* Зеркало на Python: gui_desktop/core/gas_catalog.py.
|
||||
*/
|
||||
#ifndef GUI_CATALOG_H
|
||||
@@ -14,7 +14,7 @@
|
||||
* устройствами.
|
||||
*
|
||||
* Запрос на чтение кодируется кадром GAS с DLC = 0: в исходном коде
|
||||
* SETCAN такой кодировки нет, это расширение - см. docs/GAS.md.
|
||||
* SETCAN такой кодировки нет, это расширение - см. docs/legacy/GAS.md.
|
||||
*/
|
||||
#ifndef PCAN_GAS_H
|
||||
#define PCAN_GAS_H
|
||||
19
c/set-protocol/include/setprotocol.h
Normal file
19
c/set-protocol/include/setprotocol.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file setprotocol.h
|
||||
* @brief Единственная C99-точка включения полного SETProtocol.
|
||||
*/
|
||||
#ifndef SETPROTOCOL_H
|
||||
#define SETPROTOCOL_H
|
||||
|
||||
/* Основной SET protocol v2. */
|
||||
#include "set_protocol.h"
|
||||
#include "set_can.h"
|
||||
#include "set_firmware.h"
|
||||
#include "set_telemetry.h"
|
||||
|
||||
/* Поддерживаемые форматы переходного периода. */
|
||||
#include "protocan_transport.h"
|
||||
#include "gui_frame.h"
|
||||
#include "gui_catalog.h"
|
||||
|
||||
#endif /* SETPROTOCOL_H */
|
||||
15
c/set-protocol/include/setprotocol_abi.h
Normal file
15
c/set-protocol/include/setprotocol_abi.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @file setprotocol_abi.h
|
||||
* @brief Стабильная FFI-граница SETProtocol ABI v1.
|
||||
*
|
||||
* Имена pcan_abi_* сохранены ради бинарной совместимости существующих
|
||||
* SETGUI и Android-клиентов. Новые ABI-группы добавляются через этот facade.
|
||||
*/
|
||||
#ifndef SETPROTOCOL_ABI_H
|
||||
#define SETPROTOCOL_ABI_H
|
||||
|
||||
#include "pcan_abi.h"
|
||||
|
||||
#define SETPROTOCOL_ABI_VERSION PCAN_ABI_VERSION
|
||||
|
||||
#endif /* SETPROTOCOL_ABI_H */
|
||||
@@ -1,9 +1,14 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := setcore
|
||||
LOCAL_MODULE := setprotocol
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../include
|
||||
LOCAL_SRC_FILES := \
|
||||
../../src/set_protocol.c \
|
||||
../../src/set_can.c \
|
||||
../../src/set_firmware.c \
|
||||
../../src/set_telemetry.c \
|
||||
../../src/gui_catalog.c \
|
||||
../../src/gui_frame.c \
|
||||
../../src/pcan_abi.c \
|
||||
../../src/pcan_crc.c \
|
||||
@@ -12,7 +17,7 @@ LOCAL_SRC_FILES := \
|
||||
../../src/pcan_link.c \
|
||||
../../src/pcan_ring.c \
|
||||
../../src/pcan_gas.c \
|
||||
setcore_jni.c
|
||||
setprotocol_jni.c
|
||||
LOCAL_CFLAGS := -std=c99 -Wall -Wextra -Wpedantic -fvisibility=hidden
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -1,7 +1,7 @@
|
||||
# Android port
|
||||
|
||||
`Android.mk` builds `libsetcore.so` from the same C99 sources used by MCU and
|
||||
desktop builds. `setcore_jni.c` contains only JNI marshalling; protocol rules
|
||||
`Android.mk` builds `libsetprotocol.so` from the same C99 sources used by MCU
|
||||
and desktop builds. `setprotocol_jni.c` contains only JNI marshalling; protocol rules
|
||||
remain in the core. Add the Kotlin directory as an Android source directory
|
||||
and include this `Android.mk` from the application NDK build.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package ru.setcorp.setcore
|
||||
package ru.setcorp.setprotocol
|
||||
|
||||
/** Thin Kotlin facade over the shared C99 ProtoCAN core. */
|
||||
object NativeProtoCan {
|
||||
/** Thin Kotlin facade over the shared C99 SETProtocol core. */
|
||||
object NativeSetProtocol {
|
||||
val available: Boolean by lazy {
|
||||
runCatching {
|
||||
System.loadLibrary("setcore")
|
||||
System.loadLibrary("setprotocol")
|
||||
nativeAbiVersion() == 1
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pcan_abi.h"
|
||||
#include "setprotocol_abi.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t *storage;
|
||||
@@ -18,7 +18,7 @@ typedef struct {
|
||||
} android_gui_parser_t;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeAbiVersion(JNIEnv *env, jobject self)
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeAbiVersion(JNIEnv *env, jobject self)
|
||||
{
|
||||
(void)env;
|
||||
(void)self;
|
||||
@@ -26,7 +26,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeAbiVersion(JNIEnv *env, jobject sel
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativePackId(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativePackId(
|
||||
JNIEnv *env, jobject self, jint priority, jint route, jint device_type,
|
||||
jint device_id, jint message_type, jint body)
|
||||
{
|
||||
@@ -38,7 +38,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativePackId(
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeUnpackId(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeUnpackId(
|
||||
JNIEnv *env, jobject self, jlong raw)
|
||||
{
|
||||
(void)self;
|
||||
@@ -56,7 +56,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeUnpackId(
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeCrc16(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeCrc16(
|
||||
JNIEnv *env, jobject self, jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
@@ -71,7 +71,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeCrc16(
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeEncodeFrame(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeEncodeFrame(
|
||||
JNIEnv *env, jobject self, jint sequence, jint flags, jlong can_id,
|
||||
jbyteArray input)
|
||||
{
|
||||
@@ -103,7 +103,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeEncodeFrame(
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeCreateParser(JNIEnv *env, jobject self)
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeCreateParser(JNIEnv *env, jobject self)
|
||||
{
|
||||
(void)env;
|
||||
(void)self;
|
||||
@@ -124,7 +124,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeCreateParser(JNIEnv *env, jobject s
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeDestroyParser(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeDestroyParser(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)env;
|
||||
@@ -138,7 +138,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeDestroyParser(
|
||||
|
||||
/* Each returned record is 15 bytes: seq, flags, id LE, dlc, data[8]. */
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeFeedParser(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeFeedParser(
|
||||
JNIEnv *env, jobject self, jlong handle, jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
@@ -194,7 +194,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeFeedParser(
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeParserStats(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeParserStats(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)self;
|
||||
@@ -214,7 +214,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeParserStats(
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeGuiEncode(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGuiEncode(
|
||||
JNIEnv *env, jobject self, jint message_type, jint sequence,
|
||||
jbyteArray input)
|
||||
{
|
||||
@@ -237,7 +237,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeGuiEncode(
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeCreateGuiParser(JNIEnv *env, jobject self)
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeCreateGuiParser(JNIEnv *env, jobject self)
|
||||
{
|
||||
(void)env; (void)self;
|
||||
android_gui_parser_t *parser = (android_gui_parser_t *)calloc(1U, sizeof *parser);
|
||||
@@ -252,7 +252,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeCreateGuiParser(JNIEnv *env, jobjec
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeDestroyGuiParser(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeDestroyGuiParser(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)env; (void)self;
|
||||
@@ -262,7 +262,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeDestroyGuiParser(
|
||||
|
||||
/* Records: type[1], sequence LE[2], size LE[2], payload[size]. */
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeFeedGuiParser(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeFeedGuiParser(
|
||||
JNIEnv *env, jobject self, jlong handle, jbyteArray input)
|
||||
{
|
||||
(void)self;
|
||||
@@ -300,7 +300,7 @@ Java_ru_setcorp_setcore_NativeProtoCan_nativeFeedGuiParser(
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_ru_setcorp_setcore_NativeProtoCan_nativeGuiParserStats(
|
||||
Java_ru_setcorp_setprotocol_NativeSetProtocol_nativeGuiParserStats(
|
||||
JNIEnv *env, jobject self, jlong handle)
|
||||
{
|
||||
(void)self;
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Build the SETCore shared library without imposing one host toolchain.
|
||||
"""Build the SETProtocol shared library without imposing one host toolchain.
|
||||
|
||||
CMake remains the primary build. On a Windows workstation where CMake is
|
||||
absent, the script locates Visual Studio and invokes MSVC directly. Unix-like
|
||||
@@ -19,8 +19,10 @@ ROOT = Path(__file__).resolve().parent.parent
|
||||
INCLUDE = ROOT / "include"
|
||||
SOURCES = [
|
||||
ROOT / "src" / name for name in (
|
||||
"gui_frame.c", "pcan_abi.c", "pcan_crc.c", "pcan_frame.c", "pcan_id.c",
|
||||
"pcan_link.c", "pcan_ring.c", "pcan_gas.c",
|
||||
"set_protocol.c", "set_can.c", "set_firmware.c", "set_telemetry.c",
|
||||
"gui_catalog.c", "gui_frame.c", "pcan_abi.c", "pcan_crc.c",
|
||||
"pcan_frame.c", "pcan_id.c", "pcan_link.c", "pcan_ring.c",
|
||||
"pcan_gas.c",
|
||||
)
|
||||
]
|
||||
|
||||
@@ -48,7 +50,7 @@ def _build_msvc(output: Path, build_dir: Path) -> None:
|
||||
if vcvars is None:
|
||||
raise SystemExit("Visual Studio C++ tools were not found")
|
||||
quoted_sources = " ".join(f'"{source}"' for source in SOURCES)
|
||||
import_library = build_dir / "setcore.lib"
|
||||
import_library = build_dir / "setprotocol.lib"
|
||||
command = (
|
||||
f'call "{vcvars}" && cl /nologo /W4 /std:c11 '
|
||||
f'/DPCAN_ABI_BUILD_DLL /I"{INCLUDE}" /LD {quoted_sources} '
|
||||
@@ -75,7 +77,7 @@ def main() -> int:
|
||||
args = parser.parse_args()
|
||||
output = args.output.resolve()
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
build_dir = output.parent / ".setcore-build"
|
||||
build_dir = output.parent / ".setprotocol-build"
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
if platform.system() == "Windows":
|
||||
_build_msvc(output, build_dir)
|
||||
32
doc/build-setprotocol-html.ps1
Normal file
32
doc/build-setprotocol-html.ps1
Normal file
@@ -0,0 +1,32 @@
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$outputPath = Join-Path $PSScriptRoot 'setprotocol.html'
|
||||
$sourcePath = Join-Path $PSScriptRoot '..\c\set-protocol\docs\SETPROTOCOL.md'
|
||||
|
||||
$markdown = Get-Content -Raw -LiteralPath $sourcePath -Encoding UTF8
|
||||
$html = (ConvertFrom-Markdown -InputObject $markdown).Html
|
||||
$generatedBlock = @"
|
||||
<!-- SETPROTOCOL:START -->
|
||||
<article class="card full-doc" data-source="c/set-protocol/docs/SETPROTOCOL.md">
|
||||
$html
|
||||
</article>
|
||||
<!-- SETPROTOCOL:END -->
|
||||
"@
|
||||
|
||||
$page = Get-Content -Raw -LiteralPath $outputPath -Encoding UTF8
|
||||
$pattern = '(?s)<!-- SETPROTOCOL:START -->.*?<!-- SETPROTOCOL:END -->'
|
||||
if ($page -notmatch $pattern) {
|
||||
throw 'Не найдены маркеры SETPROTOCOL:START/END в doc/setprotocol.html.'
|
||||
}
|
||||
|
||||
$page = [regex]::Replace($page, $pattern, [System.Text.RegularExpressions.MatchEvaluator]{
|
||||
param($match)
|
||||
$generatedBlock
|
||||
}, 1)
|
||||
|
||||
$page = $page.TrimEnd("`r", "`n") + [Environment]::NewLine
|
||||
$utf8WithoutBom = [System.Text.UTF8Encoding]::new($false)
|
||||
[System.IO.File]::WriteAllText($outputPath, $page, $utf8WithoutBom)
|
||||
Write-Host "[DONE] SETProtocol HTML: $outputPath"
|
||||
File diff suppressed because one or more lines are too long
608
doc/setprotocol.html
Normal file
608
doc/setprotocol.html
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user