Всё началось с одного неудобного вопроса на совещании: «Почему мы платим миллионы за SCADA, которая выглядит как Windows 98?».

Тишина. Потом — смех. А потом кто-то сказал: «А давайте напишем свою».

Так обычно начинаются истории, которые заканчиваются либо провалом, либо чем-то, чего не ожидал никто. Наша — из второй категории. За двадцать один месяц мы прошли путь от пустого Maven-проекта до платформы, которая в реальном времени управляет промышленными роботами, конвейерами, сушильными камерами и пескоструйным оборудованием. На стеке, который знаком любому Java-разработчику.

Это история про проект RIIOT. Про мосты между Spring Boot и протоколом 1979 года. Про реактивные потоки, которые несут данные от датчиков к экрану оператора быстрее, чем он успевает моргнуть. И про то, как мы научились не бояться железа.

Глава первая. Чистый лист и первый коммит

Март 2024 года. В репозитории — только pom.xml и .gitignore. Впереди — неизвестность.

Первое решение, которое нужно было принять: веб или десктоп? Для корпоративной системы ответ кажется очевидным — конечно, веб. Но мы строили не корпоративную систему. Мы строили пульт управления, который будет стоять в цеху, рядом с гудящими конвейерами и промышленными роботами. Здесь не место для капризов браузера — нужна нативная отзывчивость, прямое подключение, железная надёжность.

Выбор пал на JavaFX. Да, в 2024 году. Не потому что мы ретрограды, а потому что JavaFX 17 — это зрелый фреймворк для десктопных приложений, который идеально ложится на Spring Boot через DI-контейнер. А для промышленного интерфейса — визуальные библиотеки, о которых веб-разработчики могут только мечтать: TilesFX для дашбордов с метриками, Medusa для стрелочных приборов и индикаторов, ControlsFX для сложных элементов управления. Приборная панель, которая выглядит как приборная панель — а не как веб-страница, притворяющаяся ей.

За первую неделю мы подняли авторизацию, подключились к PostgreSQL и сверстали первые экраны на FXML. Ничего героического. Но именно тогда стало понятно: это не сайд-проект на выходные. Это — всерьёз.

Глава вторая. Интерфейс, за которым стоит цех

К лету 2024-го фронтенд начал обретать форму. 200 с лишним коммитов — и за каждым стоит конкретная потребность оператора на производстве.

Представьте рабочее место: перед вами — экран, на котором в реальном времени отображаются десятки параметров. Температура в сушильных камерах. Положение робота в пространстве. Скорость конвейера. Состояние каждого узла линии. И всё это — не статичные цифры, а живые потоки данных, которые обновляются непрерывно.

За этой кажущейся простотой — 212 Java-классов, более 50 FXML-экранов и 30 REST-клиентов, каждый из которых общается со своим эндпоинтом на бэкенде. Панель ручного управления роботом. Панель климат-контроля. Панель конвейерной линии. Журнал техпроцессов с пагинацией и фильтрами. Админка для управления ролями и правами доступа.

Но главное — не количество экранов. Главное — то, как данные попадают на них.

Мы выбрали реактивный стек — Spring WebFlux на клиенте, Server-Sent Events для потоковой передачи. Никакого поллинга. Клиент подписывается на SSE-поток — и данные текут, как вода по трубе. Project Reactor (Flux, Mono, Sinks) под капотом обеспечивает backpressure и неблокирующую обработку. Оператор видит изменения мгновенно.

Это не просто красиво на архитектурной диаграмме. Когда робот выполняет координатное перемещение, и ты видишь его позицию в реальном времени — без лагов, без подёргиваний — понимаешь, что реактивный стек здесь не хайп, а необходимость.

Глава третья. Бэкенд: микросервисы входят в чат

Январь 2025 года. Фронтенд работает, но общается напрямую с имитацией данных. Пора строить настоящий бэкенд.

Мы сразу пошли в микросервисную архитектуру. Не из моды — из инженерной логики. Система управления доступом, бизнес-логика обработки данных от оборудования и маршрутизация запросов — это три разных зоны ответственности с разными циклами обновления и разными требованиями к безопасности. Им нечего делать в одном монолите.

