Добавлен gRPC-контракт proto/device_control (TurnOn/TurnOff/SetLevel), общий Go-модуль proto/ для переиспользования сгенерированного кода. device-control-service принимает команды по gRPC, обновляет desired_state в Redis и публикует их в MQTT; слушает devices/+/telemetry и devices/+/ack для отметки живости устройства и обновления reported_state; горутина health-check переводит устройство в offline по таймауту и публикует событие в RabbitMQ (device.status_changed) для будущего notification-service. Проверено сквозным тестом через docker compose: grpcurl TurnOn/SetLevel меняет desired_state и уходит в MQTT, mosquitto_pub с ack обновляет reported_state и статус, health-check автоматически переводит устройство в offline по истечении таймаута.
device-control-service (Go)
Статус: реализован (MVP).
Зона ответственности:
- Предоставляет gRPC API (см.
proto/device_control/device_control.proto:TurnOn,TurnOff,SetLevel→CommandResult) для отправки команд устройствам — вызывается из Laravel (ручное управление) и из rule-engine-service (действия по срабатыванию правил). - Публикует команду в MQTT-топик устройства (
devices/{device_id}/commands) и слушает подтверждения (devices/{device_id}/ack). - Поддерживает паттерн Device Shadow в Redis:
desired_state(чего хочет пользователь) противreported_state(что подтвердило устройство), плюсstatusиlast_seen. - Ведёт health-check горутиной-тикером: переводит устройство в
offline, когдаlast_seenпревышает таймаут, и публикует событие в RabbitMQ (device.status_changed) для будущего notification-service — как при уходе в офлайн, так и при возврате online.
Не входит в зону ответственности: решение о том, когда отправлять команду на основе показаний датчиков — это логика rule-engine-service. Этот сервис только исполняет команды и отслеживает состояние для той команды, что ему дали.
Архитектурные решения
- gRPC — не ждёт подтверждения устройства.
CommandResult.successозначает «desired_state обновлён и команда опубликована в MQTT», а не «устройство подтвердило выполнение». Это соответствует Device Shadow: мгновенный отклик в UI, даже если устройство офлайн или ответит с задержкой.reported_stateобновляется позже, асинхронно, по приходу ack. - last_seen обновляется и по телеметрии, и по ack. Сервис подписан на
devices/+/telemetryтолько ради отметки «устройство живо» (сами данные телеметрии парсит и хранит ingest-service) — иначе датчики без исходящих команд (и, соответственно, без ack) никогда не считались бы online. - Нет зависимости от PostgreSQL. Список известных устройств — это Redis
Set (
devices:known), который пополняется по мере поступления телеметрии/ack, а не выгружается из таблицыdevices. Это удерживает сервис в границах MQTT+Redis+RabbitMQ+gRPC, как и в диаграмме архитектуры корневого README.
Запуск
cd services/device-control-service
go test ./...
go build ./cmd/device-control-service
Конфигурация — через переменные окружения (секция device-control-service в
корневом .env.example). proto/ — отдельный Go-модуль, подключается через
replace в go.mod на относительный путь ../../proto.