mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Make TOT waypoints non-optional for flight plans.
Flights without a meaningful TOT make the code around startup time (and other scheduling behaviors) unnecessarily complicated because they have to handle unpredictable flight plans. We can simplify this by requiring that all flight plans have a waypoint associated with their TOT. For custom flight plans, we can just fall back to the takeoff waypoint. For RTB flight plans (which are only synthetic flight plans injected for aborted flights), we can use the abort point. This also means that all flight plans now have, at the very least, a departure waypoint. Deleting this waypoint is invalid even for custom flights, so that's no a problem.
This commit is contained in:
parent
4f1e3da70a
commit
452848fd2a
@ -8,6 +8,7 @@ from dcs import Point
|
|||||||
from dcs.planes import C_101CC, C_101EB, Su_33
|
from dcs.planes import C_101CC, C_101EB, Su_33
|
||||||
|
|
||||||
from .flightplans.planningerror import PlanningError
|
from .flightplans.planningerror import PlanningError
|
||||||
|
from .flightplans.waypointbuilder import WaypointBuilder
|
||||||
from .flightroster import FlightRoster
|
from .flightroster import FlightRoster
|
||||||
from .flightstate import FlightState, Navigating, Uninitialized
|
from .flightstate import FlightState, Navigating, Uninitialized
|
||||||
from .flightstate.killed import Killed
|
from .flightstate.killed import Killed
|
||||||
@ -89,7 +90,11 @@ class Flight(SidcDescribable):
|
|||||||
from .flightplans.custom import CustomFlightPlan, CustomLayout
|
from .flightplans.custom import CustomFlightPlan, CustomLayout
|
||||||
|
|
||||||
self.flight_plan: FlightPlan[Any] = CustomFlightPlan(
|
self.flight_plan: FlightPlan[Any] = CustomFlightPlan(
|
||||||
self, CustomLayout(custom_waypoints=[])
|
self,
|
||||||
|
CustomLayout(
|
||||||
|
departure=WaypointBuilder(self, self.coalition).takeoff(self.departure),
|
||||||
|
custom_waypoints=[],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __getstate__(self) -> dict[str, Any]:
|
def __getstate__(self) -> dict[str, Any]:
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class AirAssaultFlightPlan(StandardFlightPlan[AirAssaultLayout]):
|
|||||||
return Builder
|
return Builder
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
return self.layout.drop_off
|
return self.layout.drop_off
|
||||||
|
|
||||||
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class AirliftFlightPlan(StandardFlightPlan[AirliftLayout]):
|
|||||||
return Builder
|
return Builder
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
return self.layout.drop_off
|
return self.layout.drop_off
|
||||||
|
|
||||||
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
||||||
|
|||||||
@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Type
|
|||||||
|
|
||||||
from .flightplan import FlightPlan, Layout
|
from .flightplan import FlightPlan, Layout
|
||||||
from .ibuilder import IBuilder
|
from .ibuilder import IBuilder
|
||||||
|
from .waypointbuilder import WaypointBuilder
|
||||||
from ..flightwaypointtype import FlightWaypointType
|
from ..flightwaypointtype import FlightWaypointType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -18,6 +19,7 @@ class CustomLayout(Layout):
|
|||||||
custom_waypoints: list[FlightWaypoint]
|
custom_waypoints: list[FlightWaypoint]
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
|
yield self.departure
|
||||||
yield from self.custom_waypoints
|
yield from self.custom_waypoints
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ class CustomFlightPlan(FlightPlan[CustomLayout]):
|
|||||||
return Builder
|
return Builder
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
target_types = (
|
target_types = (
|
||||||
FlightWaypointType.PATROL_TRACK,
|
FlightWaypointType.PATROL_TRACK,
|
||||||
FlightWaypointType.TARGET_GROUP_LOC,
|
FlightWaypointType.TARGET_GROUP_LOC,
|
||||||
@ -37,7 +39,7 @@ class CustomFlightPlan(FlightPlan[CustomLayout]):
|
|||||||
for waypoint in self.waypoints:
|
for waypoint in self.waypoints:
|
||||||
if waypoint in target_types:
|
if waypoint in target_types:
|
||||||
return waypoint
|
return waypoint
|
||||||
return None
|
return self.layout.departure
|
||||||
|
|
||||||
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
||||||
if waypoint == self.tot_waypoint:
|
if waypoint == self.tot_waypoint:
|
||||||
@ -54,7 +56,8 @@ class CustomFlightPlan(FlightPlan[CustomLayout]):
|
|||||||
|
|
||||||
class Builder(IBuilder[CustomFlightPlan, CustomLayout]):
|
class Builder(IBuilder[CustomFlightPlan, CustomLayout]):
|
||||||
def layout(self) -> CustomLayout:
|
def layout(self) -> CustomLayout:
|
||||||
return CustomLayout([])
|
builder = WaypointBuilder(self.flight, self.coalition)
|
||||||
|
return CustomLayout(builder.takeoff(self.flight.departure), [])
|
||||||
|
|
||||||
def build(self) -> CustomFlightPlan:
|
def build(self) -> CustomFlightPlan:
|
||||||
return CustomFlightPlan(self.flight, self.layout())
|
return CustomFlightPlan(self.flight, self.layout())
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class FerryFlightPlan(StandardFlightPlan[FerryLayout]):
|
|||||||
return Builder
|
return Builder
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
return self.layout.arrival
|
return self.layout.arrival
|
||||||
|
|
||||||
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
||||||
|
|||||||
@ -10,6 +10,7 @@ from __future__ import annotations
|
|||||||
import math
|
import math
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, Generic, TYPE_CHECKING, TypeGuard, TypeVar
|
from typing import Any, Generic, TYPE_CHECKING, TypeGuard, TypeVar
|
||||||
@ -40,7 +41,10 @@ INGRESS_TYPES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
class Layout(ABC):
|
class Layout(ABC):
|
||||||
|
departure: FlightWaypoint
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def waypoints(self) -> list[FlightWaypoint]:
|
def waypoints(self) -> list[FlightWaypoint]:
|
||||||
"""A list of all waypoints in the flight plan, in order."""
|
"""A list of all waypoints in the flight plan, in order."""
|
||||||
@ -135,7 +139,7 @@ class FlightPlan(ABC, Generic[LayoutT]):
|
|||||||
return self.flight.unit_type.fuel_consumption.cruise
|
return self.flight.unit_type.fuel_consumption.cruise
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
"""The waypoint that is associated with the package TOT, or None.
|
"""The waypoint that is associated with the package TOT, or None.
|
||||||
|
|
||||||
Note that the only flight plans that should have no target waypoints are
|
Note that the only flight plans that should have no target waypoints are
|
||||||
@ -246,19 +250,12 @@ class FlightPlan(ABC, Generic[LayoutT]):
|
|||||||
if waypoint == end:
|
if waypoint == end:
|
||||||
return
|
return
|
||||||
|
|
||||||
def takeoff_time(self) -> timedelta | None:
|
def takeoff_time(self) -> timedelta:
|
||||||
tot_waypoint = self.tot_waypoint
|
return self.tot - self._travel_time_to_waypoint(self.tot_waypoint)
|
||||||
if tot_waypoint is None:
|
|
||||||
return None
|
|
||||||
return self.tot - self._travel_time_to_waypoint(tot_waypoint)
|
|
||||||
|
|
||||||
def startup_time(self) -> timedelta | None:
|
def startup_time(self) -> timedelta | None:
|
||||||
takeoff_time = self.takeoff_time()
|
|
||||||
if takeoff_time is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
start_time: timedelta = (
|
start_time: timedelta = (
|
||||||
takeoff_time - self.estimate_startup() - self.estimate_ground_ops()
|
self.takeoff_time() - self.estimate_startup() - self.estimate_ground_ops()
|
||||||
)
|
)
|
||||||
|
|
||||||
# In case FP math has given us some barely below zero time, round to
|
# In case FP math has given us some barely below zero time, round to
|
||||||
|
|||||||
@ -85,7 +85,7 @@ class PatrollingFlightPlan(StandardFlightPlan[LayoutT], ABC):
|
|||||||
return {self.layout.patrol_start, self.layout.patrol_end}
|
return {self.layout.patrol_start, self.layout.patrol_end}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
return self.layout.patrol_start
|
return self.layout.patrol_start
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -40,8 +40,8 @@ class RtbFlightPlan(StandardFlightPlan[RtbLayout]):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
return None
|
return self.layout.abort_location
|
||||||
|
|
||||||
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@ -12,7 +12,6 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class StandardLayout(Layout, ABC):
|
class StandardLayout(Layout, ABC):
|
||||||
departure: FlightWaypoint
|
|
||||||
arrival: FlightWaypoint
|
arrival: FlightWaypoint
|
||||||
divert: FlightWaypoint | None
|
divert: FlightWaypoint | None
|
||||||
bullseye: FlightWaypoint
|
bullseye: FlightWaypoint
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class SweepFlightPlan(LoiterFlightPlan):
|
|||||||
return {self.layout.sweep_end}
|
return {self.layout.sweep_end}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> FlightWaypoint | None:
|
def tot_waypoint(self) -> FlightWaypoint:
|
||||||
return self.layout.sweep_end
|
return self.layout.sweep_end
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -148,7 +148,12 @@ class QFlightWaypointTab(QFrame):
|
|||||||
if not isinstance(self.flight.flight_plan, CustomFlightPlan):
|
if not isinstance(self.flight.flight_plan, CustomFlightPlan):
|
||||||
self.flight.flight_plan = CustomFlightPlan(
|
self.flight.flight_plan = CustomFlightPlan(
|
||||||
self.flight,
|
self.flight,
|
||||||
CustomLayout(custom_waypoints=self.flight.flight_plan.waypoints),
|
CustomLayout(
|
||||||
|
departure=WaypointBuilder(self.flight, self.coalition).takeoff(
|
||||||
|
self.flight.departure
|
||||||
|
),
|
||||||
|
custom_waypoints=self.flight.flight_plan.waypoints[1:],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def confirm_recreate(self, task: FlightType) -> None:
|
def confirm_recreate(self, task: FlightType) -> None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user