package control import ( "bytes" "context" "encoding/json" "fmt" "log" "net/http" "time" "tianyan-edge/internal/config" ) type Heartbeat struct { cfg *config.Config } func NewHeartbeat(cfg *config.Config) *Heartbeat { return &Heartbeat{cfg: cfg} } func (h *Heartbeat) Run(ctx context.Context) { client := &http.Client{Timeout: 5 * time.Second} ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: h.send(client) } } } func (h *Heartbeat) send(client *http.Client) { payload := map[string]any{ "edge_id": h.cfg.EdgeID, "version": h.cfg.Version, "ts": float64(time.Now().UnixMilli()) / 1000.0, } body, _ := json.Marshal(payload) url := fmt.Sprintf("%s/api/v1/edge/heartbeat", h.cfg.CloudURL) req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+h.cfg.EdgeToken) resp, err := client.Do(req) if err != nil { log.Printf("heartbeat: %v", err) return } resp.Body.Close() }