Fix NotImplementedError for some flight plans.

Implement custom behavior for some of the flight plans but also add a
base implementation that just returns the empty set.

Use ABC for FlightPlan so that we can fail-fast on these kinds of
problems.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1711
This commit is contained in:
Dan Albert 2021-11-03 17:28:13 -07:00
parent de0b267568
commit 1944a172a3

View File

@ -10,6 +10,7 @@ from __future__ import annotations
import logging import logging
import math import math
import random import random
from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
from functools import cached_property from functools import cached_property
@ -75,7 +76,7 @@ class InvalidObjectiveLocation(PlanningError):
@dataclass(frozen=True) @dataclass(frozen=True)
class FlightPlan: class FlightPlan(ABC):
package: Package package: Package
flight: Flight flight: Flight
@ -84,9 +85,10 @@ class FlightPlan:
"""A list of all waypoints in the flight plan, in order.""" """A list of all waypoints in the flight plan, in order."""
return list(self.iter_waypoints()) return list(self.iter_waypoints())
@abstractmethod
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
"""Iterates over all waypoints in the flight plan, in order.""" """Iterates over all waypoints in the flight plan, in order."""
raise NotImplementedError ...
def edges( def edges(
self, until: Optional[FlightWaypoint] = None self, until: Optional[FlightWaypoint] = None
@ -128,7 +130,7 @@ class FlightPlan:
@property @property
def combat_speed_waypoints(self) -> set[FlightWaypoint]: def combat_speed_waypoints(self) -> set[FlightWaypoint]:
raise NotImplementedError return set()
def fuel_consumption_between_points( def fuel_consumption_between_points(
self, a: FlightWaypoint, b: FlightWaypoint self, a: FlightWaypoint, b: FlightWaypoint
@ -151,6 +153,7 @@ class FlightPlan:
return self.flight.unit_type.fuel_consumption.cruise return self.flight.unit_type.fuel_consumption.cruise
@property @property
@abstractmethod
def tot_waypoint(self) -> Optional[FlightWaypoint]: def tot_waypoint(self) -> Optional[FlightWaypoint]:
"""The waypoint that is associated with the package TOT, or None. """The waypoint that is associated with the package TOT, or None.
@ -158,7 +161,7 @@ class FlightPlan:
user-planned missions without any useful waypoints and flight plans that user-planned missions without any useful waypoints and flight plans that
failed to generate. Nevertheless, we have to defend against it. failed to generate. Nevertheless, we have to defend against it.
""" """
raise NotImplementedError ...
@property @property
def tot(self) -> timedelta: def tot(self) -> timedelta:
@ -236,11 +239,13 @@ class FlightPlan:
a.position, b.position, self.speed_between_waypoints(a, b) a.position, b.position, self.speed_between_waypoints(a, b)
) )
@abstractmethod
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
raise NotImplementedError ...
@abstractmethod
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
raise NotImplementedError ...
def request_escort_at(self) -> Optional[FlightWaypoint]: def request_escort_at(self) -> Optional[FlightWaypoint]:
return None return None
@ -316,9 +321,10 @@ class FlightPlan:
return timedelta(minutes=8) return timedelta(minutes=8)
@property @property
@abstractmethod
def mission_departure_time(self) -> timedelta: def mission_departure_time(self) -> timedelta:
"""The time that the mission is complete and the flight RTBs.""" """The time that the mission is complete and the flight RTBs."""
raise NotImplementedError ...
@dataclass(frozen=True) @dataclass(frozen=True)
@ -326,19 +332,23 @@ class LoiterFlightPlan(FlightPlan):
hold: FlightWaypoint hold: FlightWaypoint
hold_duration: timedelta hold_duration: timedelta
@abstractmethod
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
raise NotImplementedError ...
@property @property
@abstractmethod
def tot_waypoint(self) -> Optional[FlightWaypoint]: def tot_waypoint(self) -> Optional[FlightWaypoint]:
raise NotImplementedError ...
@abstractmethod
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
raise NotImplementedError ...
@property @property
@abstractmethod
def push_time(self) -> timedelta: def push_time(self) -> timedelta:
raise NotImplementedError ...
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
if waypoint == self.hold: if waypoint == self.hold:
@ -354,8 +364,9 @@ class LoiterFlightPlan(FlightPlan):
return travel_time + self.hold_duration return travel_time + self.hold_duration
@property @property
@abstractmethod
def mission_departure_time(self) -> timedelta: def mission_departure_time(self) -> timedelta:
raise NotImplementedError ...
@dataclass(frozen=True) @dataclass(frozen=True)
@ -363,20 +374,23 @@ class FormationFlightPlan(LoiterFlightPlan):
join: FlightWaypoint join: FlightWaypoint
split: FlightWaypoint split: FlightWaypoint
@abstractmethod
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
raise NotImplementedError ...
@property @property
@abstractmethod
def package_speed_waypoints(self) -> set[FlightWaypoint]: def package_speed_waypoints(self) -> set[FlightWaypoint]:
raise NotImplementedError ...
@property @property
def combat_speed_waypoints(self) -> set[FlightWaypoint]: def combat_speed_waypoints(self) -> set[FlightWaypoint]:
return self.package_speed_waypoints return self.package_speed_waypoints
@property @property
@abstractmethod
def tot_waypoint(self) -> Optional[FlightWaypoint]: def tot_waypoint(self) -> Optional[FlightWaypoint]:
raise NotImplementedError ...
def request_escort_at(self) -> Optional[FlightWaypoint]: def request_escort_at(self) -> Optional[FlightWaypoint]:
return self.join return self.join
@ -415,12 +429,14 @@ class FormationFlightPlan(LoiterFlightPlan):
return self._travel_time_to_waypoint(self.join) return self._travel_time_to_waypoint(self.join)
@property @property
@abstractmethod
def join_time(self) -> timedelta: def join_time(self) -> timedelta:
raise NotImplementedError ...
@property @property
@abstractmethod
def split_time(self) -> timedelta: def split_time(self) -> timedelta:
raise NotImplementedError ...
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
if waypoint == self.join: if waypoint == self.join:
@ -482,8 +498,9 @@ class PatrollingFlightPlan(FlightPlan):
return self.patrol_end_time return self.patrol_end_time
return None return None
@abstractmethod
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
raise NotImplementedError ...
@property @property
def package_speed_waypoints(self) -> Set[FlightWaypoint]: def package_speed_waypoints(self) -> Set[FlightWaypoint]:
@ -541,6 +558,10 @@ class CasFlightPlan(PatrollingFlightPlan):
yield self.divert yield self.divert
yield self.bullseye yield self.bullseye
@property
def combat_speed_waypoints(self) -> set[FlightWaypoint]:
return {self.patrol_start, self.target, self.patrol_end}
def request_escort_at(self) -> Optional[FlightWaypoint]: def request_escort_at(self) -> Optional[FlightWaypoint]:
return self.patrol_start return self.patrol_start
@ -569,6 +590,10 @@ class TarCapFlightPlan(PatrollingFlightPlan):
yield self.divert yield self.divert
yield self.bullseye yield self.bullseye
@property
def combat_speed_waypoints(self) -> set[FlightWaypoint]:
return {self.patrol_start, self.patrol_end}
@property @property
def tot_offset(self) -> timedelta: def tot_offset(self) -> timedelta:
return -self.lead_time return -self.lead_time
@ -732,6 +757,10 @@ class SweepFlightPlan(LoiterFlightPlan):
yield self.divert yield self.divert
yield self.bullseye yield self.bullseye
@property
def combat_speed_waypoints(self) -> set[FlightWaypoint]:
return {self.sweep_end}
@property @property
def tot_waypoint(self) -> Optional[FlightWaypoint]: def tot_waypoint(self) -> Optional[FlightWaypoint]:
return self.sweep_end return self.sweep_end