17 KiB
Flight API
Interface Definition
Interface Name: IFlightAPI
Interface Methods
class IFlightAPI(ABC):
@abstractmethod
def create_flight(self, flight_data: FlightCreateRequest) -> FlightResponse:
pass
@abstractmethod
def get_flight(self, flight_id: str) -> FlightDetailResponse:
pass
@abstractmethod
def delete_flight(self, flight_id: str) -> DeleteResponse:
pass
@abstractmethod
def update_waypoint(self, flight_id: str, waypoint_id: str, waypoint: Waypoint) -> UpdateResponse:
pass
@abstractmethod
def batch_update_waypoints(self, flight_id: str, waypoints: List[Waypoint]) -> BatchUpdateResponse:
pass
@abstractmethod
def upload_image_batch(self, flight_id: str, batch: ImageBatch) -> BatchResponse:
pass
@abstractmethod
def submit_user_fix(self, flight_id: str, fix_data: UserFixRequest) -> UserFixResponse:
pass
@abstractmethod
def get_flight_status(self, flight_id: str) -> FlightStatusResponse:
pass
@abstractmethod
def create_sse_stream(self, flight_id: str) -> SSEStream:
pass
@abstractmethod
def convert_object_to_gps(self, flight_id: str, frame_id: int, pixel: Tuple[float, float]) -> ObjectGPSResponse:
pass
Component Description
Responsibilities
- Expose REST API endpoints for complete flight lifecycle management
- Handle flight CRUD operations (create, read, update, delete)
- Manage waypoints and geofences within flights
- Handle satellite data prefetching on flight creation
- Accept batch image uploads (10-50 images per request)
- Accept user-provided GPS fixes for blocked flights
- Provide real-time status updates
- Stream results via Server-Sent Events (SSE)
Scope
- FastAPI-based REST endpoints
- Request/response validation
- Coordinate with Flight Processor for all operations
- Multipart form data handling for image uploads
- SSE connection management
- Authentication and rate limiting
Flight Management Endpoints
create_flight(flight_data: FlightCreateRequest) -> FlightResponse
REST Endpoint: POST /flights
Description: Creates a new flight with initial waypoints, geofences, camera parameters, and triggers satellite data prefetching.
Called By:
- Client applications (Flight UI, Mission Planner UI)
Input:
FlightCreateRequest:
name: str
description: str
start_gps: GPSPoint
rough_waypoints: List[GPSPoint]
geofences: Geofences
camera_params: CameraParameters
altitude: float
Output:
FlightResponse:
flight_id: str
status: str # "prefetching", "ready", "error"
message: Optional[str]
created_at: datetime
Processing Flow:
- Validate request data
- Call F02 Flight Processor → create_flight()
- Flight Processor triggers satellite prefetch
- Return flight_id immediately (prefetch is async)
Error Conditions:
400 Bad Request: Invalid input data (missing required fields, invalid GPS coordinates)409 Conflict: Flight with same ID already exists500 Internal Server Error: Database or internal error
Test Cases:
- Valid flight creation: Provide valid flight data → returns 201 with flight_id
- Missing required field: Omit name → returns 400 with error message
- Invalid GPS coordinates: Provide lat > 90 → returns 400
- Concurrent flight creation: Multiple flights → all succeed
get_flight(flight_id: str) -> FlightDetailResponse
REST Endpoint: GET /flights/{flightId}
Description: Retrieves complete flight information including all waypoints, geofences, and processing status.
Called By:
- Client applications
Input:
flight_id: str
Output:
FlightDetailResponse:
flight_id: str
name: str
description: str
start_gps: GPSPoint
waypoints: List[Waypoint]
geofences: Geofences
camera_params: CameraParameters
altitude: float
status: str
frames_processed: int
frames_total: int
created_at: datetime
updated_at: datetime
Error Conditions:
404 Not Found: Flight ID does not exist500 Internal Server Error: Database error
Test Cases:
- Existing flight: Valid flightId → returns 200 with complete flight data
- Non-existent flight: Invalid flightId → returns 404
- Flight with many waypoints: Flight with 2000+ waypoints → returns 200 with all data
delete_flight(flight_id: str) -> DeleteResponse
REST Endpoint: DELETE /flights/{flightId}
Description: Deletes a flight and all associated waypoints, images, and processing data.
Called By:
- Client applications
Input:
flight_id: str
Output:
DeleteResponse:
deleted: bool
flight_id: str
Error Conditions:
404 Not Found: Flight does not exist409 Conflict: Flight is currently being processed500 Internal Server Error: Database error
Test Cases:
- Delete existing flight: Valid flightId → returns 200
- Delete non-existent flight: Invalid flightId → returns 404
- Delete processing flight: Active processing → returns 409
update_waypoint(flight_id: str, waypoint_id: str, waypoint: Waypoint) -> UpdateResponse
REST Endpoint: PUT /flights/{flightId}/waypoints/{waypointId}
Description: Updates a specific waypoint within a flight. Used for per-frame GPS refinement.
Called By:
- Internal (F13 Result Manager for per-frame updates)
- Client applications (manual corrections)
Input:
flight_id: str
waypoint_id: str
waypoint: Waypoint:
lat: float
lon: float
altitude: Optional[float]
confidence: float
timestamp: datetime
refined: bool
Output:
UpdateResponse:
updated: bool
waypoint_id: str
Error Conditions:
404 Not Found: Flight or waypoint not found400 Bad Request: Invalid waypoint data500 Internal Server Error: Database error
Test Cases:
- Update existing waypoint: Valid data → returns 200
- Refinement update: Refined coordinates → updates successfully
- Invalid coordinates: lat > 90 → returns 400
- Non-existent waypoint: Invalid waypoint_id → returns 404
batch_update_waypoints(flight_id: str, waypoints: List[Waypoint]) -> BatchUpdateResponse
REST Endpoint: PUT /flights/{flightId}/waypoints/batch
Description: Updates multiple waypoints in a single request. Used for trajectory refinements.
Called By:
- Internal (F13 Result Manager for asynchronous refinement updates)
Input:
flight_id: str
waypoints: List[Waypoint]
Output:
BatchUpdateResponse:
success: bool
updated_count: int
failed_ids: List[str]
Error Conditions:
404 Not Found: Flight not found400 Bad Request: Invalid waypoint data500 Internal Server Error: Database error
Test Cases:
- Batch update 100 waypoints: All succeed
- Partial failure: 5 waypoints fail → returns failed_ids
- Empty batch: Returns success=True, updated_count=0
- Large batch: 500 waypoints → succeeds
Image Processing Endpoints
upload_image_batch(flight_id: str, batch: ImageBatch) -> BatchResponse
REST Endpoint: POST /flights/{flightId}/images/batch
Description: Uploads a batch of 10-50 UAV images for processing.
Called By:
- Client applications
Input:
flight_id: str
ImageBatch: multipart/form-data
images: List[UploadFile]
metadata: BatchMetadata
start_sequence: int
end_sequence: int
Output:
BatchResponse:
accepted: bool
sequences: List[int]
next_expected: int
message: Optional[str]
Processing Flow:
- Validate flight_id exists
- Validate batch size (10-50 images)
- Validate sequence numbers (strict sequential)
- Pass to F05 Image Input Pipeline
- Return immediately (processing is async)
Error Conditions:
400 Bad Request: Invalid batch size, out-of-sequence images404 Not Found: flight_id doesn't exist413 Payload Too Large: Batch exceeds size limit429 Too Many Requests: Rate limit exceeded
Test Cases:
- Valid batch upload: 20 images → returns 202 Accepted
- Out-of-sequence batch: Sequence gap detected → returns 400
- Too many images: 60 images → returns 400
- Large images: 50 × 8MB images → successfully uploads
submit_user_fix(flight_id: str, fix_data: UserFixRequest) -> UserFixResponse
REST Endpoint: POST /flights/{flightId}/user-fix
Description: Submits user-provided GPS anchor point to unblock failed localization.
Called By:
- Client applications (when user responds to
user_input_neededevent)
Input:
UserFixRequest:
frame_id: int
uav_pixel: Tuple[float, float]
satellite_gps: GPSPoint
Output:
UserFixResponse:
accepted: bool
processing_resumed: bool
message: Optional[str]
Processing Flow:
- Validate flight_id exists and is blocked
- Pass to F11 Failure Recovery Coordinator
- Coordinator applies anchor to Factor Graph
- Resume processing pipeline
Error Conditions:
400 Bad Request: Invalid fix data404 Not Found: flight_id or frame_id not found409 Conflict: Flight not in blocked state
Test Cases:
- Valid user fix: Blocked flight → returns 200, processing resumes
- Fix for non-blocked flight: Returns 409
- Invalid GPS coordinates: Returns 400
convert_object_to_gps(flight_id: str, frame_id: int, pixel: Tuple[float, float]) -> ObjectGPSResponse
REST Endpoint: POST /flights/{flightId}/frames/{frameId}/object-to-gps
Description: Converts object pixel coordinates to GPS. Used by external object detection systems (e.g., Azaion.Inference) to get GPS coordinates for detected objects.
Called By:
- External object detection systems (Azaion.Inference)
- Any system needing pixel-to-GPS conversion for a specific frame
Input:
ObjectToGPSRequest:
pixel_x: float # X coordinate in image
pixel_y: float # Y coordinate in image
Output:
ObjectGPSResponse:
gps: GPSPoint
accuracy_meters: float # Estimated accuracy
frame_id: int
pixel: Tuple[float, float]
Processing Flow:
- Validate flight_id and frame_id exist
- Validate frame has been processed (has pose in Factor Graph)
- Call F13.image_object_to_gps(pixel, frame_id)
- Return GPS with accuracy estimate
Error Conditions:
400 Bad Request: Invalid pixel coordinates404 Not Found: flight_id or frame_id not found409 Conflict: Frame not yet processed (no pose available)
Test Cases:
- Valid conversion: Object at (1024, 768) → returns GPS
- Unprocessed frame: Frame not in Factor Graph → returns 409
- Invalid pixel: Negative coordinates → returns 400
get_flight_status(flight_id: str) -> FlightStatusResponse
REST Endpoint: GET /flights/{flightId}/status
Description: Retrieves current processing status of a flight.
Called By:
- Client applications (polling for status)
Input:
flight_id: str
Output:
FlightStatusResponse:
status: str # "prefetching", "ready", "processing", "blocked", "completed", "failed"
frames_processed: int
frames_total: int
current_frame: Optional[int]
current_heading: Optional[float]
blocked: bool
search_grid_size: Optional[int]
message: Optional[str]
created_at: datetime
updated_at: datetime
Error Conditions:
404 Not Found: flight_id doesn't exist
Test Cases:
- Processing flight: Returns current progress
- Blocked flight: Returns blocked=true with search_grid_size
- Completed flight: Returns status="completed" with final counts
create_sse_stream(flight_id: str) -> SSEStream
REST Endpoint: GET /flights/{flightId}/stream
Description: Opens Server-Sent Events connection for real-time result streaming.
Called By:
- Client applications
Input:
flight_id: str
Output:
SSE Stream with events:
- frame_processed
- frame_refined
- search_expanded
- user_input_needed
- processing_blocked
- flight_completed
Event Format:
{
"event": "frame_processed",
"data": {
"frame_id": 237,
"gps": {"lat": 48.123, "lon": 37.456},
"altitude": 800.0,
"confidence": 0.95,
"heading": 87.3,
"timestamp": "2025-11-24T10:30:00Z"
}
}
Error Conditions:
404 Not Found: flight_id doesn't exist- Connection closed on client disconnect
Test Cases:
- Connect to stream: Opens SSE connection successfully
- Receive frame events: Process 100 frames → receive 100 events
- Receive user_input_needed: Blocked frame → event sent
- Client reconnect: Replay missed events from last_event_id
Integration Tests
Test 1: Complete Flight Lifecycle
- POST /flights with valid data
- GET /flights/{flightId} → verify data
- GET /flights/{flightId}/stream (open SSE)
- POST /flights/{flightId}/images/batch × 40
- Receive frame_processed events via SSE
- Receive flight_completed event
- GET /flights/{flightId} → verify waypoints updated
- DELETE /flights/{flightId}
Test 2: User Fix Flow
- Create flight and process images
- Receive user_input_needed event
- POST /flights/{flightId}/user-fix
- Receive processing_resumed event
- Continue receiving frame_processed events
Test 3: Concurrent Flights
- Create 10 flights concurrently
- Upload batches to all flights in parallel
- Stream results from all flights simultaneously
- Verify no cross-contamination
Test 4: Waypoint Updates
- Create flight
- Simulate per-frame updates via PUT /flights/{flightId}/waypoints/{waypointId} × 100
- GET flight and verify all waypoints updated
- Verify refined=true flag set
Non-Functional Requirements
Performance
- create_flight: < 500ms response (prefetch is async)
- get_flight: < 200ms for flights with < 2000 waypoints
- update_waypoint: < 100ms (critical for real-time updates)
- upload_image_batch: < 2 seconds for 50 × 2MB images
- submit_user_fix: < 200ms response
- get_flight_status: < 100ms
- SSE latency: < 500ms from event generation to client receipt
Scalability
- Support 100 concurrent flight processing sessions
- Handle 1000+ concurrent SSE connections
- Handle flights with up to 3000 waypoints
- Support 10,000 requests per minute
Reliability
- Request timeout: 30 seconds for batch uploads
- SSE keepalive: Ping every 30 seconds
- Automatic SSE reconnection with event replay
- Graceful handling of client disconnects
Security
- API key authentication
- Rate limiting: 100 requests/minute per client
- Max upload size: 500MB per batch
- CORS configuration for web clients
- Input validation on all endpoints
- SQL injection prevention
Dependencies
Internal Components
- F02 Flight Processor: For all flight operations and processing orchestration
- F05 Image Input Pipeline: For batch processing
- F11 Failure Recovery Coordinator: For user fixes
- F15 SSE Event Streamer: For real-time streaming
- F03 Flight Database: For persistence (via F02)
External Dependencies
- FastAPI: Web framework
- Uvicorn: ASGI server
- Pydantic: Validation
- python-multipart: Multipart form handling
Data Models
GPSPoint
class GPSPoint(BaseModel):
lat: float # Latitude -90 to 90
lon: float # Longitude -180 to 180
CameraParameters
class CameraParameters(BaseModel):
focal_length: float # mm
sensor_width: float # mm
sensor_height: float # mm
resolution_width: int # pixels
resolution_height: int # pixels
distortion_coefficients: Optional[List[float]] = None
Polygon
class Polygon(BaseModel):
north_west: GPSPoint
south_east: GPSPoint
Geofences
class Geofences(BaseModel):
polygons: List[Polygon]
FlightCreateRequest
class FlightCreateRequest(BaseModel):
name: str
description: str
start_gps: GPSPoint
rough_waypoints: List[GPSPoint]
geofences: Geofences
camera_params: CameraParameters
altitude: float
Waypoint
class Waypoint(BaseModel):
id: str
lat: float
lon: float
altitude: Optional[float] = None
confidence: float
timestamp: datetime
refined: bool = False
FlightDetailResponse
class FlightDetailResponse(BaseModel):
flight_id: str
name: str
description: str
start_gps: GPSPoint
waypoints: List[Waypoint]
geofences: Geofences
camera_params: CameraParameters
altitude: float
status: str
frames_processed: int
frames_total: int
created_at: datetime
updated_at: datetime
FlightStatusResponse
class FlightStatusResponse(BaseModel):
status: str
frames_processed: int
frames_total: int
current_frame: Optional[int]
current_heading: Optional[float]
blocked: bool
search_grid_size: Optional[int]
message: Optional[str]
created_at: datetime
updated_at: datetime
BatchMetadata
class BatchMetadata(BaseModel):
start_sequence: int
end_sequence: int
batch_number: int
BatchUpdateResponse
class BatchUpdateResponse(BaseModel):
success: bool
updated_count: int
failed_ids: List[str]
errors: Optional[Dict[str, str]]