Files
gps-denied-desktop/docs/02_components/01_flight_api/flight_api_spec.md
T
Oleksandr Bezdieniezhnykh 310cf78ee7 fix issues
2025-11-30 01:43:23 +02:00

18 KiB
Raw Blame History

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:

  1. Validate request data
  2. Call F02 Flight Processor → create_flight()
  3. Flight Processor triggers satellite prefetch
  4. 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 exists
  • 500 Internal Server Error: Database or internal error

Test Cases:

  1. Valid flight creation: Provide valid flight data → returns 201 with flight_id
  2. Missing required field: Omit name → returns 400 with error message
  3. Invalid GPS coordinates: Provide lat > 90 → returns 400
  4. 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 exist
  • 500 Internal Server Error: Database error

Test Cases:

  1. Existing flight: Valid flightId → returns 200 with complete flight data
  2. Non-existent flight: Invalid flightId → returns 404
  3. 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 exist
  • 409 Conflict: Flight is currently being processed
  • 500 Internal Server Error: Database error

Test Cases:

  1. Delete existing flight: Valid flightId → returns 200
  2. Delete non-existent flight: Invalid flightId → returns 404
  3. 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 found
  • 400 Bad Request: Invalid waypoint data
  • 500 Internal Server Error: Database error

Test Cases:

  1. Update existing waypoint: Valid data → returns 200
  2. Refinement update: Refined coordinates → updates successfully
  3. Invalid coordinates: lat > 90 → returns 400
  4. 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 found
  • 400 Bad Request: Invalid waypoint data
  • 500 Internal Server Error: Database error

Test Cases:

  1. Batch update 100 waypoints: All succeed
  2. Partial failure: 5 waypoints fail → returns failed_ids
  3. Empty batch: Returns success=True, updated_count=0
  4. 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:

  1. Validate flight_id exists
  2. Validate batch size (10-50 images)
  3. Validate sequence numbers (strict sequential)
  4. Call F02 Flight Processor → queue_images(flight_id, batch)
  5. F02 delegates to F05 Image Input Pipeline
  6. Return immediately (processing is async)

Error Conditions:

  • 400 Bad Request: Invalid batch size, out-of-sequence images
  • 404 Not Found: flight_id doesn't exist
  • 413 Payload Too Large: Batch exceeds size limit
  • 429 Too Many Requests: Rate limit exceeded

Test Cases:

  1. Valid batch upload: 20 images → returns 202 Accepted
  2. Out-of-sequence batch: Sequence gap detected → returns 400
  3. Too many images: 60 images → returns 400
  4. 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_needed event)

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:

  1. Validate flight_id exists and is blocked
  2. Call F02 Flight Processor → handle_user_fix(flight_id, fix_data)
  3. F02 delegates to F11 Failure Recovery Coordinator
  4. Coordinator applies anchor to Factor Graph
  5. Resume processing pipeline

Error Conditions:

  • 400 Bad Request: Invalid fix data
  • 404 Not Found: flight_id or frame_id not found
  • 409 Conflict: Flight not in blocked state

Test Cases:

  1. Valid user fix: Blocked flight → returns 200, processing resumes
  2. Fix for non-blocked flight: Returns 409
  3. 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:

  1. Validate flight_id and frame_id exist
  2. Validate frame has been processed (has pose in Factor Graph)
  3. Call F02 Flight Processor → convert_object_to_gps(flight_id, frame_id, pixel)
  4. F02 delegates to F13.image_object_to_gps(flight_id, frame_id, pixel)
  5. Return GPS with accuracy estimate

Error Conditions:

  • 400 Bad Request: Invalid pixel coordinates
  • 404 Not Found: flight_id or frame_id not found
  • 409 Conflict: Frame not yet processed (no pose available)

Test Cases:

  1. Valid conversion: Object at (1024, 768) → returns GPS
  2. Unprocessed frame: Frame not in Factor Graph → returns 409
  3. 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:

  1. Processing flight: Returns current progress
  2. Blocked flight: Returns blocked=true with search_grid_size
  3. 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

Processing Flow:

  1. Validate flight_id exists
  2. Call F02 Flight Processor → create_client_stream(flight_id, client_id)
  3. F02 delegates to F15 SSE Event Streamer → create_stream()
  4. Return SSE stream to client

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:

  1. Connect to stream: Opens SSE connection successfully
  2. Receive frame events: Process 100 frames → receive 100 events
  3. Receive user_input_needed: Blocked frame → event sent
  4. Client reconnect: Replay missed events from last_event_id

Integration Tests

Test 1: Complete Flight Lifecycle

  1. POST /flights with valid data
  2. GET /flights/{flightId} → verify data
  3. GET /flights/{flightId}/stream (open SSE)
  4. POST /flights/{flightId}/images/batch × 40
  5. Receive frame_processed events via SSE
  6. Receive flight_completed event
  7. GET /flights/{flightId} → verify waypoints updated
  8. DELETE /flights/{flightId}

Test 2: User Fix Flow

  1. Create flight and process images
  2. Receive user_input_needed event
  3. POST /flights/{flightId}/user-fix
  4. Receive processing_resumed event
  5. Continue receiving frame_processed events

Test 3: Concurrent Flights

  1. Create 10 flights concurrently
  2. Upload batches to all flights in parallel
  3. Stream results from all flights simultaneously
  4. Verify no cross-contamination

Test 4: Waypoint Updates

  1. Create flight
  2. Simulate per-frame updates via PUT /flights/{flightId}/waypoints/{waypointId} × 100
  3. GET flight and verify all waypoints updated
  4. 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 operations (flight CRUD, image batching, user fixes, SSE streams, object-to-GPS conversion). F01 is a thin REST layer that delegates all business logic to F02.

Note: F01 does NOT directly call F05, F11, F13, or F15. All operations are routed through F02 to maintain a single coordinator pattern.

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]]