# 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. Пример последовательности вызовов в прошивке: ```c #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` сохраняет собственный формат истории; его динамическая перенастройка здесь не включается.