001/* An actor that delays the input by the specified amount.
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.lib;
029
030import ptolemy.actor.Director;
031import ptolemy.actor.util.CalendarQueue;
032import ptolemy.actor.util.Time;
033import ptolemy.actor.util.TimedEvent;
034import ptolemy.data.DoubleToken;
035import ptolemy.data.Token;
036import ptolemy.data.expr.Parameter;
037import ptolemy.data.type.BaseType;
038import ptolemy.kernel.CompositeEntity;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Workspace;
043
044///////////////////////////////////////////////////////////////////
045//// TimedDelay
046
047/**
048 This actor delays the input by a specified amount of time. The amount
049 of the time is required to be non-negative and has a default value 1.0.
050 The input and output types are unconstrained, except that the output type
051 must be the same as that of the input.
052 <p>
053 This actor keeps a local FIFO queue to store all received but not processed
054 inputs. The behavior of this actor on each firing is to read a token from
055 the input, if there is one, store it into the local queue, and output the
056 previously received token that is scheduled to be produced at the current
057 time.
058 <p> During the postfire() method, this actor schedules itself to fire again
059 to produce the just received token on the corresponding output channel after
060 the appropriate time delay. Note that if the value of delay is 0.0, the
061 actor schedules itself to fire at the current model time.
062 <p>
063 Occasionally, this actor is useful with the
064 delay parameter set to 0.0.  The time stamp of the output will
065 equal that of the input, but there is a "microstep" delay.
066 The discrete-event domain in Ptolemy II has a "super dense" model
067 of time, meaning that a signal from one actor to another can
068 contain multiple events with the same time stamp. These events
069 are "simultaneous," but nonetheless
070 have a well-defined sequential ordering determined by the order
071 in which they are produced.
072 If \textit{delay} is 0.0, then the fire() method of this actor
073 always produces on its output port the event consumed in the
074 \textit{previous iteration} with the same time stamp, if there
075 was one. If there wasn't such a previous iteration, then it
076 produces no output.  Its postfire() method consumes and
077 records the input for use in the next iteration, if there
078 is such an input, and also requests a refiring at the current
079 time.  This refire request triggers the next iteration (at
080 the same time stamp), on which the output is produced.
081 <p>
082 A consequence of this strategy is that this actor is
083 able to produce an output (or assert that there is no output) before the
084 input with the same time is known.   Hence, it can be used to break
085 causality loops in feedback systems. The DE director will leverage this when
086 determining the precedences of the actors. It is sometimes useful to think
087 of this zero-valued delay as an infinitesimal delay.
088
089 @deprecated Use actor.lib.TimeDelay.
090 @see ptolemy.domains.de.lib.VariableDelay
091 @see ptolemy.domains.de.lib.Server
092 @author Edward A. Lee, Lukito Muliadi, Haiyang Zheng
093 @version $Id$
094 @since Ptolemy II 1.0
095 @Pt.ProposedRating Green (hyzheng)
096 @Pt.AcceptedRating Yellow (hyzheng)
097 */
098@Deprecated
099public class TimedDelay extends DETransformer {
100    /** Construct an actor with the specified container and name.
101     *  Constrain that the output type to be the same as the input type.
102     *  @param container The composite entity to contain this one.
103     *  @param name The name of this actor.
104     *  @exception IllegalActionException If the entity cannot be contained
105     *   by the proposed container.
106     *  @exception NameDuplicationException If the container already has an
107     *   actor with this name.
108     */
109    public TimedDelay(CompositeEntity container, String name)
110            throws NameDuplicationException, IllegalActionException {
111        super(container, name);
112
113        // NOTE: The _init method is used to allow classes that extend
114        // this class to reconfig their settings. This may not be a
115        // good pattern.
116        _init();
117        output.setTypeSameAs(input);
118    }
119
120    ///////////////////////////////////////////////////////////////////
121    ////                       ports and parameters                ////
122
123    /** The amount of delay. The default for this parameter is 1.0.
124     *  This parameter must contain a DoubleToken
125     *  with a non-negative value, or an exception will be thrown when
126     *  it is set.
127     */
128    public Parameter delay;
129
130    ///////////////////////////////////////////////////////////////////
131    ////                         public methods                    ////
132
133    /** If the attribute is <i>delay</i>, then ensure that the value
134     *  is non-negative.
135     *  <p>NOTE: the newDelay may be 0.0, which may change the causality
136     *  property of the model. We leave the model designers to decide
137     *  whether the zero delay is really what they want.
138     *  @param attribute The attribute that changed.
139     *  @exception IllegalActionException If the delay is negative.
140     */
141    @Override
142    public void attributeChanged(Attribute attribute)
143            throws IllegalActionException {
144        if (attribute == delay) {
145            double newDelay = ((DoubleToken) delay.getToken()).doubleValue();
146
147            if (newDelay < 0.0) {
148                throw new IllegalActionException(this,
149                        "Cannot have negative delay: " + newDelay);
150            } else {
151                _delay = newDelay;
152            }
153        } else {
154            super.attributeChanged(attribute);
155        }
156    }
157
158    /** Clone the actor into the specified workspace. Set a type
159     *  constraint that the output type is the same as the that of input.
160     *  @param workspace The workspace for the new object.
161     *  @return A new actor.
162     *  @exception CloneNotSupportedException If a derived class has
163     *   has an attribute that cannot be cloned.
164     */
165    @Override
166    public Object clone(Workspace workspace) throws CloneNotSupportedException {
167        TimedDelay newObject = (TimedDelay) super.clone(workspace);
168        newObject.output.setTypeSameAs(newObject.input);
169        return newObject;
170    }
171
172    /** Declare that the <i>output</i>
173     *  does not depend on the <i>input</i> in a firing.
174     *  @exception IllegalActionException If the causality interface
175     *  cannot be computed.
176     *  @see #getCausalityInterface()
177     */
178    @Override
179    public void declareDelayDependency() throws IllegalActionException {
180        _declareDelayDependency(input, output, _delay);
181    }
182
183    /** Read one token from the input. Send out a token that is scheduled
184     *  to produce at the current time to the output.
185     *  @exception IllegalActionException If there is no director, or the
186     *  input can not be read, or the output can not be sent.
187     */
188    @Override
189    public void fire() throws IllegalActionException {
190        super.fire();
191
192        // consume input
193        if (input.hasToken(0)) {
194            _currentInput = input.get(0);
195        } else {
196            _currentInput = null;
197        }
198
199        // produce output
200        // NOTE: The amount of delay may be zero.
201        // In this case, if there is already some token scheduled to
202        // be produced at the current time before the current input
203        // arrives, that token is produced. While the current input
204        // is delayed to the next available firing at the current time.
205        Time currentTime = getDirector().getModelTime();
206        _currentOutput = null;
207
208        if (_delayedOutputTokens.size() > 0) {
209            TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get();
210            Time eventTime = earliestEvent.timeStamp;
211
212            if (eventTime.equals(currentTime)) {
213                _currentOutput = (Token) earliestEvent.contents;
214                output.send(0, _currentOutput);
215            }
216        }
217    }
218
219    /** Initialize the states of this actor.
220     *  @exception IllegalActionException If a derived class throws it.
221     */
222    @Override
223    public void initialize() throws IllegalActionException {
224        super.initialize();
225        _currentInput = null;
226        _currentOutput = null;
227        _delayedOutputTokens = new CalendarQueue(
228                new TimedEvent.TimeComparator());
229    }
230
231    /** Process the current input if it has not been processed. Schedule
232     *  a firing to produce the earliest output token.
233     *  @exception IllegalActionException If scheduling to refire cannot
234     *  be performed or the superclass throws it.
235     */
236    @Override
237    public boolean postfire() throws IllegalActionException {
238        Time currentTime = getDirector().getModelTime();
239        Time delayToTime = currentTime.add(_delay);
240
241        // Remove the token that is sent at the current time.
242        if (_delayedOutputTokens.size() > 0) {
243            if (_currentOutput != null) {
244                _delayedOutputTokens.take();
245            }
246        }
247
248        // Handle the refiring of the multiple tokens
249        // that are scheduled to be produced at the same time.
250        if (_delayedOutputTokens.size() > 0) {
251            TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get();
252            Time eventTime = earliestEvent.timeStamp;
253
254            if (eventTime.equals(currentTime)) {
255                _fireAt(currentTime);
256            }
257        }
258
259        // Process the current input.
260        if (_currentInput != null) {
261            _delayedOutputTokens
262                    .put(new TimedEvent(delayToTime, _currentInput));
263            _fireAt(delayToTime);
264        }
265
266        return super.postfire();
267    }
268
269    ///////////////////////////////////////////////////////////////////
270    ////                       protected method                    ////
271
272    /** Initialize the delay parameter.
273     *  @exception IllegalActionException If delay parameter cannot be set.
274     *  @exception NameDuplicationException If there already is a parameter
275     *  named "delay".
276     */
277    protected void _init()
278            throws IllegalActionException, NameDuplicationException {
279        delay = new Parameter(this, "delay", new DoubleToken(1.0));
280        delay.setTypeEquals(BaseType.DOUBLE);
281        _delay = ((DoubleToken) delay.getToken()).doubleValue();
282    }
283
284    ///////////////////////////////////////////////////////////////////
285    ////                         protected variables               ////
286
287    /** Current input. */
288    protected Token _currentInput;
289
290    /** Current output. */
291    protected Token _currentOutput;
292
293    /** The amount of delay. */
294    protected double _delay;
295
296    /** A local event queue to store the delayed output tokens. */
297    protected CalendarQueue _delayedOutputTokens;
298
299    ///////////////////////////////////////////////////////////////////
300    ////                         protected methods                 ////
301
302    /** Request a firing of this actor at the specified time
303     *  and throw an exception if the director does not agree to
304     *  do it at the requested time. This is a convenience method
305     *  provided because many actors need it.
306     *  <p>
307     *  If the executive director is a Ptides director, use
308     *  fireAt(Actor, Time, IOPort) method because the pure event this
309     *  actor generates is always safe to process.
310     *  </p>
311     *  @param time The requested time.
312     *  @exception IllegalActionException If the director does not
313     *   agree to fire the actor at the specified time, or if there
314     *   is no director.
315     */
316    @Override
317    protected void _fireAt(Time time) throws IllegalActionException {
318        Director director = getDirector();
319        if (director == null) {
320            throw new IllegalActionException(this, "No director.");
321        }
322        Time result = null;
323        result = director.fireAt(this, time);
324        if (!result.equals(time)) {
325            throw new IllegalActionException(this,
326                    "Director is unable to fire the actor at the requested time: "
327                            + time + ". It responds it will fire it at: "
328                            + result);
329        }
330    }
331}