from stats import Stats, finalize_waiting_stats, show_stats from entities import Plane, ProcessedRequest, Request from queues import EnforcingQueue, PermissiveQueue, Queue from numpy import random from typing import Final, Iterable, Optional from numpy.typing import NDArray def warn_about_outcoming_queue_overload(queue: Queue) -> None: print(f"Outcoming queue is overloaded: {len(queue)} planes/{queue.size} planes") def reject_plane(plane: Plane) -> None: stats.rejected_in_requests += 1 print(f"Rejected incoming plane №{plane.id}") def launch_plane(plane: Plane, stats: Stats) -> None: stats.launches_count += 1 print(f"Launched plane №{plane.id}") def land_plane(plane: Plane, stats: Stats) -> None: stats.landings_count += 1 print(f"Plane №{plane.id} landed") def generate_timeline(incoming_planes_per_hour: float, outcoming_planes_per_hour: float, interval_minutes: int) -> Iterable[tuple[int, int]]: def _generate(planes_per_hour: float) -> NDArray: return random.poisson(planes_per_hour / 60, interval_minutes) incoming_planes_per_minute = _generate(incoming_planes_per_hour) outcoming_planes_per_minute = _generate(outcoming_planes_per_hour) return zip(incoming_planes_per_minute, outcoming_planes_per_minute) def process_new_request(in_queue: Queue[Request], out_queue: Queue[Request], current_time: int, process_duration: int) -> ProcessedRequest: plane: Optional[Plane] if in_queue: request = in_queue.pop() plane = request.plane stats.in_waiting_time.append(current_time - request.request_time) print(f"{current_time}: Processing incoming plane №{plane.id}") process_type = "landing" else: request = out_queue.pop() plane = request.plane stats.out_waiting_time.append(current_time - request.request_time) print(f"{current_time}: Processing outcoming plane №{plane.id}") process_type = "launching" return ProcessedRequest(request.request_time, request.plane, current_time, current_time + process_duration, process_type) takeoff_duration: Final[int] = int(input("Takeoff duration: ")) # minutes max_queue: Final[int] = int(input("Max queue: ")) interval: Final[int] = int(input("Interval: ")) # minutes incoming_planes, outcoming_planes = map(float, input("Planes: ").split()) # per hour in_queue: Queue[Request] = EnforcingQueue[Request](max_queue) out_queue: Queue[Request] = PermissiveQueue[Request](max_queue, warn_about_outcoming_queue_overload) current_request: Optional[ProcessedRequest] = None stats = Stats() timeline = generate_timeline(5,2, interval) i = 0 for i, (in_planes_count, out_planes_count) in enumerate(timeline): stats.total_plane_requests += in_planes_count + out_planes_count stats.in_requests += in_planes_count stats.out_requests += out_planes_count for _ in range(in_planes_count): new_plane = Plane() try: in_queue.add(Request(i, new_plane)) stats.accepted_in_requests += 1 except IndexError: reject_plane(new_plane) stats.rejected_in_requests += 1 for _ in range(out_planes_count): new_plane = Plane() out_queue.add(Request(i, new_plane)) stats.accepted_out_requests += 1 if not current_request: if len(in_queue) or len(out_queue): current_request = process_new_request(in_queue, out_queue, i, takeoff_duration) continue stats.sleep_minutes += 1 if current_request and current_request.process_time == i: if current_request.type_ == "launching": launch_plane(current_request.plane, stats) else: land_plane(current_request.plane, stats) current_request = None stats.in_queued = len(in_queue) stats.out_queued = len(out_queue) finalize_waiting_stats(stats, in_queue, out_queue, i) show_stats(stats)