Добавлен 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 по истечении таймаута.
91 lines
2.1 KiB
Go
91 lines
2.1 KiB
Go
// Package config loads device-control-service settings from environment
|
|
// variables, matching the names used in the repo-root .env.example.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
GRPCPort int
|
|
|
|
MQTTBrokerURL string
|
|
MQTTClientID string
|
|
|
|
RedisAddr string
|
|
RedisPassword string
|
|
RedisDB int
|
|
|
|
RabbitMQURL string
|
|
|
|
HealthCheckTimeout time.Duration
|
|
HealthCheckInterval time.Duration
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
cfg := Config{
|
|
MQTTBrokerURL: fmt.Sprintf("tcp://%s:%s", getEnv("MQTT_HOST", "localhost"), getEnv("MQTT_PORT", "1883")),
|
|
MQTTClientID: getEnv("DEVICE_CONTROL_MQTT_CLIENT_ID", "device-control-service"),
|
|
|
|
RedisAddr: fmt.Sprintf("%s:%s", getEnv("REDIS_HOST", "localhost"), getEnv("REDIS_PORT", "6379")),
|
|
RedisPassword: getEnv("REDIS_PASSWORD", ""),
|
|
|
|
RabbitMQURL: fmt.Sprintf("amqp://%s:%s@%s:%s/",
|
|
getEnv("RABBITMQ_USER", "guest"),
|
|
getEnv("RABBITMQ_PASSWORD", "guest"),
|
|
getEnv("RABBITMQ_HOST", "localhost"),
|
|
getEnv("RABBITMQ_PORT", "5672"),
|
|
),
|
|
}
|
|
|
|
var err error
|
|
if cfg.GRPCPort, err = getEnvInt("DEVICE_CONTROL_GRPC_PORT", 50051); err != nil {
|
|
return Config{}, err
|
|
}
|
|
if cfg.RedisDB, err = getEnvInt("REDIS_DB", 0); err != nil {
|
|
return Config{}, err
|
|
}
|
|
if cfg.HealthCheckTimeout, err = getEnvDuration("DEVICE_CONTROL_HEALTHCHECK_TIMEOUT", 60*time.Second); err != nil {
|
|
return Config{}, err
|
|
}
|
|
if cfg.HealthCheckInterval, err = getEnvDuration("DEVICE_CONTROL_HEALTHCHECK_INTERVAL", 15*time.Second); err != nil {
|
|
return Config{}, err
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getEnv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func getEnvInt(key string, fallback int) (int, error) {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return fallback, nil
|
|
}
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: %w", key, err)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func getEnvDuration(key string, fallback time.Duration) (time.Duration, error) {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return fallback, nil
|
|
}
|
|
d, err := time.ParseDuration(v)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s: %w", key, err)
|
|
}
|
|
return d, nil
|
|
}
|