mod garmin;
pub use garmin::FplError;
use aerocontext_core::{
FlightRules, GeoPoint, NavPoint, NavPointKind, RouteBriefingRequest, RouteWaypoint,
};
use chrono::{DateTime, Utc};
use crate::route::{ExpandedRoute, RoutePoint};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum WaypointType {
Airport,
Ndb,
Vor,
Int,
IntVrp,
User,
}
impl WaypointType {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Airport => "AIRPORT",
Self::Ndb => "NDB",
Self::Vor => "VOR",
Self::Int => "INT",
Self::IntVrp => "INT-VRP",
Self::User => "USER WAYPOINT",
}
}
#[must_use]
pub fn parse(value: &str) -> Option<Self> {
match value.trim() {
"AIRPORT" => Some(Self::Airport),
"NDB" => Some(Self::Ndb),
"VOR" => Some(Self::Vor),
"INT" => Some(Self::Int),
"INT-VRP" => Some(Self::IntVrp),
"USER WAYPOINT" => Some(Self::User),
_ => None,
}
}
#[must_use]
pub fn nav_kind(self) -> NavPointKind {
match self {
Self::Airport => NavPointKind::Airport,
Self::Vor | Self::Ndb => NavPointKind::Navaid,
Self::Int | Self::IntVrp | Self::User => NavPointKind::Waypoint,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct PlanWaypoint {
pub identifier: String,
pub kind: WaypointType,
pub position: GeoPoint,
pub country_code: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default)]
#[non_exhaustive]
pub struct FlightPlan {
pub name: Option<String>,
pub created: Option<DateTime<Utc>>,
pub etd: Option<DateTime<Utc>>,
pub cruise_altitude_ft: Option<i32>,
pub aircraft_tail: Option<String>,
pub route: Vec<PlanWaypoint>,
}
impl FlightPlan {
pub fn from_fpl_bytes(bytes: &[u8]) -> Result<Self, FplError> {
garmin::parse(bytes)
}
#[must_use]
pub fn to_fpl_string(&self) -> String {
garmin::to_xml(self)
}
#[must_use]
pub fn departure(&self) -> Option<&str> {
self.route.first().map(|w| w.identifier.as_str())
}
#[must_use]
pub fn destination(&self) -> Option<&str> {
self.route.last().map(|w| w.identifier.as_str())
}
#[must_use]
pub fn nav_points(&self) -> Vec<NavPoint> {
self.route
.iter()
.map(|w| NavPoint::new(&w.identifier, w.kind.nav_kind(), w.position))
.collect()
}
#[must_use]
pub fn to_expanded_route(&self) -> ExpandedRoute {
let points = self
.route
.iter()
.map(|w| RoutePoint::new(w.position).with_ident(Some(w.identifier.clone())))
.collect();
ExpandedRoute {
points,
procedures: Vec::new(),
}
}
#[must_use]
pub fn to_route_briefing_request(
&self,
corridor_half_width_nm: f64,
flight_rules: FlightRules,
) -> RouteBriefingRequest {
let waypoints = self
.route
.iter()
.map(|w| RouteWaypoint::new(w.position).with_ident(Some(w.identifier.clone())))
.collect();
RouteBriefingRequest::new(waypoints, corridor_half_width_nm)
.with_cruise_altitude_ft(self.cruise_altitude_ft)
.with_departure_at(self.etd)
.with_flight_rules(flight_rules)
}
}
#[cfg(test)]
mod tests;