# GAS Logger — экспериментальная версия 1 Непрерывный регистратор с двумя кольцевыми банками, неизменяемым снимком и общей картой GAS / ProtoCAN GAS / Modbus FC03/FC06. Весь код и проектные/MCU адаптеры находятся в `templates`; приложения подключают ревизию сабмодулем. Ядро C99 не содержит HAL, malloc, глобальных переменных, драйвера связи или прерываний. Хранение — RAM, после отключения питания данные исчезают. Приложение → проектный источник сигналов → `gas_logger` → MCU-порт блокировки. GAS/Modbus/CAN диспетчеры → тот же экземпляр `gas_logger`. ## Состав | Файл | Назначение / зависимости | |---|---| | `include/gas_logger.h`, `src/gas_logger.c` | переносимое ядро, только стандартный C99 | | `ports/pm35/` | источники ПМ35 и кэш напряжений ПМ67, сгенерированная карта | | `ports/c28x/` | сохранение/восстановление IRQ для TI F2812/F28335 | | `ports/stm32/` | PRIMASK для STM32 F1/F4/G4, CMSIS проекта | | `src/gas_logger_pcan.c` | регион сервиса `pcan_gas`, только MCU с поддержкой `uint8_t` | | `../../python/set_devices/gas_logger.py` | валидация карты, генератор C, клиент снимков; stdlib | | `../../python/set_devices/gas_logger_maps/pm35.json` | имена, адреса GAS/CAN/Modbus, источники и регистры сервиса | | `tests/` | ядро и сквозной тест клиента с настоящим C-регистратором | ## JSON и адреса JSON — исходное описание. `ports/pm35/pm35_map.h` — автоматически созданная копия для прошивки. `tools/generate_map.py --check` проверяет их соответствие. Изменения выполняются в JSON, затем запускается генератор. CRC32 канонического JSON публикуется платой: клиент откажется читать/записывать при несовпадении карты. Это идентификатор совместимости, не механизм аутентификации. Все адреса — **нулевые индексы 16-битных регистров**, без префикса 4xxxx. В поле `can.address` указан MsgBody для **ProtoCAN GAS**, а не полный CAN ID. DeviceType/DeviceID/Route выбирает транспорт проекта. Идентификаторы legacy CAN ПМ35 и ПМ67 не переименовываются в GAS: проект сначала обновляет их кэш, после чего регистратор читает его через `source`. Экспериментальный профиль ПМ35: | GAS | Modbus FC03 | ProtoCAN GAS | Имя | Источник | |---|---|---|---|---| | 0x5000 | 0x5000 | 0x5000 | Команды УМП | modbus[127] | | 0x5001 | 0x5001 | 0x5001 | Ток, 0.1 мА | modbus[28] | | 0x5002 | 0x5002 | 0x5002 | Код ЦАП | modbus[123] | | 0x5003 | 0x5003 | 0x5003 | Начальная уставка | modbus[80] | | 0x5004 | 0x5004 | 0x5004 | Конечная уставка | modbus[81] | | 0x5005 | 0x5005 | 0x5005 | ЗПТ1 | pm67_voltage[0], ранее принятый CAN адрес 305 | | 0x5006 | 0x5006 | 0x5006 | ЗПТ2 | pm67_voltage[1], ранее принятый CAN адрес 8 | Адреса 0x5000/0x6000 — выделение **для тестового профиля**, не утверждение о поддержке этих окон существующей прошивкой. Перед включением в другую карту проект обязан проверить отсутствие пересечений со своими регионами. ## Карта сервиса База одинакова для всех трёх транспортов: `0x6000`; имена и смещения также перечислены в JSON. Многословные числа: сначала младшее 16-битное слово. Endian байтов задаёт транспорт: Modbus BE, ProtoCAN GAS LE. | Смещение | Доступ | Значение | |---|---|---| | 0, 1 | R | сигнатура 0x474C, версия 1 | | 2 | R | бит 0 запись, бит 1 снимок закреплён | | 3..5 | R | каналов, слов в записи, ёмкость банка | | 6, 7 | R | записей в текущем банке / снимке | | 8..9 | R | поколение снимка u32 | | 10..11 | R | CRC32 JSON-карты | | 12..13 | R | следующий номер записи u32 | | 14..15 | R | пропущенные снимки: запрос во время закреплённого снимка | | 16..17 | R | ошибки источников при записи | | 18 | W | 1 START, 2 SNAPSHOT, 3 RELEASE | | 19..20 | W | поколение, разрешённое к освобождению | | 0x100.. | R | записи снимка по прямому адресу, без команд курсора | Запись: `time_ms:u32, sequence:u32, event:u16, channels[N]:u16`. В профиле ПМ35 — 12 слов, 760 записей на банк, всего 18240 слов RAM для двух банков. Блочное чтение разрешено по любой границе, максимум 125 слов. Клиент собирает записи из блоков, в том числе неполных по размеру записи. Классический CAN использует до 4 слов в кадре. Для UART подходит 120/125 слов. Для 760 записей этого профиля требуется 76 чтений по 120 слов без записи курсора; состав полей отличается от старого 42-словного УМП, поэтому это не прямое измерение ускорения прежнего лога. ## Непрерывная запись и снимки `START` идемпотентен: включает запись, не очищает историю. STOP-команды нет. `SNAPSHOT` кратко блокирует IRQ, переключает активный банк и закрепляет старый. Во время загрузки новые записи продолжают поступать в другой кольцевой банк. Снимок не перезаписывается, пока клиент не передаст совпадающее поколение и `RELEASE`. Одновременные управляющие клиенты не поддерживаются. Повторный SNAPSHOT при занятом снимке возвращает BUSY и увеличивает счётчик. Пустой банк возвращает EMPTY. Долгий обрыв связи не останавливает запись, но закреплённый снимок остаётся занят: нужно явно продолжить чтение (`resume=True`) либо освободить его. Автоматического таймаута освобождения нет. После переключения новый банк начинает историю с нуля; непрерывность означает продолжение сбора, а не дублирование предыстории между снимками. Триггер/постинтервал определяет проект. Например, ПМ35 вызывает `gl_snapshot()` по завершении своей секунды после отключения, а не при начале события. Этот модуль не заменяет автоматически старую логику УМП. ## Контракт порта ```c uint32_t enter(void *user); /* сохранить и запретить IRQ */ void leave(void *user, uint32_t previous); /* восстановить ровно previous */ int source(void *user, uint16_t gas, uint16_t *value); ``` `source` выполняется под блокировкой: только быстрые чтения RAM. Ни запросов CAN/Modbus, ни ожиданий периферии. Ошибка любого канала отбрасывает всю запись. Кэш CAN обновляется под той же блокировкой. Для RTOS/многоядерных платформ нужен порт с подходящей межпоточной синхронизацией; PRIMASK — для одного ядра. На стеке блочного чтения до 125 слов; при захвате до 64 слов каналов. Массивы банков не должны перекрываться друг с другом и контекстом. ## Подключение ПМ35 / F28335 ```c #include "gas_logger_pm35.h" #include "gas_logger_c28x.h" static gas_logger logger; static uint16_t bank0[9120], bank1[9120]; /* разместить в RAM по linker map */ static gl_pm35_context source; void logger_init(volatile uint16_t *modbus, volatile uint16_t *pm67) { source.modbus = modbus; source.pm67_voltage = pm67; source.enter = gl_c28x_enter; source.leave = gl_c28x_leave; source.irq_user = 0; if (gl_pm35_init(&logger, &source, bank0, bank1, 9120) == GL_OK) gl_write(&logger, 0x6012, GL_START); } /* Из существующего таймера: gl_capture(&logger, time_ms, event); */ ``` Включить `src/gas_logger.c`, `ports/pm35/gas_logger_pm35.c` и `ports/c28x/gas_logger_c28x.c`. Include-пути — `include`, `ports/pm35`, `ports/c28x`. F2812/F28335 используют одни intrinsics TI. Для STM32 подключить `ports/stm32/gas_logger_stm32.c`, выбрать `GL_STM32_DEVICE_HEADER` (`stm32f1xx.h`/`stm32f4xx.h`/`stm32g4xx.h`) и передать `gl_stm32_enter/leave`. Проектный PM35-адаптер применим к STM-эмулятору только если его массивы соответствуют указанному контракту. FC03 диспетчер вызывает `gl_modbus_read()`, FC06 — `gl_modbus_write()`. GL_ADDRESS → exception 2, GL_ARGUMENT → 3, GL_BUSY → 6, остальные ошибки → 4. Ответ на запись отправляется лишь после GL_OK. CRC/кадры остаются в транспорте. Для `pcan_gas` добавляется регион `gl_pcan_region_init()`; сигнальные адреса маршрутизируются отдельно в `gl_can_read()` (или уже существующие GAS-регионы). Стандартные GAS-записи CAN не имеют ACK: CAN-адаптер клиента должен подтвердить результат чтением статуса; просто отправить CAN-кадр недостаточно. Передача UART/CAN выполняется **после** снятия IRQ-блокировки. ## Клиент и проверка ```python from set_devices.gas_logger import GasLoggerClient client = GasLoggerClient(read_registers, write_register, block_words=120) client.start() # после проверки сигнатуры и JSON CRC records = client.download() # фиксирует, читает, проверяет, освобождает ``` Вызовы транспорта синхронные, подтверждённые; для GUI запускать в его рабочем потоке. Исключение/отмена оставляет снимок закреплённым для явного продолжения. Записи с таймаутом автоматически не повторяются. ```powershell python c/gas-logger/tools/generate_map.py --check python c/gas-logger/tests/run_tests.py --cc C:/msys64/mingw64/bin/gcc.exe ``` Либо `cmake -S c/gas-logger -B `, сборка и `ctest --test-dir `. Ветка `codex/test-gas-logger`: потребитель SETGUI импортирует модуль из `third_party/templates` и включает JSON в пакет. Производственные прошивки ПМ35/166 пока не переключены; адреса не включены в их текущие диспетчеры. Нужны подключение к таймеру/карте конкретной платы, проверка linker RAM и испытание на устройстве. Проверки на хосте не измеряют аппаратные тайминги.