Files
templates/c/set-protocol/docs/GAS_WATCH2.md
Andrey 876b58517c feat(set-protocol): Раздели GAS и добавь пакетный сбор регистров
Сохрани адресацию отдельных регистров GAS 0x3 и выдели слотам тип 0x4 с маской Word0. Добавь единое C-ядро GAS_WATCH2, тонкие Python/JNI-порты и проверки общих эталонных байтов.

Совместимость: тип 0x4 больше не обслуживает Modbus Coil. Потребителям PCAN_MSG_MODBUS_COIL и ProtoCanMsgToModbusCoil нужно перейти на GAS_SLOT и отдельный API слотов; регистровый GAS остаётся на 0x3.
2026-10-23 14:14:48 +03:00

17 KiB
Raw Blame History

GAS_WATCH2: список адресов и пакетная выборка

GAS 0x3 использует адреса отдельных 16-битных регистров. GAS SLOT 0x4 — отдельный сервис с маской в Word0 и тремя значениями Word1…Word3. Спецификация: doc/setcan/protocan/OAP.md; адреса этого документа остаются регистровыми.

include/set_gas_watch.h и src/set_gas_watch.c реализуют выбранный клиентом список GAS-регистров, транзакционную настройку и захват значений в отдельную структуру. График и архив могут использовать одну готовую выборку. Ядро — C99, без HAL, часов ОС, heap, глобального состояния и драйверов связи.

Старые gui_watch_t, GAS_WATCH_SET / GAS_WATCH_DATA и ABI set_trend_watch_* сохраняют сигнатуры и побайтовый формат. Расширение включается явно и не меняет существующую типизированную телеметрию set_telemetry / SETProtocol v2 SUBSCRIBE.

Что подключается к МК

Код templates компилируется в прошивку. Она создаёт set_gas_watch_t в собственной RAM, предоставляет pcan_gas_map_t и вызывает API из своего диспетчера команд и таймера. Нового процесса или среды исполнения на МК нет.

Компонент Ответственность
set_gas_watch_t Собственная копия списка адресов, период, поколение и расписание
set_gas_watch_sample_t Значения, достоверность каждого слова, время и номер захвата
pcan_gas_map_t Доступ к RAM/обработчикам источников; GAS-адрес не является указателем MCU
Порт платы Блокировка, локальное время, таймер, диспетчер и очередь передачи
Python/JNI/Kotlin Преобразование моделей и pending/active без второго кодека
GUI Имена, типы, масштаб, цвет, видимость и объединение запросов вкладок

Минимальные исходники: set_gas_watch.c, pcan_gas.c, pcan_id.c, set_protocol.c; заголовки берутся из include. Полная CMake/NDK/host-сборка уже включает новый модуль. Состояние и выборка используют ограниченные массивы; реальные размеры структур определяются sizeof на целевом MCU.

Пример последовательности вызовов в прошивке:

#include "set_gas_watch.h" /* Подключает список GAS-слов и независимый от транспорта захват. */
static set_gas_watch_t watch; /* Хранит одну управляемую подписку в RAM платы; память выделяет проект. */
static set_gas_watch_sample_t sample; /* Сохраняет выборку до завершения работы всех её потребителей. */
/* При старте сессии: set_gas_watch_init(&watch); память и timer предоставляет прошивка. */
/* В диспетчере, под общей блокировкой: set_gas_watch_apply(&watch, &map, payload, size). */
/* При успехе, под той же блокировкой: set_gas_watch_encode_ack(&watch, tx, sizeof(tx)). */
/* После снятия блокировки: отправить ACK транспортом проекта, при ошибке вернуть NACK/статус. */
/* В таймере, под общей блокировкой: проверить set_gas_watch_due(&watch, now_ms). */
/* Когда пора: set_gas_watch_capture(&watch, &map, now_ms, &sample), затем снять блокировку. */
/* В задаче передачи: set_gas_watch_encode_sample(&sample, tx, sizeof(tx)), затем поставить tx в очередь. */

