001/* A DE event in the DE domain.
002
003 Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.domains.de.kernel;
029
030import java.util.List;
031
032import ptolemy.actor.Actor;
033import ptolemy.actor.IOPort;
034import ptolemy.actor.parameters.Priority;
035import ptolemy.actor.util.Time;
036import ptolemy.data.IntToken;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NamedObj;
039
040///////////////////////////////////////////////////////////////////
041//// DEEvent
042
043/**
044 This class defines the structure of events in the DE domain.
045 Conceptually, a DE event is a trigger that contains a tag and a reference to
046 its destination actor. The purpose of a DE event is to schedule its
047 destination actor to fire at the timestamp and microstep specified by
048 its tag.
049 <p>
050 A tag is a tuple of a timestamp and a microstep. The timestamp is the model
051 time when the event exists. The microstep defines the order of a sequence
052 of (simultaneous) events that exist at the same model time.
053 <p>
054 A DE event is associated with a destination, which is either an actor or
055 an IO port of an actor. A DE event, whose destination is an actor, is
056 called a <i>pure</i> event. A pure event does not have a destination IO
057 port. A DE event, whose destination is an IO port, is called a
058 <i>trigger</i> event. A trigger event has a destination actor, which is
059 the container of the destination IO port.
060 <p>
061 A DE event also has a depth, which is the topology information of its
062 destinations. For a pure event, the depth is that of its destination actor.
063 For a trigger event, the depth is that of its destination IO port. A larger
064 value of depth indicates a lower priority when the simulator processes
065 events with the same tag.
066 <p>
067 Two DE events can be compared to see which one happens first. The order
068 is defined by the relationship between their time stamps, microsteps, and
069 depths. See {@link DEEventQueue} for more details. DE events can be compared
070 by using the compareTo() method.
071 <p>
072 This class is final to improve the simulation performance because new
073 events get created and discarded through the whole simulation.
074
075 @author Lukito Muliadi, Edward A. Lee, Haiyang Zheng, Contributor: Christopher Brooks
076 @version $Id$
077 @since Ptolemy II 0.2
078 @Pt.ProposedRating Green (hyzheng)
079 @Pt.AcceptedRating Green (hyzheng)
080 */
081public class DEEvent implements Comparable {
082    /** Construct a pure event with the specified destination actor,
083     *  timestamp, microstep, and depth.
084     *  @param actor The destination actor
085     *  @param timeStamp The time when the event occurs.
086     *  @param microstep The phase of execution within a fixed time.
087     *  @param depth The topological depth of the destination actor.
088     *  @exception IllegalActionException If the actor has a priority parameter,
089     *  but its value cannot be obtained, which should be an integer.
090     */
091    public DEEvent(Actor actor, Time timeStamp, int microstep, int depth)
092            throws IllegalActionException {
093        this(actor, null, timeStamp, microstep, depth);
094    }
095
096    /** Construct a trigger event with the specified destination IO port,
097     *  timestamp, microstep, and depth.
098     *  @param ioPort The destination IO port.
099     *  @param timeStamp The time when the event occurs.
100     *  @param microstep The phase of execution within a fixed time.
101     *  @param depth The topological depth of the destination IO Port.
102     *  @exception IllegalActionException If the actor has a priority parameter,
103     *  but its value cannot be obtained, which should be an integer.
104     */
105    public DEEvent(IOPort ioPort, Time timeStamp, int microstep, int depth)
106            throws IllegalActionException {
107        this(ioPort == null ? null : (Actor) ioPort.getContainer(), ioPort,
108                timeStamp, microstep, depth);
109    }
110
111    /** Return the destination actor for this event.
112     *  @return The destination actor.
113     */
114    public final Actor actor() {
115        return _actor;
116    }
117
118    /** Compare the tag and depth of this event with those of the argument
119     *  event for the order. Return -1, 0, or 1 if this event happens
120     *  earlier than, the same time as, or later than the argument event.
121     *  <p>
122     *  Their timestamps are compared first. If the two timestamps are not
123     *  the same, their order defines the events' order. Otherwise, the
124     *  microsteps of events are compared for the order, where an event with
125     *  the smaller microstep happens earlier. If the events have the same
126     *  microstep, their depths are compared. The event with a smaller depth
127     *  happens earlier. If the two events have the same tag and depth, then
128     *  they happen at the same time.
129     *
130     *  @param event The event to compare against.
131     *  @return -1, 0, or 1, depends on the order of the events.
132     */
133    public final int compareTo(DEEvent event) {
134        // Directly use the private fields and avoid method call overhead.
135        if (_timestamp.compareTo(event.timeStamp()) > 0) {
136            return 1;
137        } else if (_timestamp.compareTo(event.timeStamp()) < 0) {
138            return -1;
139        } else if (_microstep > event.microstep()) {
140            return 1;
141        } else if (_microstep < event.microstep()) {
142            return -1;
143        } else if (_depth > event.depth()) {
144            return 1;
145        } else if (_depth < event.depth()) {
146            return -1;
147        } else if (_priority < event._priority) {
148            return -1;
149        } else if (_priority > event._priority) {
150            return 1;
151        } else {
152            return 0;
153        }
154    }
155
156    /** Compare this event with the argument event for an order.
157     *  See {@link #compareTo(DEEvent event)} for the comparison rules.
158     *  The argument event has to be an instance of DEEvent. Otherwise, a
159     *  ClassCastException will be thrown.
160     *
161     *  @param event The event to compare against.
162     *  @return -1, 0, or 1, depending on the order of the events.
163     *  @exception ClassCastException If the argument event is not an instance
164     *  of DEEvent.
165     */
166    @Override
167    public final int compareTo(Object event) {
168        return compareTo((DEEvent) event);
169    }
170
171    /** Return the depth of this event. For a pure event, it is the depth of
172     *  the destination actor in the topological sort. For a trigger event, it
173     *  is the depth of the destination IO port.
174     *  @return The depth of this event.
175     */
176    public final int depth() {
177        return _depth;
178    }
179
180    /** Indicate whether some other object is equal to this DE Event.
181     *  DEEvents are equal if they are associated with the same actors
182     *  and compareTo() returns 0;
183     *  @param object The object with which to compare.
184     *  @return true if the object is a DEEvent and the fields of
185     *  the object and of this object are equal.
186     *  @see #hashCode()
187     */
188    @Override
189    public boolean equals(Object object) {
190        if (!(object instanceof DEEvent)) {
191            return false;
192        }
193        return compareTo(object) == 0 && ((DEEvent) object).actor() == _actor;
194    }
195
196    /** Return true if this event has the same tag with the specified one,
197     *  and their depths are the same.
198     *  @param event The event to compare against.
199     *  @return True if this event has the same tag with the specified one,
200     *  and their depths are the same.
201     */
202    public final boolean hasTheSameTagAndDepthAs(DEEvent event) {
203        return hasTheSameTagAs(event) && _depth == event.depth();
204    }
205
206    /** Return true if this event has the same tag as the argument DE event.
207     *  @param event The DE event to compare against.
208     *  @return True if this event has the same tag as the specified one.
209     */
210    public boolean hasTheSameTagAs(DEEvent event) {
211        return _timestamp.equals(event.timeStamp())
212                && _microstep == event.microstep();
213    }
214
215    /** Return the hash code for the event object.
216     *  @return The hash code for the event object.
217     *  @see #equals(Object)
218     */
219    @Override
220    public int hashCode() {
221        int primitiveFieldHash = _depth >>> _microstep;
222        int objectFieldHash = (_actor != null ? _actor.hashCode()
223                : 1) >>> (_ioPort != null ? _ioPort.hashCode() : 1);
224        // If the exclusive or of the primitive is 0, then just
225        // return the xor of the hashes of the actor and ioport
226        if (primitiveFieldHash == 0) {
227            return objectFieldHash;
228        }
229        return primitiveFieldHash >>> objectFieldHash;
230
231    }
232
233    /** Return the destination IO port of this event. Note that
234     *  for a pure event, the destination IO Port is null.
235     *  @return The destination ioPort.
236     */
237    public final IOPort ioPort() {
238        return _ioPort;
239    }
240
241    /** Return the microstep of this event.
242     *  @return The microstep of this event.
243     */
244    public final int microstep() {
245        return _microstep;
246    }
247
248    /** Return the timestamp.
249     *  @return The timestamp.
250     */
251    public final Time timeStamp() {
252        return _timestamp;
253    }
254
255    /** Return a description of the event, including the the tag, depth,
256     *  and destination information.
257     *  @return The token as a string with the time stamp.
258     */
259    @Override
260    public String toString() {
261        String name = "null";
262        if (_actor != null) {
263            name = ((NamedObj) _actor).getFullName();
264        }
265        if (_ioPort != null) {
266            return "DEEvent(time = " + _timestamp + ", microstep = "
267                    + _microstep + ", depth = " + _depth + ", dest = " + name
268                    + "." + _ioPort.getName() + ").";
269        } else {
270            return "DEEvent(time = " + _timestamp + ", microstep = "
271                    + _microstep + ", depth = " + _depth + ", dest = " + name
272                    + ")" + " -- A PURE EVENT.";
273        }
274    }
275
276    /** Update the depth of this event if the new depth is no less than
277     *  0. Otherwise, do nothing.
278     *  @param newDepth The new depth for this event.
279     */
280    void _updateDepth(int newDepth) {
281        // This method is package protected because the class is
282        // final and thus making this protected makes little sense.
283        // DEDirector calls _updateDepth in two locations
284        if (_depth >= 0) {
285            _depth = newDepth;
286        }
287    }
288
289    ///////////////////////////////////////////////////////////////////
290    ////                         protected variables               ////
291
292    /** The destination actor. */
293    protected Actor _actor;
294
295    /** The depth of this event. */
296    protected int _depth;
297
298    /** The destination IO port. */
299    protected IOPort _ioPort;
300
301    /** The microstep of this event. */
302    protected int _microstep;
303
304    /** The priority of the event (used when the timestamp, depth and
305     *  microstep cannot resolve a conflict.
306     */
307    protected int _priority;
308
309    /** The timestamp of the event. */
310    protected Time _timestamp;
311
312    ///////////////////////////////////////////////////////////////////
313    ////                         package private   methods         ////
314
315    /** Construct a pure event with the specified destination actor, IO port
316     *  timestamp, microstep, and depth.
317     *  @param actor The destination actor
318     *  @param ioPort The destination IO port.
319     *  @param timeStamp The time when the event occurs.
320     *  @param microstep The phase of execution within a fixed time.
321     *  @param depth The topological depth of the destination actor or the IO
322     *  port (if not null).
323     *  @exception IllegalActionException If the actor has a priority parameter,
324     *  but its value cannot be obtained, which should be an integer.
325     */
326    private DEEvent(Actor actor, IOPort ioPort, Time timeStamp, int microstep,
327            int depth) throws IllegalActionException {
328        _actor = actor;
329        _ioPort = ioPort;
330        _timestamp = timeStamp;
331        _microstep = microstep;
332        _depth = depth;
333        _priority = 0;
334        if (_actor != null) {
335            List<Priority> priorityList = ((NamedObj) _actor)
336                    .attributeList(Priority.class);
337            if (!priorityList.isEmpty()) {
338                Priority priority = priorityList.get(0);
339                _priority = ((IntToken) priority.getToken()).intValue();
340            }
341        }
342    }
343
344}