package main import ( "encoding/json" "fmt" "log" "net/http" "os" "sync" ) // Define the DroneEvent struct as JSON type DroneEvent struct { DroneID string `json:"drone_id"` Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` Timestamp string `json:"timestamp"` } // Shared state type alias type Server struct { // This is a shared state between handlers // It is protected by a mutex to prevent concurrent access mu sync.RWMutex history []DroneEvent } // Ingest handler func (s *Server) ingestHandler(w http.ResponseWriter, r *http.Request) { var event DroneEvent if err := json.NewDecoder(r.Body).Decode(&event); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } fmt.Printf("Data ingested: %+v\n", event) s.mu.Lock() s.history = append(s.history, event) s.mu.Unlock() w.WriteHeader(http.StatusCreated) } // Snapshot handler func (s *Server) snapshotHandler(w http.ResponseWriter, r *http.Request) { timeParam := r.URL.Query().Get("time") if timeParam == "" { http.Error(w, "Missing 'time' query parameter", http.StatusBadRequest) return } s.mu.RLock() defer s.mu.RUnlock() latestStates := make(map[string]DroneEvent) for _, event := range s.history { if event.Timestamp <= timeParam { latestStates[event.DroneID] = event } } result := make([]DroneEvent, 0, len(latestStates)) for _, event := range latestStates { result = append(result, event) } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(result); err != nil { log.Printf("Error encoding response: %v", err) } } func main() { // By default, listen on port 3000 port := os.Getenv("BLACKBOX_PORT") if port == "" { port = "3000" } server := &Server{ history: make([]DroneEvent, 0), } mux := http.NewServeMux() mux.HandleFunc("POST /ingest", server.ingestHandler) mux.HandleFunc("GET /snapshot", server.snapshotHandler) fmt.Printf("Black Box Service is running on port %s...\n", port) if err := http.ListenAndServe(":"+port, mux); err != nil { log.Fatal(err) os.Exit(1) } }