Skip to content

Commit 3c043c5

Browse files
committed
Everything works!
1 parent a5902e7 commit 3c043c5

53 files changed

Lines changed: 2820 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Build Containers
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build-environment-service:
11+
name: Build Environment Service
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Build Docker image
17+
run: docker build -t uav-flight-optimizer/environment-service:test ./environment_service
18+
19+
build-routing-engine:
20+
name: Build Routing Engine
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Build Docker image
26+
run: docker build -t uav-flight-optimizer/routing-engine:test ./routing_engine
27+
28+
build-frontend:
29+
name: Build Frontend
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v4
33+
34+
- name: Build Docker image
35+
run: docker build -t uav-flight-optimizer/frontend:test ./frontend
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Test Pathfinding
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test-go:
11+
name: Go Environment Service Tests
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up Go
17+
uses: actions/setup-go@v5
18+
with:
19+
go-version: '1.21'
20+
21+
- name: Run Go tests
22+
working-directory: ./environment_service
23+
run: go test ./... -v -race
24+
25+
test-python:
26+
name: Python Routing Engine Tests
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- name: Set up Python
32+
uses: actions/setup-python@v5
33+
with:
34+
python-version: '3.11'
35+
36+
- name: Install dependencies
37+
working-directory: ./routing_engine
38+
run: |
39+
python -m pip install --upgrade pip
40+
pip install -r requirements.txt
41+
42+
- name: Run pathfinding tests
43+
working-directory: ./routing_engine
44+
run: python -m pytest tests/ -v --tb=short

.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Dependencies
2+
node_modules/
3+
__pycache__/
4+
*.pyc
5+
.pytest_cache/
6+
*.egg-info/
7+
8+
# Build outputs
9+
frontend/build/
10+
dist/
11+
*.exe
12+
13+
# Environment
14+
.env
15+
.env.local
16+
.env.*.local
17+
18+
# IDE
19+
.vscode/
20+
.idea/
21+
*.swp
22+
*.swo
23+
24+
# OS
25+
.DS_Store
26+
Thumbs.db
27+
28+
# Docker
29+
*.log

Makefile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.PHONY: build run stop test test-go test-python clean
2+
3+
# Build all containers
4+
build:
5+
docker-compose build
6+
7+
# Run the full stack locally
8+
run:
9+
docker-compose up --build -d
10+
11+
# Stop all services
12+
stop:
13+
docker-compose down
14+
15+
# Run all tests
16+
test: test-go test-python
17+
18+
# Run Go tests for environment service
19+
test-go:
20+
cd environment_service && go test ./... -v
21+
22+
# Run Python tests for routing engine
23+
test-python:
24+
cd routing_engine && python -m pytest tests/ -v
25+
26+
# Deploy to Kubernetes
27+
deploy:
28+
kubectl apply -f k8s/
29+
30+
# Remove Kubernetes deployment
31+
undeploy:
32+
kubectl delete -f k8s/
33+
34+
# Clean build artifacts
35+
clean:
36+
docker-compose down -v --rmi local
37+
cd frontend && rm -rf node_modules build
38+
cd routing_engine && rm -rf __pycache__ .pytest_cache

