001/* An actor that delays the input for a certain amount of real time.
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.actor.lib;
029
030import ptolemy.actor.parameters.PortParameter;
031import ptolemy.data.LongToken;
032import ptolemy.data.Token;
033import ptolemy.data.type.BaseType;
034import ptolemy.kernel.CompositeEntity;
035import ptolemy.kernel.Port;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038import ptolemy.kernel.util.StringAttribute;
039import ptolemy.kernel.util.Workspace;
040
041///////////////////////////////////////////////////////////////////
042//// Sleep
043
044/**
045 <p>On each firing, read at most one token from each input channel, sleep
046 by the specified amount of real time, and then produce the same input
047 tokens on the respective output channels. This actor calls Thread.sleep()
048 in the fire() method, so the thread that calls fire() will be suspended.
049 If fire() is called multiple times in one iteration, sleep is only called
050 the first time.
051 If the width of the output port is less than that of the input port,
052 the tokens in the extra channels are lost.
053 </p><p>
054 The effect of this actor is different in different domains.
055 In domains where all actors are iterated from within a single director
056 thread (like SDF and DE), then multiple instances of this actor will
057 result in cumulative time delays. That is, the time taken by an iteration
058 of the model will be greater than the sum of the sleep times of all the
059 instances. In domains where actors execute in their own thread (like PN
060 and CSP), only the execution of the individual actor is slowed.
061 Note that another way to slow down the execution of a model while running
062 inside vergil is to turn on animation.</p>
063
064 @author Jie Liu, Christopher Hylands, Edward A. Lee
065 @version $Id$
066 @since Ptolemy II 1.0
067
068 @Pt.ProposedRating Green (eal)
069 @Pt.AcceptedRating Red (cxh) Review _wasSleepCalledInFireYet, esp locking
070 */
071public class Sleep extends Transformer {
072    /** Construct an actor with the given container and name.
073     *  @param container The container.
074     *  @param name The name of this actor.
075     *  @exception IllegalActionException If the actor cannot be contained
076     *   by the proposed container.
077     *  @exception NameDuplicationException If the container already has an
078     *   actor with this name.
079     */
080    public Sleep(CompositeEntity container, String name)
081            throws NameDuplicationException, IllegalActionException {
082        super(container, name);
083        sleepTime = new PortParameter(this, "sleepTime");
084        sleepTime.setExpression("0L");
085        sleepTime.setTypeEquals(BaseType.LONG);
086
087        Port sleepPort = sleepTime.getPort();
088        StringAttribute sleepCardinal = new StringAttribute(sleepPort,
089                "_cardinal");
090        sleepCardinal.setExpression("SOUTH");
091
092        // Data type polymorphic, multiports.
093        input.setMultiport(true);
094        output.setMultiport(true);
095        output.setTypeAtLeast(input);
096        output.setWidthEquals(input, true);
097    }
098
099    ///////////////////////////////////////////////////////////////////
100    ////                     ports and parameters                  ////
101
102    /** The sleep time in milliseconds. This has type long and default
103     *  "0L".
104     */
105    public PortParameter sleepTime;
106
107    ///////////////////////////////////////////////////////////////////
108    ////                         public methods                    ////
109
110    /** Override the base class to set type constraints.
111     *  @param workspace The workspace for the cloned object.
112     *  @exception CloneNotSupportedException If cloned ports cannot have
113     *   as their container the cloned entity (this should not occur), or
114     *   if one of the attributes cannot be cloned.
115     *  @return A new instance of Sleep.
116     */
117    @Override
118    public Object clone(Workspace workspace) throws CloneNotSupportedException {
119        Sleep newObject = (Sleep) super.clone(workspace);
120        newObject.output.setTypeAtLeast(newObject.input);
121        newObject.output.setWidthEquals(newObject.input, true);
122        return newObject;
123    }
124
125    /** Read input tokens, call Thread.sleep(), and then
126     *  transfer tokens from inputs to outputs, at most one token from each
127     *  channel.  If fire() is called twice in a row without an
128     *  intervening call to either postfire() or prefire(), then no
129     *  sleep is performed, an inputs are copied to the output immediately.
130     *  <p>
131     *  If the width of the output port is less than
132     *  that of the input port, the tokens in the extra channels
133     *  are lost.
134     *  @exception IllegalActionException Not thrown in this base class
135     */
136    @Override
137    public void fire() throws IllegalActionException {
138        if (!_wasSleepCalledInFireYet) {
139            _wasSleepCalledInFireYet = true;
140            super.fire();
141            sleepTime.update();
142
143            int inputWidth = input.getWidth();
144            Token[] inputs = new Token[inputWidth];
145
146            for (int i = 0; i < inputWidth; i++) {
147                if (input.hasToken(i)) {
148                    inputs[i] = input.get(i);
149                }
150            }
151
152            try {
153                long sleepTimeValue = ((LongToken) sleepTime.getToken())
154                        .longValue();
155
156                if (_debugging) {
157                    _debug(getName() + ": Wait for " + sleepTimeValue
158                            + " milliseconds.");
159                }
160
161                Thread.sleep(sleepTimeValue);
162            } catch (InterruptedException e) {
163                // Ignore...
164            }
165
166            int outputWidth = output.getWidth();
167
168            for (int i = 0; i < inputWidth; i++) {
169                if (inputs[i] != null) {
170                    if (i < outputWidth) {
171                        output.send(i, inputs[i]);
172                    }
173                }
174            }
175        }
176    }
177
178    /** Reset the flag that fire() checks so that fire() only sleeps once.
179     *  @exception IllegalActionException If the base class throws it.
180     */
181    @Override
182    public void initialize() throws IllegalActionException {
183        super.initialize();
184        _wasSleepCalledInFireYet = false;
185    }
186
187    /** Reset the flag that fire() checks so that fire() only sleeps once.
188     *  @exception IllegalActionException If the parent class throws it.
189     *  @return Whatever the superclass returns (probably true).
190     */
191    @Override
192    public boolean postfire() throws IllegalActionException {
193        _wasSleepCalledInFireYet = false;
194        return super.postfire();
195    }
196
197    ///////////////////////////////////////////////////////////////////
198    ////                         private variables                 ////
199
200    /** True if sleep was called in fire().  Thread.sleep() should only
201     *   be called once in fire().
202     */
203    private boolean _wasSleepCalledInFireYet = false;
204}