Skip to content

v0.4.1

Latest

Choose a tag to compare

@github-actions github-actions released this 19 Oct 09:48
9a6347d

Настройки тегов из channel.tag-settings, быстрый возврат связи по OPC UA, защита от лавины полной переотправки в Kafka,
опрос контроллера сразу после записи, сравнение старой SCADA и шлюза на одном эмуляторе. Формат сообщений Kafka, ключи и
топики телеметрии и команд не менялись; новый входной топик channel.tag-settings необязателен (нет топика — работа по YAML).

Что может задеть при обновлении

  • GATEWAY_SUPERVISE_INTERVAL_MS по умолчанию 200 вместо 10000. Пауза между попытками подключения к контроллеру: первые 300
    неудачных попыток (≈ минута) — 0,2 с, дальше не реже раза в 2 с; после отказа сервера (пароль, защита, лимит сессий) — не короче 2 с.
    Если в окружении значение задано явно, оно сохраняется (и никогда не сокращается). Чтобы вернуть прежнее поведение —
    GATEWAY_SUPERVISE_INTERVAL_MS=10000.

Добавлено

  • Настройки тегов из топика channel.tag-settings (контракт бэкенда от 19.10.2026). channel публикует в сжатый топик период опроса и зону
    нечувствительности канала (ключ — имя тега, null — настройки сняты); шлюз читает топик целиком с начала (без группы, каждому экземпляру нужна полная
    копия) и применяет записи на лету, без перезапуска. Период: тег с poll.mode = "period" читается не чаще periodMs: у OPC UA читаются только
    «созревшие» узлы, у PAC и Modbus снимок/блоки читаются целиком, а в обработку и Kafka идут созревшие теги; цикл контроллера по-прежнему определяют
    pollingRate из YAML, чаще цикла тег не читается. Зона: deadband подменяет history.deadband тега в локальной истории; фильтр публикации в Kafka
    не меняется. Тег без записи и tombstone — значения из YAML. Нет топика — шлюз пишет предупреждение, повторяет раз в 10 с и работает по YAML.
    Настройки: CHANNEL_TAG_SETTINGS_TOPIC (channel.tag-settings), GATEWAY_TAG_SETTINGS_ENABLED (true); метрика scada_tag_settings_tags.
    Тесты: разбор записи, расписание (каждый N-й цикл, дрожание тикера, пропущенное не догоняется), зона истории, сквозной tests/tag_settings.rs.

