001/* A timer that produces an event with a time delay specified by the input.
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.util.CalendarQueue;
031import ptolemy.actor.util.CausalityInterface;
032import ptolemy.actor.util.Time;
033import ptolemy.actor.util.TimedEvent;
034import ptolemy.data.BooleanToken;
035import ptolemy.data.DoubleToken;
036import ptolemy.data.Token;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Workspace;
043
044///////////////////////////////////////////////////////////////////
045//// Timer
046
047/**
048 A timer actor delays an event with a time delay specified by its input.
049 <p>
050 When a timer actor receives an input, if the input value is
051 bigger than 0.0, the timer schedules itself to fire again some time
052 later to generate an output. The amount of delay is specified by the
053 input value. The value of output is specified by the <i>value</i>
054 parameter of this actor. If the input value is 0.0, an output is
055 produced in the next firing with a bigger microstep. If the input is
056 less than 0.0, an exception will be thrown.
057
058 <p> This actor is different from the {@link
059 ptolemy.domains.de.lib.NonInterruptibleTimer} actor because the
060 NonInterruptibleTimer actor delays the processing of a new input if
061 it has not finished processing a previous input, while the
062 Timer actor begins processing inputs immediately upon their arrival.
063
064 @author Jie Liu, Edward A. Lee, Haiyang Zheng
065 @version $Id$
066 @since Ptolemy II 1.0
067 @Pt.ProposedRating Yellow (hyzheng)
068 @Pt.AcceptedRating Yellow (hyzheng)
069 @deprecated Use VariableDelay instead, which is essentially identical,
070  or ResettableTimer for a more reasonable timer behavior.
071 */
072@Deprecated
073public class Timer extends DETransformer {
074    /** Construct an actor with the specified container and name.
075     *  Declare that the input can only receive double tokens and the output
076     *  has a data type the same as the value parameter.
077     *  @param container The container.
078     *  @param name The name of this actor.
079     *  @exception IllegalActionException If the entity cannot be contained
080     *   by the proposed container.
081     *  @exception NameDuplicationException If the container already has an
082     *   actor with this name.
083     */
084    public Timer(CompositeEntity container, String name)
085            throws NameDuplicationException, IllegalActionException {
086        super(container, name);
087        value = new Parameter(this, "value", new BooleanToken(true));
088        input.setTypeEquals(BaseType.DOUBLE);
089        output.setTypeSameAs(value);
090    }
091
092    ///////////////////////////////////////////////////////////////////
093    ////                      ports and parameters                 ////
094
095    /** The value produced at the output.  This can have any type,
096     *  and it defaults to a boolean token with value <i>true</i>.
097     */
098    public Parameter value;
099
100    ///////////////////////////////////////////////////////////////////
101    ////                         public methods                    ////
102
103    /** Clone the actor into the specified workspace. This calls the
104     *  base class, links the type of the <i>value</i> parameter
105     *  to the output and sets the data type of the input to be double.
106     *  @param workspace The workspace for the new object.
107     *  @return A new actor.
108     *  @exception CloneNotSupportedException If a derived class has
109     *   has an attribute that cannot be cloned.
110     */
111    @Override
112    public Object clone(Workspace workspace) throws CloneNotSupportedException {
113        Timer newObject = (Timer) super.clone(workspace);
114        newObject.output.setTypeSameAs(newObject.value);
115        newObject.input.setTypeEquals(BaseType.DOUBLE);
116        newObject._causalityInterface = null;
117        return newObject;
118    }
119
120    /** Declare that the <i>output</i>
121     *  does not depend on the <i>input</i> in a firing.
122     *  @exception IllegalActionException If the causality interface
123     *  cannot be computed.
124     *  @see #getCausalityInterface()
125     */
126    @Override
127    public void declareDelayDependency() throws IllegalActionException {
128        _declareDelayDependency(input, output, 0.0);
129    }
130
131    /** Read one token from the input. Send out a token that is scheduled
132     *  to produce at the current time to the output.
133     *
134     *  @exception IllegalActionException If there is no director, or can not
135     *  send or get tokens from ports.
136     */
137    @Override
138    public void fire() throws IllegalActionException {
139        super.fire();
140        _delay = -1.0;
141
142        if (input.hasToken(0)) {
143            _currentInput = input.get(0);
144
145            double delayValue = ((DoubleToken) _currentInput).doubleValue();
146
147            if (delayValue < 0) {
148                throw new IllegalActionException("Delay can not be negative.");
149            } else {
150                _delay = delayValue;
151            }
152        } else {
153            _currentInput = null;
154        }
155
156        Time currentTime = getDirector().getModelTime();
157        _currentOutput = null;
158
159        if (_delayedOutputTokens.size() > 0) {
160            TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get();
161            Time eventTime = earliestEvent.timeStamp;
162
163            if (eventTime.equals(currentTime)) {
164                _currentOutput = (Token) earliestEvent.contents;
165                output.send(0, value.getToken());
166                return;
167            } else {
168                // no tokens to be produced at the current time.
169            }
170        }
171    }
172
173    /** Initialize the internal states of this actor.
174     *  @exception IllegalActionException If a derived class throws it.
175     */
176    @Override
177    public void initialize() throws IllegalActionException {
178        super.initialize();
179        _currentInput = null;
180        _currentOutput = null;
181        _delayedOutputTokens = new CalendarQueue(
182                new TimedEvent.TimeComparator());
183    }
184
185    /** Update the internal states of this actor. If the current input
186     *  is not processed in the fire method, schedule a refiring of this
187     *  actor to produce an output in a future time,
188     * (the current model time + delay specified by the input value).
189     *
190     *  @exception IllegalActionException If scheduling to refire cannot
191     *  be performed or the superclass throws it.
192     */
193    @Override
194    public boolean postfire() throws IllegalActionException {
195        Time currentTime = getDirector().getModelTime();
196        Time delayToTime = currentTime.add(_delay);
197
198        // Remove the token that is already sent at the current time.
199        if (_delayedOutputTokens.size() > 0) {
200            if (_currentOutput != null) {
201                _delayedOutputTokens.take();
202            }
203        }
204
205        // handle the refiring of the multiple tokens
206        // that are scheduled to produce at the same time.
207        if (_delayedOutputTokens.size() > 0) {
208            TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get();
209            Time eventTime = earliestEvent.timeStamp;
210
211            if (eventTime.equals(currentTime)) {
212                _fireAt(currentTime);
213            }
214        }
215
216        // Schedule a future firing to process the current input.
217        if (_currentInput != null) {
218            _delayedOutputTokens
219                    .put(new TimedEvent(delayToTime, value.getToken()));
220            _fireAt(delayToTime);
221        }
222
223        return super.postfire();
224    }
225
226    ///////////////////////////////////////////////////////////////////
227    ////                         protected variables               ////
228
229    /** The causality interface, if it has been created. */
230    protected CausalityInterface _causalityInterface;
231
232    /** The amount of delay. */
233    protected double _delay;
234
235    /** A local queue to store the delayed tokens. */
236    protected CalendarQueue _delayedOutputTokens;
237
238    /** Current input. */
239    protected Token _currentInput;
240
241    /** Current output. */
242    protected Token _currentOutput;
243}