/* Sim_entity.java */ package eduni.simjava; import eduni.simanim.*; import java.util.Vector; import java.util.Enumeration; /** * This class represents entities, or processes, running in the system. * To create a new type of entity, it should be extended and * a definition for the body() method given. The body() * method is called by the Sim_system and defines the behaviour of * the entity during the simulation.

* The methods with names starting with the prefix sim_ are * runtime methods, and should only be called from within the entity's * body() method. * @see Sim_system * @version 0.1, 25 June 1995 * @author Ross McNab */ public class Sim_entity extends Thread { // Private data members private String name; // The entities name private int me; // Unique id private Sim_event evbuf; // For incoming events private int state; // Our current state from list below private Semaphore restart; // Used by Sim_system to schedule us private Vector ports; // Our outgoing ports private Anim_entity aent; // Hacky Anim_entity pointer // // Public library interface // // Public constructor /** The standard constructor. * @param name The name to be associated with this entity * @return A new instance of the class Sim_entity */ public Sim_entity(String name) { this.name = name; me = -1; state = RUNNABLE; restart = new Semaphore(0); ports = new Vector(); // Now a hacky setDaemon so that if entities are the only active // threads in a simulation then the runtime will exit. // Saves having to explicitly kill still active entity threads // at the end of the simulation //this.setDaemon(true); FIXED aent = null; // Adding this to Sim_system automatically Sim_system.add(this); } // Anim constructor /** The constructor for use with the eduni.simanim animation package. * @param name The name to be associated with this entity * @param image_name The name of the gif image file for this entity's * icon, (without the .gif extension). * @param x The X co-ordinate at which the entity should be drawn * @param y The Y co-ordinate at which the entity should be drawn */ public Sim_entity(String name, String image_name, int x, int y) { this.name = name; me = -1; state = RUNNABLE; restart = new Semaphore(0); ports = new Vector(); // Adding this to Sim_system automatically Sim_system.add(this); // Now a hacky setDaemon so that if entities are the only active // threads in a simulation then the runtime will exit. // Saves having to explicitly kill still active entity threads // at the end of the simulation //this.setDaemon(true); FIXED // Now anim stuff aent = new Anim_entity(name, image_name); aent.set_position(x, y); ((Sim_anim)Sim_system.get_trcout()).add_entity(aent); } /** Make entity icon invisible */ public void set_invisible(boolean b) { if (aent!=null) { aent.set_invisible(b); } } // Public access methods /** Get the name of this entity * @return The entity's name */ public String get_name() { return name; } /** Get the unique id number assigned to this entity * @return The id number */ public int get_id() { return me; } // Search for the port that the event came from /** Search through this entity's ports, for the one which sent this event. * @param ev The event * @return A reference to the port which sent the event, or null if * it could not be found */ public Sim_port get_port(Sim_event ev) { Sim_port found = null, curr; Enumeration e; for (e = ports.elements(); e.hasMoreElements();) { curr = (Sim_port)e.nextElement(); if(ev.get_src() == curr.get_dest()) found = curr; } return found; } // Search for a port by name /** Search through this entity's ports, for one called name. * @param name The name of the port to search for * @return A reference to the port, or null if it could not be found */ public Sim_port get_port(String name) { Sim_port found = null, curr; Enumeration e; for (e = ports.elements(); e.hasMoreElements();) { curr = (Sim_port)e.nextElement(); if(name.compareTo(curr.get_pname()) == 0) found = curr; } if(found == null) System.out.println("Sim_entity: could not find port "+name+ " on entity "+this.name); return found; } // Public update methods /** Add a port to this entity. * @param port A reference to the port to add */ public void add_port(Sim_port port) { Anim_port aport; ports.addElement(port); port.set_src(this.me); if(((aport = port.get_aport()) != null) && (aent != null)) { aent.add_port(aport); } } /** Add a parameter to this entity. * Used with the eduni.simanim package for animation. * @param param A reference to the parameter to add */ public void add_param(Anim_param param) { aent.add_param(param); } // The body function which should be overidden /** The method which defines the behavior of the entity. This method * should be overidden in subclasses of Sim_entity. */ public void body() { System.out.println("Entity "+name+" has no body()."); } // Runtime methods /** Causes the entity to hold for delay units of simulation time. * @param delay The amount of time to hold */ public void sim_hold(double delay) { Sim_system.hold(me,delay); /* Pause me now */ Sim_system.paused(); restart.p(); } /** An interruptable hold. * Causes the entity to hold for delay units of simulation time. * @param delay The amount of time to hold * @param ev Returns the event if hold interrupted * @return The amount of time left on the hold (0.0 if no interruptions) */ public double sim_hold_for(double delay, Sim_event ev) { double start_t = Sim_system.sim_clock(); sim_schedule(me, delay, 9999); // Send self 'hold done' msg // Sim_event ev2 = new Sim_event(); sim_wait_for(Sim_system.SIM_ANY,ev); if (ev.get_tag() == 9999) { return 0.0; } else { // interrupted Sim_type_p p = new Sim_type_p(9999); double time_left = delay - (ev.event_time() - start_t); int success = sim_cancel(p,null); return time_left; } } /** Write a trace message. * @param level The level at which the trace should be printed, used * with Sim_system.set_trc_level() to control * what traces are printed * @param msg The message to be printed */ public void sim_trace(int level, String msg) { if((level & Sim_system.get_trc_level()) != 0) { Sim_system.trace(me, msg); } } // The schedule functions /** Send an event to another entity, by id number with data. * @param dest The unique id number of the destination entity * @param delay How long from the current simulation time the event * should be sent * @param tag An user-defined number representing the type of event. * @param data A reference to data to be sent with the event. */ public void sim_schedule(int dest, double delay, int tag, Object data) { Sim_system.send(me, dest, delay, tag, data); } /** Send an event to another entity, by id number and with no data. * @param dest The unique id number of the destination entity * @param delay How long from the current simulation time the event * should be sent * @param tag An user-defined number representing the type of event. */ public void sim_schedule(int dest, double delay, int tag){ Sim_system.send(me, dest, delay, tag, null); } /** Send an event to another entity, by a port reference with data. * @param dest A reference to the port to send the event out of * @param delay How long from the current simulation time the event * should be sent * @param tag An user-defined number representing the type of event. * @param data A reference to data to be sent with the event. */ public void sim_schedule(Sim_port dest, double delay, int tag, Object data) { Sim_system.send(me, dest.get_dest(), delay, tag, data); } /** Send an event to another entity, by a port reference with no data. * @param dest A reference to the port to send the event out of * @param delay How long from the current simulation time the event * should be sent * @param tag An user-defined number representing the type of event. */ public void sim_schedule(Sim_port dest, double delay, int tag) { Sim_system.send(me, dest.get_dest(), delay, tag, null); } /** Send an event to another entity, by a port name with data. * @param dest The name of the port to send the event out of * @param delay How long from the current simulation time the event * should be sent * @param tag An user-defined number representing the type of event. * @param data A reference to data to be sent with the event. */ public void sim_schedule(String dest, double delay, int tag, Object data) { Sim_system.send(me, get_port(dest).get_dest(), delay, tag, data); } /** Send an event to another entity, by a port name with no data. * @param dest The name of the port to send the event out of * @param delay How long from the current simulation time the event * should be sent * @param tag An user-defined number representing the type of event. */ public void sim_schedule(String dest, double delay, int tag) { Sim_system.send(me, get_port(dest).get_dest(), delay, tag, null); } /** Hold until the entity receives an event. * @param ev The event received is copied into ev if * it points to an blank event, or discarded if ev is * null */ public void sim_wait(Sim_event ev) { Sim_system.wait(me); Sim_system.paused(); restart.p(); // I think this bit is fairly non-standard and hacky: // If they passed us a null event ref then just drop the new event // Otherwise copy the new event's values into the one they passed us if((ev != null) && (evbuf != null)) ev.copy(evbuf); } /** Count how many events matching a predicate are waiting * for this entity on the deferred queue. * @param p The event selection predicate * @return The count of matching events */ public int sim_waiting(Sim_predicate p) { return Sim_system.waiting(me, p); } /** Count how many events are waiting of this entity on the deferred queue * @return The count of events */ public int sim_waiting() { return Sim_system.waiting(me, Sim_system.SIM_ANY); } /** Extract the first event waiting for this entity on the deferred * queue, matched by the predicate p. * @param p An event selection predicate * @param ev The event matched is copied into ev if * it points to a blank event, or discarded if ev is * null */ public void sim_select(Sim_predicate p, Sim_event ev) { Sim_system.select(me, p); if((ev != null) && (evbuf != null)) ev.copy(evbuf); } /** Cancel the first event waiting for this entity on the future * queue, matched by the predicate p. Returns the * number of events cancelled (0 or 1). * @param p An event selection predicate * @param ev The event matched is copied into ev if * it points to a blank event, or discarded if ev is * null */ public int sim_cancel(Sim_predicate p, Sim_event ev) { Sim_system.cancel(me, p); if((ev != null) && (evbuf != null)) ev.copy(evbuf); if (evbuf != null) { return 1;} else { return 0; } } /** Repeatedly sim_wait() until the entity receives an event * matched by a predicate, all other received events * are discarded. * @param p The event selection predicate * @param ev The event matched is copied into ev if * it points to a blank event, or discarded if ev is * null */ public void sim_wait_for(Sim_predicate p, Sim_event ev) { boolean matched = false; while(!matched) { sim_wait(ev); if (p.match(ev)) matched = true; else sim_putback(ev); } } /** Put an event back on the defered queue. * @param ev The event to reinsert */ public void sim_putback(Sim_event ev) { Sim_system.putback(ev); } /** Get the first event matching a predicate from the deferred queue, * or, if none match, wait for a matching event to arrive. * @param p The predicate to match * @param ev The event matched is copied into ev if * it points to a blank event, or discarded if ev is * null */ public void sim_get_next(Sim_predicate p, Sim_event ev) { if (sim_waiting(p) > 0) sim_select(p, ev); else sim_wait_for(p,ev); } /** Get the first event from the deferred queue waiting on the entity, * or, if there are none, wait for an event to arrive. * @param ev The event matched is copied into ev if * it points to a blank event, or discarded if ev is * null */ public void sim_get_next(Sim_event ev) { sim_get_next(Sim_system.SIM_ANY, ev); } /** Get the id of the currently running entity * @return A unique entity id number */ public int sim_current() { return this.get_id(); } /** Send on an event to an other entity, through a port. * @param ev A reference to the event to send * @param p A reference to the port through which to send */ public void send_on(Sim_event ev, Sim_port p) { sim_schedule(p.get_dest(), 0.0, ev.type(), ev.get_data()); } // // Package level methods // // Package access methods int get_state() { return state; } Sim_event get_evbuf() { return evbuf; } // The states static final int RUNNABLE = 0; static final int WAITING = 1; static final int HOLDING = 2; static final int FINISHED = 3; // Package update methods void restart() { restart.v(); } void set_going() { restart.v(); } // FIXME same as restart, both needed? void set_state(int state) { this.state = state; } void set_id(int id) { me = id; } void set_evbuf(Sim_event e) { evbuf = e; } void poison() { restart.v(); this.stop(); } /** Internal method - don't overide */ public final void run() { // Connect all our ports to their destination entities //Sim_port curr; //Enumeration e; //for (e = ports.elements(); e.hasMoreElements();) { // ((Sim_port)e.nextElement()).connect(me); //} Sim_system.paused(); // Tell the system we're up and running restart.p(); // initially we pause 'till we get the go ahead from system body(); state = FINISHED; Sim_system.paused(); } }