Просмотр исходного кода

introduce the database and controller connections for all inbounds

Sven Czarnian 2 лет назад
Родитель
Сommit
f69e365623

+ 3 - 3
src/airport/airport.service.ts

@@ -25,9 +25,9 @@ export class AirportService {
   }
 
   async airport(icao: string): Promise<Airport> {
-    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;
     });
   }
 

+ 4 - 1
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 {}

+ 44 - 0
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;
+}

+ 32 - 0
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;
+}

+ 49 - 0
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;
+}

+ 56 - 0
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;
+}

+ 31 - 0
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;
+}

+ 49 - 0
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;
+}

+ 18 - 0
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>(InboundController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});

+ 213 - 0
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<T>(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<T>(
+    input: ControllerInput | ControllerInputDto,
+  ): T {
+    return {
+      reportedTime: input.reportedTime,
+      remainingRoute: WaypointConverter.convertList(input.remainingRoute),
+      requestedRunway: input.requestedRunway,
+      plannedStand: input.plannedStand,
+    } as T;
+  }
+
+  private static convertFlightState<T>(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<WaypointDto, CoordinateDto>(
+          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<InboundDto[]> {
+    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<boolean> {
+    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<void> {
+    await this.inboundService.callsignKnown(callsign).then(async (known) => {
+      if (!known) {
+        throw new HttpException('Inbound not found', HttpStatus.NOT_FOUND);
+      }
+
+      await this.inboundService.remove(callsign);
+    });
+  }
+}

+ 17 - 0
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 {}

+ 18 - 0
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>(InboundService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});

+ 61 - 0
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<InboundDocument>,
+  ) {}
+
+  async callsignKnown(callsign: string): Promise<boolean> {
+    return this.inboundModel
+      .findOne({ callsign })
+      .then((inbound) => inbound !== null);
+  }
+
+  async add(inbound: Inbound): Promise<boolean> {
+    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<boolean> {
+    return this.callsignKnown(callsign).then(async (known) => {
+      if (!known) return false;
+      await this.inboundModel.deleteOne({ callsign });
+      return true;
+    });
+  }
+
+  async update(inbound: Inbound): Promise<boolean> {
+    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<Inbound[]> {
+    return this.inboundModel.find({ destination: icao });
+  }
+}

+ 42 - 0
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);

+ 32 - 0
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);

+ 43 - 0
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);

+ 59 - 0
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);

+ 37 - 0
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);

+ 41 - 0
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);