001/* Base class for time-based sources.
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.Director;
031import ptolemy.actor.TimedActor;
032import ptolemy.actor.util.Time;
033import ptolemy.data.BooleanToken;
034import ptolemy.data.DoubleToken;
035import ptolemy.data.expr.Parameter;
036import ptolemy.data.type.BaseType;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.util.Attribute;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.NameDuplicationException;
041
042///////////////////////////////////////////////////////////////////
043//// TimedSource
044
045/**
046 Base class for time-based sources.  A time-based source is
047 a source where the output value is a function of current time.
048 For some sequence-based domains, such as SDF, actors of this type
049 probably do not make sense because current time is not incremented.
050 This actor has a parameter, <i>stopTime</i>, that optionally controls
051 the duration for which the actor is fired. When current time reaches
052 the stopTime, postfire() returns false. This indicates
053 to the director that this actor should not be invoked again.
054 The default value of stopTime is <i>Infinity</i>, which results in postfire
055 always returning true.  In other words, this makes the lifetime
056 infinite. Derived classes must call super.postfire() for this mechanism to
057 work.
058
059 @author Edward A. Lee
060 @version $Id$
061 @since Ptolemy II 0.3
062 @Pt.ProposedRating Green (eal)
063 @Pt.AcceptedRating Green (bilung)
064 */
065public class TimedSource extends Source implements TimedActor {
066    /** Construct an actor with the given container and name.
067     *  The <i>stopTime</i> parameter is also constructed.
068     *  @param container The container.
069     *  @param name The name of this actor.
070     *  @exception IllegalActionException If the actor cannot be contained
071     *   by the proposed container.
072     *  @exception NameDuplicationException If the container already has an
073     *   actor with this name.
074     */
075    public TimedSource(CompositeEntity container, String name)
076            throws NameDuplicationException, IllegalActionException {
077        super(container, name);
078        stopTime = new Parameter(this, "stopTime");
079        stopTime.setExpression("Infinity");
080        stopTime.setTypeEquals(BaseType.DOUBLE);
081
082        stopTimeIsLocal = new Parameter(this, "stopTimeIsLocal");
083        stopTimeIsLocal.setTypeEquals(BaseType.BOOLEAN);
084        stopTimeIsLocal.setExpression("false");
085
086        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-20\" y=\"-20\" "
087                + "width=\"40\" height=\"40\" " + "style=\"fill:lightGrey\"/>\n"
088                + "<circle cx=\"0\" cy=\"0\" r=\"17\""
089                + "style=\"fill:white\"/>\n"
090                + "<line x1=\"0\" y1=\"-15\" x2=\"0\" y2=\"-13\"/>\n"
091                + "<line x1=\"0\" y1=\"14\" x2=\"0\" y2=\"16\"/>\n"
092                + "<line x1=\"-15\" y1=\"0\" x2=\"-13\" y2=\"0\"/>\n"
093                + "<line x1=\"14\" y1=\"0\" x2=\"16\" y2=\"0\"/>\n"
094                + "<line x1=\"0\" y1=\"-8\" x2=\"0\" y2=\"0\"/>\n"
095                + "<line x1=\"0\" y1=\"0\" x2=\"11.26\" y2=\"-6.5\"/>\n"
096                + "</svg>\n");
097    }
098
099    ///////////////////////////////////////////////////////////////////
100    ////                     ports and parameters                  ////
101
102    /** The time at which postfire() should return false. This is a
103     *  double that defaults to Infinity, which means that postfire()
104     *  never returns false (or at least, doesn't do so due to stopTime
105     *  having been exceeded).
106     */
107    public Parameter stopTime;
108
109    /** If true, use the local time to compare against the <i>stopTime</i>
110     *  parameter, rather than the global time. Local time may differ
111     *  from global time inside modal models and certain domains
112     *  that manipulate time. This is a boolean that defaults
113     *  to false.
114     */
115    public Parameter stopTimeIsLocal;
116
117    ///////////////////////////////////////////////////////////////////
118    ////                         public methods                    ////
119
120    /** If the <i>stopTime</i> parameter is changed and the model is
121     *  executing, then if the new value is greater
122     *  than zero and greater than the current time, then ask the director
123     *  to fire this actor at that time.  If the new value is less than
124     *  the current time, then request refiring at the current time.
125     *  @param attribute The attribute that changed.
126     *  @exception IllegalActionException If the superclass throws it.
127     */
128    @Override
129    public void attributeChanged(Attribute attribute)
130            throws IllegalActionException {
131        if (attribute == stopTime) {
132            double newStopTimeValue = ((DoubleToken) stopTime.getToken())
133                    .doubleValue();
134
135            if (_executing) {
136                Time newStopTime = new Time(getDirector(), newStopTimeValue);
137                Director director = getDirector();
138
139                if (director != null) {
140                    Time currentTime;
141                    boolean localTime = ((BooleanToken) stopTimeIsLocal
142                            .getToken()).booleanValue();
143                    if (localTime) {
144                        currentTime = director.getModelTime();
145                    } else {
146                        currentTime = director.getGlobalTime();
147                    }
148
149                    if (newStopTime.compareTo(currentTime) > 0) {
150                        // NOTE: Do not throw an exception if the director ignores this
151                        // stop time request or returns some other value of time.
152                        // postfire() will return false on the next firing after time
153                        // equals the stop time or exceeds it.
154                        director.fireAt(this, newStopTime);
155                    } else {
156                        /* Do not throw an exception here because it makes it
157                         * impossible to change the stop time after the model has run.
158                         *
159                        throw new IllegalActionException(this, "The stop time "
160                                + newStopTime
161                                + " is earlier than the current time "
162                                + currentTime);
163                         */
164                    }
165                }
166
167                _stopTime = newStopTime;
168            }
169        } else {
170            super.attributeChanged(attribute);
171        }
172    }
173
174    /** Get the stop time.
175     *  @return The stop time.
176     *  @deprecated As of Ptolemy II 4.1, replaced by
177     *  {@link #getModelStopTime}
178     */
179    @Deprecated
180    public double getStopTime() {
181        return getModelStopTime().getDoubleValue();
182    }
183
184    /** Get the stop time.
185     *  @return The stop time.
186     */
187    public Time getModelStopTime() {
188        return _stopTime;
189    }
190
191    /** Initialize the actor. Schedule a refiring of this actor at the
192     *  stop time given by the <i>stopTime</i> parameter.
193     *  @exception IllegalActionException If there is no director.
194     */
195    @Override
196    public void initialize() throws IllegalActionException {
197        super.initialize();
198
199        Director director = getDirector();
200
201        if (director == null) {
202            throw new IllegalActionException(this, "No director!");
203        }
204
205        double stopTimeValue = ((DoubleToken) stopTime.getToken())
206                .doubleValue();
207        _stopTime = new Time(getDirector(), stopTimeValue);
208
209        Time currentTime;
210        boolean localTime = ((BooleanToken) stopTimeIsLocal.getToken())
211                .booleanValue();
212        if (localTime) {
213            currentTime = director.getModelTime();
214        } else {
215            currentTime = director.getGlobalTime();
216        }
217
218        if (!_stopTime.isInfinite() && _stopTime.compareTo(currentTime) > 0) {
219            // NOTE: Do not throw an exception if the director ignores this
220            // stop time request or returns some other value of time.
221            // postfire() will return false on the next firing after time
222            // equals the stop time or exceeds it.
223            director.fireAt(this, _stopTime);
224            _executing = true;
225        }
226    }
227
228    /** Return false if the current time is greater than or equal to
229     *  the <i>stopTime</i> parameter value.
230     *  Otherwise, return true.  Derived classes should call this
231     *  at the end of their postfire() method and return its returned
232     *  value.
233     *  @exception IllegalActionException Not thrown in this base class.
234     */
235    @Override
236    public boolean postfire() throws IllegalActionException {
237        if (!super.postfire()) {
238            // Presumably, stopRequested is true.
239            return false;
240        }
241        Time currentTime;
242        boolean localTime = ((BooleanToken) stopTimeIsLocal.getToken())
243                .booleanValue();
244        if (localTime) {
245            currentTime = getDirector().getModelTime();
246        } else {
247            currentTime = getDirector().getGlobalTime();
248        }
249
250        if (currentTime.compareTo(_stopTime) >= 0) {
251            return false;
252        }
253
254        return true;
255    }
256
257    /** Return false if the current time is greater than or equal to
258     *  the <i>stopTime</i> parameter value.
259     *  Otherwise, return what the superclass returns.
260     *  @exception IllegalActionException Not thrown in this base class.
261     */
262    @Override
263    public boolean prefire() throws IllegalActionException {
264        Boolean result = super.prefire();
265        Time currentTime;
266        boolean localTime = ((BooleanToken) stopTimeIsLocal.getToken())
267                .booleanValue();
268        if (localTime) {
269            currentTime = getDirector().getModelTime();
270        } else {
271            currentTime = getDirector().getGlobalTime();
272        }
273        if (currentTime.compareTo(_stopTime) >= 0) {
274            return false;
275        }
276        return result;
277    }
278
279    /** Override the base class to reset a flag that indicates that the
280     *  model is executing. This method is invoked exactly once per execution
281     *  of an application.  None of the other action methods should be
282     *  be invoked after it.
283     *  @exception IllegalActionException Not thrown in this base class.
284     */
285    @Override
286    public void wrapup() throws IllegalActionException {
287        super.wrapup();
288        _executing = false;
289    }
290
291    ///////////////////////////////////////////////////////////////////
292    ////                         private variables                 ////
293    // Flag indicating that the model is running.
294    private boolean _executing = false;
295
296    // stop time.
297    private Time _stopTime;
298}