6. Programmer's CookbookDespite all theoretical information presented in previous chapters, the user may still be confused and not able to use J-Sim at all. This chapter should help him by giving him practical information on how to build up a simulation program efficiently in the shortest possible time. 6.1. Creating a SimulationThe simulation is usually created in the main() method of a class founded especially for this purpose. Please note that there is no need to inherit a new subclass of JSimSimulation, this class can be used directly. Start with a file called Example.java. In its main() method, we will create a simulation named "First simulation". Because some processes are to be added and activated in the future, all exceptions that could possibly be thrown out must be caught. The best way how to do it is to write just one exception handler at the end of the main() method, catching all instances of JSimException and its subclasses. In its corresponding finally block, the user must shut down the simulation. If the user forgets to do it, the program will hang up upon termination since there will be some suspended threads. Note that the class must be named Example to have the name identical with the name of the file (without the extension, of course). Also, all necessary classes from the J-Sim package must be imported. import cz.zcu.fav.kiv.jsim.*; public class Example { public static void main(String[] args) { JSimSimulation simulation = null; try { simulation = new JSimSimulation("First simulation"); // Some code will be inserted here: // ...................... } // try catch (JSimException e) { e.printStackTrace(); e.printComment(System.err); } // catch finally { simulation.shutdown(); } } // main } // ExampleTrying to compile this file now (javac Example.java) will result in an error saying that JSimException is never thrown in the body of the try block. This is just a preparation of the source code for further improvement, please wait with the compilation until some processes are added into the simulation. 6.2. Creating a ProcessSeveral necessary steps must be done in order to create a process:
Now, compile both files and run the example:
Now, it is possible to compile both files without errors, however, when the example is run, nothing happens. This is due to the fact that the process was not activated and the simulation was not asked to run. 6.3. Running the SimulationWhen a process is created, it should be activated. A process can be activated by the main program or by another process. However, if there is just one process within the simulation, or no process has been activated yet, the latter case is not possible. A process will be activated at a certain time when its activate() method is called with the required time as parameter. Example (file Example.java): process = new MyProcess("My process No 1", simulation, ... other parameters ...); process.activate(1.2345);Once a process is activated, it is able to care about its own scheduling using the hold() method; therefore, no other activation is needed. Now, attention should be turned to the simulation and its execution. Every simulation can be principally executed in two ways: a pre-programmed way and an interactive way when the simulation's execution is driven by the user. The second one will be explained as first, thanks to its enormous ease. After the point where processes are activated, a call to the simulation's runGUI() method must be inserted. The method will switch the simulation to the graphics mode and will wait until the user presses the "Quit" button. Example (file Example.java): simulation = new JSimSimulation("First simulation"); process = new MyProcess("My process No 1", simulation, ... other parameters ...); process.activate(1.2345); simulation.runGUI();The second possibility is based on calls to the step() method. Usually, it is done in a while or for cycle as shown here: while (simulation.step() == true) ;The step() method returns true if the simulation is not terminated, i.e. it can do another step. This is a potentially dangerous construction since the simulation can contain an infinite process. Such a process will make the simulation infinite too; therefore, the step() method will never return false and the program will never finish. There are two ways how to avoid this. Limit the execution either by time or by a number of steps to be executed. The time limitation can be realized in the way shown below, using the simulation's method getCurrentTime(): while ((simulation.getCurrentTime() < 100.0) && (simulation.step() == true)) ;The second way of limitation is usually realized by a for cycle which has a counter of steps executed: for (int i = 0; (i < 25) && (simulation.step() == true); i++) ;In both cases, if the simulation terminates before the condition is fulfilled, other steps are not required to be executed. 6.4. Influencing Other ProcessesJ-Sim provides two methods in class JSimProcess allowing processes to influence other processes present in the system: cancel() and activate(). The first one deletes all process' events from the calendar, the second one adds a new event to the calendar. In order to influence another process, a process needs a reference to it. Since processes have no access to other processes during their life, they must obtain this reference upon their creation and store it into a variable. That is shown in the following example (file MyProcess.java): public class MyProcess extends JSimProcess { public JSimProcess anotherProcess; public MyProcess(String name, JSimSimulation sim, JSimProcess another) throws JSimSimulationAlreadyTerminatedException, JSimInvalidParametersException, JSimTooManyProcessesException { super(name, sim); anotherProcess = another; } // constructor protected void life() { // ..... anotherProcess.cancel(); // ..... anotherProcess.activate(time); // ..... } // life } // class MyProcessThen, the process can influence another process' scheduling. However, sometimes more than one process needs to be influenced. One possible solution is to pass as many processes as needed to the constructor, for example as an array. Another solution is to add a method to the influencing process that other processes will use to set the process to be influenced. In this case, processes using the method must have a reference to the influencing process in order to be able to invoke the method. This can be arranged in the simple way described above - one parameter in the constructor. Also note that the method can be invoked from outside only when the influencing process is passive, i.e. another process (which calls the method) is active. Therefore, only one process can be influenced during one step. A simple example is shown below: public class MyProcess extends JSimProcess { public JSimProcess anotherProcess; // No changes in constructor // No changes in life() public void setAnother(JSimProcess another) { anotherProcess = another; } // setAnother } // class MyProcess 6.5. Working with QueuesBefore any queue can be created and used, a simulation must exist which will be used as a parameter of queue's constructor. All this is usually done in the main program but not necessarily. An example follows (file Example.java): public static void main(String[] args) { JSimSimulation sim = null; JSimHead queue = null; try { sim = new JSimSimulation("Simulation with queues"); queue = new JSimHead("Little queue", sim); // ... other code } // mainAs soon as the queue is created, some elements can be inserted into it. When creating them, the data they hold are passed to them as the only parameter of their constructor. In the following example, five integer numbers will be inserted into the queue already created in previous example: // ..... JSimLink element; int i; // ..... for (i = 0; i < 5; i++) { element = new JSimLink(new Integer(i)); element.into(queue); } // forOf course, the user is not limited to integer values only. Instances of any class can be inserted into the queue, for example floating-point numbers. In the following example, two floating-point numbers are inserted into the queue, one just after the first element and one just before the last one, using JSimLink's methods follow() and precede(): // ..... JSimLink before, after; // ..... after = new JSimLink(new Float(0.5)); before = new JSimLink(new Float(3.5)); after.follow(queue.first()); before.precede(queue.last());To retrieve the data back and to print it, the user must distinguish between objects of different data types. Every element's data type can be obtained using its getDataType() method. Then, the user can decide whether to retype the data obtained from getData() to Integer or to Float. The following example prints out all data present in the queue, removing them from the queue as soon as they are printed out. To remove an element from the queue, its out() method is called. Since there are not usually any other reference to the element, it is disposed automatically by JVM. After each removal, the number of elements left in the queue is printed out. // ..... String dataType; Integer intObject; Float floatObject; // ..... while (!queue.empty()) { element = queue.first(); dataType = element.getDataType(); System.out.println("The first element is of type: " + dataType); switch (dataType.charAt(dataType.lastIndexOf('.') + 1)) { case 'I': // Integer intObject = (Integer) element.getData(); System.out.println("Data: " + intObject.toString()); break; case 'F': // Float floatObject = (Float) element.getData(); System.out.println("Data: " + floatObject.toString()); break; default: System.out.println("There is an unknown object at" + " the head of the queue."); } // switch element.out(); System.out.println("Number of items in the queue: " + queue.cardinal()); } // while
|