Всё началось с одного неудобного вопроса на совещании: «Почему мы платим миллионы за 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 года. Фронтенд работает, но общается напрямую с имитацией данных. Пора строить настоящий бэкенд.
Мы сразу пошли в микросервисную архитектуру. Не из моды — из инженерной логики. Система управления доступом, бизнес-логика обработки данных от оборудования и маршрутизация запросов — это три разных зоны ответственности с разными циклами обновления и разными требованиями к безопасности. Им нечего делать в одном монолите.
Родились три сервиса:
Eureka Discovery — реестр, в котором сервисы находят друг друга. Простой, как сама идея service discovery: поднялся — зарегистрировался, упал — выбыл.
API Gateway — единая точка входа. Сюда приходят все запросы от клиента, здесь проверяется JWT-токен, здесь RBAC решает, имеет ли оператор третьей смены право запустить техпроцесс или только наблюдать. Access-токен живёт час, refresh — семь дней. Учётные данные пользователей лежат в отдельной схеме PostgreSQL, физически изолированной от бизнес-данных. Паранойя? Нет. Просто промышленная безопасность.
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 миллисекунд. В мире промышленной автоматизации это — когда оператор видит, что температура в камере превысила допустимое значение, до того как сработает аварийная защита. Разница — не в скорости, а в цене ошибки.
Наш конвейер данных выглядит так:
ModbusReader непрерывно опрашивает контроллеры, получает значения регистров
Значения попадают в TagCacher — кэш на Caffeine — и одновременно публикуются в реактивный Sink
Эндпоинт отдаёт SSE-поток — бесконечный поток событий в формате text/event-stream
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. Без вендорлока. Без лицензий на рабочее место. Без интерфейса из прошлого века.
Иногда, чтобы изменить подход к промышленному софту, достаточно одного неудобного вопроса на совещании.

Компонент | Технология |
Язык | 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 |