Пример показывает места вызовов; проект предоставляет map, payload, буферы, now_ms, lock и транспорт. Он обязан проверять результаты API. Для [0x0000, 0x0001, 0xFF01] ядро читает три разрозненных GAS-слова и формирует один логический пакет, не читая промежуточный диапазон. Адреса передаются при настройке; DATA содержит значения и метаданные.

Настройка и поколения

Набор ограничен 64 словами по 16 бит, а не 64 произвольными параметрами. Многословный параметр занимает несколько позиций. Дубли адресов отклоняются; владелец подписки объединяет запросы вкладок до отправки.

set_gas_watch_request проверяет модель и строит payload. set_gas_watch_apply проверяет весь пакет и карту: все адреса должны существовать, разрешать чтение и иметь RAM либо callback чтения. Проверка не вызывает источники. Отказ сохраняет прежние адреса, период, sequence и расписание. Частичного принятия нет; коды заданы в set_gas_watch_result_t.

config_id — ненулевой 32-битный номер, назначаемый управляющим клиентом. Любое изменение периода или порядка/состава адресов требует нового поколения. После первой команды принимается модульная разность new_id - old_id в диапазоне 1..0x7FFFFFFF. Переход 0xFFFFFFFF → 1 допустим, ноль зарезервирован. Разрыв между последовательными поколениями меньше 2^31. Поколения нельзя переиспользовать, пока в обмене могут оставаться старые пакеты.

Точный повтор действующего id со всем прежним содержимым успешен и не сбрасывает захват: потерянный ACK можно запросить повторно. Тот же id с другим содержимым, старый id и разность 2^31 дают SET_GAS_WATCH_CONFLICT. Остановка — новая конфигурация period_ms = 0, count = 0, карта не требуется. Пустой список с ненулевым периодом и непустой список с нулевым запрещены.

set_gas_watch_encode_ack возвращает полную применённую конфигурацию, включая адреса. Клиент проверяет её через set_gas_watch_ack и заменяет active только после успеха. Новая команда хранится как pending; DATA сопоставляются с active. ACK уже заменённого pending отвергается. Ошибка команды/ACK сохраняет прежний active.

Один экземпляр обслуживает одного управляющего клиента. Если устройство сохранило состояние после переподключения, клиент продолжает номера поколений. reset() адаптера очищает только его pending/active, не посылает stop и не сбрасывает устройство. Проект может явно вызвать set_gas_watch_init при завершении управляющей сессии, разрешив новой сессии произвольный первый id.

Захват и синхронизация

Приложение сериализует все обращения к состоянию, изменения карты, обновления исходного кэша и захват одним подходящим lock/IRQ-портом. Транзакционность API означает отсутствие частичной настройки при ошибке; присваивание C-структуры само по себе не даёт межпоточную атомарность. Ядро не управляет IRQ и не содержит скрытых ожиданий.

Под lock выполняются применение конфигурации, подготовка ACK, проверка set_gas_watch_due и set_gas_watch_capture. Сам capture допускает явный захват вне расписания и не проверяет due. После снятия lock приложение кодирует готовую выборку и передаёт её транспорту. Кодировщик не обращается к источникам.

При блокировке IRQ callbacks читают только быструю RAM: без запросов CAN/Modbus, ожидания периферии и выделения памяти. Удалённые источники обновляют кэш заранее. Общая блокировка согласует чтение кэша, но не синхронизирует физические измерения нескольких устройств и не определяет свежесть удалённых данных.

Отказ отдельного источника даёт valid = 0, value = 0 и не отменяет выборку. Корректное нулевое значение имеет valid = 1. Повреждённая карта целиком или остановленная подписка возвращают false без изменения состояния и выхода.

Первый номер выборки нового поколения — 0; далее он увеличивается по модулю 2^32. Каждый захват учитывается, даже если транспорт затем его потерял. Повторная отправка готовой выборки сохраняет тот же номер. Время передаётся портом в миллисекундах устройства, без подмены часами GUI. Потребитель разворачивает переполнение времени в длинную ось своего сеанса.

