private async Task HandleAirplaneDeparting(Airplane airplane, IDictionary <string, AirplaneState> future) { DepartingState departingState = (DepartingState)airplane.AirplaneState; FlightPlan flightPlan = airplane.FlightPlan; Fix nextFix = flightPlan.GetNextFix(departingState.Airport); if (future.Values.OfType <EnrouteState>().Any(enrouteState => enrouteState.To == nextFix)) { future[flightPlan.CallSign] = new HoldingState(departingState.Airport); await SendInstructionAsync(flightPlan.CallSign, new HoldInstruction(departingState.Airport)).ConfigureAwait(false); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Issued holding instruction for {CallSign} at {Fix} because of traffic contention at {DesiredFix}", flightPlan.CallSign, departingState.Airport.Name, nextFix.Name); } } else { future[flightPlan.CallSign] = new EnrouteState(departingState.Airport, nextFix); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Airplane {CallSign} completed departure from {DeparturePoint} and is enroute to destination, next fix {NextFix}", flightPlan.CallSign, departingState.Airport.Name, nextFix.Name); } } }
private async Task HandleAirplaneHolding(Airplane airplane, IDictionary <string, AirplaneState> future) { HoldingState holdingState = (HoldingState)airplane.AirplaneState; FlightPlan flightPlan = airplane.FlightPlan; // Case 1: airplane holding at destination airport if (holdingState.Fix == flightPlan.Destination) { // Grant approach clearance if no other airplane is cleared for approach at the same airport. if (!future.Values.OfType <ApproachState>().Any(state => state.Airport == flightPlan.Destination)) { future[flightPlan.CallSign] = new ApproachState(flightPlan.Destination); await SendInstructionAsync(flightPlan.CallSign, new ApproachClearance(flightPlan.Destination)).ConfigureAwait(false); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Airplane {CallSign} is has been cleared for approach at {Destination}", flightPlan.CallSign, flightPlan.Destination.Name); } } else { future[flightPlan.CallSign] = new HoldingState(flightPlan.Destination); if (logger_.IsEnabled(LogLevel.Debug)) { // Technically no new instruction has been issued, but the old instruction remains in place, // so we will reuse InstructionIssued event here logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Airplane {CallSign} should continue holding at {Destination} because of other traffic landing", flightPlan.CallSign, flightPlan.Destination.Name); } } return; } // Case 2: holding at some point enroute Fix nextFix = flightPlan.GetNextFix(holdingState.Fix); if (future.Values.OfType <EnrouteState>().Any(enrouteState => enrouteState.To == nextFix)) { future[flightPlan.CallSign] = holdingState; if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Airplane {CallSign} should continue holding at {Fix} because of traffic contention at {DesiredFix}", flightPlan.CallSign, holdingState.Fix.Name, nextFix.Name); } } else { future[flightPlan.CallSign] = new EnrouteState(holdingState.Fix, nextFix); // We always optmimistically give an enroute clearance all the way to the destination await SendInstructionAsync(flightPlan.CallSign, new EnrouteClearance(flightPlan.Destination, flightPlan.FlightPath)); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Airplane {CallSign} should end holding at {Fix} and proceed to destination. Next fix {NextFix}. Issued new enroute clearance.", flightPlan.CallSign, holdingState.Fix.Name, nextFix.Name); } } }
private async Task HandleAirplaneEnroute(Airplane airplane, IDictionary <string, AirplaneState> future) { EnrouteState enrouteState = (EnrouteState)airplane.AirplaneState; FlightPlan flightPlan = airplane.FlightPlan; if (enrouteState.To == flightPlan.Destination) { // Any other airplanes cleared for landing at this airport? if (future.Values.OfType <ApproachState>().Any(state => state.Airport == flightPlan.Destination)) { future[flightPlan.CallSign] = new HoldingState(flightPlan.Destination); await SendInstructionAsync(flightPlan.CallSign, new HoldInstruction(flightPlan.Destination)); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Issued holding instruction for {CallSign} at {Destination} because another airplane has been cleared for approach at the same airport", flightPlan.CallSign, flightPlan.Destination.Name); } } else { future[flightPlan.CallSign] = new ApproachState(flightPlan.Destination); await SendInstructionAsync(flightPlan.CallSign, new ApproachClearance(flightPlan.Destination)).ConfigureAwait(false); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Issued approach clearance for {CallSign} at {Destination}", flightPlan.CallSign, flightPlan.Destination.Name); } } } else { Fix nextFix = flightPlan.GetNextFix(enrouteState.To); // Is another airplane destined to the same fix? if (future.Values.OfType <EnrouteState>().Any(state => state.To == nextFix)) { // Hold at the end of the current route leg future[flightPlan.CallSign] = new HoldingState(enrouteState.To); await SendInstructionAsync(flightPlan.CallSign, new HoldInstruction(enrouteState.To)).ConfigureAwait(false); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Issued holding instruction for {CallSign} at {Fix} because of traffic contention at {DesiredFix}", flightPlan.CallSign, enrouteState.To.Name, nextFix.Name); } } else { // Just let it proceed to next fix, no instruction necessary future[flightPlan.CallSign] = new EnrouteState(enrouteState.To, nextFix); if (logger_.IsEnabled(LogLevel.Debug)) { logger_.LogDebug(LoggingEvents.InstructionIssued, null, "ATC: Airplane {CallSign} is flying from {FromFix} to {ToFix}, next fix {NextFix}", flightPlan.CallSign, enrouteState.From.Name, enrouteState.To.Name, nextFix.Name); } } } }