Сохрани адресацию отдельных регистров GAS 0x3 и выдели слотам тип 0x4 с маской Word0. Добавь единое C-ядро GAS_WATCH2, тонкие Python/JNI-порты и проверки общих эталонных байтов. Совместимость: тип 0x4 больше не обслуживает Modbus Coil. Потребителям PCAN_MSG_MODBUS_COIL и ProtoCanMsgToModbusCoil нужно перейти на GAS_SLOT и отдельный API слотов; регистровый GAS остаётся на 0x3.
17 KiB
GAS_WATCH2: список адресов и пакетная выборка
GAS
0x3использует адреса отдельных 16-битных регистров. GAS SLOT0x4— отдельный сервис с маской в 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 сохраняет собственный формат истории; его
динамическая перенастройка здесь не включается.