Первый захват разрешён сразу; следующий срок — now_ms + period_ms. После опоздания нет пачки догоняющих выборок. Приложение проверяет таймер чаще одного раза за 2^31 мс для корректного модульного сравнения u32.

Изменение GAS и конфигурации не меняет готовую выборку. Приложение хранит её до окончания чтения всеми потребителями, используя очередь или двойной буфер при необходимости. Входные/выходные структуры и массивы API не должны перекрываться в памяти.

Wire payload версии 2

Числа little-endian, размеры ниже в байтах. Выравнивание C-структур в обмене не используется. Для GUI protocol v1 добавлены отдельные типы:

Тип GUI v1 Назначение
0x14 GAS_WATCH2_SET Настройка и полное эхо принятой конфигурации
0x15 GAS_WATCH2_DATA Выборка; внешний sequence равен 0

Внешняя версия остаётся GUI v1. Старые типы 0x12/0x13 не меняются. Неподдерживаемая команда завершается явным отказом, автоматического fallback нет. 0x0014 UNSUBSCRIBE SETProtocol v2 относится к другому кадрированию и сохраняет смысл. Обёртка нового GAS_WATCH2 для SETProtocol v2 согласуется отдельно с его существующей типизированной телеметрией.

Конфигурация и ACK: 12 + 2N байт, максимум 140.

Смещение Тип Значение
0 u16 версия payload: 2
2 u16 период, мс
4 u32 config_id, не ноль
8 u16 N, число адресов: 0..64
10 u16 резерв, строго 0
12 N × u16 уникальные GAS-адреса в порядке значений

Выборка: 16 + ceil(N/8) + 2N байт, максимум 152.

Смещение Тип Значение
0 u16 версия payload: 2
2 u16 N, число значений: 1..64
4 u32 config_id
8 u32 sequence захвата
12 u32 timestamp_ms устройства
16 ceil(N/8) байт битовая маска достоверности
16 + ceil(N/8) N × u16 сырые значения в порядке списка

Канал i соответствует биту i % 8 байта i / 8, младший бит первым. Неиспользуемые старшие биты последнего байта нулевые. Недостоверные слова обязательно нулевые: кодировщик нормализует их, декодировщик отвергает неканонический вход. Ошибки версии, точной длины, id, ожидаемого количества или вместимости выхода возвращают -1 без изменения выходных аргументов. Успешный результат — число слов, включая отмеченные недостоверными.

Classic CAN требует сегментации логического пакета портом: восемь байт данных кадра не вмещают всю выборку. Окно Modbus для снимка подключается отдельным проектным адаптером.

Клиенты и проверки

python/protocan/gas_watch.py предоставляет GasWatchConfig, GasWatchSample, NativeGasWatch; Android-порт — GuiGasWatch2.kt и JNI. Оба используют C request/ack/decode; недостоверный канал представлен Python None / Kotlin null. Старый DLL сообщает о необходимости пересборки нового режима вместо второго кодека на языке GUI.

Стабильные ABI-символы set_gas_watch_request, set_gas_watch_ack, set_gas_watch_decode добавлены без изменения версии и прежних символов. Сборки CMake, прямой host builder и Android NDK включают новый исходник. Общий эталон — tests/fixtures/gas-watch-v2.json. C-тест gas_watch_v2 проверяет wire, транзакционность, поколения, ошибки источников, независимость выборки, bitmap и переполнения. Python/JVM используют те же байты через настоящие DLL/JNI.

Это изменение общего ядра и адаптеров в templates. Переключение внешних SETGUI/Android на новую ревизию сабмодуля, объединение подписок вкладок и подключение к диспетчеру конкретной платы выполняются в этих проектах. Архивный c/gas-logger сохраняет собственный формат истории; его динамическая перенастройка здесь не включается.