Files
home_automatization/services/rule-engine-service/internal/config/config.go
T
cacto abc83faf05 Реализация rule-engine-service: RabbitMQ → правила из PostgreSQL → gRPC
Слушает telemetry.new_reading (ручной ack/nack, реквизишн только при
транспортных ошибках gRPC), кэширует активные automation_rules в памяти
с периодическим обновлением из PostgreSQL (JOIN с devices — резолвит
внутренние ID в external_id, которым оперируют MQTT/Redis/gRPC). Условия
правил (>,<,>=,<=,=,!=) оцениваются обобщённо, без привязки к конкретным
типам устройств. При срабатывании вызывает device-control-service по gRPC
и публикует automation.rule_triggered в RabbitMQ.

Проверено сквозным тестом через docker compose на полном пайплайне:
mosquitto_pub → ingest-service (ClickHouse + telemetry.new_reading) →
rule-engine-service (совпадение правила) → device-control-service (gRPC →
Redis desired_state + MQTT-команда) → automation.rule_triggered. Показание
ниже порога проверено отдельно — правило корректно не срабатывает.
2026-07-26 21:18:59 +05:00

71 lines
1.5 KiB
Go

// Package config loads rule-engine-service settings from environment
// variables, matching the names used in the repo-root .env.example.
package config
import (
"fmt"
"os"
"time"
)
type Config struct {
PostgresDSN string
DeviceControlGRPCTarget string
RabbitMQURL string
RuleCacheRefreshInterval time.Duration
}
func Load() (Config, error) {
cfg := Config{
PostgresDSN: fmt.Sprintf("postgres://%s:%s@%s:%s/%s",
getEnv("POSTGRES_USER", "home_automation"),
getEnv("POSTGRES_PASSWORD", ""),
getEnv("POSTGRES_HOST", "localhost"),
getEnv("POSTGRES_PORT", "5432"),
getEnv("POSTGRES_DB", "home_automation"),
),
DeviceControlGRPCTarget: fmt.Sprintf("%s:%s",
getEnv("DEVICE_CONTROL_HOST", "localhost"),
getEnv("DEVICE_CONTROL_GRPC_PORT", "50051"),
),
RabbitMQURL: fmt.Sprintf("amqp://%s:%s@%s:%s/",
getEnv("RABBITMQ_USER", "guest"),
getEnv("RABBITMQ_PASSWORD", "guest"),
getEnv("RABBITMQ_HOST", "localhost"),
getEnv("RABBITMQ_PORT", "5672"),
),
}
interval, err := getEnvDuration("RULE_ENGINE_CACHE_REFRESH_INTERVAL", 15*time.Second)
if err != nil {
return Config{}, err
}
cfg.RuleCacheRefreshInterval = interval
return cfg, nil
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
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
}