/* Simulation of a CarWash */ /* coding: Holger Uhlig */ /* 20.1.1997 */ /* changed: 16.4.1997 */ import java.awt.*; import java.lang.*; import java.net.URL; /* class to store events sorted by time-index */ class EventList extends java.lang.Object { int eNr; float eTime; EventList next; public EventList (float eventTime) { eNr=0; eTime=eventTime; next=null; } /* insert event into list */ public void insertEvent (int eventNr, float eventTime) { EventList hEL; if (eventTime <= eTime) if (next == null) { next=new EventList(eventTime); next.eNr=eventNr; next.next=null; } else if (eventTime < next.eTime) next.insertEvent(eventNr, eventTime); else { hEL=next; next=new EventList(eventTime); next.eNr=eventNr; next.next=hEL; } else { hEL=next; next=new EventList(eTime); next.eNr=eNr; next.next=hEL; eNr=eventNr; eTime=eventTime; } } /* get event with next time-index */ public EventList nextEvent () { EventList hEL1; EventList hEL2; hEL1=this; hEL2=this; while (hEL1.next != null) { hEL2=hEL1; hEL1=hEL1.next; } hEL2.next=null; return hEL1; } } /* class to show animations */ class AnimationPanel extends java.awt.Panel { Graphics backGraphics; Graphics carWashFrontGraphics; Image backImage; Image carWashImage; Image carWashFrontImage; Image carImage; Image wheelImage[]; MediaTracker tracker1; boolean animationOn; public AnimationPanel (SimCarWash parent) { backImage=parent.createImage(533, 247); backGraphics=backImage.getGraphics(); backGraphics.drawString("loading images", 100, 100); repaint(); tracker1=new MediaTracker(this); carWashImage=parent.getImage(parent.getDocumentBase(), "CARWASH.GIF"); tracker1.addImage (carWashImage, 0); carImage=parent.getImage(parent.getDocumentBase(), "CAR.GIF"); tracker1.addImage (carImage, 0); wheelImage=new Image[4]; for (int i=0; i<4; i++) { wheelImage[i]=parent.getImage(parent.getDocumentBase(), "WHEEL"+i+".GIF"); tracker1.addImage (wheelImage[i], 0); } try {tracker1.waitForAll();} catch (InterruptedException e) { }; backGraphics.clearRect(0, 0, 533, 247); if (tracker1.isErrorAny()) { animationOn=false; backGraphics.drawString("animation not available", 100, 100); } else { animationOn=true; backGraphics.drawImage(carWashImage, 0, 0, this); } repaint(); carWashFrontImage=parent.createImage(512, 247); carWashFrontGraphics=carWashFrontImage.getGraphics(); carWashFrontGraphics.drawImage(carWashImage, 0, 0, this); carWashFrontGraphics.dispose(); } /* show animation of car arriving at carwash */ public void carToCarWash (long frameTime) { if (animationOn) { backGraphics.clearRect(0, 0, 533, 247); backGraphics.drawImage(carWashImage, 472, 0, this); for (int i=0; i<10; i++) { backGraphics.clearRect(-361+(i*40), 112, 394, 125); backGraphics.drawImage(carImage, -321+(i*40), 112, this); backGraphics.drawImage(wheelImage[i % 4], -321+70+(i*40), 184, this); backGraphics.drawImage(wheelImage[i % 4], -321+291+(i*40), 184, this); repaint(); pause(frameTime); } } else pause(10*frameTime); } /* show animation of car going into carwash */ public void carInCarWash (long frameTime) { if (animationOn) { backGraphics.clearRect(0, 0, 533, 247); backGraphics.drawImage(carImage, 39, 112, this); for (int i=0; i<12; i++) { backGraphics.drawImage(wheelImage[i % 4], 109, 184, this); backGraphics.drawImage(wheelImage[i % 4], 330, 184, this); backGraphics.drawImage(carWashImage, 472-(i*40), 0, this); repaint(); pause(frameTime); } } else pause(12*frameTime); } /* show animation of car arriving at end of waiting queue */ public void carToCar (long frameTime) { if (animationOn) { backGraphics.clearRect(0, 0, 533, 247); backGraphics.drawImage(carImage, 472, 112, this); for (int i=0; i<10; i++) { backGraphics.clearRect(-361+(i*40), 112, 394, 125); backGraphics.drawImage(carImage, -321+(i*40), 112, this); backGraphics.drawImage(wheelImage[i % 4], -321+70+(i*40), 184, this); backGraphics.drawImage(wheelImage[i % 4], -321+291+(i*40), 184, this); repaint(); pause(frameTime); } } else pause(10*frameTime); } /* show animation of car leaving carwash */ public void carOffCarWash (long frameTime) { if (animationOn) { backGraphics.clearRect(0, 0, 533, 247); for (int i=0; i<29; i++) { backGraphics.clearRect(-361+(i*40), 112, 394, 125); backGraphics.drawImage(carWashImage, -472, 0, this); backGraphics.drawImage(carImage, -321+(i*40), 112, this); backGraphics.drawImage(wheelImage[i %4], -321+70+(i*40), 184, this); backGraphics.drawImage(wheelImage[i %4], -321+291+(i*40), 184, this); backGraphics.drawImage(carWashFrontImage, -472, 0, this); repaint(); pause(frameTime); } } else pause(29*frameTime); } public void update (Graphics g) { paint(g); } public void pause (long duration) { try { Thread.sleep(duration); } catch (InterruptedException e) { } } public void paint (Graphics g) { g.drawImage(backImage, (size().width-533)/2, (size().height-247)/2, this); } } /* class to show Pictogram of waitingQueue */ class PictogramPanel extends java.awt.Panel { Graphics backGraphics; Image backImage; Image pictoImage[]; MediaTracker tracker1; boolean animationOn; public PictogramPanel (SimCarWash parent){ backImage=parent.createImage(533, 26); backGraphics=backImage.getGraphics(); tracker1=new MediaTracker(this); pictoImage=new Image[3]; for (int i=1; i<4; i++) { pictoImage[i-1]=parent.getImage(parent.getDocumentBase(), "PICTO"+i+".GIF"); tracker1.addImage (pictoImage[i-1], 0); } try {tracker1.waitForAll();} catch (InterruptedException e) { }; if (tracker1.isErrorAny()) { animationOn=false; backGraphics.drawString("pictogram not available", 100, 10); } else { animationOn=true; } repaint(); } public void drawCarPictogram (int position, int number) { backGraphics.drawImage (pictoImage[0], 477-position*53, 7, this); backGraphics.setXORMode (Color.white); String numberStr=number+""; backGraphics.drawString (numberStr, 501-position*53-(backGraphics.getFontMetrics().stringWidth(numberStr)/2), 18); backGraphics.setPaintMode (); } public void drawPictogram (int carCount, boolean busy) { if (animationOn) { backGraphics.clearRect (0, 0, 533, 26); backGraphics.fillRect (477, 3, 533, 22); if (busy) backGraphics.drawImage (pictoImage[1], 480, 7, this); if (carCount<10) { for (int i=1; i<=carCount; i++) drawCarPictogram(i, i); } else { for (int i=1; i<5; i++) drawCarPictogram(i, i); backGraphics.drawImage (pictoImage[2], 212, 7, this); for (int i=6; i<10; i++) drawCarPictogram(i, carCount-9+i); } repaint(); } } public void update (Graphics g) { paint(g); } public void paint (Graphics g) { g.drawImage(backImage, (size().width-533)/2, (size().height-26)/2, this); } } /* main class - simulation of carwash */ public class SimCarWash extends java.applet.Applet implements Runnable { AnimationPanel panel1; PictogramPanel panel3; Panel panel2; Panel panel4; Button button1; Choice choice1; TextField textf1; TextField textf2; TextField textf3; Thread simulation; float simduration; float arrivingRate; float washingRate; float throuput; EventList eventList1; int waitingCars; int washedCars; int speed; boolean carWashBusy; float simTime; Label label1; Label label2; Label label3; public void init () { this.setLayout (new BorderLayout ()); panel1=new AnimationPanel(this); panel1.setLayout (new GridLayout (1, 1, 0, 0)); panel3=new PictogramPanel(this); panel3.setLayout (new GridLayout (1, 1, 0, 0)); panel4=new Panel(); panel4.setLayout (new GridLayout(2, 1, 0, 0)); panel4.add (new Label("CarWash - Simulation", Label.CENTER)); panel4.add (panel3); add ("North", panel4); add ("Center", panel1); panel2=new Panel(); panel2.setLayout (new GridLayout (7, 2)); button1=new Button("Start"); choice1=new Choice(); choice1.addItem("realtime"); choice1.addItem("doublespeed"); choice1.addItem("quadspeed"); choice1.addItem("fullspeed"); simduration=1; arrivingRate=8; washingRate=8; speed=1; textf1=new TextField (""+simduration); textf2=new TextField (""+arrivingRate); textf3=new TextField (""+washingRate); label1=new Label("0 cars", Label.RIGHT); label2=new Label("0 cars/min", Label.RIGHT); label3=new Label("0 cars", Label.RIGHT); panel2.add (button1); panel2.add (choice1); panel2.add (new Label("simulationtime (hours):")); panel2.add (textf1); panel2.add (new Label("arrivingrate (cars/min):")); panel2.add (textf2); panel2.add (new Label("washingrate (cars/min):")); panel2.add (textf3); panel2.add (new Label("washed cars:")); panel2.add (label1); panel2.add (new Label("throuput:")); panel2.add (label2); panel2.add (new Label("waiting cars:")); panel2.add (label3); add ("South", panel2); } /* create exponential distributed number */ public float exponDistributed (float lambda) { return ((float)(-(1/lambda)*Math.log(Math.random()))); } public void stop () { if (simulation != null) { simulation.stop(); simulation=null; button1.setLabel("Start"); } } public void pause (long duration) { try { Thread.sleep(duration); } catch (InterruptedException e) { } } /* simulation run */ public void run () { int frames=1; int animation=1; float restTime; float animTime; float frameTime; try {simduration=new Float(textf1.getText()).floatValue();} catch (NumberFormatException e1) {} textf1.setText(""+simduration); try {arrivingRate=new Float(textf2.getText()).floatValue();} catch (NumberFormatException e1) {} textf2.setText(""+arrivingRate); try {washingRate=new Float(textf3.getText()).floatValue();} catch (NumberFormatException e1) {} textf3.setText(""+washingRate); simTime=0; waitingCars=0; washedCars=0; throuput=0; label1.setText(washedCars+" cars"); label2.setText(throuput+" cars/min"); label3.setText(waitingCars+" cars"); carWashBusy=false; eventList1=new EventList(simduration*60); eventList1.insertEvent(1, exponDistributed(arrivingRate)); EventList cEvent=eventList1.nextEvent(); restTime=cEvent.eTime; panel3.drawPictogram(waitingCars, carWashBusy); while (cEvent.eNr != 0) { if (speed != 0) pause ((long)restTime); else pause (10); simTime=cEvent.eTime; switch (cEvent.eNr) { case 1: if (carWashBusy) { frames=10; if (waitingCars == 0) animation=2; else animation=3; waitingCars++; } else { carWashBusy=true; eventList1.insertEvent(2, simTime+exponDistributed(washingRate)); frames=22; animation=1; } eventList1.insertEvent(1, simTime+exponDistributed(arrivingRate)); break; case 2: if (waitingCars == 0) { carWashBusy=false; frames=31; animation=4; } else { waitingCars--; eventList1.insertEvent(2, simTime+exponDistributed(washingRate)); frames=41; animation=5; } washedCars++; throuput=washedCars/simTime; break; } cEvent=eventList1.nextEvent(); if (speed != 0) animTime=(cEvent.eTime-simTime)*60000/speed; else animTime=0; frameTime=animTime/frames; if (frameTime>100) { frameTime=100; restTime=animTime-(frameTime*frames); } else restTime=0; if (frameTime<10) restTime=animTime; else {switch (animation) { case 1: panel1.carToCarWash((long)frameTime); panel1.carInCarWash((long)frameTime); break; case 2: panel1.carToCarWash((long)frameTime); break; case 3: panel1.carToCar((long)frameTime); break; case 4: panel1.carOffCarWash((long)frameTime); break; case 5: panel1.carOffCarWash((long)frameTime); panel1.carInCarWash((long)frameTime); break; } } label1.setText(washedCars+" cars"); label2.setText(throuput+" cars/min"); label3.setText(waitingCars+" cars"); panel3.drawPictogram(waitingCars, carWashBusy); } button1.setLabel("Start"); } public boolean action (Event e, Object arg) { if (e.target == button1) { if (button1.getLabel() == "Start") { if (simulation != null) { simulation.stop(); simulation=null; } simulation=new Thread(this); simulation.start(); button1.setLabel("Stop"); } else { simulation.stop(); simulation=null; button1.setLabel("Start"); } return true; } if (e.target == choice1) { switch (choice1.getSelectedIndex()) { case 0: speed=1; break; case 1: speed=2; break; case 2: speed=4; break; case 3: speed=0; break; } return true; } return false; } }