docker-compose.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
version: '3.8'
2+
3+
services:
4+
environment-service:
5+
build: ./environment_service
6+
ports:
7+
- "8081:8081"
8+
environment:
9+
- PORT=8081
10+
healthcheck:
11+
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8081/health"]
12+
interval: 10s
13+
timeout: 5s
14+
retries: 3
15+
networks:
16+
- uav-net
17+
18+
routing-engine:
19+
build: ./routing_engine
20+
ports:
21+
- "8000:8000"
22+
environment:
23+
- ENVIRONMENT_SERVICE_URL=http://environment-service:8081
24+
depends_on:
25+
environment-service:
26+
condition: service_healthy
27+
healthcheck:
28+
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
29+
interval: 10s
30+
timeout: 5s
31+
retries: 3
32+
networks:
33+
- uav-net
34+
35+
frontend:
36+
build: ./frontend
37+
ports:
38+
- "3000:3000"
39+
environment:
40+
- REACT_APP_API_URL=http://localhost:8000
41+
- REACT_APP_MAPBOX_TOKEN=${MAPBOX_TOKEN:-pk.placeholder}
42+
depends_on:
43+
routing-engine:
44+
condition: service_healthy
45+
networks:
46+
- uav-net
47+
48+
redis:
49+
image: redis:7-alpine
50+
ports:
51+
- "6379:6379"
52+
volumes:
53+
- redis-data:/data
54+
healthcheck:
55+
test: ["CMD", "redis-cli", "ping"]
56+
interval: 10s
57+
timeout: 5s
58+
retries: 3
59+
networks:
60+
- uav-net
61+
62+
networks:
63+
uav-net:
64+
driver: bridge
65+
66+
volumes:
67+
redis-data:

