001/* A clock source for sequence-capable domains.
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.TypedAtomicActor;
031import ptolemy.actor.TypedIOPort;
032import ptolemy.actor.util.Time;
033import ptolemy.data.ArrayToken;
034import ptolemy.data.DoubleToken;
035import ptolemy.data.IntToken;
036import ptolemy.data.Token;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.ArrayType;
039import ptolemy.data.type.BaseType;
040import ptolemy.kernel.CompositeEntity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.InternalErrorException;
044import ptolemy.kernel.util.NameDuplicationException;
045import ptolemy.kernel.util.Workspace;
046
047///////////////////////////////////////////////////////////////////
048//// SequentialClock
049
050/**
051 A clock source for sequence-capable domains.  This actor is considerably
052 simpler than the Clock actor.  On each firing, it produces the next value
053 from its <i>values</i> parameter, and schedules another firing at
054 a future time determined by the <i>offsets</i> and <i>period</i> parameters.
055 <p>
056 This actor can be used in the DE domain
057 to generate a sequence of events at regularly spaced
058 intervals.  It cannot be used in CT, because CT will invoke it at times
059 where it has not requested a firing, and it will inappropriately advance
060 to the next output value.
061 <p>
062 At the beginning of each time interval of length given by <i>period</i>,
063 it initiates a sequence of output events with values given by
064 <i>values</i> and offset into the period given by <i>offsets</i>.
065 These parameters contain arrays, which are required to have the same length.
066 The <i>offsets</i> array must be nondecreasing and nonnegative,
067 or an exception will be thrown when it is set.
068 Moreover, its largest entry must be smaller than <i>period</i>
069 or an exception will be thrown by the fire() method.
070 <p>
071 The <i>values</i> parameter by default
072 contains an array of IntTokens with values 1 and 0.  The default
073 <i>offsets</i> array is {0.0, 1.0}.  Thus, the default output will be
074 alternating 1 and 0 with 50% duty cycle.  The default period
075 is 2.0.
076 <p>
077 The actor uses the fireAt() method of the director to request
078 firing at the beginning of each period plus each of the offsets.
079 It assumes that all of its firings are in response to such
080 requests.
081 <p>
082 The type of the output can be any token type. This type is inferred from the
083 element type of the <i>values</i> parameter.
084 <p>
085 This actor is a timed source; the untimed version is Pulse.
086
087 @author Edward A. Lee
088 @version $Id$
089 @since Ptolemy II 1.0
090 @Pt.ProposedRating Yellow (eal)
091 @Pt.AcceptedRating Red (yuhong)
092 @deprecated Use Clock instead.
093 */
094@Deprecated
095public class SequentialClock extends TypedAtomicActor implements SequenceActor {
096    // NOTE: This cannot extend Source, because it doesn't have a trigger
097    // input.  This is too bad, since it results in a lot of duplicated
098    // code with Clock.
099
100    /** Construct an actor with the specified container and name.
101     *  @param container The container.
102     *  @param name The name of this actor.
103     *  @exception IllegalActionException If the entity cannot be contained
104     *   by the proposed container.
105     *  @exception NameDuplicationException If the container already has an
106     *   actor with this name.
107     */
108    public SequentialClock(CompositeEntity container, String name)
109            throws NameDuplicationException, IllegalActionException {
110        super(container, name);
111
112        output = new TypedIOPort(this, "output", false, true);
113
114        period = new Parameter(this, "period", new DoubleToken(2.0));
115        period.setTypeEquals(BaseType.DOUBLE);
116
117        offsets = new Parameter(this, "offsets");
118        offsets.setExpression("{0.0, 1.0}");
119        offsets.setTypeEquals(new ArrayType(BaseType.DOUBLE));
120
121        // Call this so that we don't have to copy its code here...
122        attributeChanged(offsets);
123
124        // set the values parameter
125        IntToken[] defaultValues = new IntToken[2];
126        defaultValues[0] = new IntToken(1);
127        defaultValues[1] = new IntToken(0);
128
129        ArrayToken defaultValueToken = new ArrayToken(BaseType.INT,
130                defaultValues);
131        values = new Parameter(this, "values", defaultValueToken);
132
133        // set type constraint
134        output.setTypeAtLeast(ArrayType.elementType(values));
135
136        // Call this so that we don't have to copy its code here...
137        attributeChanged(values);
138    }
139
140    ///////////////////////////////////////////////////////////////////
141    ////                     ports and parameters                  ////
142
143    /** The output port.  The type of this port is determined by from
144     *  the <i>values</i> parameter.
145     */
146    public TypedIOPort output = null;
147
148    /** The offsets at which the specified values will be produced.
149     *  This parameter must contain an array of doubles, and it defaults
150     *  to {0.0, 1.0}.
151     */
152    public Parameter offsets;
153
154    /** The period of the output waveform.
155     *  This parameter must contain a DoubleToken, and defaults to 2.0.
156     */
157    public Parameter period;
158
159    /** The values that will be produced at the specified offsets.
160     *  This parameter must contain an ArrayToken, and defaults to {1, 0}.
161     */
162    public Parameter values;
163
164    ///////////////////////////////////////////////////////////////////
165    ////                         public methods                    ////
166
167    /** If the argument is the <i>offsets</i> parameter, check that the
168     *  array is nondecreasing and has the right dimension; if the
169     *  argument is <i>period</i>, check that it is positive. Other
170     *  sanity checks with <i>period</i> and <i>values</i> are done in
171     *  the fire() method.
172     *  @param attribute The attribute that changed.
173     *  @exception IllegalActionException If the offsets array is not
174     *   nondecreasing and nonnegative, or it is not a row vector.
175     */
176    @Override
177    public void attributeChanged(Attribute attribute)
178            throws IllegalActionException {
179        if (attribute == offsets) {
180            ArrayToken offsetsValue = (ArrayToken) offsets.getToken();
181            _offsets = new double[offsetsValue.length()];
182
183            double previous = 0.0;
184
185            for (int i = 0; i < offsetsValue.length(); i++) {
186                _offsets[i] = ((DoubleToken) offsetsValue.getElement(i))
187                        .doubleValue();
188
189                // Check nondecreasing property.
190                if (_offsets[i] < previous) {
191                    throw new IllegalActionException(this,
192                            "Value of offsets is not nondecreasing "
193                                    + "and nonnegative.");
194                }
195
196                previous = _offsets[i];
197            }
198        } else if (attribute == period) {
199            double periodValue = ((DoubleToken) period.getToken())
200                    .doubleValue();
201
202            if (periodValue <= 0.0) {
203                throw new IllegalActionException(this,
204                        "Period is required to be positive.  "
205                                + "Period given: " + periodValue);
206            }
207        } else {
208            super.attributeChanged(attribute);
209        }
210    }
211
212    /** Clone the actor into the specified workspace. This calls the
213     *  base class and then sets the parameter public members to refer
214     *  to the parameters of the new actor.
215     *  @param workspace The workspace for the new object.
216     *  @return A new actor.
217     *  @exception CloneNotSupportedException If a derived class contains
218     *   an attribute that cannot be cloned.
219     */
220    @Override
221    public Object clone(Workspace workspace) throws CloneNotSupportedException {
222        SequentialClock newObject = (SequentialClock) super.clone(workspace);
223        try {
224            newObject.output
225                    .setTypeAtLeast(ArrayType.elementType(newObject.values));
226        } catch (IllegalActionException e) {
227            throw new InternalErrorException(e);
228        }
229        return newObject;
230    }
231
232    /** Output the current value of the clock.
233     *  @exception IllegalActionException If the <i>values</i> and
234     *   <i>offsets</i> parameters do not have the same length, or if
235     *   the value in the offsets parameter is encountered that is greater
236     *   than the period, or if there is no director.
237     */
238    @Override
239    public void fire() throws IllegalActionException {
240        super.fire();
241        output.send(0, _currentValue);
242    }
243
244    /** Schedule the first firing and initialize local variables.
245     *  @exception IllegalActionException If the parent class throws it,
246     *   or if the <i>values</i> parameter is not a row vector, or if the
247     *   fireAt() method of the director throws it, or if the director does not
248     *   agree to fire the actor at the specified time.
249     */
250    @Override
251    public synchronized void initialize() throws IllegalActionException {
252        super.initialize();
253
254        _firstFiring = true;
255        _phase = 0;
256
257        // Note: SequentialClock getCurrentTime() calls don't have to rewritten
258        // for DT because this actor is a pure source without any trigger.
259        // All calls to getCurrentTime will return the global time of
260        // the system.
261        // Schedule the first firing.
262        Time currentTime = getDirector().getModelTime();
263        Time nextFiringTime = currentTime.add(_offsets[0]);
264
265        // NOTE: This must be the last line, because it could result
266        // in an immediate iteration.
267        _fireAt(nextFiringTime);
268    }
269
270    /** Update the state of the actor and schedule the next firing,
271     *  if appropriate.
272     *  @exception IllegalActionException If the director throws it when
273     *   scheduling the next firing, or if the length of the values and
274     *   offsets parameters don't match.
275     */
276    @Override
277    public boolean postfire() throws IllegalActionException {
278        if (!super.postfire()) {
279            return false;
280        }
281        double periodValue = ((DoubleToken) period.getToken()).doubleValue();
282
283        // Set the cycle start time here rather than in initialize
284        // so that we at least start out well aligned.
285        if (_firstFiring) {
286            _cycleStartTime = getDirector().getModelTime();
287            _firstFiring = false;
288        }
289
290        // Increment to the next phase.
291        _phase++;
292
293        if (_phase >= _offsets.length) {
294            _phase = 0;
295            _cycleStartTime = _cycleStartTime.add(periodValue);
296        }
297
298        if (_offsets[_phase] >= periodValue) {
299            throw new IllegalActionException(this,
300                    "Offset number " + _phase + " with value "
301                            + _offsets[_phase] + " must be less than the "
302                            + "period, which is " + periodValue);
303        }
304
305        Time nextIterationTime = _cycleStartTime.add(_offsets[_phase]);
306        _fireAt(nextIterationTime);
307
308        return true;
309    }
310
311    /** Set the current value.
312     *  @return True.
313     *  @exception IllegalActionException If there is no director.
314     */
315    @Override
316    public boolean prefire() throws IllegalActionException {
317        if (!super.prefire()) {
318            return false;
319        }
320        ArrayToken val = (ArrayToken) values.getToken();
321
322        if (val == null || val.length() <= _phase) {
323            throw new IllegalActionException(this,
324                    "Offsets and values parameters lengths do not match.");
325        }
326
327        _currentValue = val.getElement(_phase);
328        return true;
329    }
330
331    ///////////////////////////////////////////////////////////////////
332    ////                         private variables                 ////
333    // The following are all transient because they need not be cloned.
334    // Either the clone method or the initialize() method sets them.
335    // The current value of the clock output.
336    private transient Token _currentValue;
337
338    // The most recent cycle start time.
339    private transient Time _cycleStartTime;
340
341    // Indicator of the first firing cycle.
342    private boolean _firstFiring = true;
343
344    // Cache of offsets array value.
345    private transient double[] _offsets;
346
347    // The phase of the next output.
348    private transient int _phase;
349}