Исправлено

  • Возврат связи по OPC UA после обрыва контроллера занимал 5–10 с и больше. Две причины. (1) Закрытие уже оборвавшейся сессии
    ждало ответа от мёртвого соединения до конца таймаута операции (5 с по умолчанию), и только потом шлюз пробовал подключиться заново;
    теперь, если цикл событий сессии завершён, закрывать нечего. (2) Между неудачными попытками подключения стояла фиксированная пауза
    GATEWAY_SUPERVISE_INTERVAL_MS (10 с), а после обрыва ещё 1 с: контроллер, вернувшийся через пару секунд, ждал шлюз до 10 с.
    Теперь по умолчанию пауза 0,2 с (первые 300 неудачных попыток, ≈ минута; дальше не реже раза в 2 с, чтобы журнал не рос при
    многодневной недоступности контроллера). Быстро повторяются только попытки после потери связи (порт закрыт, обрыв, таймаут); если
    сервер ответил отказом (неверный пароль, защита, лимит сессий), пауза не короче 2 с: иначе каждая попытка оставляла бы на сервере
    незавершённую сессию (на симуляторе тест неверного пароля за секунды исчерпал лимит сессий и уронил следующие тесты).
    Изменение поведения: значение по умолчанию GATEWAY_SUPERVISE_INTERVAL_MS 10000 → 200.
    На симуляторе возврат после обрыва в 5 с / 30 с: было 5,4 с и до 10 с, стало ≈ 0,15 с (по PAC — 0,65 с: ждёт цикла опроса). Цена на недоступный контроллер:
    0,017 ядра и 1,7 КБ/с журнала, нагрузка на Kafka не меняется. Тесты poller::tests::reconnect_is_fast_at_first_and_slows_down_for_a_long_outage,
    a_longer_configured_pause_is_never_shortened.
  • PLC-симулятор не принимался драйвером старой SCADA (EasyDriver). Ответы PAC шли без завершающего нуля (драйвер исполняет их как
    C-строки и читал хвост прошлого ответа), на GET_DEVICES симулятор отвечал другим форматом, чем прошивка (снимок t={…}), а
    рецептурный менеджер был набором приборов RECMAN[n], а не массивом t.RECMAN[n]. Теперь реальный драйвер находит все 2 741
    тегов станции «Танки нормализации» (раньше 222–253 не находил). Симулятор также понимает формат записи драйвера EasyDriver
    (__OBJECT2:set_cmd( "S_PAR_F", 1, 10 ) — двойные кавычки, пробелы, __RECMAN[n]) и раз в 5 с пишет в журнал число запросов состояния. Нужно для
    замеров «старая SCADA против шлюза» (docs/OLD_VS_NEW.md).
  • Полная переотправка всех тегов при «мигающем» брокере Kafka. Каждое восстановление доставки после сбоя запускало отправку всех тегов всех
    контроллеров заново (при 54 800 тегах — залп из 54 800 сообщений), и при частых сбоях и восстановлениях шлюз почти не публиковал «по
    исключению». Теперь флаг сбоя снимается как раньше, но новая полная отправка идёт не чаще раза в 30 с; недоставленное в паузе уходит
    с обычной плановой полной отправкой (GATEWAY_PUBLISH_FULL_RESEND_MS). Тест kafka::tests::flapping_broker_causes_one_resync_per_interval.
    (Основная причина шторма — переполнение очереди продюсера принималось за сбой доставки — исправлена раньше, см. ниже.)
  • OPC UA-фасад ptusa-opcua зависал целиком после нескольких записей. Обработчик записи (синхронный, под блокировкой адресного
    пространства библиотеки OPC UA) ждал ответа прошивки через драйвер ввода-вывода основного рантайма, а этот драйвер в тот же момент
    держал рабочий поток, остановленный параллельным запросом чтения на той же блокировке. Ответ прошивки некому было принять, сервер
    переставал отвечать на всё, в том числе на новые подключения (у шлюза — таймауты записи и чтения, «handshake завис», потеря связи).
    Чаще всего проявлялось при быстром опросе фасада (период 100 мс и меньше). Теперь команды идут по отдельному соединению в отдельном
    рантайме со своим драйвером; после обрыва соединение открывается заново, команда повторяется один раз. Тест
    writes_during_fast_polling_do_not_freeze_the_server (300 записей при опросе 5 мс и параллельном чтении) на прежнем коде виснет.
  • Недоступная Kafka останавливала опрос ПЛК: ложная «потеря связи» и кадры BAD. Приём команд пересоздаёт консьюмера в цикле, пока
    брокера нет; создание (fetch_metadata, чтение позиций — до 10 с каждое) шло синхронно прямо в асинхронной задаче и держало
    рабочий поток рантайма, а вместе с ним задачи, лежащие в его очереди, в том числе ответ на чтение из ПЛК. Чтение упиралось в таймаут
    5 с, шлюз писал «Потеряна связь» и слал монитору BAD, хотя ПЛК был жив. Нашёл дымовой тест стенда (сбой Kafka на 45 с): шлюз
    теперь создаёт консьюмера в отдельном блокирующем потоке (spawn_blocking), опрос от Kafka не зависит.
  • Переполнение очереди продюсера Kafka запускало лавину повторной отправки. Сообщение, которое очередь librdkafka
    (по умолчанию 100 000) не приняла, считалось сбоем доставки; следующая удачная отправка запускала повторную отправку
    всех тегов, она снова переполняла очередь, и так по кругу. На стенде в 750 тысяч тегов шлюз публиковал каждое
    снятое значение (785 тысяч сообщений в секунду вместо ожидаемых 250 тысяч) и накопил 14,8 млн ошибок за минуту.
    Теперь отказ очереди — не сбой доставки: тег забывает, что он опубликован, и уходит следующим же циклом опроса,
    остальные теги это не затрагивает. Новая метрика scada_kafka_queue_full_total; предупреждение в журнале — не чаще
    раза в 30 с. На реальной станции (2–3 тысячи тегов) очередь не переполняется, поведение не меняется.