environment_service/Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM golang:1.21-alpine AS builder
2+
3+
WORKDIR /app
4+
COPY go.mod go.sum ./
5+
RUN go mod download
6+
COPY . .
7+
RUN CGO_ENABLED=0 GOOS=linux go build -o /environment-service ./cmd/api
8+
9+
FROM alpine:3.19
10+
RUN apk --no-cache add ca-certificates
11+
WORKDIR /app
12+
COPY --from=builder /environment-service .
13+
EXPOSE 8081
14+
CMD ["./environment-service"]
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"os"
9+
"strconv"
10+
11+
"github.com/gorilla/mux"
12+
"github.com/mtepenner/uav-flight-optimizer/environment_service/internal/airspace"
13+
"github.com/mtepenner/uav-flight-optimizer/environment_service/internal/topography"
14+
"github.com/mtepenner/uav-flight-optimizer/environment_service/internal/weather"
15+
)
16+
17+
func main() {
18+
port := os.Getenv("PORT")
19+
if port == "" {
20+
port = "8081"
21+
}
22+
23+
windService := weather.NewWindVectorService()
24+
elevationService := topography.NewElevationService()
25+
geofenceService := airspace.NewGeofenceService()
26+
27+
r := mux.NewRouter()
28+
29+
// Health check
30+
r.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
31+
w.Header().Set("Content-Type", "application/json")
32+
json.NewEncoder(w).Encode(map[string]string{"status": "healthy"})
33+
}).Methods("GET")
34+
35+
// Wind vectors endpoint
36+
r.HandleFunc("/api/v1/wind", func(w http.ResponseWriter, r *http.Request) {
37+
lat, _ := strconv.ParseFloat(r.URL.Query().Get("lat"), 64)
38+
lon, _ := strconv.ParseFloat(r.URL.Query().Get("lon"), 64)
39+
alt, _ := strconv.ParseFloat(r.URL.Query().Get("alt"), 64)
40+
radius, _ := strconv.ParseFloat(r.URL.Query().Get("radius"), 64)
41+
if radius == 0 {
42+
radius = 10.0
43+
}
44+
45+
grid := windService.GetWindGrid(lat, lon, alt, radius)
46+
w.Header().Set("Content-Type", "application/json")
47+
json.NewEncoder(w).Encode(grid)
48+
}).Methods("GET")
49+
50+
// Elevation endpoint
51+
r.HandleFunc("/api/v1/elevation", func(w http.ResponseWriter, r *http.Request) {
52+
lat, _ := strconv.ParseFloat(r.URL.Query().Get("lat"), 64)
53+
lon, _ := strconv.ParseFloat(r.URL.Query().Get("lon"), 64)
54+
radius, _ := strconv.ParseFloat(r.URL.Query().Get("radius"), 64)
55+
resolution, _ := strconv.ParseFloat(r.URL.Query().Get("resolution"), 64)
56+
if resolution == 0 {
57+
resolution = 30.0
58+
}
59+
if radius == 0 {
60+
radius = 10.0
61+
}
62+
63+
grid := elevationService.GetElevationGrid(lat, lon, radius, resolution)
64+
w.Header().Set("Content-Type", "application/json")
65+
json.NewEncoder(w).Encode(grid)
66+
}).Methods("GET")
67+
68+
// Geofence endpoint
69+
r.HandleFunc("/api/v1/geofences", func(w http.ResponseWriter, r *http.Request) {
70+
lat, _ := strconv.ParseFloat(r.URL.Query().Get("lat"), 64)
71+
lon, _ := strconv.ParseFloat(r.URL.Query().Get("lon"), 64)
72+
radius, _ := strconv.ParseFloat(r.URL.Query().Get("radius"), 64)
73+
if radius == 0 {
74+
radius = 50.0
75+
}
76+
77+
zones := geofenceService.GetGeofences(lat, lon, radius)
78+
w.Header().Set("Content-Type", "application/json")
79+
json.NewEncoder(w).Encode(zones)
80+
}).Methods("GET")
81+
82+
// Combined environment data for a bounding box
83+
r.HandleFunc("/api/v1/environment", func(w http.ResponseWriter, r *http.Request) {
84+
lat, _ := strconv.ParseFloat(r.URL.Query().Get("lat"), 64)
85+
lon, _ := strconv.ParseFloat(r.URL.Query().Get("lon"), 64)
86+
alt, _ := strconv.ParseFloat(r.URL.Query().Get("alt"), 64)
87+
radius, _ := strconv.ParseFloat(r.URL.Query().Get("radius"), 64)
88+
if radius == 0 {
89+
radius = 10.0
90+
}
91+
if alt == 0 {
92+
alt = 100.0
93+
}
94+
95+
result := map[string]interface{}{
96+
"wind_grid": windService.GetWindGrid(lat, lon, alt, radius),
97+
"elevation_grid": elevationService.GetElevationGrid(lat, lon, radius, 30.0),
98+
"geofences": geofenceService.GetGeofences(lat, lon, radius),
99+
}
100+
101+
w.Header().Set("Content-Type", "application/json")
102+
json.NewEncoder(w).Encode(result)
103+
}).Methods("GET")
104+
105+
// CORS middleware
106+
handler := corsMiddleware(r)
107+
108+
log.Printf("Environment Service starting on port %s", port)
109+
if err := http.ListenAndServe(fmt.Sprintf(":%s", port), handler); err != nil {
110+
log.Fatalf("Failed to start server: %v", err)
111+
}
112+
}
113+
114+
func corsMiddleware(next http.Handler) http.Handler {
115+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
116+
w.Header().Set("Access-Control-Allow-Origin", "*")
117+
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
118+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
119+
if r.Method == "OPTIONS" {
120+
w.WriteHeader(http.StatusOK)
121+
return
122+
}
123+
next.ServeHTTP(w, r)
124+
})
125+
}

environment_service/go.mod

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module github.com/mtepenner/uav-flight-optimizer/environment_service
2+
3+
go 1.21
4+
5+
require (
6+
github.com/gorilla/mux v1.8.1
7+
google.golang.org/grpc v1.62.1
8+
google.golang.org/protobuf v1.33.0
9+
)
10+
11+
require (
12+
golang.org/x/net v0.22.0 // indirect
13+
golang.org/x/sys v0.18.0 // indirect
14+
golang.org/x/text v0.14.0 // indirect
15+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
16+
)

environment_service/go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
github.com/gorilla/mux v1.8.1 h1:TuMoUvkRETdXqU+OBfcEMlNt4+pV+EaKDtbDIB3Bvhs=
2+
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
3+
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
4+
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
5+
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBHDefyWNYveAYMNF1Wum0DYQ4=
6+
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
7+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
8+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
9+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8IjnO/TaOxw5MgL1LP/B0gMKHfaHI5SPkDMCaRUYiEo=
10+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
11+
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
12+
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
13+
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
14+
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=

0 commit comments

Comments
 (0)