17.4.1 The container protocol and implementation

The schedule.dylan file.

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 :: <aircraft>, container :: <single-storage>,
     direction :: <symbol>)
 => (container-available? :: <boolean>)
  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 :: <aircraft>, container :: <multiple-storage>,
     direction :: <symbol>)
 => (container-available? :: <boolean>)
  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 :: <aircraft>, container :: <runway>,
     direction :: <symbol>) 
 => (container-available? :: <boolean>)
  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 :: <aircraft>, container :: <single-storage>,
     direction :: <symbol>)
 => ()
  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 :: <aircraft>, container :: <multiple-storage>,
     direction :: <symbol>)
 => ()
  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 :: <aircraft>, container :: <gate>, 
     direction :: <symbol>)
 => ()
  next-method();
  vehicle.direction := #"outbound";
  values();
end method move-in-vehicle; 

define method move-out-vehicle
    (vehicle :: <aircraft>, container :: <single-storage>,
     direction :: <symbol>)
 => ()
  container.vehicle-currently-occupying := #f;
  values();
end method move-out-vehicle; 

define method move-out-vehicle
    (vehicle   :: <aircraft>,
     container :: <multiple-storage>, direction :: <symbol>)
 => ()
  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 :: <vehicle-storage>, direction :: <symbol>)
 => (next-vehicle :: false-or(<vehicle>),
     next-storage :: false-or(<vehicle-storage>),
     time-to-execute :: false-or(<time-offset>));
  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 :: <single-storage>, desired-direction :: <symbol>)
 => (vehicle :: false-or(<aircraft>))
  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 :: <multiple-storage>, desired-direction :: <symbol>)
 => (vehicle :: false-or(<aircraft>))
  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 :: <sky>, aircraft :: <aircraft>)
 => (next-class :: false-or(<class>), duration :: false-or(<time-offset>))
  if (aircraft.direction == #"inbound")
    values(<runway>, flying-time(aircraft, storage.airport-below));
  end if;
end method next-landing-step; 

define method next-landing-step
    (storage :: <runway>, aircraft :: <aircraft>)
 => (next-class :: <class>, duration :: <time-offset>)
  select (aircraft.direction)
    #"inbound"  => values(<taxiway>, brake-time(aircraft, storage));
    #"outbound" => values(<sky>, takeoff-time(aircraft, storage));
  end select;
end method next-landing-step; 

define method next-landing-step
    (storage :: <taxiway>, aircraft :: <aircraft>)
 => (next-class :: <class>, duration :: <time-offset>)
  select (aircraft.direction)
    #"inbound"  => values(<gate>, gate-time(aircraft, storage));
    #"outbound" => values(<runway>, runway-time(aircraft, storage));
  end select;
end method next-landing-step; 

define method next-landing-step
    (storage :: <gate>, aircraft :: <aircraft>)
 => (next-class :: <class>, duration :: <time-offset>)
  values(<taxiway>, gate-turnaround(aircraft, storage));
end method next-landing-step;