introduce the database and controller connections for all inbounds
This commit is contained in:
		| @@ -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; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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
									
								
								src/inbound/dto/aircraft.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/inbound/dto/aircraft.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/dto/controllerinput.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/inbound/dto/controllerinput.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/dto/flightstate.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/inbound/dto/flightstate.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/dto/inbound.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/inbound/dto/inbound.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/dto/planning.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/inbound/dto/planning.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/dto/predictedwaypoint.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/inbound/dto/predictedwaypoint.dto.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/inbound.controller.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/inbound/inbound.controller.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/inbound.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								src/inbound/inbound.controller.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/inbound.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/inbound/inbound.module.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/inbound.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/inbound/inbound.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/inbound.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/inbound/inbound.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/models/aircraft.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/inbound/models/aircraft.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/models/controllerinput.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/inbound/models/controllerinput.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/models/flightstate.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/inbound/models/flightstate.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/models/inbound.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/inbound/models/inbound.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/models/planning.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/inbound/models/planning.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/inbound/models/predictedwaypoint.model.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/inbound/models/predictedwaypoint.model.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
		Reference in New Issue
	
	Block a user