diff --git a/src/airport/airport.service.ts b/src/airport/airport.service.ts index 87fb88b..5936c94 100644 --- a/src/airport/airport.service.ts +++ b/src/airport/airport.service.ts @@ -25,9 +25,9 @@ export class AirportService { } async airport(icao: string): Promise { - return this.airportModel.find({ icao }).then((response) => { - if (!response || response.length !== 1) return null; - return response[0]; + return this.airportModel.findOne({ icao }).then((response) => { + if (!response) return null; + return response; }); } diff --git a/src/app.module.ts b/src/app.module.ts index 07a81a7..b1f91c5 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,6 +8,8 @@ import { AirportModule } from './airport/airport.module'; import { LoggingModule } from './logging/logging.module'; import { LoggingController } from './logging/logging.controller'; import { AirportController } from './airport/airport.controller'; +import { InboundModule } from './inbound/inbound.module'; +import { InboundController } from './inbound/inbound.controller'; @Module({ imports: [ @@ -30,7 +32,8 @@ import { AirportController } from './airport/airport.controller'; PerformanceModule, AirportModule, LoggingModule, + InboundModule, ], - controllers: [LoggingController, AirportController], + controllers: [LoggingController, AirportController, InboundController], }) export class AppModule {} diff --git a/src/inbound/dto/aircraft.dto.ts b/src/inbound/dto/aircraft.dto.ts new file mode 100644 index 0000000..df0385b --- /dev/null +++ b/src/inbound/dto/aircraft.dto.ts @@ -0,0 +1,44 @@ +import { IsNotEmpty, Length, IsInt, Min, Max } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class AircraftDto { + @IsNotEmpty() + @ApiProperty({ + description: 'The ICAO code of the aircraft', + example: 'A359', + }) + type: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The Wake Turbulence Category (possible: L, M, H, S)', + example: 'M', + }) + @Length(1) + wtc: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The Wake Turbulence Category (possible: A, B, C, D, E, F)', + example: 'C', + }) + @Length(1) + wakeRecat: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The number of engines', + example: 2, + }) + @IsInt() + @Min(1) + @Max(6) + engineCount: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The engine type (possible: ELECTRIC, TURBOPROP, JET)', + example: 'JET', + }) + engineType: string; +} diff --git a/src/inbound/dto/controllerinput.dto.ts b/src/inbound/dto/controllerinput.dto.ts new file mode 100644 index 0000000..262be1e --- /dev/null +++ b/src/inbound/dto/controllerinput.dto.ts @@ -0,0 +1,32 @@ +import { IsNotEmpty, IsOptional, IsDateString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { WaypointDto } from '../../generic/dto/waypoint.dto'; + +export class ControllerInputDto { + @IsOptional() + @ApiProperty({ + description: 'The timestamp of the last report', + }) + @IsDateString() + reportedTime: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The remaining route of the inbound', + }) + remainingRoute: WaypointDto[]; + + @IsOptional() + @ApiProperty({ + description: 'The requested runway by the pilot', + example: '25L', + }) + requestedRunway: string; + + @IsOptional() + @ApiProperty({ + description: 'The planned stand after landing', + example: 'A05', + }) + plannedStand: string; +} diff --git a/src/inbound/dto/flightstate.dto.ts b/src/inbound/dto/flightstate.dto.ts new file mode 100644 index 0000000..8aceadf --- /dev/null +++ b/src/inbound/dto/flightstate.dto.ts @@ -0,0 +1,49 @@ +import { IsNotEmpty, IsInt, Min, Max } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { CoordinateDto } from '../../generic/dto/coordinate.dto'; + +export class FlightStateDto { + @IsNotEmpty() + @ApiProperty({ + description: 'The current position', + }) + position: CoordinateDto; + + @IsNotEmpty() + @ApiProperty({ + description: 'The reported ground speed', + example: 400, + }) + @IsInt() + @Min(0) + @Max(700) + groundSpeed: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The reported ground track', + example: 400, + }) + @IsInt() + @Min(0) + @Max(360) + groundTrack: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The reported altitude', + example: 30000, + }) + @IsInt() + @Min(0) + @Max(60000) + altitude: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The reported vertical speed', + example: 400, + }) + @IsInt() + verticalSpeed: number; +} diff --git a/src/inbound/dto/inbound.dto.ts b/src/inbound/dto/inbound.dto.ts new file mode 100644 index 0000000..d30c91f --- /dev/null +++ b/src/inbound/dto/inbound.dto.ts @@ -0,0 +1,56 @@ +import { IsNotEmpty, IsOptional, Length } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { AircraftDto } from './aircraft.dto'; +import { ControllerInputDto } from './controllerinput.dto'; +import { FlightStateDto } from './flightstate.dto'; +import { PlanningDto } from './planning.dto'; + +export class InboundDto { + @IsNotEmpty() + @ApiProperty({ + description: 'The used callsign', + example: 'DLH3PM', + }) + callsign: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The filed destination', + example: 'EDDB', + }) + @Length(4) + destination: string; + + @IsNotEmpty() + @ApiProperty({ + description: + 'The reported controller level (possible: UNK, DEL, GRD, TWR, APP, DEP, CTR)', + example: 'EDDB', + }) + @Length(3) + reporter: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The used aircraft', + }) + aircraft: AircraftDto; + + @IsNotEmpty() + @ApiProperty({ + description: 'The current flight state', + }) + flightState: FlightStateDto; + + @IsOptional() + @ApiProperty({ + description: 'The data given by the controller', + }) + controllerData: ControllerInputDto; + + @IsOptional() + @ApiProperty({ + description: 'The planned arrival', + }) + plan: PlanningDto; +} diff --git a/src/inbound/dto/planning.dto.ts b/src/inbound/dto/planning.dto.ts new file mode 100644 index 0000000..357e04c --- /dev/null +++ b/src/inbound/dto/planning.dto.ts @@ -0,0 +1,31 @@ +import { IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { PredictedWaypointDto } from './predictedwaypoint.dto'; + +export class PlanningDto { + @IsNotEmpty() + @ApiProperty({ + description: 'The planned arrival route', + example: 'KETAP25L', + }) + arrivalRoute: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The planned arrival runway', + example: '25L', + }) + arrivalRunway: string; + + @IsNotEmpty() + @ApiProperty({ + description: 'The planned route', + }) + plannedRoute: PredictedWaypointDto[]; + + @IsNotEmpty() + @ApiProperty({ + description: 'Indicates if the plan is fixed', + }) + fixedPlan: boolean; +} diff --git a/src/inbound/dto/predictedwaypoint.dto.ts b/src/inbound/dto/predictedwaypoint.dto.ts new file mode 100644 index 0000000..5715fed --- /dev/null +++ b/src/inbound/dto/predictedwaypoint.dto.ts @@ -0,0 +1,49 @@ +import { IsNotEmpty, IsInt, Min, Max } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { WaypointDto } from 'src/generic/dto/waypoint.dto'; + +export class PredictedWaypointDto { + @IsNotEmpty() + @ApiProperty({ + description: 'The waypoint', + }) + waypoint: WaypointDto; + + @IsNotEmpty() + @ApiProperty({ + description: 'The planned altitude', + example: 23000, + }) + @IsInt() + @Min(0) + @Max(60000) + altitude: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The planned indicated airspeed', + example: 200, + }) + @IsInt() + @Min(0) + @Max(600) + indicatedAirspeed: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The planned groundspeed', + example: 500, + }) + @IsInt() + @Min(0) + @Max(800) + groundspeed: number; + + @IsNotEmpty() + @ApiProperty({ + description: 'The planned time overhead the waypoint', + example: 'Wed, 14 Jun 2017 07:00:00z', + }) + @IsInt() + plannedOverheadTime: string; +} diff --git a/src/inbound/inbound.controller.spec.ts b/src/inbound/inbound.controller.spec.ts new file mode 100644 index 0000000..bb89814 --- /dev/null +++ b/src/inbound/inbound.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { InboundController } from './inbound.controller'; + +describe('InboundController', () => { + let controller: InboundController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [InboundController], + }).compile(); + + controller = module.get(InboundController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/inbound/inbound.controller.ts b/src/inbound/inbound.controller.ts new file mode 100644 index 0000000..d30bd9b --- /dev/null +++ b/src/inbound/inbound.controller.ts @@ -0,0 +1,213 @@ +import { + Body, + Controller, + Get, + Delete, + Post, + Query, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import { ApiBody, ApiQuery, ApiResponse } from '@nestjs/swagger'; +import { AirportService } from 'src/airport/airport.service'; +import { CoordinateConverter, WaypointConverter } from 'src/generic/converters'; +import { CoordinateDto } from 'src/generic/dto/coordinate.dto'; +import { WaypointDto } from 'src/generic/dto/waypoint.dto'; +import { AircraftDto } from './dto/aircraft.dto'; +import { ControllerInputDto } from './dto/controllerinput.dto'; +import { FlightStateDto } from './dto/flightstate.dto'; +import { InboundDto } from './dto/inbound.dto'; +import { PlanningDto } from './dto/planning.dto'; +import { PredictedWaypointDto } from './dto/predictedwaypoint.dto'; +import { InboundService } from './inbound.service'; +import { Aircraft } from './models/aircraft.model'; +import { ControllerInput } from './models/controllerinput.model'; +import { FlightState } from './models/flightstate.model'; +import { Inbound } from './models/inbound.model'; +import { Planning } from './models/planning.model'; +import { PredictedWaypoint } from './models/predictedwaypoint.model'; + +@Controller('inbound') +export class InboundController { + constructor( + private readonly inboundService: InboundService, + private readonly airportService: AirportService, + ) {} + + private static convertAircraft(aircraft: Aircraft | AircraftDto): T { + return { + type: aircraft.type, + wtc: aircraft.wtc, + wakeRecat: aircraft.wakeRecat, + engineCount: aircraft.engineCount, + engineType: aircraft.engineType, + } as T; + } + + private static convertControllerInput( + input: ControllerInput | ControllerInputDto, + ): T { + return { + reportedTime: input.reportedTime, + remainingRoute: WaypointConverter.convertList(input.remainingRoute), + requestedRunway: input.requestedRunway, + plannedStand: input.plannedStand, + } as T; + } + + private static convertFlightState(input: FlightState | FlightStateDto): T { + return { + position: CoordinateConverter.convert(input.position), + groundSpeed: input.groundSpeed, + groundTrack: input.groundTrack, + altitude: input.altitude, + verticalSpeed: input.verticalSpeed, + } as T; + } + + private static convertPredictedWaypoints( + waypoints: PredictedWaypoint[], + ): PredictedWaypointDto[] { + const retval: PredictedWaypointDto[] = []; + waypoints.forEach((input) => + retval.push({ + waypoint: WaypointConverter.convert( + input.waypoint, + ), + altitude: input.altitude, + indicatedAirspeed: input.indicatedAirspeed, + groundspeed: input.groundspeed, + plannedOverheadTime: input.plannedOverheadTime, + }), + ); + return retval; + } + + private static convertPlanning(plan: Planning): PlanningDto { + if (plan === undefined) return undefined; + + return { + arrivalRoute: plan.arrivalRoute, + arrivalRunway: plan.arrivalRunway, + plannedRoute: InboundController.convertPredictedWaypoints( + plan.plannedRoute, + ), + fixedPlan: plan.fixedPlan, + }; + } + + private static convertInboundToInboundDto(inbound: Inbound): InboundDto { + return { + callsign: inbound.callsign, + destination: inbound.destination, + reporter: inbound.reporter, + aircraft: InboundController.convertAircraft(inbound.aircraft), + flightState: InboundController.convertFlightState(inbound.flightState), + controllerData: InboundController.convertControllerInput( + inbound.controllerData, + ), + plan: InboundController.convertPlanning(inbound.plan), + }; + } + + private static convertInboundDtoInbound(inbound: InboundDto): Inbound { + return { + callsign: inbound.callsign, + destination: inbound.destination, + reporter: inbound.reporter, + aircraft: InboundController.convertAircraft(inbound.aircraft), + flightState: InboundController.convertFlightState(inbound.flightState), + controllerData: InboundController.convertControllerInput( + inbound.controllerData, + ), + plan: undefined, + }; + } + + @Get('/inbounds') + @ApiQuery({ + name: 'icao', + description: 'The ICAO code of the airport', + type: String, + }) + @ApiResponse({ + status: 200, + description: 'All known inbounds', + type: [InboundDto], + }) + async inbounds(@Query('icao') icao: string): Promise { + return this.inboundService.airportInbounds(icao).then((inbounds) => { + if (!inbounds) { + throw new HttpException( + 'No inbounds for the airport found', + HttpStatus.NOT_FOUND, + ); + } + + const retval: InboundDto[] = []; + inbounds.forEach((inbound) => + retval.push(InboundController.convertInboundToInboundDto(inbound)), + ); + + return retval; + }); + } + + @Post('/insert') + @ApiBody({ + description: 'The new or updated inbound', + type: InboundDto, + }) + @ApiResponse({ + status: 201, + description: 'Inserted or updated the inbound', + }) + @ApiResponse({ + status: 404, + description: 'The destination airport is unknown', + }) + async insert(inbound: InboundDto): Promise { + return this.airportService.airport(inbound.destination).then((airport) => { + if (!airport) { + throw new HttpException('Airport not found', HttpStatus.NOT_FOUND); + } + + return this.inboundService + .callsignKnown(inbound.callsign) + .then((known) => { + const internalInbound = + InboundController.convertInboundDtoInbound(inbound); + + if (known) { + return this.inboundService.update(internalInbound); + } else { + return this.inboundService.add(internalInbound); + } + }); + }); + } + + @Delete('/remove') + @ApiQuery({ + name: 'callsign', + description: 'The callsign of the inbound', + type: String, + }) + @ApiResponse({ + status: 200, + description: 'The inbound is deleted', + }) + @ApiResponse({ + status: 404, + description: 'The inbound is unknown', + }) + async remove(@Query('callsign') callsign: string): Promise { + await this.inboundService.callsignKnown(callsign).then(async (known) => { + if (!known) { + throw new HttpException('Inbound not found', HttpStatus.NOT_FOUND); + } + + await this.inboundService.remove(callsign); + }); + } +} diff --git a/src/inbound/inbound.module.ts b/src/inbound/inbound.module.ts new file mode 100644 index 0000000..b21e07a --- /dev/null +++ b/src/inbound/inbound.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { InboundSchema } from './models/inbound.model'; +import { InboundService } from './inbound.service'; +import { InboundController } from './inbound.controller'; +import { AirportModule } from 'src/airport/airport.module'; + +@Module({ + imports: [ + MongooseModule.forFeature([{ name: 'inbound', schema: InboundSchema }]), + AirportModule, + ], + providers: [InboundService], + controllers: [InboundController], + exports: [MongooseModule, InboundService], +}) +export class InboundModule {} diff --git a/src/inbound/inbound.service.spec.ts b/src/inbound/inbound.service.spec.ts new file mode 100644 index 0000000..7c7ad0a --- /dev/null +++ b/src/inbound/inbound.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { InboundService } from './inbound.service'; + +describe('InboundService', () => { + let service: InboundService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [InboundService], + }).compile(); + + service = module.get(InboundService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/inbound/inbound.service.ts b/src/inbound/inbound.service.ts new file mode 100644 index 0000000..73a2f7b --- /dev/null +++ b/src/inbound/inbound.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { Inbound, InboundDocument } from './models/inbound.model'; + +@Injectable() +export class InboundService { + constructor( + @InjectModel('inbound') + private readonly inboundModel: Model, + ) {} + + async callsignKnown(callsign: string): Promise { + return this.inboundModel + .findOne({ callsign }) + .then((inbound) => inbound !== null); + } + + async add(inbound: Inbound): Promise { + return this.callsignKnown(inbound.callsign).then(async (known) => { + if (known) return false; + + // modify the inbound data + inbound.plan = undefined; + inbound.controllerData.reportedTime = new Date().toUTCString(); + + await this.inboundModel.create(inbound); + + return true; + }); + } + + async remove(callsign: string): Promise { + return this.callsignKnown(callsign).then(async (known) => { + if (!known) return false; + await this.inboundModel.deleteOne({ callsign }); + return true; + }); + } + + async update(inbound: Inbound): Promise { + return this.callsignKnown(inbound.callsign).then(async (known) => { + if (!known) return false; + + // modify the inbound data + inbound.plan = undefined; + inbound.controllerData.reportedTime = new Date().toUTCString(); + + await this.inboundModel.findOneAndUpdate( + { callsign: inbound.callsign }, + inbound, + ); + + return true; + }); + } + + async airportInbounds(icao: string): Promise { + return this.inboundModel.find({ destination: icao }); + } +} diff --git a/src/inbound/models/aircraft.model.ts b/src/inbound/models/aircraft.model.ts new file mode 100644 index 0000000..b9d2813 --- /dev/null +++ b/src/inbound/models/aircraft.model.ts @@ -0,0 +1,42 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; + +export type AircraftDocument = Aircraft & Document; + +@Schema() +export class Aircraft { + @Prop({ + required: true, + type: String, + }) + type: string; + + @Prop({ + required: true, + type: String, + enum: ['L', 'M', 'H', 'S'], + }) + wtc: string; + + @Prop({ + required: true, + type: String, + enum: ['A', 'B', 'C', 'D', 'E', 'F'], + }) + wakeRecat: string; + + @Prop({ + required: true, + type: Number, + }) + engineCount: number; + + @Prop({ + required: true, + type: String, + enum: ['ELECTRIC', 'TURBOPROP', 'JET'], + }) + engineType: string; +} + +export const AircraftSchema = SchemaFactory.createForClass(Aircraft); diff --git a/src/inbound/models/controllerinput.model.ts b/src/inbound/models/controllerinput.model.ts new file mode 100644 index 0000000..5cddaa5 --- /dev/null +++ b/src/inbound/models/controllerinput.model.ts @@ -0,0 +1,32 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { Waypoint, WaypointSchema } from 'src/generic/models/waypoint.model'; + +export type ControllerInputDocument = ControllerInput & Document; + +@Schema() +export class ControllerInput { + @Prop({ + type: String, + }) + reportedTime: string; + + @Prop({ + required: true, + type: [WaypointSchema], + }) + remainingRoute: Waypoint[]; + + @Prop({ + type: String, + }) + requestedRunway: string; + + @Prop({ + type: String, + }) + plannedStand: string; +} + +export const ControllerInputSchema = + SchemaFactory.createForClass(ControllerInput); diff --git a/src/inbound/models/flightstate.model.ts b/src/inbound/models/flightstate.model.ts new file mode 100644 index 0000000..c4efe17 --- /dev/null +++ b/src/inbound/models/flightstate.model.ts @@ -0,0 +1,43 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { + Coordinate, + CoordinateSchema, +} from '../../generic/models/coordinate.model'; + +export type FlightStateDocument = FlightState & Document; + +@Schema() +export class FlightState { + @Prop({ + required: true, + type: CoordinateSchema, + }) + position: Coordinate; + + @Prop({ + required: true, + type: Number, + }) + groundSpeed: number; + + @Prop({ + required: true, + type: Number, + }) + groundTrack: number; + + @Prop({ + required: true, + type: Number, + }) + altitude: number; + + @Prop({ + required: true, + type: Number, + }) + verticalSpeed: number; +} + +export const FlightStateSchema = SchemaFactory.createForClass(FlightState); diff --git a/src/inbound/models/inbound.model.ts b/src/inbound/models/inbound.model.ts new file mode 100644 index 0000000..b37ef14 --- /dev/null +++ b/src/inbound/models/inbound.model.ts @@ -0,0 +1,59 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { Aircraft, AircraftSchema } from './aircraft.model'; +import { + ControllerInput, + ControllerInputSchema, +} from './controllerinput.model'; +import { FlightState, FlightStateSchema } from './flightstate.model'; +import { Planning, PlanningSchema } from './planning.model'; + +export type InboundDocument = Inbound & Document; + +@Schema() +export class Inbound { + @Prop({ + required: true, + index: true, + type: String, + }) + callsign: string; + + @Prop({ + required: true, + index: true, + type: String, + }) + destination: string; + + @Prop({ + required: true, + type: String, + enum: ['UNK', 'DEL', 'GRD', 'TWR', 'APP', 'DEP', 'CTR'], + }) + reporter: string; + + @Prop({ + required: true, + type: AircraftSchema, + }) + aircraft: Aircraft; + + @Prop({ + required: true, + type: FlightStateSchema, + }) + flightState: FlightState; + + @Prop({ + type: ControllerInputSchema, + }) + controllerData: ControllerInput; + + @Prop({ + type: PlanningSchema, + }) + plan: Planning; +} + +export const InboundSchema = SchemaFactory.createForClass(Inbound); diff --git a/src/inbound/models/planning.model.ts b/src/inbound/models/planning.model.ts new file mode 100644 index 0000000..0083c21 --- /dev/null +++ b/src/inbound/models/planning.model.ts @@ -0,0 +1,37 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { + PredictedWaypoint, + PredictedWaypointSchema, +} from './predictedwaypoint.model'; + +export type PlanningDocument = Planning & Document; + +@Schema() +export class Planning { + @Prop({ + required: true, + type: String, + }) + arrivalRoute: string; + + @Prop({ + required: true, + type: String, + }) + arrivalRunway: string; + + @Prop({ + required: true, + type: [PredictedWaypointSchema], + }) + plannedRoute: PredictedWaypoint[]; + + @Prop({ + required: true, + type: Boolean, + }) + fixedPlan: boolean; +} + +export const PlanningSchema = SchemaFactory.createForClass(Planning); diff --git a/src/inbound/models/predictedwaypoint.model.ts b/src/inbound/models/predictedwaypoint.model.ts new file mode 100644 index 0000000..4be31af --- /dev/null +++ b/src/inbound/models/predictedwaypoint.model.ts @@ -0,0 +1,41 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { Waypoint, WaypointSchema } from '../../generic/models/waypoint.model'; + +export type PredictedWaypointDocument = PredictedWaypoint & Document; + +@Schema() +export class PredictedWaypoint { + @Prop({ + required: true, + type: WaypointSchema, + }) + waypoint: Waypoint; + + @Prop({ + required: true, + type: Number, + }) + altitude: number; + + @Prop({ + required: true, + type: Number, + }) + indicatedAirspeed: number; + + @Prop({ + required: true, + type: Number, + }) + groundspeed: number; + + @Prop({ + required: true, + type: String, + }) + plannedOverheadTime: string; +} + +export const PredictedWaypointSchema = + SchemaFactory.createForClass(PredictedWaypoint);