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

178 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <build>`, сборка и `ctest --test-dir <build>`.
Ветка `codex/test-gas-logger`: потребитель SETGUI импортирует модуль из
`third_party/templates` и включает JSON в пакет. Производственные прошивки
ПМ35/166 пока не переключены; адреса не включены в их текущие диспетчеры.
Нужны подключение к таймеру/карте конкретной платы, проверка linker RAM и
испытание на устройстве. Проверки на хосте не измеряют аппаратные тайминги.