001/* Discrete Event (DE) domain receiver.
002
003 Copyright (c) 1998-2015 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.LinkedList;
031import java.util.List;
032
033import ptolemy.actor.AbstractReceiver;
034import ptolemy.actor.Actor;
035import ptolemy.actor.CompositeActor;
036import ptolemy.actor.Director;
037import ptolemy.actor.IOPort;
038import ptolemy.actor.NoRoomException;
039import ptolemy.actor.NoTokenException;
040import ptolemy.data.Token;
041import ptolemy.kernel.util.IllegalActionException;
042
043///////////////////////////////////////////////////////////////////
044//// DEReceiver
045
046/** An implementation of the ptolemy.actor.Receiver interface for the
047 DE domain.
048 <p>
049 The put() method stores the given token in this receiver and posts a
050 trigger event to the director. The director is responsible to dequeue that
051 trigger event and invoke the actor that contains this receiver.
052 The get() method returns the first available token from the receiver.
053 <p>
054 Before firing an actor, the director is expected to put at least one
055 token into at least one of the receivers contained by the actor.
056
057 @author Lukito Muliadi, Edward A. Lee, Jie Liu, Haiyang Zheng
058 @version $Id$
059 @since Ptolemy II 0.2
060 @Pt.ProposedRating Green (hyzheng)
061 @Pt.AcceptedRating Yellow (hyzheng)
062 */
063public class DEReceiver extends AbstractReceiver {
064    /** Construct an empty DEReceiver with no container.
065     */
066    public DEReceiver() {
067        super();
068    }
069
070    /** Construct an empty DEReceiver with the specified container.
071     *  @param container The container.
072     *  @exception IllegalActionException If the container does
073     *  not accept this receiver.
074     */
075    public DEReceiver(IOPort container) throws IllegalActionException {
076        super(container);
077    }
078
079    ///////////////////////////////////////////////////////////////////
080    ////                         public methods                    ////
081
082    /** Clear this receiver of any contained tokens.
083     */
084    @Override
085    public void clear() {
086        _tokens.clear();
087    }
088
089    /** Return a list with tokens that are currently in the receiver
090     *  available for get() or getArray(), beginning with the oldest one.
091     *  @return A list of instances of Token.
092     */
093    @Override
094    public List<Token> elementList() {
095        return _tokens;
096    }
097
098    /** Get the first token from the receiver. The token returned is one that
099     *  was put in the receiver with a timestamp equal to or earlier than
100     *  the current time. If there is no token, throw an exception. If this
101     *  receiver contains more than one event, the oldest event is removed
102     *  first. In other words, this receiver has a FIFO behavior.
103     *  @return A token.
104     *  @exception NoTokenException If there are no more tokens. This is
105     *   a runtime exception, so it need not to be declared explicitly.
106     */
107    @Override
108    public Token get() throws NoTokenException {
109        if (_tokens.isEmpty()) {
110            throw new NoTokenException(getContainer(),
111                    "No more tokens in the DE receiver.");
112        }
113
114        return (Token) _tokens.removeFirst();
115    }
116
117    /** Return true, indicating that there is always room.
118     *  @return True.
119     */
120    @Override
121    public final boolean hasRoom() {
122        return true;
123    }
124
125    /** Return true if the receiver has room for putting the given number of
126     *  tokens into it (via the put() method).
127     *  Returning true in this method should also guarantee that calling
128     *  the put() method will not result in an exception.
129     *  @param tokens An int indicating the number of spaces available.
130     *  @return True.
131     */
132    @Override
133    public boolean hasRoom(int tokens) {
134        return true;
135    }
136
137    /** Return true if there is at least one token available to the
138     *  get() method.
139     *  @return True if there are more tokens.
140     */
141    @Override
142    public boolean hasToken() {
143        return !_tokens.isEmpty();
144    }
145
146    /** Return true if there are <i>numberOfTokens</i>
147     *  tokens tokens available to the get() method.
148     *  @param numberOfTokens An int indicating how many tokens are needed.
149     *  @return True if there are numberOfTokens tokens available.
150     */
151    @Override
152    public boolean hasToken(int numberOfTokens) {
153        return _tokens.size() >= numberOfTokens;
154    }
155
156    /** Put a token into this receiver and post a trigger event to the director.
157     *  The director will be responsible to dequeue the trigger event at
158     *  the correct timestamp and microstep and invoke the corresponding actor
159     *  whose input port contains this receiver. This receiver may contain
160     *  more than one events.
161     *  @param token The token to be put, or null to put no token.
162     *  @exception IllegalActionException If cannot get the director or if
163     *   the current microstep is zero.
164     *  @exception NoRoomException Not thrown in this class.
165     */
166    @Override
167    public void put(Token token)
168            throws IllegalActionException, NoRoomException {
169        if (token == null) {
170            return;
171        }
172        DEDirector dir = _getDirector();
173        dir._enqueueTriggerEvent(getContainer());
174        _tokens.add(token);
175    }
176
177    ///////////////////////////////////////////////////////////////////
178    ////                         protected variables               ////
179
180    /** The version of the workspace of container, used for
181     *  caching by _getDirector().
182     *  Derived classes that modify the cache may need to update
183     *  this variable.
184     */
185    protected long _directorVersion = -1;
186
187    /** List for storing tokens.  Access with clear(), add(), and take(). */
188    protected LinkedList _tokens = new LinkedList();
189
190    ///////////////////////////////////////////////////////////////////
191    ////                         private methods                   ////
192
193    /** Return the director that created this receiver.
194     *  If this receiver is an inside receiver of
195     *  an output port of an opaque composite actor,
196     *  then the director will be the local director
197     *  of the container of its port. Otherwise, it's the executive
198     *  director of the container of its port.Note that
199     *  the director returned is guaranteed to be non-null.
200     *  This method is read synchronized on the workspace.
201     *  @return An instance of DEDirector.
202     *  @exception IllegalActionException If there is no container port, or
203     *   if the port has no container actor, or if the actor has no director,
204     *   or if the director is not an instance of DEDirector.
205     */
206    private DEDirector _getDirector() throws IllegalActionException {
207        IOPort port = getContainer();
208
209        if (port != null) {
210            if (_directorVersion == port.workspace().getVersion()) {
211                return _director;
212            }
213
214            // Cache is invalid.  Reconstruct it.
215            try {
216                port.workspace().getReadAccess();
217
218                Actor actor = (Actor) port.getContainer();
219
220                if (actor != null) {
221                    Director dir;
222
223                    if (!port.isInput() && actor instanceof CompositeActor
224                            && ((CompositeActor) actor).isOpaque()) {
225                        dir = actor.getDirector();
226                    } else {
227                        dir = actor.getExecutiveDirector();
228                    }
229
230                    if (dir != null) {
231                        if (dir instanceof DEDirector) {
232                            _director = (DEDirector) dir;
233                            _directorVersion = port.workspace().getVersion();
234                            return _director;
235                        } else {
236                            throw new IllegalActionException(getContainer(),
237                                    "Does not have a DEDirector.");
238                        }
239                    } else {
240                        throw new IllegalActionException(getContainer(),
241                                "No outside director found.");
242                    }
243                }
244            } finally {
245                port.workspace().doneReading();
246            }
247        }
248
249        throw new IllegalActionException(getContainer(),
250                "Does not have an IOPort as the container of the receiver.");
251    }
252
253    ///////////////////////////////////////////////////////////////////
254    ////                         private variables                 ////
255    // The director where this DEReceiver should register for De events.
256    private DEDirector _director;
257}