Изменено

  • Симулятор по умолчанию отдаёт ВСЕ каналы базы каналов станции только по OPC UA. Новая конфигурация
    simulator/config/station_opcua.yaml — 2647 каналов config/stations/BN1_MCA1.yaml (включено в базе 1834) с теми же
    адресами узлов ns=2;s=<прибор>.<поле>, что у OPC UA-фасада эмулятора мойки: шлюзу нужен один и тот же файл станции.
    Прежний симулятор описывал 2517 тегов, из них 489 не относились к базе каналов, а 619 каналов базы в нём
    отсутствовали. Modbus и PAC симулятор умеет по-прежнему, но поднимает их, только если в конфигурации есть их теги
    (config/replay_config.yaml — три протокола, для интеграционных тестов); healthcheck проверяет порт OPC UA и порты
    настроенных протоколов. Подкоманда simulator station-config пересобирает конфигурацию из базы каналов; тест следит, что
    файл в репозитории не устарел.
  • docker-compose.yml и scripts/stack_smoke.sh: стенд — симулятор станции по OPC UA и шлюз с
    config/stations/BN1_MCA1.yaml (PLC_HOST вместо SIM_HOST); порты 5020 и 10000 на хост не пробрасываются.

Добавлено

  • Два готовых режима чтения одной станции: «EasyDriver» (PAC, driver-master) и OPC UA. config/stations/BN1_MCA1_easydriver.yaml
    (получается из станции для OPC UA скриптом tools/station_to_easydriver.sh; тест следит, что каналы, имена, типы и доступ не
    разошлись), simulator/config/station_pac.yaml (simulator station-config pac: все 2647 каналов только по PAC, серверы OPC UA и
    Modbus не поднимаются), scripts/stand.sh opcua|easydriver, up-moika.sh opcua|easydriver (профиль opcua — фасад). У PAC-режима
    каналы с пробелами в индексе (ST_CH[ 1 ]) читаются как ST_CH[1].
  • Проверка эффекта записи теперь и для PAC (GATEWAY_COMMANDS_VERIFY_MS): код 0 PAC означает только «команду приняли», а прошивка
    принимает почти любую запись. Шлюз читает значение до записи, отпускает соединение (опрос не ждёт), через заданное время запрашивает
    свежий снимок и при неизменном значении отвечает FAILED_NOT_APPLIED. Проверено на настоящей прошивке и тестом
    write_effect::pac_write_to_a_program_owned_valve_is_reported_as_not_applied_until_manual_mode.
  • scripts/compare_protocols.py, loadsim --protocol pac, PROTOCOL=pac scripts/stress_test.sh, scripts/pac_probe_noauth.py и
    docs/PROTOCOL_COMPARISON.md: сравнение EasyDriver и OPC UA на настоящей прошивке и симуляторе (охват, нагрузка, нагрузка на ПЛК при
    нескольких шлюзах, запись, обрыв связи, масштаб до миллиона значений в секунду, безопасность), выводы и список того, чего нет в PAC-режиме по
    сравнению с драйвером savushkin-r-d/driver.
  • scripts/stack_smoke.sh с MODE=easydriver и вторая строка матрицы в CI: дымовой тест стенда (сбои Kafka, БД, ПЛК) идёт и по
    OPC UA, и по PAC.
  • scripts/stress_test.sh и docs/STRESS_TEST.md: ступенчатый стресс-тест (от одной станции до миллиона значений тегов в
    секунду), метрики шлюза, Kafka и источника, готовые таблица и results.json. loadsim run --first N разносит источник
    из сотен серверов по нескольким процессам.

Документация

  • docs/PILOT_CHECKLIST.md: пошаговый чек-лист пробного запуска на реальном оборудовании (ступени: запуск без оборудования, только чтение, нагрузка на контроллер, сбои, запись одного тега, монитор, прогон на сутки), критерии прохождения каждой ступени, когда остановиться, что собрать для разбора, откат.

Образ: docker pull ghcr.io/euzireael/scada_rust:v0.4.1