001/* A clock source.
002
003 Copyright (c) 1998-2016 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.Manager;
031import ptolemy.actor.parameters.PortParameter;
032import ptolemy.actor.util.Time;
033import ptolemy.data.ArrayToken;
034import ptolemy.data.DoubleToken;
035import ptolemy.data.IntToken;
036import ptolemy.data.StringToken;
037import ptolemy.data.Token;
038import ptolemy.data.expr.Parameter;
039import ptolemy.data.type.ArrayType;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.CompositeEntity;
042import ptolemy.kernel.util.Attribute;
043import ptolemy.kernel.util.IllegalActionException;
044import ptolemy.kernel.util.InternalErrorException;
045import ptolemy.kernel.util.NameDuplicationException;
046import ptolemy.kernel.util.Settable;
047import ptolemy.kernel.util.Workspace;
048
049///////////////////////////////////////////////////////////////////
050//// Clock
051
052/**
053 This actor produces a periodic signal, a sequence of events at
054 regularly spaced intervals.
055 At the beginning of each time interval of length given by <i>period</i>,
056 starting from the time at which initialize() is invoked,
057 this actor initiates a sequence of output events with values given by
058 <i>values</i> and offset into the period given by <i>offsets</i>.
059 These parameters contain arrays, which are required to have the same length.
060 The <i>offsets</i> array contains doubles, which
061 must be nondecreasing and nonnegative,
062 or an exception will be thrown when it is set.
063 If any entry is greater than or equal to the <i>period</i>
064 then the corresponding output will never be produced.
065 <p>
066 The <i>values</i> parameter by default
067 contains an array of IntTokens with values 1 and 0.  The default
068 <i>offsets</i> array is {0.0, 1.0}.  Thus, the default output will be
069 alternating 1 and 0 with 50% duty cycle.  The default period
070 is 2.0.
071 <p>
072 The type of the output can be any token type. This type is inferred
073 from the element type of the <i>values</i> parameter.
074 <p>
075 If the <i>period</i> is changed at any time, either by
076 providing an input or by changing the parameter, then the
077 new period will take effect as soon as possible. That is,
078 if there is already a period in progress, it may be cut
079 short if the new period is shorter so that its time matches
080 the new period. But it will only be cut short if current
081 time has not passed the cycle start time plus the new period.
082 Otherwise, the period in progress will run to completion.
083 <p>
084 This actor can generate finite sequences by specifying
085 a finite <i>numberOfCycles</i>. The numberOfCycles has a default value
086 UNBOUNDED, indicating infinite length of executions. If numberOfCycles is
087 a positive number, once the specified number of cycles has been completed,
088 then this actor returns false from the postfire() method, which indicates
089 to the director that the actor should not be fired again.
090 (A cycle is "completed" each time the last event in the <i>values</i>
091 array is produced).
092 <p>
093 The actor can also generate a finite sequence by giving a finite
094 value to the <i>stopTime</i> parameter. This gives a time rather than
095 a number of cycles, and thus can be used to stop the clock in the middle
096 of a cycle, unlike <i>numberOfCycles</i>.  Just like <i>numberOfCycles</i>,
097 when the stop time is reached, the actor's postfire() method returns
098 false.
099 <p>
100 If the <i>trigger</i> input is connected, then an output will only
101 be produced if a input has been received since the last output.
102 The trigger input has no effect on the first output. After the
103 first output event, no further output event will be produced
104 until a time greater than or equal to the time at which a trigger
105 input is received. At that time, the output produced will have
106 whatever value would have been produced if the trigger input
107 was not connected. Note that this trigger is typically useful
108 in a feedback situation, where the output of the clock
109 eventually results in a trigger input. If the time-stamp
110 of that trigger input is less than the time between clock
111 events, then the clock will behave as if there were no
112 trigger input. Otherwise, it will "skip beats."
113 <p>
114 This actor can be a bit tricky to use inside a ModalModel.
115 In particular, if the actor is in a state refinement, then
116 it may "skip a beat" because of the state not being the current
117 state at the time of the beat. If this occurs, the clock will
118 simply stop firing, and will produce no further outputs.
119 To prevent this, the clock may be reinitialized
120 (by setting the <i>reset</i> flag
121 of a modal model transition). Alternatively, you can assign
122 a value to the
123 the <i>period</i> of the Clock in the <i>setActions</i>
124 of the transition. This will also have the effect of
125 waking up the clock, but with a subtle difference.
126 If you use a <i>reset</i> transition, the clock starts
127 over upon entering the destination state. If you set
128 the <i>period</i> parameter instead, then the clock
129 behaves as if it had been running all along (except
130 that its period may get changed). Thus, in the first
131 case, the output events are aligned with the time
132 of the transition, while in the second case, they
133 are aligned with the start time of the model execution.
134 <p>
135 This actor is a timed source; the untimed version is Pulse.
136
137 @author Edward A. Lee, Haiyang Zheng
138 @version $Id$
139 @since Ptolemy II 0.3
140 @deprecated Use DiscreteClock.
141 @Pt.ProposedRating Yellow (eal)
142 @Pt.AcceptedRating Yellow (yuhong)
143 */
144@Deprecated
145public class Clock extends TimedSource {
146    /** Construct an actor with the specified container and name.
147     *  @param container The container.
148     *  @param name The name of this actor.
149     *  @exception IllegalActionException If the entity cannot be contained
150     *   by the proposed container.
151     *  @exception NameDuplicationException If the container already has an
152     *   actor with this name.
153     */
154    public Clock(CompositeEntity container, String name)
155            throws NameDuplicationException, IllegalActionException {
156        super(container, name);
157
158        period = new PortParameter(this, "period");
159        period.setExpression("2.0");
160        period.setTypeEquals(BaseType.DOUBLE);
161
162        offsets = new Parameter(this, "offsets");
163        offsets.setExpression("{0.0, 1.0}");
164        offsets.setTypeEquals(new ArrayType(BaseType.DOUBLE));
165
166        // Call this so that we don't have to copy its code here...
167        attributeChanged(offsets);
168
169        // Set the values parameter.
170        values = new Parameter(this, "values");
171        values.setExpression("{1, 0}");
172
173        // Set type constraint on the output.
174        output.setTypeAtLeast(ArrayType.elementType(values));
175
176        // Call this so that we don't have to copy its code here...
177        attributeChanged(values);
178
179        // Set the numberOfCycles parameter.
180        // Create a symbolic name for the default.
181        Parameter unbounded = new Parameter(this, "UNBOUNDED");
182        unbounded.setPersistent(false);
183        unbounded.setExpression("-1");
184        unbounded.setVisibility(Settable.EXPERT);
185        numberOfCycles = new Parameter(this, "numberOfCycles");
186        numberOfCycles.setTypeEquals(BaseType.INT);
187        numberOfCycles.setExpression("UNBOUNDED");
188
189        // Set the output signal type as DISCRETE to indicate
190        // that the outputs of this actor are discrete events.
191        new Parameter(output, "signalType", new StringToken("DISCRETE"));
192
193        // Set the trigger signal type as DISCRETE.
194        new Parameter(trigger, "signalType", new StringToken("DISCRETE"));
195    }
196
197    ///////////////////////////////////////////////////////////////////
198    ////                     ports and parameters                  ////
199
200    /** The number of cycles to produce, or UNBOUNDED to specify no limit.
201     *  This is an integer with default UNBOUNDED.
202     */
203    public Parameter numberOfCycles;
204
205    /** The offsets at which the specified values will be produced.
206     *  This parameter must contain an array of doubles, and it defaults
207     *  to {0.0, 1.0}.
208     */
209    public Parameter offsets;
210
211    /** The period of the output waveform.
212     *  This is a double that defaults to 2.0.
213     */
214    public PortParameter period;
215
216    /** The values that will be produced at the specified offsets.
217     *  This parameter must contain an ArrayToken, and it defaults to
218     *  {1, 0}
219     */
220    public Parameter values;
221
222    ///////////////////////////////////////////////////////////////////
223    ////                         public methods                    ////
224
225    /** If the argument is the <i>offsets</i> parameter, check that the
226     *  array is nondecreasing and has the right dimension; if the
227     *  argument is <i>period</i>, check that it is positive. Other
228     *  sanity checks with <i>period</i> and <i>values</i> are done in
229     *  the fire() method.
230     *  @param attribute The attribute that changed.
231     *  @exception IllegalActionException If the offsets array is not
232     *   nondecreasing and nonnegative, or if the director will not
233     *   respect the fireAt() call.
234     */
235    @Override
236    public void attributeChanged(Attribute attribute)
237            throws IllegalActionException {
238        if (attribute == offsets) {
239            ArrayToken offsetsValue = (ArrayToken) offsets.getToken();
240            _offsets = new double[offsetsValue.length()];
241
242            double previous = 0.0;
243
244            for (int i = 0; i < offsetsValue.length(); i++) {
245                _offsets[i] = ((DoubleToken) offsetsValue.getElement(i))
246                        .doubleValue();
247
248                // Check nondecreasing property.
249                if (_offsets[i] < previous) {
250                    throw new IllegalActionException(this,
251                            "Value of offsets is not nondecreasing "
252                                    + "and nonnegative.");
253                }
254
255                previous = _offsets[i];
256            }
257            // Re-initialize the actor if we are running.
258            if (getManager() != null) {
259                Manager.State state = getManager().getState();
260                if (state == Manager.ITERATING || state == Manager.PAUSED) {
261                    // Reinitialize.
262                    initialize();
263                }
264            }
265        } else if (attribute == period) {
266            double periodValue = ((DoubleToken) period.getToken())
267                    .doubleValue();
268            if (_debugging) {
269                _debug("Setting period to " + periodValue);
270            }
271
272            if (periodValue <= 0.0) {
273                throw new IllegalActionException(this,
274                        "Period is required to be positive.  "
275                                + "Period given: " + periodValue);
276            }
277            // Schedule the next firing if we are running.
278            if (_initialized) {
279                // If this model has been dormant (e.g. in a ModalModel)
280                // then it needs to catch up.
281                _catchUp();
282                // The _tentativeNextOutputTime may already
283                // be in the future beyond the point where we want it
284                // with the new period. Seems kind of tricky to get the
285                // right value. Only if the _phase is zero is this an
286                // issue, since in that case, the cycleStartTime has
287                // been updated to the start of the new cycle, which
288                // is too far in the future.
289                if (_phase == 0 && _firstOutputProduced) {
290                    Time potentialNextOutputTime = _tentativeCycleStartTime
291                            .subtract(_previousPeriod).add(periodValue);
292                    if (potentialNextOutputTime
293                            .compareTo(getDirector().getModelTime()) >= 0) {
294                        _tentativeNextOutputTime = potentialNextOutputTime;
295                        _tentativeCycleStartTime = potentialNextOutputTime;
296                        // If this occurs outside fire(), e.g. in a modal
297                        // model state transition, we also need to set the _cycleStartTime
298                        // and _nextOutputTime.
299                        if (!_tentative) {
300                            _nextOutputTime = _tentativeNextOutputTime;
301                            _cycleStartTime = _tentativeCycleStartTime;
302                        }
303                    }
304                }
305                _fireAt(_tentativeNextOutputTime);
306            }
307            _previousPeriod = periodValue;
308        } else {
309            super.attributeChanged(attribute);
310        }
311    }
312
313    /** Clone the actor into the specified workspace. This calls the
314     *  base class and then sets the parameter public members to refer
315     *  to the parameters of the new actor.
316     *  @param workspace The workspace for the new object.
317     *  @return A new actor.
318     *  @exception CloneNotSupportedException If a derived class contains
319     *   an attribute that cannot be cloned.
320     */
321    @Override
322    public Object clone(Workspace workspace) throws CloneNotSupportedException {
323        Clock newObject = (Clock) super.clone(workspace);
324
325        try {
326            ArrayToken offsetsValue = (ArrayToken) offsets.getToken();
327            newObject._offsets = new double[offsetsValue.length()];
328            System.arraycopy(_offsets, 0, newObject._offsets, 0,
329                    _offsets.length);
330        } catch (IllegalActionException ex) {
331            // CloneNotSupportedException does not have a constructor
332            // that takes a cause argument, so we use initCause
333            CloneNotSupportedException throwable = new CloneNotSupportedException();
334            throwable.initCause(ex);
335            throw throwable;
336        }
337        try {
338            newObject.output
339                    .setTypeAtLeast(ArrayType.elementType(newObject.values));
340        } catch (IllegalActionException e) {
341            // Should have been caught before.
342            throw new InternalErrorException(e);
343        }
344
345        return newObject;
346    }
347
348    /** Output the current value of the clock if the time is right
349     *  and, if connected, a trigger has been received.
350     *  @exception IllegalActionException If
351     *   the value in the offsets parameter is encountered that is greater
352     *   than the period, or if there is no director.
353     */
354    @Override
355    public void fire() throws IllegalActionException {
356        // Cannot call super.fire() because it consumes
357        // trigger inputs.
358        Time currentTime = getDirector().getModelTime();
359        if (_debugging) {
360            _debug("Called fire() at time " + currentTime);
361        }
362
363        // Use the strategy pattern here so that derived classes can
364        // override how this is done.
365        _updateTentativeValues();
366
367        // This must be after the above update because it may trigger
368        // a call to attributeChanged(), which uses the tentative values.
369        // Moreover, we should set a flag so that if attributeChanged()
370        // is called, then it is notified that the change is tentative.
371        // It is tentative because the input may be tentative.
372        // We should not commit any state changes in fire().
373        try {
374            _tentative = true;
375            period.update();
376        } finally {
377            _tentative = false;
378        }
379
380        // Check the trigger input, if it is connected.
381        boolean triggerConnected = false;
382        if (trigger.numberOfSources() > 0) {
383            triggerConnected = true;
384            for (int i = 0; i < trigger.getWidth(); i++) {
385                if (trigger.isKnown(i) && trigger.hasToken(i)) {
386                    trigger.get(i);
387                    _tentativeTriggered = true;
388                }
389            }
390        }
391
392        if (_enabled) {
393            _catchUp();
394            // Produce an output only if we exactly match a phase time
395            // and, if the trigger input is connected, we have been triggered.
396            // Also make sure that if the phase is the same as the previous phase,
397            // then time has incremented.
398            if (_isTimeForOutput()) {
399                if (!triggerConnected || _tentativeTriggered) {
400                    output.send(0, _getValue(_tentativePhase));
401                }
402                // Even if we skip the output because of the lack
403                // of a trigger, we need to act as if we produced an
404                // output for the purposes of scheduling the next event.
405                _outputProduced = true;
406            }
407        }
408    }
409
410    /** Schedule the first firing and initialize local variables.
411     *  @exception IllegalActionException If the parent class throws it,
412     *   or if the <i>values</i> parameter is not a row vector, or if the
413     *   fireAt() method of the director throws it, or if the director will not
414     *   respect the fireAt() call.
415     */
416    @Override
417    public void initialize() throws IllegalActionException {
418        super.initialize();
419
420        // Start cycles at the current time.
421        // This is important in modal models that reinitialize the actor.
422        _cycleStartTime = getDirector().getModelTime();
423        _tentativeCycleStartTime = _cycleStartTime;
424        _cycleCount = 0;
425        _phase = 0;
426        _tentativePhase = _phase;
427        _nextOutputTime = _cycleStartTime.add(_offsets[_phase]);
428        _tentativeNextOutputTime = _nextOutputTime;
429
430        // Make sure the first output is enabled.
431        _firstOutputProduced = false;
432        _outputProduced = false;
433        _enabled = true;
434        _tentativeEnabled = _enabled;
435        _previousPeriod = ((DoubleToken) period.getToken()).doubleValue();
436
437        // Enable without a trigger input on the first firing.
438        _triggered = true;
439        _tentativeTriggered = _triggered;
440
441        if (_debugging) {
442            _debug("Requesting firing at time " + _nextOutputTime);
443        }
444        _fireAt(_nextOutputTime);
445
446        _initialized = true;
447    }
448
449    /** Update the state of the actor and schedule the next firing,
450     *  if appropriate.
451     *  @exception IllegalActionException If the director throws it when
452     *   scheduling the next firing.
453     */
454    @Override
455    public boolean postfire() throws IllegalActionException {
456        if (_debugging) {
457            _debug("Postfiring at " + getDirector().getModelTime());
458        }
459        _updateStates();
460
461        if (_outputProduced) {
462            _firstOutputProduced = true;
463        }
464        return super.postfire();
465    }
466
467    /** Check that the length of the <i>values</i> and
468     *  <i>offsets</i> parameters are the same.
469     *  @return True.
470     *  @exception IllegalActionException If the <i>values</i> and
471     *   <i>offsets</i> parameters do not have the same length.
472     */
473    @Override
474    public boolean prefire() throws IllegalActionException {
475        if (_debugging) {
476            _debug("Called prefire()");
477        }
478        // Check the length of the values and offsets arrays.
479        // This is done here because it cannot be done in
480        // attributeChanged(), since the two parameters are set
481        // separately, and checking in initialize() is not really
482        // sufficient, since the values of these parameters can
483        // change at run time.
484        ArrayToken val = (ArrayToken) values.getToken();
485        if (_offsets.length != val.length()) {
486            throw new IllegalActionException(this,
487                    "Values and offsets vectors do not have the same length.");
488        }
489
490        // Cannot call super.prefire() because it has different semantics
491        // for the trigger input.
492        return true;
493    }
494
495    /** Override the base class to indicate that the actor has not
496     *  been initialized.
497     *  @exception IllegalActionException If the superclass throws it.
498     */
499    @Override
500    public void wrapup() throws IllegalActionException {
501        super.wrapup();
502        _initialized = false;
503    }
504
505    ///////////////////////////////////////////////////////////////////
506    ////                         protected methods                 ////
507
508    /** Catch up the tentative view
509     *  of what the next output time should be.
510     *  This sets _tentativeNextOutputTime to a value that
511     *  is equal to or greater than current time, and it updates
512     *  _tentativePhase and _tentativeCycleStartTime to correspond
513     *  with this _tentativeNextOutputTime. If _tentativeNextOutputTime
514     *  is already equal to or greater than current time, then do nothing.
515     *  @exception IllegalActionException If the period is invalid.
516     */
517    protected void _catchUp() throws IllegalActionException {
518        Time currentTime = getDirector().getModelTime();
519        if (_tentativeNextOutputTime == null) {
520            // Initialization hasn't happened yet. No catch up to do.
521            return;
522        }
523        if (_tentativeNextOutputTime.compareTo(currentTime) >= 0) {
524            return;
525        }
526        // Find the first cycle time and phase greater than the
527        // current one that equals or exceeds current time.
528        // It might not be the very next phase because we could
529        // have been disabled in a modal model, or we could have
530        // skipped cycles due to not being triggered.
531        double periodValue = ((DoubleToken) period.getToken()).doubleValue();
532        Time phaseStartTime = _tentativeCycleStartTime
533                .add(_offsets[_tentativePhase]);
534        while (phaseStartTime.compareTo(currentTime) < 0) {
535            _tentativePhase++;
536            if (_tentativePhase >= _offsets.length) {
537                _tentativePhase = 0;
538                _tentativeCycleStartTime = _tentativeCycleStartTime
539                        .add(periodValue);
540            }
541            phaseStartTime = _tentativeCycleStartTime
542                    .add(_offsets[_tentativePhase]);
543        }
544        _tentativeNextOutputTime = phaseStartTime;
545    }
546
547    /** Get the specified output value, checking the form of the values
548     *  parameter.
549     *  @param index The index of the output values.
550     *  @return A token that contains the output value.
551     *  @exception IllegalActionException If the index is out of the range of
552     *  the values parameter.
553     */
554    protected Token _getValue(int index) throws IllegalActionException {
555        ArrayToken val = (ArrayToken) values.getToken();
556
557        if (val == null || val.length() <= index) {
558            throw new IllegalActionException(this,
559                    "Index out of range of the values parameter.");
560        }
561
562        return val.getElement(index);
563    }
564
565    /** Return true if the current time is the right time for an output.
566     *  @return True if the current time matches the _nextOutputTime.
567     *  @exception IllegalActionException If the time is not right an
568     *   a refiring cannot be requested.
569     */
570    protected boolean _isTimeForOutput() throws IllegalActionException {
571        Time currentTime = getDirector().getModelTime();
572        return _tentativeNextOutputTime.equals(currentTime);
573    }
574
575    /** Copy values committed in initialize() or in the last postfire()
576     *  into the corresponding tentative variables. In effect, this loads
577     *  the last known good value for these variables, which is particularly
578     *  important if time has gone backwards. This is done in a
579     *  protected method because derived classes may want to override
580     *  it.
581     *  @exception IllegalActionException Not thrown in this base class.
582     */
583    protected void _updateTentativeValues() throws IllegalActionException {
584        _outputProduced = false;
585        _tentativeCycleStartTime = _cycleStartTime;
586        _tentativeEnabled = _enabled;
587        _tentativeNextOutputTime = _nextOutputTime;
588        _tentativePhase = _phase;
589        _tentativeTriggered = _triggered;
590    }
591
592    /** Update the states and request refiring if necessary.
593     *  @exception IllegalActionException If the numberOfCycles parameter does
594     *  not contain a valid parameter or can not request refiring, or if the director will not
595     *   respect the fireAt() call..
596     */
597    protected void _updateStates() throws IllegalActionException {
598        // Schedule another firing if we are enabled
599        // and either an output was produced
600        // or a trigger input was received.
601        boolean triggerConnected = trigger.numberOfSources() > 0;
602        boolean fireAtNeeded = _tentativeEnabled
603                && (!triggerConnected && _outputProduced || triggerConnected
604                        && _tentativeTriggered && !_outputProduced);
605        _cycleStartTime = _tentativeCycleStartTime;
606        _phase = _tentativePhase;
607        if (_outputProduced) {
608            _phase++;
609            if (_phase == _offsets.length) {
610                double periodValue = ((DoubleToken) period.getToken())
611                        .doubleValue();
612                _cycleStartTime = _cycleStartTime.add(periodValue);
613                // Make the tentative value match, in case attributeChanged()
614                // is called before the next firing.
615                _tentativeCycleStartTime = _cycleStartTime;
616                _cycleCount++;
617                _phase = 0;
618            }
619            _tentativeTriggered = false;
620        }
621        _triggered = _tentativeTriggered;
622        _enabled = _tentativeEnabled;
623        _nextOutputTime = _cycleStartTime.add(_offsets[_phase]);
624
625        if (fireAtNeeded) {
626            if (_debugging) {
627                _debug("Requesting firing at: " + _nextOutputTime + ".");
628            }
629            _fireAt(_nextOutputTime);
630        }
631
632        // This should be computed after the above so that a firing
633        // gets requested for the tail end of the output pulses.
634        int cycleLimit = ((IntToken) numberOfCycles.getToken()).intValue();
635        _enabled = _enabled && (cycleLimit <= 0 || _cycleCount <= cycleLimit);
636    }
637
638    ///////////////////////////////////////////////////////////////////
639    ////                         protected variables               ////
640
641    /** The count of cycles executed so far, or 0 before the start. */
642    protected transient int _cycleCount;
643
644    /** The most recent cycle start time. */
645    protected transient Time _cycleStartTime;
646
647    /** Indicator of whether the specified number of cycles have
648     *  been completed. Also used in derived classes to turn on
649     *  and off the clock.
650     */
651    protected transient boolean _enabled;
652
653    /** Indicator of whether the first output has been produced. */
654    protected transient boolean _firstOutputProduced = false;
655
656    /** The time for the next output. */
657    protected transient Time _nextOutputTime;
658
659    /** Cache of offsets array value. */
660    protected transient double[] _offsets;
661
662    /** Indicator of whether an output was produced in this iteration. */
663    protected transient boolean _outputProduced = false;
664
665    /** The phase of the next output. */
666    protected transient int _phase;
667
668    /** The tentative time for the next output. */
669    protected transient Time _tentativeNextOutputTime;
670
671    ///////////////////////////////////////////////////////////////////
672    ////                         private variables                 ////
673
674    /** True if the actor has been initialized. */
675    private transient boolean _initialized;
676
677    /** The previous value of the period. */
678    private transient double _previousPeriod;
679
680    // Following variables recall data from the fire to the postfire method.
681
682    /** The tentative start time of the most recent cycle. */
683    private transient Time _tentativeCycleStartTime;
684
685    /** Flag indicating that an update to period is occurring
686     *  in the fire() method.
687     */
688    private transient boolean _tentative = false;
689
690    /** The indicator of whether the specified number of cycles
691     *  have been completed. */
692    private transient boolean _tentativeEnabled;
693
694    /** The tentative phase of the next output. */
695    private transient int _tentativePhase;
696
697    /** Tentative indicator of triggered state. */
698    private transient boolean _tentativeTriggered;
699
700    /** Indicator of whether trigger inputs have arrived
701     *  since the last output.
702     */
703    private transient boolean _triggered;
704}