The Airport Application ======================= In this chapter, we present the entire first draft of our airport example application. The code in this chapter is complete, and, given the libraries defined in :doc:`time-mod`, and :doc:`heap`, the code should run in a standard Dylan implementation. This example pulls together many of the techniques presented so far. The ``definitions.dylan`` file ------------------------------ This file contains common definitions that are used throughout several libraries in the airport example. The ``definitions.dylan`` file. .. code-block:: dylan module: definitions // This file contains constants and other definitions used in common with // the other parts of the airport example // The capital letters of the alphabet define constant $letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // This type represents positive integers define constant = limited(, min: 1); define constant $hours-per-day = 24; define constant $minutes-per-hour = 60; define constant $seconds-per-minute = 60; define constant $seconds-per-hour = $minutes-per-hour * $seconds-per-minute; // This method returns the union of the false type and a type you specify, // as a simple shorthand // This method may already be provided by your Dylan implementation define method false-or (other-type :: ) => (combined-type :: ) type-union(singleton(#f), other-type); end method false-or; The ``airport-classes.dylan`` file ---------------------------------- This file contains all the main classes specific to the airport example. Several methods that describe or initialize these objects are included as well. Physical objects ~~~~~~~~~~~~~~~~ The classes that follow describe fundamental attributes of tangible objects. The *airport-classes.dylan* file. .. code-block:: dylan module: airport // PHYSICAL OBJECTS AND SIZE // Used to keep track of object dimensions and object capacities // All dimensions are in feet define class () slot length :: , init-keyword: length:; slot width :: , init-keyword: width:; slot height :: , init-keyword: height:; end class ; define abstract class () slot current-position :: , init-keyword: current-position:; slot physical-size :: , init-keyword: physical-size:; end class ; define method say (physical-object :: ) => () format-out("object at "); say(physical-object.current-position); end method say; In the preceding portion of the ``airport-classes.dylan`` file, we define the class ````, which allows us to specify the external dimensions and container volume of various objects. For example, we might want to specify that certain gate areas might be too small to hold the large aircraft. We also define the base class for all tangible objects, ````. Next, we define the classes where aircraft are normally located. .. _nlanding-vehicle-containers: Vehicle containers ~~~~~~~~~~~~~~~~~~ The ``airport-classes.dylan`` file. *(continued)* .. code-block:: dylan // VEHICLE STORAGE // The default size for a vehicle container define constant $default-capacity = make(, length: 350, width: 200, height: 100); // This class represents a location where an aircraft could be stored define abstract class () slot storage-capacity :: = $default-capacity, init-keyword: capacity:; each-subclass slot name-prefix :: = "Storage", setter: #f; slot identifier :: , required-init-keyword: id:; slot connected-to :: ; end class ; // By using the name-prefix each-subclass slot, we share one say method // for all vehicle containers define method say (storage :: ) => () format-out("%s %s", storage.name-prefix, storage.identifier); end method say; define method object-fits? (object :: , container :: ) => (fits? :: ) let object-size = object.physical-size; let container-capacity = container.storage-capacity; object-size.length < container-capacity.length & object-size.height < container-capacity.height & object-size.width < container-capacity.width; end method object-fits?; // Vehicle storage that can hold only one aircraft regardless of direction // Direction in this context is either #"inbound" or #"outbound" define abstract class () slot vehicle-currently-occupying :: false-or() = #f; end class ; // Vehicle storage that can hold multiple aircraft, with distinct queues // for each direction define abstract class () slot vehicles-by-direction :: = make(); slot maxima-by-direction :: = make(); keyword directions:; keyword maxima:; end class ; // In a real airport, there would be many paths an aircraft could take // For our simple airport example, we define only the #"inbound" and // #"outbound" paths // The directions parameter is a sequence of these aircraft path names // Multiple storage containers can limit the number of aircraft that // they can hold for each path; this is the maxima parameter // This initialize method creates a queue to hold aircraft for each // direction, and stores the queue in a table indexed by direction // This method also stores the maximum number of aircaft for that // direction in a different table define method initialize (object :: , #key directions :: , maxima :: ) next-method (); for (direction in directions, maximum in maxima) object.vehicles-by-direction[direction] := make(); object.maxima-by-direction[direction] := maximum; end for; end method initialize; // From the preceding basic vehicle containers, we can build specific // containers for each aircraft-transition location define class () inherited slot name-prefix, init-value: "Gate"; end class ; // Given a zero-based terminal number, and a one-based gate number, create // an return a string with a gate letter and a terminal number in it define method generate-gate-id (term :: , gate :: ) => (gate-id :: ) format-to-string("%c%d", $letters[term], gate); end method generate-gate-id; // Gates-per-terminal is a vector; each element of the vector is the // number of gates to create for the terminal at that index // Returns a vector of all the gate instances define method generate-gates (gates-per-terminal :: , default-gate-capacity :: ) => (gates :: ) let result = make(, size: reduce1(\+, gates-per-terminal)); let result-index = 0; for (term from 0 below gates-per-terminal.size) for (gate from 1 to gates-per-terminal[term]) result[result-index] := make(, id: generate-gate-id(term, gate), capacity: default-gate-capacity); result-index := result-index + 1; end for; end for; result; end method generate-gates; // This class represents the part of the airspace over a given airport define class () // The airport over which this piece of sky is located slot airport-below :: , required-init-keyword: airport:; inherited slot name-prefix, init-value: "Sky"; required keyword inbound-aircraft:; end class ; // When a sky instance is created, a sequence of inbound aircraft is // provided // This method initializes the direction slot of the aircraft to // #"inbound", and places the aircraft in the inbound queue of the sky // instance define method initialize (sky :: , #key inbound-aircraft :: ) next-method(sky, directions: #[#"inbound", #"outbound"], maxima: vector(inbound-aircraft.size, inbound-aircraft.size)); let inbound-queue = sky.vehicles-by-direction [#"inbound"]; for (vehicle in inbound-aircraft) vehicle.direction := #"inbound"; push-last(inbound-queue, vehicle); end for; // Connect the airport to the sky sky.airport-below.sky-above := sky; end method initialize; // This class represents a strip of land where aircraft land and take off define class () inherited slot name-prefix, init-value: "Runway"; end class ; // Taxiways connect runways and gates define class () inherited slot name-prefix, init-value: "Taxiway"; end class ; In the preceding portion of the ``airport-classes.dylan`` file, we define the tangible objects that represent the various normal locations for aircraft in and around an airport. These locations are known as containers or vehicle storage. We can connect vehicle-storage instances to one another to form an airport. Instances of ```` can hold only one aircraft at a time, whereas instances of ```` can hold more than one aircraft at a time. Also, instances of ```` treat inbound aircraft separately from outbound aircraft. We define the ``object-fits?`` method, which determines whether a physical object can fit into a container. We also define methods for creating, initializing, and describing various containers. Note the use of the ``each-subclass`` slot ``name-prefix``, which permits one ``say`` method on the ```` class to cover all the vehicle-container classes. Each subclass of vehicle storage can override the inherited value of this slot, to ensure that the proper name of the vehicle storage is used in the description of instances of that subclass. The ````, ````, and ```` classes are all abstract, because it is not sensible to instantiate them. They contain partial implementations that they contribute to their subclasses. In the ``generate-gates`` method, the ``gates-per-terminal`` parameter is a vector that contains the count of gates for each terminal. By adding up all the elements of that vector with ``reduce1``, we can compute the total number of gates at the airport, and thus the size of the vector that can hold all the gates. Next, we examine the classes, initialization methods, and ``say`` methods for the vehicles in the application. Vehicles ~~~~~~~~ The ``airport-classes.dylan`` file. *(continued)* .. code-block:: dylan // VEHICLES // The class that represents all self-propelled devices define abstract class () // Every vehicle has a unique identification code slot vehicle-id :: , required-init-keyword: id:; // The normal operating speed of this class of vehicle in miles per hour each-subclass slot cruising-speed :: ; // Allow individual differences in the size of particular aircraft, // while providing a suitable default for each class of aircraft each-subclass slot standard-size :: ; end class ; define method initialize (vehicle :: , #key) next-method(); unless (slot-initialized?(vehicle, physical-size)) vehicle.physical-size := vehicle.standard-size; end unless; end method initialize; define method say (object :: ) => () format-out("Vehicle %s", object.vehicle-id); end method say; // This class represents companies that fly commercial aircraft define class () slot name :: , required-init-keyword: name:; slot code :: , required-init-keyword: code:; end class ; define method say (object :: ) => () format-out("Airline %s", object.name); end method say; // This class represents a regularly scheduled trip for a commercial // airline define class () slot airline :: , required-init-keyword: airline:; slot number :: , required-init-keyword: number:; end class ; define method say (object :: ) => () format-out("Flight %s %d", object.airline.code, object.number); end method say; // This class represents vehicles that normally fly for a portion of // their trip define abstract class () slot altitude :: , init-keyword: altitude:; // Direction here is either #"inbound" or #"outbound" slot direction :: ; // The next step this aircraft might be able to make slot next-transition :: , required-init-keyword: transition:, setter: #f; end class ; define method initialize (vehicle :: , #key) next-method(); // There is a one-to-one correspondence between aircraft instances and // transition instances // An aircraft can only make one transition at a time // Connect the aircraft to its transition vehicle.next-transition.transition-aircraft := vehicle; end method initialize; // The next step an aircraft might be able to make define class () slot transition-aircraft :: , init-keyword: aircraft:; slot from-container :: , init-keyword: from:; slot to-container :: , init-keyword: to:; // The earliest possible time that the transition could take place slot earliest-arrival :: , init-keyword: arrival:; // Has this transition already been entered in the sorted sequence? // This flag saves searching the sorted sequence slot pending? :: = #f, init-keyword: pending?:; end class ; // Describes one step of an aircraft’s movements define method say (transition :: ) => () say(transition.earliest-arrival); format-out(": "); say(transition.transition-aircraft); format-out(" at "); say(transition.to-container); end method say; // Commercial aircraft are aircraft that may have a flight // assigned to them define abstract class () slot aircraft-flight :: false-or() = #f, init-keyword: flight:; end class ; define method say (object :: ) => () let flight = object.aircraft-flight; if (flight) say(flight); else format-out("Unscheduled Aircraft %s", object.vehicle-id); end if; end method say; // The class that represents all commericial Boeing 707 aircraft define class () inherited slot cruising-speed, init-value: 368; inherited slot standard-size, init-value: make(, length: 153, width: 146, height: 42); end class ; define method say (aircraft :: ) => () if (aircraft.aircraft-flight) next-method(); else format-out("Unscheduled B707 %s", aircraft.vehicle-id); end if; end method say; In the preceding code, we model everything from the most general class of vehicle down to the specific class that represents the Boeing 707. We also model the transition steps that an aircraft may take as it travels throughout the airport, and the airlines and flights associated with commercial aircraft. Airports ~~~~~~~~ Finally, we present the class that represents the entire airport and provide the method that briefly describes the airport. The ``airport-classes.dylan`` file. *(continued)* .. code-block:: dylan // AIRPORTS // The class that represents all places where people and aircraft meet define class () // The name of the airport, such as "San Fransisco International Airport" slot name :: , init-keyword: name:; // The three letter abbreviation, such as "SFO" slot code :: , init-keyword: code:; // The airspace above the airport slot sky-above :: ; end class ; define method say (airport :: ) => () format-out("Airport %s", airport.code); end method say; The ``vehicle-dynamics.dylan`` file ----------------------------------- The ``vehicle-dynamics.dylan`` file contains stubs for calculations that predict the behavior of the aircraft involved in the example. True aeronautical calculations are beyond the scope of this book. The ``vehicle-dynamics.dylan`` file. .. code-block:: dylan module: airport // We do not need to type these constants strongly, because the Dylan // compiler will figure them out for us define constant $average-b707-brake-speed = 60.0; // Miles per hour define constant $feet-per-mile = 5280.0; define constant $average-b707-takeoff-speed = 60.0; // Miles per hour define constant $takeoff-pause-time = 120; // Seconds define constant $average-b707-taxi-speed = 10.0; define constant $average-b707-gate-turnaround-time = 34 * $seconds-per-minute; // Seconds // Computes how long it will take an aircraft to reach an airport define method flying-time (aircraft :: , destination :: ) => (duration :: ) // A simplistic calculation that assumes that the aircraft will // average a particular cruising speed for the trip make(, total-seconds: ceiling/(distance-3d(aircraft, destination), aircraft.cruising-speed / as(, $seconds-per-hour))); end method flying-time; // Computes the distance between an aircraft and an airport, // taking into account the altitude of the aircraft // Assumes the altitude of the aircraft is the height // above the ground level of the airport define method distance-3d (aircraft :: , destination :: ) => (distance :: ) // Miles // Here, a squared plus b squared is equals to c squared, where c is the // hypotenuse, and a and b are the other sides of a right triangle sqrt((aircraft.altitude / $feet-per-mile) ^ 2 + distance-2d(aircraft.current-position, destination.current-position) ^ 2); end method distance-3d; // The distance between two positions, ignoring altitude define method distance-2d (position1 :: , position2 :: ) => (distance :: ) // Miles // When we have a relative position for the first argument (the // aircraft), we assume the relative position is relative to the second // argument (the airport) position1.distance; end method distance-2d; // It would be sensible to provide a distance-2d method that computed // the great-circle distance between two absolute positions // Our example does not need this computation, which is // beyond the scope of this book // The time it takes to go from the point of touchdown to the entrance // to the taxiway define method brake-time (aircraft :: , runway :: ) => (duration :: ) make(, total-seconds: ceiling/(runway.physical-size.length / $feet-per-mile, $average-b707-brake-speed / $seconds-per-hour)); end method brake-time; // The time it takes to go from the entrance of the taxiway to the point // of takeoff define method takeoff-time (aircraft :: , runway :: ) => (duration :: ) make(, total-seconds: ceiling/(runway.physical-size.length / $feet-per-mile, $average-b707-takeoff-speed / $seconds-per-hour) + $takeoff-pause-time); end method takeoff-time; // The time it takes to taxi from the runway entrance across the taxiway // to the gate define method gate-time (aircraft :: , taxiway :: ) => (duration :: ) make(, total-seconds: ceiling/(taxiway.physical-size.length / $feet-per-mile, $average-b707-taxi-speed / $seconds-per-hour)); end method gate-time; // The time it takes to taxi from the gate across the taxiway to the // entrance of the runway define method runway-time (aircraft :: , taxiway :: ) => (duration :: ) gate-time(aircraft, taxiway); end method runway-time; // The time it takes to unload, service, and load an aircraft. define method gate-turnaround (aircraft :: , gate :: ) => (duration :: ) make(, total-seconds: $average-b707-gate-turnaround-time); end method gate-turnaround; .. _nlanding-schedule-file: The ``schedule.dylan`` file --------------------------- This file contains the key generic functions and methods that compute the schedule of aircraft transitions using the sorted sequence, time, and position libraries, as well as the classes and methods described so far in this chapter. First, we present the five key generic functions that make up our container protocol, followed by an implementation of that protocol for the container classes defined in `Vehicle containers`_. The container protocol and implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``schedule.dylan`` file. .. code-block:: dylan module: airport // The following generic functions constitute the essential protocol for // interaction between containers and vehicles // Returns true if container is available for aircraft in direction define generic available? (vehicle, container, direction); // Moves vehicle into container in the given direction define generic move-in-vehicle (vehicle, container, direction); // Moves vehicle out of container in the given direction define generic move-out-vehicle (vehicle, container, direction); // Returns the aircraft next in line to move out of container in direction define generic next-out (container, direction); // Returns the class of the next container to move vehicle into, // and how long it will take to get there define generic next-landing-step (container, vehicle); // A single storage container is available if the aircraft fits into the // the container, and there is not already a vehicle in the container define method available? (vehicle :: , container :: , direction :: ) => (container-available? :: ) object-fits?(vehicle, container) & ~ (container.vehicle-currently-occupying); end method available?; // A multiple storage container is available if the aircraft fits into // the container, and there are not too many aircraft already queued in // the container for the specified direction define method available? (vehicle :: , container :: , direction :: ) => (container-available? :: ) object-fits?(vehicle, container) & size(container.vehicles-by-direction[direction]) < container.maxima-by-direction[direction]; end method available?; // Avoids jamming the runway with inbound traffic, which would prevent // outbound aircraft from taking off // The runway is clear to inbound traffic only if there is space in the // next container inbound from the runway define method available? (vehicle :: , container :: , direction :: ) => (container-available? :: ) next-method() & select (direction) #"outbound" => #t; #"inbound" => let (class) = next-landing-step(container, vehicle); if (class) find-available-connection(container, class, vehicle) ~== #f; end if; end select; end method available?; // A slot is used to keep track of which aircraft is in a single // storage container define method move-in-vehicle (vehicle :: , container :: , direction :: ) => () container.vehicle-currently-occupying := vehicle; values(); end method move-in-vehicle; // A deque is used to keep track of which aircraft are traveling in a // particular direction in a multiple storage container define method move-in-vehicle (vehicle :: , container :: , direction :: ) => () let vehicles = container.vehicles-by-direction[direction]; push-last(vehicles, vehicle); values(); end method move-in-vehicle; // When an aircraft reaches the gate, it begins its outbound journey define method move-in-vehicle (vehicle :: , container :: , direction :: ) => () next-method(); vehicle.direction := #"outbound"; values(); end method move-in-vehicle; define method move-out-vehicle (vehicle :: , container :: , direction :: ) => () container.vehicle-currently-occupying := #f; values(); end method move-out-vehicle; define method move-out-vehicle (vehicle :: , container :: , direction :: ) => () let vehicles = container.vehicles-by-direction[direction]; // Assumes that aircraft always exit container in order, and // that this aircraft is next pop(vehicles); values(); end method move-out-vehicle; // Determines what vehicle, if any, could move to the next container // If there is such a vehicle, then this method returns the vehicle, // the next container in the direction of travel, // and the time that it would take to make that transition define method next-out (container :: , direction :: ) => (next-vehicle :: false-or(), next-storage :: false-or(), time-to-execute :: false-or()); let next-vehicle = next-out-internal(container, direction); if (next-vehicle) let (class, time) = next-landing-step(container, next-vehicle); if (class) let next-container = find-available-connection(container, class, next-vehicle); if (next-container) values(next-vehicle, next-container, time); end if; end if; end if; end method next-out; // This method is just a helper method for the next-out method // We need different methods based on the class of container define method next-out-internal (container :: , desired-direction :: ) => (vehicle :: false-or()) let vehicle = container.vehicle-currently-occupying; if (vehicle & vehicle.direction == desired-direction) vehicle; end; end method next-out-internal; define method next-out-internal (container :: , desired-direction :: ) => (vehicle :: false-or()) let vehicle-queue = container.vehicles-by-direction[desired-direction]; if (vehicle-queue.size > 0) vehicle-queue[0]; end; end method next-out-internal; // The following methods return the class of the next container to which a // vehicle can move from a particular container // They also return an estimate of how long that transition will take define method next-landing-step (storage :: , aircraft :: ) => (next-class :: false-or(), duration :: false-or()) if (aircraft.direction == #"inbound") values(, flying-time(aircraft, storage.airport-below)); end if; end method next-landing-step; define method next-landing-step (storage :: , aircraft :: ) => (next-class :: , duration :: ) select (aircraft.direction) #"inbound" => values(, brake-time(aircraft, storage)); #"outbound" => values(, takeoff-time(aircraft, storage)); end select; end method next-landing-step; define method next-landing-step (storage :: , aircraft :: ) => (next-class :: , duration :: ) select (aircraft.direction) #"inbound" => values(, gate-time(aircraft, storage)); #"outbound" => values(, runway-time(aircraft, storage)); end select; end method next-landing-step; define method next-landing-step (storage :: , aircraft :: ) => (next-class :: , duration :: ) values(, gate-turnaround(aircraft, storage)); end method next-landing-step; The scheduling algorithm ~~~~~~~~~~~~~~~~~~~~~~~~ The next methods form the core of the airport application. The ``schedule.dylan`` file. *(continued)* .. code-block:: dylan // Searches all of the vehicle storage of class class-of-next, which is // connected to container and has room for aircraft define method find-available-connection (storage :: , class-of-next :: , aircraft :: ) => (next-container :: false-or()) block (return) for (c in storage.connected-to) if (instance?(c, class-of-next) & available?(aircraft, c, aircraft.direction)) return(c); end if; end for; end block; end method find-available-connection; // Generate new transitions to be considered for the next move // The transitions will be placed in the sorted sequence, which will order // them by earliest arrival time define method generate-new-transitions (container :: , active-transitions :: , containers-visited :: ) => () unless(element(containers-visited, container, default: #f)) // Keep track of which containers we have searched for new possible // transitions // We avoid looping forever by checking each container just once containers-visited[container] := #t; local method consider-transition (direction) // See whether any vehicle is ready to transition out of a container let (vehicle, next-container, time) = next-out(container, direction); unless (vehicle == #f | vehicle.next-transition.pending?) // If there is a vehicle ready, and it is not already in the // sorted sequence of pending transitions, then prepare the // transition instance associated with the vehicle let transition = vehicle.next-transition; transition.from-container := container; transition.to-container := next-container; // The vehicle may have been waiting // Take this situation into account when computing the earliest // arrival into the next container transition.earliest-arrival := transition.earliest-arrival + time; // Flag the vehicle as pending, to save searching through the // active-transitions sorted sequence later transition.pending? := #t; // Add the transition to the set to be considered add!(active-transitions, transition); end unless; end method consider-transition; // Consider both inbound and outbound traffic consider-transition(#"outbound"); consider-transition(#"inbound"); // Make sure that every container connected to this one is checked for (c in container.connected-to) generate-new-transitions(c, active-transitions, containers-visited); end for; end unless; end method generate-new-transitions; // Main loop of the program // See what possible transitions exist, then execute the earliest // transitions that can be completed // Returns the time of the last transition define method process-aircraft (airport :: , #key time = $midnight) => (time :: ) format-out("Detailed aircraft schedule for "); say(airport); format-out("\n\n"); let sky = airport.sky-above; let containers-visited = make(); let active-transitions = make(, value-function: earliest-arrival); // We do not have to use return as the name of the exit procedure block (done) while (#t) // Each time through, start by considering every container fill!(containers-visited, #f); // For every container, see if any vehicles are ready to transition // If any are, add transition instances to the active-transitions // sorted sequence generate-new-transitions(sky, active-transitions, containers-visited); // If there are no more transitions, we have completed our task if (empty?(active-transitions)) done(); end; // Find the earliest transition that can complete, because there is // still room available in the destination container let transition-index = find-key(active-transitions, method (transition) available?(transition.transition-aircraft, transition.to-container, transition.transition-aircraft.direction); end); // If none can complete, there is a problem with the simulation // This situation should never occur, but is useful for debugging // incorrect container configurations if (transition-index == #f) error("Pending transitions but none can complete."); end if; // Otherwise, the earliest transition that can complete has been // found: Execute the transition let transition = active-transitions[transition-index]; let vehicle = transition.transition-aircraft; let vehicle-direction = vehicle.direction; move-out-vehicle(vehicle, transition.from-container, vehicle-direction); move-in-vehicle(vehicle, transition.to-container, vehicle-direction); // This transition is complete; remove it from consideration transition.pending? := #f; remove!(active-transitions, transition); // Compute the actual time of arrival at the next container, and // display the message time := (transition.earliest-arrival := max(time, transition.earliest-arrival)); say(transition); format-out("\n"); end while; end block; time; end method process-aircraft; The ``process-aircraft`` method uses components from the time, space and sorted sequence libraries, the container classes and protocols, and the vehicle classes and methods to schedule the aircraft arriving and departing from an airport. The ``generate-new-transitions`` method assists by examining the current state of all containers in the airport, and by noting any new steps that vehicles could take. .. _nlanding-airport-test-file: The ``airport-test.dylan`` file ------------------------------- The ``airport-test.dylan`` file contains test data, and the code that constructs a model of the simple airport described in :ref:`design-goals-airport-application`. The final method is a top-level testing function that builds the airport model and executes the main aircraft scheduling function. After defining the test, we show the results of running it. The ``airport-test.dylan`` file. .. code-block:: dylan module: airport-test // To keep the example relatively simple, we will use variables to hold // test data for the flights and aircraft // Ordinarily, this information would be read from a file or database define variable *flight-numbers* = #[62, 7, 29, 12, 18, 44]; define variable *aircraft-distances* = #[3.0, 10.0, 175.0, 450.0, 475.0, 477.0]; // Miles define variable *aircraft-headings* = #[82, 191, 49, 112, 27, 269]; // Degrees define variable *aircraft-altitudes* = #[7000, 15000, 22000, 22500, 22000, 21000]; // Feet define variable *aircraft-ids* = #["72914", "82290", "18317", "26630", "43651", "40819"]; define constant $default-runway-size = make(, length: 10000, width: 200, height: 100); // Feet define constant $default-taxiway-size = make(, length: 900, width: 200, height: 100); // Feet // Assumes that there is only one runway, and one taxiway // The taxiway-count variable will determine how many aircraft can wait // in line for each direction of the taxiway define method build-simple-airport (#key gates-per-terminal :: = #[2], capacity :: = $default-capacity, runway-size :: = $default-runway-size, taxiway-size :: = $default-taxiway-size, taxiway-count :: = 5, position-report-time :: = make(, total-seconds: encode-total-seconds(6, 0, 0))) => (airport :: ) let gates = generate-gates(gates-per-terminal, capacity); let taxiway = make(, id: "Echo", directions: #[#"inbound", #"outbound"], maxima: vector(taxiway-count, taxiway-count), capacity: capacity, physical-size: taxiway-size); let runway = make(, id: "11R-29L", capacity: capacity, physical-size: runway-size); let keystone-air = make(, name: "Keystone Air", code: "KN"); let flights = map(method (fn) make(, airline: keystone-air, number: fn) end, *flight-numbers*); let aircraft = map(method (aircraft-flight, aircraft-distance, aircraft-heading, aircraft-altitude, aircraft-id) make(, flight: aircraft-flight, current-position: make(, distance: aircraft-distance, angle: make(, total-seconds: encode-total-seconds (aircraft-heading, 0, 0))), altitude: aircraft-altitude, id: aircraft-id, transition: make(, arrival: position-report-time)); end, flights, *aircraft-distances*, *aircraft-headings*, *aircraft-altitudes*, *aircraft-ids*); let airport = make(, name: "Belefonte Airport", code: "BLA", current-position: make(, latitude: make(, total-seconds: encode-total-seconds(40, 57, 43), direction: #"north"), longitude: make(, total-seconds: encode-total-seconds(77, 40, 24), direction: #"west"))); let sky = make(, inbound-aircraft: aircraft, airport: airport, id: concatenate("over ", airport.code)); airport.sky-above := sky; runway.connected-to := vector(taxiway, sky); let taxiway-vector = vector(taxiway); for (gate in gates) gate.connected-to := taxiway-vector; end for; let runway-vector = vector(runway); taxiway.connected-to := concatenate(runway-vector, gates); sky.connected-to := runway-vector; airport; end method build-simple-airport; define method test-airport () => (last-transition :: ) process-aircraft(build-simple-airport()); end method test-airport; Now, we show the result of running ``test-airport``: .. code-block:: dylan-console ? test-airport(); => Detailed aircraft schedule for Airport BLA => 6:00: Flight KN 62 at Runway 11R-29L => 6:02: Flight KN 62 at Taxiway Echo => 6:02: Flight KN 7 at Runway 11R-29L => 6:03: Flight KN 62 at Gate A1 => 6:04: Flight KN 7 at Taxiway Echo => 6:05: Flight KN 7 at Gate A2 => 6:28: Flight KN 29 at Runway 11R-29L => 6:30: Flight KN 29 at Taxiway Echo => 6:37: Flight KN 62 at Taxiway Echo => 6:37: Flight KN 29 at Gate A1 => 6:38: Flight KN 62 at Runway 11R-29L => 6:39: Flight KN 7 at Taxiway Echo => 6:42: Flight KN 62 at Sky over BLA => 6:42: Flight KN 7 at Runway 11R-29L => 6:46: Flight KN 7 at Sky over BLA => 7:11: Flight KN 29 at Taxiway Echo => 7:12: Flight KN 29 at Runway 11R-29L => 7:16: Flight KN 29 at Sky over BLA => 7:16: Flight KN 12 at Runway 11R-29L => 7:18: Flight KN 12 at Taxiway Echo => 7:18: Flight KN 18 at Runway 11R-29L => 7:19: Flight KN 12 at Gate A1 => 7:20: Flight KN 18 at Taxiway Echo => 7:20: Flight KN 44 at Runway 11R-29L => 7:21: Flight KN 18 at Gate A2 => 7:22: Flight KN 44 at Taxiway Echo => 7:53: Flight KN 12 at Taxiway Echo => 7:53: Flight KN 44 at Gate A1 => 7:54: Flight KN 12 at Runway 11R-29L => 7:55: Flight KN 18 at Taxiway Echo => 7:58: Flight KN 12 at Sky over BLA => 7:58: Flight KN 18 at Runway 11R-29L => 8:02: Flight KN 18 at Sky over BLA => 8:27: Flight KN 44 at Taxiway Echo => 8:28: Flight KN 44 at Runway 11R-29L => 8:32: Flight KN 44 at Sky over BLA => {class } The ``definitions-library.dylan`` file -------------------------------------- The ``definitions-library.dylan`` file provides common definitions for all the libraries in the airport example. Note that this library and module, and the other libraries and modules that follow, do not separate the library implementation module from the library interface module, as discussed in :ref:`libraries-roles-of-modules`. Dylan allows several different approaches to library and module architecture. Here, we present an alternative organization. The ``definitions-library.dylan`` file. .. code-block:: dylan module: dylan-user define library definitions export definitions; use dylan; end library definitions; define module definitions export $letters, ; export $hours-per-day, $minutes-per-hour; export $seconds-per-minute, $seconds-per-hour, false-or; use dylan; end module definitions; The ``definitions.lid`` file ---------------------------- The ``definitions.lid`` file. .. code-block:: dylan library: definitions files: definitions-library definitions The ``airport-library.dylan`` file ---------------------------------- The airport library implements the main scheduling system for the airport example. This library assumes that your Dylan implementation provides a ``format-out`` library, which supplies the ``format-out`` and ``format-to-string`` functions. This library also assumes that there is a ``transcendentals`` library, which supplies the ``sqrt`` (square root) function. The ``airport-library.dylan`` file. .. code-block:: dylan module: dylan-user define library airport export airport; use dylan; use transcendentals; use say; use format-out; use definitions; use sorted-sequence; use angle; use time; end library airport; define module airport export , length, height, width, current-position, current-position-setter; export physical-size, physical-size-setter, $default-capacity; export storage-capacity, storage-capacity-setter, identifier; export connected-to, connected-to-setter; export , generate-gates, , , ; export , name, name-setter, code, code-setter, ; export aircraft-flight, aircraft-flight-setter, number, number-setter, altitude, altitude-setter; export , , , sky-above, sky-above-setter; export process-aircraft; use dylan; use transcendentals, import: {sqrt}; use say; use format-out, import: {format-out}; use format, import: {format-to-string}; use definitions; use sorted-sequence; use time; use angle, export: {direction, direction-setter}; use position; end module airport; The ``airport.lid`` file ------------------------ The ``airport.lid`` file. .. code-block:: dylan library: airport files: airport-library airport-classes vehicle-dynamics schedule The ``airport-test-library.dylan`` file --------------------------------------- The ``airport-test`` library implements a simple test case for the scheduling system defined in the ``airport`` library. The ``airport-test-library.dylan`` file. .. code-block:: dylan module: dylan-user define library airport-test export airport-test; use dylan; use time; use angle; use airport; end library airport-test; define module airport-test export test-airport; use dylan; use time; use angle; use position; use airport; end module airport-test; The ``airport-test.lid`` file ----------------------------- The ``airport-test.lid`` file. .. code-block:: dylan library: airport-test files: airport-test-library airport-test Summary ------- In this chapter, we presented a complete first draft of the airport application, based on the techniques presented in previous chapters. Although the example is complete and meets its stated design goals, we can still make a number of improvements. For example, we could take advantage of Dylan’s multiple inheritance to eliminate certain repetitive slots. We could provide a container-implementor module interface, and open the classes and generic functions so that users could add their own classes of containers and extend the scope of the application. We could take advantage of Dylan’s exception handling to better deal with unusual situations that might occur during the simulation. In the chapters that follow, we show the Dylan language features that enable such improvements.