001/* A composite where events can occur inside between the times when
002   the outside model invokes the composite.
003
004 Copyright (c) 2004-2018 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.actor.lib.hoc;
030
031import java.io.Writer;
032import java.util.Iterator;
033import java.util.List;
034import java.util.PriorityQueue;
035
036import ptolemy.actor.Actor;
037import ptolemy.actor.CompositeActor;
038import ptolemy.actor.Director;
039import ptolemy.actor.Executable;
040import ptolemy.actor.FiringEvent;
041import ptolemy.actor.IOPort;
042import ptolemy.actor.QueueReceiver;
043import ptolemy.actor.Receiver;
044import ptolemy.actor.SuperdenseTimeDirector;
045import ptolemy.actor.parameters.ParameterPort;
046import ptolemy.actor.util.SuperdenseTime;
047import ptolemy.actor.util.Time;
048import ptolemy.data.BooleanToken;
049import ptolemy.data.expr.Parameter;
050import ptolemy.data.type.BaseType;
051import ptolemy.kernel.CompositeEntity;
052import ptolemy.kernel.util.IllegalActionException;
053import ptolemy.kernel.util.NameDuplicationException;
054import ptolemy.kernel.util.Workspace;
055
056///////////////////////////////////////////////////////////////////
057//// CatchUpComposite
058
059/**
060This composite allows events to occur in the inside model
061between the times when the outside model invokes this composite.
062The inside model is required to have a director.
063<p>
064When this composite actor is fired, it fires the
065inside model to process any unprocessed events it may have
066at earlier times than the environment time of the firing.
067If in any of these "catch up" firings the inside model
068attempts to produce outputs, this director will throw an
069exception. This director then transfers any available
070inputs to the inside model and fires the inside model
071one more time. On this "caught up" firing, the inside
072model is permitted to produce outputs.
073<p>
074By default, if an actor contained by this composite requests
075a firing at a future time, then that request is passed up to
076the containing director. If <i>fireOnlyWhenTriggered</i> is set
077to <i>true</i>, however, then such requests are not passed up.
078In this case, the next firing will occur whenever the container
079has an input to provide to this composite, and catching up will
080occur only at that time. Note that if this composite produces
081outputs, you have to be very careful to ensure that those outputs
082are produced only at times when an input trigger is provided.
083<p>
084Note that a firing of this composite results in one
085or more complete iterations of the inside model, including
086invocations of postfire. Thus, this actor changes state
087in the fire() method, and hence only implements the
088weak actor semantics. Therefore, it should not be used
089inside domains that require a strict actor semantics,
090such as Continuous, which requires that fire() not
091change state (so that backtracking can occur).
092This composite probably can be used in SR, where it
093will be treated as a strict as actor and hence will be
094fired only once per iteration, and only when all inputs
095are known.
096
097 @author Edward A. Lee
098 @version $Id$
099 @since Ptolemy II 11.0
100 @Pt.ProposedRating Yellow (eal)
101 @Pt.AcceptedRating Red (neuendor)
102 */
103public class CatchUpComposite extends MirrorComposite {
104
105    /** Create an actor with a name and a container.
106     *  The container argument must not be null, or a
107     *  NullPointerException will be thrown.  This actor will use the
108     *  workspace of the container for synchronization and version counts.
109     *  If the name argument is null, then the name is set to the empty string.
110     *  Increment the version of the workspace.
111     *  @param container The container actor.
112     *  @param name The name of this actor.
113     *  @exception IllegalActionException If the container is incompatible
114     *   with this actor.
115     *  @exception NameDuplicationException If the name coincides with
116     *   an actor already in the container.
117     */
118    public CatchUpComposite(CompositeEntity container, String name)
119            throws IllegalActionException, NameDuplicationException {
120        super(container, name);
121        _init(container);
122    }
123
124    /** Construct a CatchUpComposite in the specified workspace with
125     *  no container and an empty string as a name. You can then change
126     *  the name with setName(). If the workspace argument is null, then
127     *  use the default workspace.
128     *  @param workspace The workspace that will list the actor.
129     *  @exception IllegalActionException If the container is incompatible
130     *   with this actor.
131     *  @exception NameDuplicationException If the name of the
132     *   director coincides with an actor already in the container.
133     */
134    public CatchUpComposite(Workspace workspace)
135            throws IllegalActionException, NameDuplicationException {
136        // This constructor is needed if CatchUpComposite is used as an
137        // actor-oriented class.
138        super(workspace);
139        // FIXME: The null argument will be a problem!
140        _init(null);
141    }
142
143    ///////////////////////////////////////////////////////////////////
144    ////                         public methods                    ////
145
146    /** Clone the object into the specified workspace. This overrides
147     *  the base class to instantiate a new CatchUpDirector.
148     *  @param workspace The workspace for the new object.
149     *  @return A new NamedObj.
150     *  @exception CloneNotSupportedException If any of the attributes
151     *   cannot be cloned.
152     *  @see #exportMoML(Writer, int, String)
153     */
154    @Override
155    public Object clone(Workspace workspace) throws CloneNotSupportedException {
156        CatchUpComposite result = (CatchUpComposite) super.clone(workspace);
157        try {
158            // Remove the old inner CatchUpDirector(s) that is(are) in the wrong workspace.
159            // FIXME: Is this really needed? Following IterateOverArray.
160            String catchUpDirectorName = null;
161            Iterator catchUpDirectors = result
162                    .attributeList(CatchUpDirector.class).iterator();
163            while (catchUpDirectors.hasNext()) {
164                CatchUpDirector oldCatchUpDirector = (CatchUpDirector) catchUpDirectors
165                        .next();
166                if (catchUpDirectorName == null) {
167                    catchUpDirectorName = oldCatchUpDirector.getName();
168                }
169                oldCatchUpDirector.setContainer(null);
170            }
171
172            // Create a new CatchUpDirector that is in the right workspace.
173            CatchUpDirector catchUpDirector = result.new CatchUpDirector(
174                    workspace);
175            catchUpDirector.setContainer(result);
176            catchUpDirector.setName(catchUpDirectorName);
177        } catch (Throwable throwable) {
178            throw new CloneNotSupportedException(
179                    "Could not clone: " + throwable);
180        }
181        return result;
182    }
183
184    /** Fire any piggybacked actors and then delegate the firing to the director.
185     *  This overrides the base class to not transfer inputs or outputs.
186     *  Those operations are handled by the director.
187     *  @exception IllegalActionException If there is no director, or if
188     *   the director's fire() method throws it, or if the actor is not
189     *   opaque.
190     */
191    @Override
192    public void fire() throws IllegalActionException {
193        if (_debugging) {
194            _debug("Calling fire()");
195        }
196
197        try {
198            _workspace.getReadAccess();
199
200            // First invoke piggybacked methods.
201            if (_piggybacks != null) {
202                // Invoke the fire() method of each piggyback.
203                for (Executable piggyback : _piggybacks) {
204                    piggyback.fire();
205                }
206            }
207            if (_derivedPiggybacks != null) {
208                // Invoke the fire() method of each piggyback.
209                for (Executable piggyback : _derivedPiggybacks) {
210                    piggyback.fire();
211                }
212            }
213            _director.fire();
214        } finally {
215            _workspace.doneReading();
216        }
217
218        if (_debugging) {
219            _debug("Called fire()");
220        }
221    }
222
223    ///////////////////////////////////////////////////////////////////
224    ////                         private variables                 ////
225
226    /** The contained composite actor. */
227    private Contents _contents;
228
229    /** The default value icon.  This is static so that we avoid doing
230     *  string concatenation each time we construct this object.
231     */
232    private static String _defaultIcon = "<svg>\n"
233            + "<rect x=\"-30\" y=\"-20\" width=\"60\" "
234            + "height=\"40\" style=\"fill:cyan\"/>\n"
235            + "<rect x=\"-28\" y=\"-18\" width=\"56\" "
236            + "height=\"36\" style=\"fill:lightgrey\"/>\n"
237            + "<rect x=\"-15\" y=\"-10\" width=\"10\" height=\"8\" "
238            + "style=\"fill:white\"/>\n"
239            + "<rect x=\"-15\" y=\"2\" width=\"10\" height=\"8\" "
240            + "style=\"fill:white\"/>\n"
241            + "<rect x=\"5\" y=\"-4\" width=\"10\" height=\"8\" "
242            + "style=\"fill:white\"/>\n"
243            + "<line x1=\"-5\" y1=\"-6\" x2=\"0\" y2=\"-6\"/>"
244            + "<line x1=\"-5\" y1=\"6\" x2=\"0\" y2=\"6\"/>"
245            + "<line x1=\"0\" y1=\"-6\" x2=\"0\" y2=\"6\"/>"
246            + "<line x1=\"0\" y1=\"0\" x2=\"5\" y2=\"0\"/>" + "</svg>\n";
247
248    /** The director. */
249    private CatchUpDirector _director;
250
251    ///////////////////////////////////////////////////////////////////
252    ////                         private methods                   ////
253
254    /** Initialize the actor.
255     *  @param container The container.
256     *  @exception IllegalActionException If the container is incompatible
257     *   with this actor.
258     *  @exception NameDuplicationException If the name coincides with
259     *   an actor already in the container.
260     */
261    private void _init(CompositeEntity container)
262            throws IllegalActionException, NameDuplicationException {
263        setClassName("ptolemy.actor.lib.hoc.CatchUpComposite");
264
265        // Create the CatchUpDirector in the proper workspace.
266        _director = new CatchUpDirector(workspace());
267        _director.setContainer(this);
268        _director.setName("CatchUpDirector");
269
270        // Create the composite actor for the contents.
271        _contents = new Contents(this, "Contents");
272
273        // Override the default icon.
274        _attachText("_iconDescription", _defaultIcon);
275    }
276
277    ///////////////////////////////////////////////////////////////////
278    ////                         inner classes                     ////
279
280    ///////////////////////////////////////////////////////////////////
281    //// CatchUpDirector
282
283    /** This is a specialized director that, when fired, fires the
284     *  inside model to process any unprocessed events it may have
285     *  at earlier times than the environment time of the firing.
286     *  If in any of these "catch up" firings the inside model
287     *  attempts to produce outputs, this director will throw an
288     *  exception. This director then transfers any available
289     *  inputs to the inside model and fires the inside model
290     *  one more time. On this "caught up" firing, the inside
291     *  model is permitted to produce outputs.
292     */
293    private class CatchUpDirector extends Director
294            implements SuperdenseTimeDirector {
295
296        /** Construct an CatchUpDirector in the specified workspace with
297         *  no container and an empty string as a name.
298         *  @param workspace The workspace.
299         *  @exception IllegalActionException If the container is incompatible
300         *   with this actor.
301         *  @exception NameDuplicationException If the name coincides with
302         *   an actor already in the container.
303         */
304        public CatchUpDirector(Workspace workspace)
305                throws IllegalActionException, NameDuplicationException {
306            super(workspace);
307            setPersistent(false);
308        }
309
310        /** Clone the object into the specified workspace.
311         *  @param workspace The workspace for the new object.
312         *  @return A new NamedObj.
313         *  @exception CloneNotSupportedException If any of the attributes
314         *   cannot be cloned.
315         *  @see #exportMoML(Writer, int, String)
316         */
317        @Override
318        public Object clone(Workspace workspace)
319                throws CloneNotSupportedException {
320            CatchUpDirector result = (CatchUpDirector) super.clone(workspace);
321            result._pendingFiringTimes = null;
322            return result;
323        }
324
325        /** Invoke one or more iterations of the contained actor of the
326         *  container of this director, allowing it to process any internal
327         *  events that it may have at earlier times than the current time
328         *  of the enclosing model.  That is, perform zero or more
329         *  "catch up" iterations of the contained actor, where the contained
330         *  actor will see an earlier time than the current time of
331         *  the enclosing model, followed by exactly one "caught up"
332         *  iteration.  The inputs of this composite will be visible to
333         *  the inside model only on the "caught up" iteration.
334         *  @exception IllegalActionException If any called method of
335         *   of the contained actor throws it, or if the contained
336         *   actor is not opaque.
337         */
338        @Override
339        public void fire() throws IllegalActionException {
340            // Don't call "super.fire();" here, this actor contains its
341            // own director.
342            CompositeActor container = (CompositeActor) getContainer();
343            _postfireReturns = true;
344
345            Director enclosingDirector = container.getExecutiveDirector();
346            if (enclosingDirector == null) {
347                throw new IllegalActionException(container,
348                        "No enclosing director!");
349            }
350            int microstep = 1;
351            if (enclosingDirector instanceof SuperdenseTimeDirector) {
352                microstep = ((SuperdenseTimeDirector) enclosingDirector)
353                        .getIndex();
354            }
355            SuperdenseTime environmentTime = new SuperdenseTime(getModelTime(),
356                    microstep);
357
358            // Perform catch up iterations, if necessary.
359            if (_pendingFiringTimes != null) {
360                SuperdenseTime firstPendingFiringTime = _pendingFiringTimes
361                        .peek();
362                while (firstPendingFiringTime != null) {
363                    int comparison = firstPendingFiringTime
364                            .compareTo(environmentTime);
365                    if (comparison < 0) {
366                        // Catch up iteration is needed.
367                        _pendingFiringTimes.poll();
368
369                        // Perform catch up firing.
370                        try {
371                            _catchUpTime = firstPendingFiringTime.timestamp();
372                            _microstep = firstPendingFiringTime.index();
373
374                            if (!_fireContents()) {
375                                // Postfire returned false.
376                                break;
377                            }
378                            // If any output port has tokens, this is an error.
379                            List<IOPort> ports = outputPortList();
380                            for (IOPort port : ports) {
381                                for (int i = 0; i < port.getWidth(); i++) {
382                                    if (port.hasTokenInside(i)) {
383                                        throw new IllegalActionException(port,
384                                                "Illegal output during catch up iteration. "
385                                                        + "Composite actor is attempting to produce an output at time "
386                                                        + _catchUpTime
387                                                        + ", but environment time is past that at "
388                                                        + environmentTime
389                                                                .timestamp());
390                                    }
391                                }
392                            }
393                        } finally {
394                            _catchUpTime = null;
395                        }
396
397                        // In case another catch up iteration is needed:
398                        firstPendingFiringTime = _pendingFiringTimes.peek();
399                    } else if (comparison == 0) {
400                        // Caught up firing will satisfy the request.
401                        _pendingFiringTimes.poll();
402                        break;
403                    } else {
404                        // Pending request is in the future.
405                        // Proceed directly to caught up firing.
406                        break;
407                    }
408                }
409            }
410
411            // Perform caught up iteration only if no catch iteration has returned
412            // false from postfire().
413            if (_postfireReturns == true) {
414                _microstep = microstep;
415                // Transfer inputs.
416                for (Iterator<?> inputPorts = inputPortList()
417                        .iterator(); inputPorts.hasNext() && !_stopRequested;) {
418                    IOPort p = (IOPort) inputPorts.next();
419                    if (p instanceof ParameterPort) {
420                        ((ParameterPort) p).getParameter().update();
421                    } else {
422                        transferInputs(p);
423                    }
424                }
425
426                if (!_stopRequested) {
427                    _fireContents();
428
429                    // Transfer outputs.
430                    if (!_stopRequested) {
431                        transferOutputs();
432                    }
433                }
434
435                // If there is a pending request in the future, delegate
436                // a fireAt() request.
437                if (_pendingFiringTimes != null) {
438                    SuperdenseTime firstPendingFiringTime = _pendingFiringTimes
439                            .peek();
440                    if (firstPendingFiringTime != null && firstPendingFiringTime
441                            .compareTo(environmentTime) > 0) {
442                        // First argument is ignored, so it can be null.
443                        fireAt(null, firstPendingFiringTime.timestamp(),
444                                firstPendingFiringTime.index());
445                    }
446                }
447            }
448        }
449
450        /** Request a firing of the given actor at the given model
451         *  time.  This class delegates the request to the enclosing
452         *  director. If the enclosing director returns the same
453         *  requested time, then nothing further happens. If the
454         *  enclosing director returns a later time, then this method
455         *  records the requested time so that on the next firing
456         *  a "catch up" iteration will be performed at that time.
457         *  If the enclosing director returns an earlier time,
458         *  then this method also records the requested time,
459         *  and on the next firing, will again attempt to delegate
460         *  to the container.
461         *  @param actor The actor scheduled to be fired.
462         *  @param time The requested time.
463         *  @return An instance of Time with the current time value, or
464         *   if there is an executive director, the time at which the
465         *   container of this director will next be fired
466         *   in response to this request.
467         *  @see #fireAtCurrentTime(Actor)
468         *  @exception IllegalActionException If there is an executive director
469         *   and it throws it. Derived classes may choose to throw this
470         *   exception for other reasons.
471         */
472        @Override
473        public Time fireAt(Actor actor, Time time)
474                throws IllegalActionException {
475            // Unless the actor specifically requests a particular microstep,
476            // we assume it knows nothing about microsteps. We use microstep 1
477            // as the default, since this is the default for discrete events.
478            // The Continuous domain will specifically request a firing at
479            // microstep 0.
480            return fireAt(actor, time, 1);
481        }
482
483        /** Request a firing of the given actor at the given model
484         *  time with the given microstep. This method behaves exactly
485         *  like {@link #fireAt(Actor, Time)}, except that it also
486         *  passes up to the executive director the microstep, if there
487         *  is one.
488         *  @param actor The actor scheduled to be fired.
489         *  @param time The requested time.
490         *  @param microstep The requested microstep.
491         *  @return An instance of Time with the current time value, or
492         *   if there is an executive director, the time at which the
493         *   container of this director will next be fired
494         *   in response to this request.
495         *  @see #fireAtCurrentTime(Actor)
496         *  @see #fireContainerAt(Time)
497         *  @exception IllegalActionException If there is an executive director
498         *   and it throws it. Derived classes may choose to throw this
499         *   exception for other reasons.
500         */
501        @Override
502        public Time fireAt(Actor actor, Time time, int microstep)
503                throws IllegalActionException {
504            if (_debugging) {
505                _debug("**** Requesting that enclosing director refire me at "
506                        + time + " with microstep " + microstep);
507            }
508            // Translate the local time into an environment time.
509            Time environmentTime = localClock
510                    .getEnvironmentTimeForLocalTime(time);
511            Director director = getExecutiveDirector();
512            if (director instanceof SuperdenseTimeDirector) {
513                int environmentMicrostep = ((SuperdenseTimeDirector) director)
514                        .getIndex();
515                // Microstep should be greater than that of the enclosing director
516                // if the time matches current time.
517                if (getModelTime().equals(time)
518                        && microstep < environmentMicrostep) {
519                    microstep = environmentMicrostep + 1;
520                }
521            }
522
523            if (((BooleanToken) _contents.fireOnlyWhenTriggered.getToken())
524                    .booleanValue()) {
525                // Do not pass the request up the hierarchy.
526                if (_pendingFiringTimes == null) {
527                    _pendingFiringTimes = new PriorityQueue<SuperdenseTime>();
528                }
529                _pendingFiringTimes.add(new SuperdenseTime(time, microstep));
530                // Do as below and return time anyway since the inside
531                // actor will think its time matches the requested firing time.
532                return time;
533            } else {
534                Time response = director.fireAt(CatchUpComposite.this,
535                        environmentTime, microstep);
536                int comparison = response.compareTo(time);
537                if (comparison == 0) {
538                    return time;
539                } else {
540                    if (_pendingFiringTimes == null) {
541                        _pendingFiringTimes = new PriorityQueue<SuperdenseTime>();
542                    }
543                    _pendingFiringTimes
544                            .add(new SuperdenseTime(time, microstep));
545                    return time;
546                }
547            }
548        }
549
550        /** Return a superdense time index for the current time,
551         *  where the index is equal to the microstep.
552         *  @return A superdense time index.
553         *  @see #setIndex(int)
554         *  @see ptolemy.actor.SuperdenseTimeDirector
555         */
556        @Override
557        public int getIndex() {
558            return _microstep;
559        }
560
561        /** If a "catch up" iteration is in progress, then return the
562         *  time that the inside model should see. Otherwise, return
563         *  the environment time.
564         *  @return The current time.
565         *  @see #setModelTime(Time)
566         */
567        @Override
568        public Time getModelTime() {
569            if (_catchUpTime == null) {
570                return super.getModelTime();
571            }
572            return _catchUpTime;
573        }
574
575        /** Initialize contained actors
576         *  @exception IllegalActionException If the initialize() method of
577         *   the super class throws it.
578         */
579        @Override
580        public void initialize() throws IllegalActionException {
581            // Initialize the microstep to zero, even though
582            // discrete models normally want to run with microstep 1 or higher.
583            // During initialization, some contained actors will request
584            // firings. One of those might be a Continuous subsystem,
585            // which will explicitly request a firing at microstep 0.
586            // Others will have their requests automatically set
587            // to microstep 1. Thus, with normal discrete models,
588            // the only events in the event queue after initialization
589            // will all have microstep 1, and hence that is where the
590            // simulation will start.
591            _microstep = 0;
592            // This could be getting re-initialized during execution
593            // (e.g., if we are inside a modal model), in which case,
594            // if the enclosing director is a superdense time director,
595            // we should initialize to its microstep, not to our own.
596            // NOTE: Some (weird) directors pretend they are not embedded even
597            // if they are (e.g. in Ptides), so we call _isEmbedded() to give
598            // the subclass the option of pretending it is not embedded.
599            if (isEmbedded()) {
600                Director executiveDirector = getExecutiveDirector();
601                if (executiveDirector instanceof SuperdenseTimeDirector) {
602                    _microstep = ((SuperdenseTimeDirector) executiveDirector)
603                            .getIndex();
604                }
605            }
606            super.initialize();
607        }
608
609        /** Return a new instance of QueueReceiver.
610         *  We use a QueueReceiver rather than the base class's MailboxReceiver
611         *  so that more than one token can be transferred to the inside
612         *  model on each firing.
613         *  @return A new instance of QueueReceiver.
614         *  @see QueueReceiver
615         */
616        @Override
617        public Receiver newReceiver() {
618            return new QueueReceiver();
619        }
620
621        /** Override the base class to return the logical AND of
622         *  what the base class postfire() method returns and the
623         *  flag set in fire().
624         */
625        @Override
626        public boolean postfire() throws IllegalActionException {
627            boolean superReturns = super.postfire();
628            return superReturns && _postfireReturns;
629        }
630
631        /** Set the superdense time index. This should only be
632         *  called by an enclosing director.
633         *  @exception IllegalActionException Not thrown in this base class.
634         *  @see #getIndex()
635         *  @see ptolemy.actor.SuperdenseTimeDirector
636         */
637        @Override
638        public void setIndex(int index) throws IllegalActionException {
639            if (_debugging) {
640                _debug("Setting superdense time index to " + index);
641            }
642            _microstep = index;
643        }
644
645        //////////////////////////////////////////////////////////////
646        ////                   private methods                    ////
647
648        /** Iterate the contents composite.
649         *  @exception IllegalActionException If the composite throws it.
650         *  @return false if postfire() returns false.
651         */
652        private boolean _fireContents() throws IllegalActionException {
653            if (_debugging) {
654                _debug(new FiringEvent(this, _contents,
655                        FiringEvent.BEFORE_ITERATE, 1));
656            }
657            if (_contents.iterate(1) == Executable.STOP_ITERATING) {
658                _postfireReturns = false;
659            }
660            if (_debugging) {
661                _debug(new FiringEvent(this, _contents,
662                        FiringEvent.AFTER_ITERATE, 1));
663            }
664            return _postfireReturns;
665        }
666
667        //////////////////////////////////////////////////////////////
668        ////                   private variables                  ////
669
670        /** The time that the inside model should see as the current time
671         *  during a "catch up" iteration, or null if there is no catch
672         *  up iteration in progress.
673         */
674        private Time _catchUpTime;
675
676        /** The current microstep. */
677        protected int _microstep = 1;
678
679        /** A sorted list of times of pending fireAt() requests. */
680        private PriorityQueue<SuperdenseTime> _pendingFiringTimes;
681
682        /** Indicator that the inside model returned false in postfire. */
683        private boolean _postfireReturns = true;
684    }
685
686    ///////////////////////////////////////////////////////////////////
687    //// Contents
688
689    /** Contents composite that overrides the base class to have a parameter
690     *  indicating whether to fire only when triggered.
691     */
692    public class Contents extends MirrorComposite.MirrorCompositeContents {
693
694        // The Contents class should be public so that cloning works.
695        // To test: (cd $PTII/ptolemy/configs/test/; $PTII/bin/ptjacl allConfigs.tcl)
696
697        public Contents(CompositeEntity container, String name)
698                throws IllegalActionException, NameDuplicationException {
699            super(container, name);
700
701            fireOnlyWhenTriggered = new Parameter(this,
702                    "fireOnlyWhenTriggered");
703            fireOnlyWhenTriggered.setTypeEquals(BaseType.BOOLEAN);
704            fireOnlyWhenTriggered.setExpression("false");
705        }
706
707        /** If false (the default), then whenever any contained actor
708         *  makes a fireAt() request, that request is passed up to the container,
709         *  and hence this composite will be fired at the requested time.
710         *  If set to true, then the request will not be passed up,
711         *  and this composite will be fired only when the container
712         *  chooses to fire it. If the containing director is DE, for example,
713         *  that will occur only when there is an input event to provide to
714         *  this composite.
715         */
716        public Parameter fireOnlyWhenTriggered;
717    }
718}