Родились три сервиса:

  1. Eureka Discovery — реестр, в котором сервисы находят друг друга. Простой, как сама идея service discovery: поднялся — зарегистрировался, упал — выбыл.

  2. API Gateway — единая точка входа. Сюда приходят все запросы от клиента, здесь проверяется JWT-токен, здесь RBAC решает, имеет ли оператор третьей смены право запустить техпроцесс или только наблюдать. Access-токен живёт час, refresh — семь дней. Учётные данные пользователей лежат в отдельной схеме PostgreSQL, физич��ски изолированной от бизнес-данных. Паранойя? Нет. Просто промышленная безопасность.

  3. Main Service — сердце системы. Spring Boot 3.4, полностью реактивный стек на WebFlux, R2DBC для неблокирующего доступа к PostgreSQL, Caffeine для кэширования тегов. 19 REST-контроллеров. Движок техпроцессов. И — то, ради чего всё затевалось — мост к железу через Modbus.

Глава четвёртая. Modbus, или, как разговаривать с железом

Вот мы и добрались до самого интересного — точки, где Java встречается с заводским цехом.

Modbus — протокол, которому больше сорока лет. Он был придуман в 1979 году компанией Modicon для общения с программируемыми логическими контроллерами. Принцип прост до безобразия: есть регистры, ты их читаешь или пишешь в них. Никаких очередей сообщений, никакого TLS, никакого heartbeat. Просто байты по TCP или последовательному порту.

Эта простота обманчива. За ней скрывается целый букет проблем, каждую из которых нам пришлось решить руками.

Проблема первая: потокобезопасность. Modbus-соединение — один сокет. Два потока одновременно отправили запрос — получили мусор в ответе. Решение: ThreadSafeModbusClient — обёртка, которая синхронизирует все операции чтения и записи. Просто? Да. Но без этого — ничего не работает.

Проблема вторая: производительность. ПЛК отдаёт данные порциями — максимум 125 регистров за один запрос. Наивный подход — запрашивать по одному — генерирует сотни запросов в секунду. Задержки растут, контроллер захлёбывается. OptimizedModbusReader упаковывает запросы в пакеты максимального размера. Вместо 100 запросов — один. Разница — на порядок.

Проблема третья: обрывы связи. Промышленная среда — это электромагнитные помехи, перегрузки сети, перезагрузки контроллеров. Связь будет рваться, вопрос — как на это реагировать. ModbusReconnectionManager реализует экспоненциальный backoff: потеряли соединение — ждём секунду, пробуем снова. Не получилось — две секунды. Четыре. Восемь. Без паники, без участия оператора. Устройство вернулось — система подхватывает автоматически.

Проблема четвёртая: запись команд. Переместить робот в точку с координатами — это не один запрос, а серия записей в регистры, строго последовательная. ModbusWriter реализует очередь команд, гарантируя порядок выполнения.

Четыре компонента. Четыре проблемы, каждая из которых могла бы остановить проект. Но вместе они образуют надёжный мост между миром Java и миром промышленных контроллеров.

Глава пятая. Техпроцесс — оркестр из железа

У каждого производственного цикла есть рецепт. Не метафорический — буквальный.

Рецепт — это шаблон, описывающий последовательность действий: какие слои нанести, при какой температуре сушить, с каким воздушным потоком, сколько минут. Рецепт состоит из слоёв — этапов обработки. Каждый слой содержит действия — конкретные команды оборудованию: переместить робот, включить конвейер, дождаться значения датчика.

Когда оператор запускает техпроцесс, движок начинает исполнять рецепт шаг за шагом. Конечный автомат проводит процесс через цепочку состояний:

СТАРТ → ВЫПОЛНЕНИЕ → РАБОТА РОБОТА → СУШКА → ГОТОВО

На каждом переходе — запись в журнал. Полная трассировка: кто запустил, когда, что произошло на каждом этапе. Если на любом шаге теряется связь с устройством — процесс встаёт на паузу, оператор получает уведомление и решает: ждать восстановления, повторить или пропустить.

Звучит просто. Но за этой простотой — координация десятков единиц оборудования в реальном времени. Робот должен закончить перемещение до того, как конвейер начнёт движение. Сушильная камера должна выйти на температуру до того, как в неё поступит заготовка. Датчик должен подтвердить, что дверца камеры закрыта, прежде чем начнётся нагрев.

Это хореография. И TPAlgorithmService — её хореограф.

Глава шестая. Реальное время — не маркетинг

В мире веб-разработки «реальное время» — это когда чат-сообщение приходит за 200 миллисекунд. В мире промышленной автоматизации это — когда оператор видит, что температура в камере превысила допустимое значение, до того как сработает аварийная защита. Разница — не в скорости, а в цене ошибки.

