package main import ( "context" "flag" "log" "os" "os/signal" "sync" "syscall" "time" ) var logger = log.New(os.Stderr, "", log.Ltime|log.Lshortfile) func main() { cfgPath := flag.String("c", "/srv/energy/collector/config.json", "path to config file") flag.Parse() cfg, err := loadConfig(*cfgPath) if err != nil { log.Fatalf("config: %v", err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() db, err := NewDB(ctx, cfg.DB.DSN) if err != nil { log.Fatalf("db: %v", err) } defer db.Close() alpha, err := NewAlphaEss(cfg.AlphaEss) if err != nil { log.Fatalf("alphaess: %v", err) } defer alpha.Close() meters, err := NewMeterPoller(cfg.MQTT) if err != nil { log.Fatalf("meters: %v", err) } defer meters.Close() interval := time.Duration(cfg.SampleRate) * time.Second go runPollLoop(ctx, alpha, meters, db, interval) sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig logger.Println("shutting down") } func runPollLoop(ctx context.Context, alpha *AlphaEss, meters *MeterPoller, db *DB, interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: poll(ctx, alpha, meters, db) } } } func poll(ctx context.Context, alpha *AlphaEss, meters *MeterPoller, db *DB) { t := time.Now() // Poll AlphaEss and all SDM630 meters in parallel. var ( inverterData *InverterData meterData []MeterReading wg sync.WaitGroup ) wg.Add(2) go func() { defer wg.Done() d, err := alpha.Poll() if err != nil { logger.Printf("alphaess: %v", err) return } inverterData = d }() go func() { defer wg.Done() meterData = meters.PollAll() }() wg.Wait() // Write everything with the same timestamp t. if inverterData != nil { if err := db.WriteInverter(ctx, t, inverterData); err != nil { logger.Printf("write inverter: %v", err) } } for _, r := range meterData { if err := db.WriteMeter(ctx, t, r); err != nil { logger.Printf("write meter %s: %v", r.Device, err) } } }