diff --git a/.gitignore b/.gitignore index 3bb0a86..3dc4ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ out/ .envrc localjson ilp-rest-service/ilp-cw-api/results.json + +target +drone-black-box/drone-black-box \ No newline at end of file diff --git a/ilp-rest-service/compose.yaml b/compose.yaml similarity index 100% rename from ilp-rest-service/compose.yaml rename to compose.yaml diff --git a/drone-black-box/go.mod b/drone-black-box/go.mod new file mode 100644 index 0000000..95eb60e --- /dev/null +++ b/drone-black-box/go.mod @@ -0,0 +1,3 @@ +module drone-black-box + +go 1.25.3 diff --git a/drone-black-box/main.go b/drone-black-box/main.go new file mode 100644 index 0000000..be41a48 --- /dev/null +++ b/drone-black-box/main.go @@ -0,0 +1,95 @@ +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) + } +} diff --git a/ilp-rest-service/flake.lock b/flake.lock similarity index 100% rename from ilp-rest-service/flake.lock rename to flake.lock diff --git a/ilp-rest-service/flake.nix b/flake.nix similarity index 94% rename from ilp-rest-service/flake.nix rename to flake.nix index dd84284..834f374 100644 --- a/ilp-rest-service/flake.nix +++ b/flake.nix @@ -32,6 +32,9 @@ gron fx google-java-format + oha + gopls + go ]; shellHook = '' export JAVA_HOME=${pkgs.jdk21}