Repository navigation
Настройки тегов из 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_MS10000 → 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