1   package main;
2   
3   import simworldobjects.*;
4   import intellego.Intellego;
5   import util.*;
6   import interfaces.*;
7   import real.*;
8   
9   import java.awt.*;
10  import java.lang.*;
11  import java.awt.event.*;
12  import javax.swing.*;
13  import javax.swing.event.*;
14  import java.io.*;
15  import java.util.*;
16  
17  /**
18  * Provides a user interface for a simulation
19  *
20  * @author Graham Ritchie
21  */
22  public class SimUI extends JInternalFrame implements Runnable
23  {
24      private Container mainContainer;
25      private SimDisplay display=null;
26      private JLayeredPane mainPane;
27      private LinkedList controllerList;
28      private LinkedList robotList;
29      private SimWorld world=null;
30      private boolean running=false;
31      private boolean paused=false;
32      private int UPDATE_TIME=30;
33      private static int screenWidth=1000;
34      private static int screenHeight=800;
35      
36      private static final String NO_CLASS="__NOCLASS__";
37      
38      /**
39      * Sets up the main window for an empty simulation
40      */
41      public SimUI()
42      {
43          // window setup
44          super("Simulator:",true,true,true,true);
45          setSize(screenWidth,screenHeight);
46          
47          setupWindow();
48          
49           // stop the simulation when the window closes.
50          addInternalFrameListener(new InternalFrameAdapter() 
51          {
52              public void internalFrameClosing(InternalFrameEvent e) 
53              {
54                  Intellego.addToLog("SimUI.init(): simulator quitting");
55                  
56                  // stop running (if we are)
57                  running=false;
58              
59                  // stop all the controllers
60                  for (int i=0;i<controllerList.size();i++)
61                  {
62                      Controller c=(Controller)controllerList.get(i);
63                      c.halt();
64                  }
65              }
66          });
67      }
68      
69      /**
70      * Loads a new simulation with the specified controller preloaded
71      *
72      * @param controllerClassName the class name of the controller
73      */
74      public SimUI(String controllerClassName)
75      {
76          // window setup
77          super("Simulator:",true,true,true,true);
78          setSize(screenWidth,screenHeight);
79          
80          setupWindow();
81          
82          // set up a world
83          selectWorld();
84          
85          // add this controller
86          Controller c=createController(controllerClassName);
87          addController(c);
88      }
89      
90      /**
91      * Sets up the main window
92      */
93      public void setupWindow()
94      {
95          // set up main pane
96          mainContainer=getContentPane();
97              mainContainer.setLayout(new BoxLayout(mainContainer,BoxLayout.Y_AXIS));
98          
99          // create and set the menu bar
100         JMenuBar mb=createMenuBar();
101         setJMenuBar(mb);
102         
103         // set up required lists
104         controllerList=new LinkedList();
105         robotList=new LinkedList();
106     }   
107     
108     /**
109     * Creates the menu bar for the main window, and adds action listeners 
110     * to the menus.
111     *
112     * @return the menu bar
113     */
114     private JMenuBar createMenuBar()
115     {
116         // menu bar
117         JMenuBar mb=new JMenuBar();
118         
119         // menus
120         JMenu controlMenu=new JMenu("Controls");
121         
122         // control menu items
123         JMenuItem pickWorld=new JMenuItem("Pick SimWorld");
124         JMenuItem addNewRobot=new JMenuItem("Add Robot");
125         JMenuItem startSim=new JMenuItem("Start Simulation");
126         JMenuItem pauseSim=new JMenuItem("Pause Simulation"); //gbb
127         JMenuItem unpauseSim=new JMenuItem("Unpause Simulation"); //gbb
128         JMenuItem stopSim=new JMenuItem("Stop Simulation");
129         
130         // add action listeners
131         pickWorld.addActionListener(new ActionListener() 
132         {
133             public void actionPerformed(ActionEvent e) 
134             {
135                 selectWorld();
136             }
137         });
138         
139         addNewRobot.addActionListener(new ActionListener() 
140         {
141             public void actionPerformed(ActionEvent e) 
142             {
143                 addRobot();
144             }
145         });
146         
147         startSim.addActionListener(new ActionListener() 
148         {
149             public void actionPerformed(ActionEvent e) 
150             {
151                 startSimulation();
152             }
153         });
154         
155         stopSim.addActionListener(new ActionListener() 
156         {
157             public void actionPerformed(ActionEvent e) 
158             {
159                 stopSimulation();
160             }
161         });
162         
163         // add items to control menu
164         controlMenu.add(pickWorld);
165         controlMenu.add(addNewRobot);
166         controlMenu.add(startSim);
167         controlMenu.add(stopSim);
168         
169         // add menus to menu bar
170         mb.add(controlMenu);
171         
172         return mb;
173     }
174     
175     /**
176     * Main loop: updates the world and repaints the screen 
177     */
178     public void run()
179     {
180         if(world==null || display==null)
181         {
182             MainInterface.displayMessage("You must select a SimWorld and a SimDisplay first");
183         }
184         else
185         {
186             while (running)
187             {
188                 // update SimWorld
189                 world.tick();
190                 
191                 // repaint the display screen
192                 display.repaint();   
193             
194                 // sleep for a bit
195                 try{Thread.sleep(UPDATE_TIME);}catch(Exception e){}
196             }
197         }
198     }
199     
200     /**
201     * Allows the user to pick a simworld for their simulation, and loads it
202     */
203     public void selectWorld()
204     {
205         // create simworld by getting the class name from the user
206         String className=getClassName("Pick a SimWorld class file:","simworlds");
207         
208         // check user actually selected a file
209         if (className.equals(NO_CLASS))
210         {
211             // do nothing
212         }
213         else
214         {
215             // if there is already a world open, stop the current sim and get rid of it.
216             if(world!=null)
217             {
218                 stopSimulation();
219                 world=null;
220                 mainContainer.remove(display);
221                 display=null;
222             }
223             
224             // try to create a simworld from this class
225             SimWorld s=createSimWorld(className);
226             
227             // check simworld creation was successful
228             if(s!=null)
229             {
230                 // set s as the world for this simulation
231                 world=s;
232                 
233                 // and create a new display with this simworld
234                 display=new SimpleDisplay(world);
235                 
236                 display.setVisible(true);
237                 
238                 // add the simdisplay to the main pane
239                 mainContainer.add(display, BorderLayout.CENTER);
240                 
241                 // set size to force display to repaint
242                 setSize(screenWidth,screenHeight);
243             }
244             else // simworld creation failed, so just bail out
245             {
246                 MainInterface.displayMessage("Failed to create SimWorld");
247                 Intellego.addToLog("SimUI.selectWorld(): Failed to create SimWorld with name "+className);
248             }
249         }
250     }
251     
252     /**
253     * Dynamically loads a simworld class
254     *
255     * @param name the name of the simworld as a string
256     * 
257     * @return the simworld, or null if unsuccessful
258     */
259     public SimWorld createSimWorld(String name)
260     {
261         SimWorld s=null;
262         
263         try
264         {
265             // try to create the class
266             Class simworldClass=Class.forName(name);
267             
268             try
269             {
270                 // try to cast the class to a SimWorld, this will fail if the class
271                 // is not a valid Intellego SimWorld
272                 s=(SimWorld)simworldClass.newInstance();
273             }
274             catch (Exception e)
275             {
276                 MainInterface.displayMessage("Error: this class is not a valid Intellego SimWorld");
277             }
278         }
279         catch (Exception e)
280         {
281             // failed to create the class, so return null
282             MainInterface.displayMessage("Error creating class");
283             Intellego.addToLog("SimUI.createSimWorld(): Error creating class: "+name+": "+e);
284             return null;
285         }
286         
287         // return the simworld
288         return s;
289     }
290     
291     /**
292     * Allows the user to pick a robot class, and loads it into this simulation
293     */
294     public void addRobot()
295     {
296         if(world==null || display==null)
297         {
298             MainInterface.displayMessage("You must select a SimWorld and a SimDisplay first");
299         }
300         else
301         {
302             // get the name of the class from the user
303             String className=getClassName("Pick a Controller class file:","controllers");
304             
305             // check user actually selected a class name
306             if (className.equals(NO_CLASS))
307             {
308                  // do nothing
309             }
310             else
311             {
312                 // create an instance of this class
313                 Controller c=createController(className);
314                 
315                 if(c!=null)
316                 {
317                     // and add it to the system if it is not null
318                     addController(c);
319                 }
320                 else
321                 {
322                     MainInterface.displayMessage("Failed to create Controller");
323                     Intellego.addToLog("SimUI.addRobot(): Failed to create Controller with name "+className);
324                 }
325             }
326         }
327     }
328     
329     /**
330     * Gets the class name of the required class from the user 
331     * using a JFileChooser
332     *
333     * @param title the title to display in the FileChooser window
334     * @param base_dir the directory to start looking in
335     *
336     * @return the classname as string
337     */
338     public String getClassName(String title,String base_dir)
339     {
340         String className=" ";
341         
342         // pop up a file chooser dialog
343         JFileChooser chooser=new JFileChooser(new File(System.getProperties().getProperty("user.dir"),base_dir));
344         
345         // add a filename filter for class files
346         String[] extensions={".class"};
347         chooser.addChoosableFileFilter(new FileChooserFilter(extensions,"Compiled Class File"));
348         
349         chooser.setDialogTitle(title);
350         int returnVal=chooser.showOpenDialog(this);
351         
352         try
353         {
354             if (returnVal==JFileChooser.APPROVE_OPTION) // user has selected a file
355             {
356                 // get the user's selected file
357                 File file=chooser.getSelectedFile();
358                 
359                 // get the classname
360                 className=file.getName();
361     
362                 Intellego.addToLog("SimUI.getClassName(): opening file: "+className);
363                             
364                 // chop .class extension off
365                 className=className.substring(0,file.getName().length() - 6);
366                 
367                 Intellego.addToLog("SimUI.getClassName(): attempting to open class: "+className);
368                 
369                 // return the classname
370                 return (className);
371             }
372             else // user selected cancel or some other button we are going to ignore
373             {
374                 return NO_CLASS;
375             }
376         }
377         catch(Exception e)
378         {
379             return null;
380         }
381     }
382     
383     /**
384     * Dynamically loads a controller class
385     *
386     * @param name the name of the controller as a string
387     *
388     * @return the controller, or null if unsuccessful
389     */
390     public Controller createController(String name)
391     {
392         Controller c=null;
393         
394         try
395         {
396             // try to create the class
397             Class controllerClass=Class.forName(name);
398             
399             try
400             {
401                 // try to cast the class to a Controller, this will fail if the class
402                 // is not a valid Intellego Controller
403                 c=(Controller)controllerClass.newInstance();
404             }
405             catch (Exception e)
406             {
407                 MainInterface.displayMessage("Error: class is not a valid Intellego Controller class");
408                 Intellego.addToLog("SimUI.createController(): Attempt to create Intellego Controller "+name+" failed. Not a valid Controller.");
409             }
410         }
411         catch (Exception e)
412         {
413             // failed to create the class, so return null
414             MainInterface.displayMessage("Error creating class");
415             Intellego.addToLog("SimUI.createController(): Error creating class "+name+": "+e);
416             return null;
417         }
418         
419         // return the controller
420         return c;
421     }
422     
423     /**
424     * Adds a controller to this simulation
425     *
426     * @param c the controller to be added
427     */
428     public void addController(Controller c)
429     {
430         // check if controller creation was successsful
431         if(c!=null)
432         {
433             // ... if so then add this controller to the list
434             controllerList.add(c);
435             
436             // and get robot details
437             InitRobotDialog d=new InitRobotDialog();
438             
439             // create the robot
440             d.createRobot(world,c,display);
441             
442             // and repaint the display
443             repaint();
444         }
445         else // failed to create controller
446         {
447             MainInterface.displayMessage("Failed to add controller");
448             Intellego.addToLog("SimUI.addController: Failed to add controller");
449         }
450     }
451     
452     /**
453     * Starts this simulation (by starting a new thread for this instance)
454     */
455     public void startSimulation()
456     {
457         // check there is a simworld and a simdisplay open
458         if (world==null || display==null)
459         {
460             // if not display an error message
461             MainInterface.displayMessage("You must pick a SimWorld and a SimDisplay first");
462         }
463         else
464         {
465             // check we're not already running
466             if(!running)
467             {
468                 // set this sim running in a new thread
469                 running=true;
470                 
471                 Thread t=new Thread(this);
472                 t.start();
473             }
474             
475             // start up all the controllers
476             for (int i=0;i<controllerList.size();i++)
477             {
478                 Controller c=(Controller)controllerList.get(i);
479                 Thread u=new Thread(c);
480                 u.setPriority(Thread.MIN_PRIORITY);
481                 u.start();
482                 Intellego.addToLog("SimUI.run(): Started controller");
483             }
484         }
485     }
486     
487     /**
488     * Stops this simulation
489     */
490     public void stopSimulation()
491     {
492         // check there is a simworld and a simdisplay open
493         if (world==null || display==null)
494         {
495             // if not display an error message
496             MainInterface.displayMessage("No simulation to stop");
497         }
498         else
499         {
500             running=false;
501             
502             // stop all the controllers
503             for (int i=0;i<controllerList.size();i++)
504             {
505                 Controller c=(Controller)controllerList.get(i);
506                 c.halt();
507             }
508         }
509     }
510 
511 
512 
513 }
514