001/* A non-interruptible timer that produces an event with a time delay
002 specified by the input.
003
004 Copyright (c) 2004-2014 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029package ptolemy.domains.de.lib;
030
031import java.util.LinkedList;
032
033import ptolemy.actor.util.Time;
034import ptolemy.actor.util.TimedEvent;
035import ptolemy.data.DoubleToken;
036import ptolemy.data.Token;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040import ptolemy.kernel.util.NameDuplicationException;
041
042///////////////////////////////////////////////////////////////////
043//// NonInterruptibleTimer
044
045/**
046 A NonInterruptibleTimer actor works similar to the {@link Timer} actor,
047 except that if a NonInterruptibleTimer actor has not finished processing
048 the previous input, a new input has to be delayed for processing.
049 In other words, it can not be interrupted to respond new inputs. Instead,
050 the new inputs will be queued and processed in a first come first serve
051 (FCFS) fashion. This actor extends the Timer actor.
052 <p>
053 The key difference between the NonInterruptibleTimer actor and the Server
054 actor is how the service time is specified.  In the NonInterruptibleTimer
055 actor, whenever an input arrives, the value of the input token specifies
056 the service time.  This actor will guarantee that much service time to be
057 given to that input.  In the Server actor, service times for inputs ar
058 decided by the ServiceTime parameter, which may change any time during an
059 execution. In particular, how much service time an input actually gets is
060 decided the value of the ServiceTime parameter at the time the server is
061 ready to serve that input.
062
063 @see Timer
064 @author Haiyang Zheng
065 @version $Id$
066 @since Ptolemy II 4.1
067 @Pt.ProposedRating Yellow (hyzheng)
068 @Pt.AcceptedRating Red (hyzheng)
069 @deprecated Use ptolemy.actor.lib.ResettableTimer.
070 */
071@Deprecated
072public class NonInterruptibleTimer extends Timer {
073    /** Construct an actor with the specified container and name.
074     *  @param container The container.
075     *  @param name The name of this actor.
076     *  @exception IllegalActionException If the entity cannot be contained
077     *   by the proposed container.
078     *  @exception NameDuplicationException If the container already has an
079     *   actor with this name.
080     */
081    public NonInterruptibleTimer(CompositeEntity container, String name)
082            throws NameDuplicationException, IllegalActionException {
083        super(container, name);
084    }
085
086    ///////////////////////////////////////////////////////////////////
087    ////                         public methods                    ////
088
089    /** Read one token from the input. Send out a token that is scheduled
090     *  to produce at the current time to the output.
091     *
092     *  @exception IllegalActionException If the delay value is negative, or
093     *  this actor can not send tokens to ports, or this actor can not get
094     *  tokens from ports.
095     */
096    @Override
097    public void fire() throws IllegalActionException {
098        // Don't call "super.fire();" here, the parent class is an actor.
099        if (_debugging) {
100            // We usually do this in the parent class hierarchy, but since
101            // we are not calling super.fire(), we do it here.
102            _debug("NonInterruptibleTimer: Called fire()");
103        }
104        _delay = -1.0;
105
106        if (input.hasToken(0)) {
107            _currentInput = input.get(0);
108            _delayedInputTokensList.addLast(_currentInput);
109
110            double delayValue = ((DoubleToken) _currentInput).doubleValue();
111
112            if (delayValue < 0) {
113                throw new IllegalActionException("Delay can not be negative.");
114            } else {
115                _delay = delayValue;
116            }
117        } else {
118            _currentInput = null;
119        }
120
121        Time currentTime = getDirector().getModelTime();
122        _currentOutput = null;
123
124        if (_delayedOutputTokens.size() > 0) {
125            if (currentTime.compareTo(_nextTimeFree) == 0) {
126                TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens
127                        .get();
128                Time eventTime = earliestEvent.timeStamp;
129
130                if (!eventTime.equals(currentTime)) {
131                    throw new InternalErrorException("Timer time is "
132                            + "reached, but output is not available.");
133                }
134
135                _currentOutput = (Token) earliestEvent.contents;
136                output.send(0, _currentOutput);
137                return;
138            } else {
139                // no tokens to be produced at the current time.
140            }
141        }
142    }
143
144    /** Reset the states of the server to indicate that the timer is not
145     *  processing any inputs.
146     *  @exception IllegalActionException If the base class throws it.
147     */
148    @Override
149    public void initialize() throws IllegalActionException {
150        super.initialize();
151        _nextTimeFree = Time.NEGATIVE_INFINITY;
152        _delayedInputTokensList = new LinkedList();
153    }
154
155    /** If there are delayed inputs that are not processed and the timer
156     *  is not busy, begin processing the earliest input and schedule
157     *  a future firing to produce it.
158     *  @exception IllegalActionException If there is no director or can not
159     *  schedule future firings to handle delayed input events.
160     */
161    @Override
162    public boolean postfire() throws IllegalActionException {
163        Time currentTime = getDirector().getModelTime();
164
165        // Remove the current output token from _delayedTokens.
166        if (_currentOutput != null) {
167            _delayedOutputTokens.take();
168        }
169
170        // If the delayedInputTokensList is not empty and the
171        // delayedOutputTokens is empty (meaning the timer is ready to process
172        // a new input), get the first input in the delayedInputTokensList,
173        // put it into the delayedOutputTokens, and begin processing it.
174        // Schedule a refiring to produce the corresponding
175        // output at the time: current time + delay specified by the input
176        // being processed.
177        if (_delayedInputTokensList.size() != 0
178                && _delayedOutputTokens.isEmpty()) {
179            // NOTE: the input has a fixed data type as double.
180            DoubleToken delayToken = (DoubleToken) _delayedInputTokensList
181                    .removeFirst();
182            double delay = delayToken.doubleValue();
183            _nextTimeFree = currentTime.add(delay);
184            _delayedOutputTokens
185                    .put(new TimedEvent(_nextTimeFree, value.getToken()));
186            _fireAt(_nextTimeFree);
187        }
188
189        return !_stopRequested;
190    }
191
192    ///////////////////////////////////////////////////////////////////
193    ////                         private variables                 ////
194    // Next time the server becomes free.
195    private Time _nextTimeFree;
196
197    // List of delayed input tokens, whose finishing times can not be decided.
198    private LinkedList _delayedInputTokensList;
199}