package main import ( "context" "encoding/json" "fmt" "log" "os" "os/signal" "syscall" "time" "example.com/mqtt5-learning/internal/config" "example.com/mqtt5-learning/internal/mqtt" ) func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { cfg, err := config.Load(os.Args[1:]) if err != nil { return fmt.Errorf("failed to load config: %w", err) } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() base := "lab/v1/devices/" + cfg.MQTT.DeviceID inbox := make(chan mqtt.Message, 16) manager, err := mqtt.Start(ctx, cfg, []string{base + "/telemetry", base + "/responses"}, inbox) if err != nil { return fmt.Errorf("failed to start MQTT client: %w", err) } defer mqtt.Stop(manager) ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() runID := time.Now().UnixNano() sequence := 0 for { select { case <-ctx.Done(): log.Print("shutting down") return nil case msg := <-inbox: log.Printf("received message on topic %s retained=%t payload=%s", msg.Topic, msg.Retain, string(msg.Payload)) case <-ticker.C: sequence++ payload, err := json.Marshal(struct { ID string `json:"id"` Command string `json:"command"` }{ ID: fmt.Sprintf("ping-%d-%d", runID, sequence), Command: "ping", }) if err != nil { return fmt.Errorf("failed to marshal payload: %w", err) } if err := mqtt.Publish(ctx, manager, base+"/commands", payload); err != nil { log.Printf("command publication failed or outcome uncertain: %v", err) } } } }