Наш конвейер данных выглядит так:

  1. ModbusReader непрерывно опрашивает контроллеры, получает значения регистров

  2. Значения попадают в TagCacher — кэш на Caffeine — и одновременно публикуются в реактивный Sink

  3. Эндпоинт отдаёт SSE-поток — бесконечный поток событий в формате text/event-stream

  4. JavaFX-клиент подписывается на этот поток через WebClient и мгновенно обновляет интерфейс

Буфер Sink рассчитан на 12 000 событий — запас для пиковых нагрузок. Реактивный стек обеспечивает backpressure: если потребитель не успевает обрабатывать, система не захлёбывается, а адаптируется.

Ни одного Thread.sleep. Ни одного таймера на поллинг. Данные текут — от датчика к экрану — как электрический ток по проводу.

Глава седьмая. Безопасность на производстве — это не файрвол

Когда мы говорим «безопасность» в контексте завода, мы имеем в виду не DDoS-атаки. Мы имеем в виду ситуацию, когда стажёр случайно отправил команду перемещения робота, не убедившись, что в рабочей зоне нет людей.

Поэтому контроль доступа в RIIOT — не формальность, а критическая функция:

  • JWT на каждый запрос. Никаких сессий, никаких кук. Токен — единственный пропуск;

  • Ролевая модель. Администратор, оператор, наблюдатель — у каждого свой набор доступных действий. Пермишены привязаны к конкретным API-маршрутам;

  • Изоляция данных. Учётные записи хранятся в отдельной схеме БД. Компрометация бизнес-данных не даёт доступа к аутентификации — и наоборот;

  • Аудит всего. Каждое действие оператора, каждое изменение настроек, каждый переход техпроцесса — в журнале. С меткой времени и идентификатором пользователя;

  • OWASP Dependency Check встроен в пайплайн сборки — уязвимые зависимости не проходят.

Глава восьмая. Что мы поняли за 21 месяц

За двадцать один месяц — от первого коммита в марте 2024-го до сегодняшнего дня — мы собрали свою коллекцию граблей и откровений.

Modbus — это не страшно. Страшно — это когда ты думаешь, что он страшный, и поэтому не закладываешь обработку ошибок. Сам протокол примитивен. Сложность — в надёжности обвязки вокруг него.

Реактивный стек — не серебряная пуля, но для IoT — почти. Когда у тебя сотни датчиков, каждый из которых генерирует поток событий, императивный подход задыхается. WebFlux, R2DBC, SSE — это не модные слова, а единственный способ не плодить тысячи потоков.

JavaFX жив. И для промышленных интерфейсов — вне конкуренции. Попробуйте нарисовать аналоговый прибор со стрелкой в React. А теперь попробуйте в Medusa. Вопросы отпадают.

Микросервисы в IoT — это про изоляцию отказов. Если упал сервис авторизации, Main Service продолжает собирать данные с оборудования. Если упал Main Service, шлюз возвращает понятные ошибки, а не тишину.

Что дальше

Проект живёт и развивается. Впереди — structured logging с correlation ID для сквозной трассировки от клика оператора до записи в регистр. Heartbeat для SSE-стримов, чтобы клиент мог отличить «нет новых данных» от «потеряна связь». Детальные метрики по каждому Modbus-соединению. И расширение покрытия тестами — в первую очередь, интеграционными, для Modbus-слоя и движка техпроцессов.

Но уже сейчас RIIOT — это работающая система, которая доказывает простую мысль: промышленная автоматизация не обязана быть заложницей проприетарных решений. Spring Boot, WebFlux, JavaFX и открытые Modbus-библиотеки — достаточно, чтобы построить полноценную SCADA. Без вендорлока. Без лицензий на рабочее место. Без интерфейса из прошлого века.

Иногда, чтобы изменить подход к промышленному софту, достаточно одного неудобного вопроса на совещании.

https://indpages.ru/wp-content/uploads/2025/11/2-2.jpg

Видео о внедрении проекта


Компонент

Технология

Язык

Java 17

Фреймворк

Spring Boot 3.2–3.4, Spring WebFlux

Микросервисы

Spring Cloud Netflix Eureka, Spring Cloud Gateway

БД

PostgreSQL (R2DBC + Flyway)

Кэширование

Caffeine

Безопасность

Spring Security, JWT (JJWT), RBAC

Промышленный протокол

Modbus TCP/Serial (j2mod, DigitalPetri)

Десктоп UI

JavaFX 17, TilesFX, Medusa, ControlsFX

Маппинг и кодогенерация

MapStruct, Lombok

API-документация

OpenAPI / Swagger (springdoc)

Экспорт данных

Apache POI

Сборка

Maven