8.9 Animating the Gas Station Problem

 < Day Day Up > 



8.9 Animating the Gas Station Problem

The animator can easily be used with concurrent programs from Chapter 3 and the techniques used in Exhibit 5 (Program8.5), as shown in Program8.7 (Exhibits 7 through 9), for the Gas Station problem presented in Chapter 3 and animated here. One change has been made in the animation given in Exhibit 5 (Program8.5). As suggested in Section 8.7, the creation of the path and the wait have been abstracted into a single method, named move. This greatly decreased the amount of code needed and the complexity of the program.

Exhibit 7: Program8.7a: Pump Class

start example

 import java.awt.Graphics; import java.awt.Color; import animator.*; class Pump implements DrawListener {   private static final int FREE = 0;   private static final int BUSY = 1;   private int state = FREE;   /**    *  The draw method allows this class to be used with the    *  gas station animation.    */   public void draw(DrawEvent de) {     Graphics g = de.getGraphics();     g.setColor(Color.black);     g.drawRect(325, 10, 100, 25);     if (state = = BUSY)       g.drawString("Pumping," 350, 30);     else       g.drawString("Pump Free," 350, 30);     g.setColor(Color.gray);     g.fillRect(350, 100, 50, 50);     g.fillOval(350, 75, 50, 25);   }   /**    *  Method to call when a car first wishes to use a pump. It    *  adds a 1/2 second to the simulated time in the problem.    */   synchronized public void usePump() {     try {       // Pre-condition processing (guard)       while(true) {         if (state = = FREE)           break;         wait();       }       // Simulate pulling to the pump by waiting 1/2 second.       Thread.sleep(500);       // Post-condition processing, change state.       state = BUSY;       notifyAll();     } catch (InterruptedException e) {     }   }   /**    *  Simulate pumping the gas by waiting 5 seconds.    */   synchronized public void pumpGas() {     try {       // Pre-condition processing (guard)       while(true) {         if (state = = BUSY)           break;         wait();       }       // Simulate pumping gas by waiting 5 seconds.       Thread.sleep(5000);       // Post-condition processing, no change state needed.     } catch (InterruptedException e) {     }   }   /**    *  Leave the pump, freeing it for the next customer.    */   synchronized public void leave() {     try {       // Pre-condition processing (guard)       while(true) {         if (state = = BUSY)           break;         wait();       }       // Simulate leaving the pump by waiting 1/2 second.       Thread.sleep(500);       // Post-condition processing, change state.       state = FREE;       notifyAll();     } catch (InterruptedException e) {     }   } } 

end example

To animate the gas station, the Pump class and the Car class were both made DrawListeners so that they could be drawn as part of the animation. They are both given a draw method. Because the Pump is passive and never moves, it does not need a thread to run, and its draw method is simply called from the animator; no path is needed. For the Car object, the run method has to be changed to produce an animated effect so that each major event that the Car has to process can be animated. When a car arrives (when it first wakes up from its sleep), it is put into the animation at an initial Parking Spot. When the car obtains the Pump (through the call to usePump), it is animated to move to the pump, and it pumps gas. When it is finished, it leaves the Pump and drives off the screen, opening up the pump for the next car.

Exhibit 8: Program8.7b: Car Class

start example

 import java.util.Random; import java.util.Date; import java.awt.Graphics; import java.awt.Color; import java.awt.Point; import animator.*; class Car implements Runnable, DrawListener {   private static int totalTime = 0,// Total time spent by all                                    // cars     totalCars = 0;                 // Total number of cars   private int customerNumber;      // The customer number to                                    // set Random value and for                                    // intermediate output.   private Pump myPump;             // The pump the customer                                    // should use.   private Animator animator;   private Path myPath;   private int parkingSpot;   private static int ParkingSpots[] = {100, 175, 250, 325};   private static int nextSpot = 0;   private static int getFreeParkingSpot() {     int returnValue = ParkingSpots[nextSpot];     nextSpot = (nextSpot + 1)% ParkingSpots.length;     return returnValue;   }   /**    *  Constructor. Set the customerNumber and pump.    */   public Car(int customerNumber, Pump pump, Animator animator) {     this.animator = animator;     this.myPump = pump;     this.customerNumber = customerNumber;   }   /**    * Static function to calculate average at the end of    * the simulation.    */   public static float calcAverage() {     return totalTime/totalCars;   }   /**    *  This function implements the procedural mechanism for    *  moving this object. It is synchronized, so in the    *  synchronized block it sets up the path and then calls    *  wait to stop the thread until the movement on the path    *  has completed.    */   private synchronized void move(int startX, int startY, int       endX,     int endY,int numberOfSteps) {     try {       myPath = new StraightLinePath(startX, startY, endX, endY,           numberOfSteps);       wait();     } catch(InterruptedException e) {     }   }   /**    *  The draw method allows this pump to be drawn as part of    *  an animation.    */   public synchronized void draw(DrawEvent de) {     // Get the Graphics to draw on.     Graphics g = de.getGraphics();     // Get where to draw. If at end of path notify the car.     Point p = myPath.nextPosition();     int x = (int)p.getX();     int y = (int)p.getY();     if (! myPath.hasMoreSteps()) {        notify();     }     // Draw the boxy car     g.setColor(Color.red);     g.fillRect(x, y, 100, 25);     g.fillRect(x+20, y-20, 60, 20);     g.setColor(Color.black);     g.fillOval(x+10, y+20, 20, 20);     g.fillOval(x+70, y+20, 20, 20);     g.drawString("" + customerNumber, x+45, y);   }   /**    * Thread that implements the pump.    */   public void run() {     // Declare variables need for simulation.     final int WAIT_TIME = 80000;     long startTime, endTime;     Random random = new Random(customerNumber);     int waitTime = random.nextInt(WAIT_TIME);     // Wait a random amount of time before coming to pump.     try {       Thread.sleep(waitTime);     } catch (InterruptedException e) {     }     startTime = (new Date()).getTime();     // Add To animation     parkingSpot = getFreeParkingSpot();     myPath = new StraightLinePath(50,  // This just ensures       parkingSpot, 50, parkingSpot, 1);// the object is at                  // a valid position     animator.addDrawListener(this);     // Use the pump, pump the gas, and leave.     System.out.println("Customer " + customerNumber +        " arrives at station");     myPump.usePump();     // Customer can now use the pump, so move customer to the pump.     move(50, parkingSpot, 150, parkingSpot, 10);     move(150, parkingSpot, 175, 175, 10);     move(175, 175, 325, 175, 10);     System.out.println("Customer " + customerNumber +        " pumps gas");     myPump.pumpGas();     System.out.println("Customer " + customerNumber +        " leaves pump");     myPump.leave();     move(325, 175, 400, 175, 10);     animator.removeDrawListener(this);     endTime = (new Date()).getTime();     System.out.println("Time = " + (endTime - startTime));     // Update the variables to calculate the average. If this     // is the last car, let the main() finish and print the     // average. Note that to use the static variables we need     // to get the lock on the class.     synchronized(Car.class) {       totalTime + = (endTime - startTime);       totalCars++;     }   } } 

end example

Exhibit 9: Program8.7c: Gas Station Class

start example

 import animator.*; public class GasStation {   static final int TOTAL_CARS = 10;   public static void main(String args[]) {     Animator animator = new Animator();     Thread carThreads[] = new Thread[TOTAL_CARS];     try {       // Create the monitor (passive object).       Pump pump1 = new Pump();       animator.addDrawListener(pump1);       // Start all the Car threads       for (int i = 0; i < TOTAL_CARS; i++) {         Car car = new Car(i, pump1, animator);         (carThreads[i] = new Thread(car)).start();       }       // Start the animation       animator.setVisible(true);       // Now suspend and wait for simulation to finish.       for (int i = 0; i < TOTAL_CARS; i++) {         carThreads[i].join();       }     } catch (InterruptedException e) {     }     // Print average time at the end of the simulation.     System.out.println("Average time to get gas = " +       Car.calcAverage());   } } 

end example

Note that the ability to put the animation in a separate thread allows the thread to be a procedural definition of the behavior of an individual entity (in this case, the car). This makes writing descriptions of behaviors easier to follow, as the behavior is simply stated as what each individual object does, just like active objects in Chapter 3. The Car run method moves through the steps required to simulate its behavior. The ability to allow the GUI thread to coordinate with the Car threads makes the modeling of the system straightforward. This makes the use of cooperative synchronization a very nice addition to a programmer's toolbox.



 < Day Day Up > 



Creating Components. Object Oriented, Concurrent, and Distributed Computing in Java
The .NET Developers Guide to Directory Services Programming
ISBN: 849314992
EAN: 2147483647
Year: 2003
Pages: 162

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net