/** * The simjava package is a support library for discrete * event simulations. * @version 1.2, 4 August 1997 * @author Ross McNab, Fred Howell */ package eduni.simjava; import eduni.simanim.*; import java.text.NumberFormat; import java.util.Vector; import java.util.Enumeration; import java.util.NoSuchElementException; /** * This is the system class which manages the simulation. All of * the members of this class are static, so there is no need to * create an instance of this class. * @version 1.0, 4 September 1996 * @version 1.2, 14 July 1997 * @author Ross McNab, Fred Howell */ public class Sim_system { // Private data members static private Vector entities; // The current entity list static private Evqueue future; // The future event queue static private Evqueue deferred; // The deferred event queue static private double clock; // Holds the current global sim time static private boolean running; // Has the run() member been called yet static private Semaphore onestopped; static private Sim_output trcout; // The output object for trace messages static private int trc_level; static private boolean auto_trace; // Should we print auto trace messages? static private boolean animation; // Are we running as an animation applet? static private Thread simThread; static private NumberFormat nf; // // Public library interface // // Initialise system /** Initialise the system, this function does the job of a * constructor, and should be called at the start of any simulation * program. It comes in several flavours depending on what context * the simulation is running.

* This is the simplest, and should be used for standalone simulations * It sets up trace output to a file in the current directory called * `tracefile' */ static public void initialise() { initialise(new Sim_outfile(), null); } /** This version of initialise() is used by the standard animation * package eduni.simanim */ static public void initialise(Sim_anim out, Thread sim) { animation = true; initialise(((Sim_output)out), sim); } /** This version of initialise() is for experienced users who * want to use the trace output to draw a graph or an animation * as part of the application. * @param out The object to be used for trace output * @param sim The thread the simulation is running under, (set to * null if not applicable */ static public void initialise(Sim_output out, Thread sim) { System.out.println("SimJava V1.2 - Ross McNab and Fred Howell"); entities = new Vector(); future = new Evqueue(); deferred = new Evqueue(); clock = 0.0; running = false; onestopped = new Semaphore(0); trcout = out; trcout.initialise(); trc_level = 0xff; auto_trace = true; simThread = sim; // Set the default number format nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(4); nf.setMinimumFractionDigits(2); } /** Returns the number format used for generating * times in trace lines */ static public NumberFormat getNumberFormat() { return nf; } // The two standard predicates /** A standard predicate that matches any event. */ static public Sim_any_p SIM_ANY = new Sim_any_p(); /** A standard predicate that does not match any events. */ static public Sim_none_p SIM_NONE = new Sim_none_p(); // Public access methods /** Get the current simulation time. * @return The simulation time */ static public double clock() { return clock; } /** A different name for Sim_system.clock(). * @return The current simulation time */ static public double sim_clock() { return clock; } /** Get the current number of entities in the simulation * @return A count of entities */ static public int get_num_entities() { return entities.size(); } /** Get the current trace level (initially 0xff), which * controls trace output. * @return The trace level flags */ static public int get_trc_level() { return trc_level; } /** Find an entity by its id number. * @param id The entity's unique id number * @return A reference to the entity, or null if it could not be found */ static public Sim_entity get_entity(int id) { return (Sim_entity)entities.elementAt(id); } /** Find an entity by its name. * @param name The entity's name * @return A reference to the entity, or null if it could not be found */ static public Sim_entity get_entity(String name) { Enumeration e; Sim_entity ent, found = null; for (e = entities.elements(); e.hasMoreElements();) { ent = (Sim_entity)e.nextElement(); if(name.compareTo(ent.get_name()) == 0) found = ent; } if(found == null) System.out.println("Sim_system: could not find entity "+name); return found; } /** Find out an entities unique id number from its name. * @param name The entity's name * @return The entity's unique id number, or -1 if it could not be found */ static public int get_entity_id(String name) { return entities.indexOf(get_entity(name)); } /** Find the currently running entity. * @return A reference to the entity, or null if none are running */ static public Sim_entity current_ent() { return (Sim_entity)Sim_entity.currentThread(); //Sim_entity ent, found = null; //Enumeration e; //for (e = entities.elements(); e.hasMoreElements();) { // ent = (Sim_entity)e.nextElement(); // if(ent.is_running()) // found = ent; //return found; } // Public update methods /** Set the trace level flags which control trace output. * @param level The new flags */ static public void set_trc_level(int level) { trc_level = level; } /** Switch the auto trace messages on and off. * @param on If true then the messages are switched on */ static public void set_auto_trace(boolean on) { auto_trace = on; } /** Add a new entity to the simulation. * This is now done automatically in the Sim_entity constructor, * so there is no need to call this explicitly. * @param e A reference to the new entity */ static public void add(Sim_entity e) { Sim_event evt; if (running()) { /* Post an event to make this entity */ evt = new Sim_event(Sim_event.CREATE,clock,current_ent().get_id(),0,0, e); future.add(evt); } else { if (e.get_id()==-1) { // Only add once! e.set_id(entities.size()); entities.addElement(e); } } } /** Add a new entity to the simulation, when the simulation is running. * Note this is an internal method and should not be called from * user simulations. Use add() instead to add entities * on the fly. * @param e A reference to the new entity */ static synchronized void add_entity_dynamically(Sim_entity e) { e.set_id(entities.size()); if (e==null) { System.out.println("Adding null entity :("); } else { System.out.println("Adding: "+e.get_name()); } entities.addElement(e); e.start(); onestopped.p(); } /** Link the ports the ports on two entities, so that events secheduled * from one port are sent to the other. * @param ent1 The name of the first entity * @param port1 The name of the port on the first entity * @param ent2 The name of the second entity * @param port2 The name of the port on the second entity */ static public void link_ports(String ent1, String port1, String ent2, String port2) { Sim_port p1,p2; Sim_entity e1, e2; e1 = get_entity(ent1); e2 = get_entity(ent2); if (e1==null) { System.out.println("Sim_system: "+ent1+" not found"); } else if (e2==null) { System.out.println("Sim_system: "+ent2+" not found"); } else { p1 = e1.get_port(port1); p2 = e2.get_port(port2); if (p1==null) { System.out.println("Sim_system: "+port1+" not found"); } else if (p2==null) { System.out.println("Sim_system: "+port1+" not found"); } else { p1.connect(e2); p2.connect(e1); if(animation) { ((Sim_anim)trcout).link_ports(ent1, port1, ent2, port2); } } } } /** Start the simulation running. This should be called after * all the entities have been setup and added, and their ports linked * Phase 1. */ static public void run_start() { Enumeration e; Sim_entity ent; int i; running = true; // Start all the entities' threads System.out.println("Sim_system: Starting entities"); for (e = entities.elements(); e.hasMoreElements();) { ent = (Sim_entity)e.nextElement(); ent.start(); } System.out.println("Sim_system: Waiting for entities to startup"); // Wait 'till they're all up and ready for(i=0; i 0) { Sim_event first = future.pop(); process_event(first); // Check if next events are at same time... boolean trymore = (future.size()>0); while (trymore) { Sim_event next = future.top(); if (next.event_time() == first.event_time()) { process_event(future.pop()); trymore = (future.size()>0); } else trymore = false; } } else { running = false; System.out.println("Sim_system: No more future events"); } return running; } /** Stop the simulation */ static public void run_stop() { Enumeration e; Sim_entity ent; // Attempt to kiiiillll all the entity threads for (e = entities.elements(); e.hasMoreElements();) { ent = (Sim_entity)e.nextElement(); ent.poison(); } System.out.println("Exiting Sim_system.run()"); trcout.close(); } /** Start the simulation running. This should be called after * all the entities have been setup and added, and their ports linked */ static public void run() { // Now the main loop System.out.println("Sim_system: Entering main loop"); run_start(); while(run_tick()) { } run_stop(); } // // Package level methods // static boolean running() { return running; } static public Sim_output get_trcout() { return trcout; } // Entity service methods // Called by an entity just before it become non-RUNNABLE static void paused() { onestopped.v(); } static synchronized void hold(int src, double delay) { Sim_event e = new Sim_event(Sim_event.HOLD_DONE,clock+delay,src); future.add(e); ((Sim_entity)entities.elementAt(src)).set_state(Sim_entity.HOLDING); if (auto_trace) { trace(src,"sim_hold starting"); } } static synchronized void send(int src, int dest, double delay, int tag, Object data) { Sim_event e = new Sim_event(Sim_event.SEND, clock+delay, src, dest, tag, data); if (auto_trace) { trace(src, "scheduling event type "+tag+" for "+ ((Sim_entity)entities.elementAt(dest)).get_name()+ " with delay "+delay); } future.add(e); } static synchronized void wait(int src) { ((Sim_entity)entities.elementAt(src)).set_state(Sim_entity.WAITING); if (auto_trace) { trace(src,"waiting for an event"); } } static synchronized void trace(int src, String msg) { trcout.println("u:"+((Sim_entity)entities.elementAt(src)).get_name()+ " at "+clock+": "+msg); } static synchronized int waiting(int d, Sim_predicate p ) { int w = 0; Enumeration e; Sim_event ev; for (e = deferred.elements(); e.hasMoreElements();) { ev = (Sim_event)e.nextElement(); if(ev.get_dest() == d) { // trcout.println("t: in waiting, - event from "+ev.get_src()+" to "+ // ev.get_dest() + " type:"+ev.type()+" time:"+ev.event_time()); if(p.match(ev)) w++; } } return w; } static synchronized void select(int src, Sim_predicate p) { Enumeration e; Sim_event ev=null; boolean found = false; // retrieve + remove event with dest == src for (e = deferred.elements(); (e.hasMoreElements()) && !found;) { ev = (Sim_event)e.nextElement(); if(ev.get_dest() == src) { if(p.match(ev)) { deferred.removeElement(ev); found = true; } } } if(found) { ((Sim_entity)entities.elementAt(src)).set_evbuf((Sim_event)ev.clone()); if (auto_trace) { trace(src,"sim_select returning (event time was "+ev.event_time()+")"); } } else { ((Sim_entity)entities.elementAt(src)).set_evbuf(null); if (auto_trace) { trace(src,"sim_select returning (no event found)"); } } } static synchronized void cancel(int src, Sim_predicate p) { Enumeration e; Sim_event ev=null; boolean found = false; // retrieve + remove event with dest == src for (e = future.elements(); (e.hasMoreElements()) && !found;) { ev = (Sim_event)e.nextElement(); if(ev.get_src() == src) { if(p.match(ev)) { future.removeElement(ev); found = true; } } } if(found) { ((Sim_entity)entities.elementAt(src)).set_evbuf((Sim_event)ev.clone()); if (auto_trace) { trace(src,"sim_cancel returning (event time was "+ev.event_time()+")"); } } else { ((Sim_entity)entities.elementAt(src)).set_evbuf(null); if (auto_trace) { trace(src,"sim_cancel returning (no event found)"); } } } static synchronized void putback(Sim_event ev) { deferred.add(ev); } // // Private internal methods // static private void process_event(Sim_event e) { int dest, src; Sim_entity dest_ent; //System.out.println("Sim_system: Processing event"); // Update the system's clock if (e.event_time() < clock) { System.out.println("Sim_system: Error - past event detected!"); System.out.println("Time: "+clock+", event time: "+e.event_time()+ ", event type: "+e.get_type()); } clock = e.event_time(); // Ok now process it switch(e.get_type()) { case(Sim_event.ENULL): System.out.println("Sim_system: Error - event has a null type"); break; case(Sim_event.CREATE): Sim_entity newe = (Sim_entity)e.get_data(); add_entity_dynamically(newe); break; case(Sim_event.SEND): // Check for matching wait dest = e.get_dest(); if (dest<0) System.out.println("Sim_system: Error - attempt to send to a null entity"); else { dest_ent = (Sim_entity)entities.elementAt(dest); if (dest_ent.get_state() == Sim_entity.WAITING) { dest_ent.set_evbuf((Sim_event)e.clone()); dest_ent.set_state(Sim_entity.RUNNABLE); } else { deferred.add(e); } } break; case(Sim_event.HOLD_DONE): src = e.get_src(); if (src<0) System.out.println("Sim_system: Error - NULL entity holding"); else ((Sim_entity)entities.elementAt(src)).set_state(Sim_entity.RUNNABLE); break; } } }