Files
templates/c/gas-logger/README.md

13 KiB
Raw Blame History

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() по завершении своей секунды после отключения, а не при начале события. Этот модуль не заменяет автоматически старую логику УМП.

Контракт порта

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

#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-блокировки.

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

from set_devices.gas_logger import GasLoggerClient
client = GasLoggerClient(read_registers, write_register, block_words=120)
client.start()  # после проверки сигнатуры и JSON CRC
records = client.download()  # фиксирует, читает, проверяет, освобождает

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

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 <build>, сборка и ctest --test-dir <build>. Ветка codex/test-gas-logger: потребитель SETGUI импортирует модуль из third_party/templates и включает JSON в пакет. Производственные прошивки ПМ35/166 пока не переключены; адреса не включены в их текущие диспетчеры. Нужны подключение к таймеру/карте конкретной платы, проверка linker RAM и испытание на устройстве. Проверки на хосте не измеряют аппаратные тайминги.