001/* An FSMDirector governs the execution of a modal model.
002
003 Copyright (c) 1999-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 */
027package ptolemy.domains.modal.kernel;
028
029import java.util.HashMap;
030import java.util.Iterator;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.Map;
034
035import ptolemy.actor.Actor;
036import ptolemy.actor.CompositeActor;
037import ptolemy.actor.Director;
038import ptolemy.actor.IOPort;
039import ptolemy.actor.InvariantViolationException;
040import ptolemy.actor.NoTokenException;
041import ptolemy.actor.QuasiTransparentDirector;
042import ptolemy.actor.Receiver;
043import ptolemy.actor.SuperdenseTimeDirector;
044import ptolemy.actor.TypedActor;
045import ptolemy.actor.util.BooleanDependency;
046import ptolemy.actor.util.CausalityInterface;
047import ptolemy.actor.util.Dependency;
048import ptolemy.actor.util.ExplicitChangeContext;
049import ptolemy.actor.util.Time;
050import ptolemy.data.BooleanToken;
051import ptolemy.data.Token;
052import ptolemy.data.expr.ParseTreeEvaluator;
053import ptolemy.data.expr.Variable;
054import ptolemy.domains.modal.modal.Refinement;
055import ptolemy.kernel.CompositeEntity;
056import ptolemy.kernel.Entity;
057import ptolemy.kernel.util.Attribute;
058import ptolemy.kernel.util.IllegalActionException;
059import ptolemy.kernel.util.InternalErrorException;
060import ptolemy.kernel.util.ModelErrorHandler;
061import ptolemy.kernel.util.NameDuplicationException;
062import ptolemy.kernel.util.Nameable;
063import ptolemy.kernel.util.NamedObj;
064import ptolemy.kernel.util.StringAttribute;
065import ptolemy.kernel.util.Workspace;
066
067///////////////////////////////////////////////////////////////////
068//// FSMDirector
069
070/**
071 * An FSMDirector governs the execution of a modal model. A modal
072 * model is a TypedCompositeActor with a FSMDirector as local
073 * director. The mode control logic is captured by a mode controller,
074 * an instance of FSMActor contained by the composite actor. Each
075 * state of the mode controller represents a mode of operation and can
076 * be refined by an opaque CompositeActor contained by the same
077 * composite actor.
078
079 * <p> The mode controller contains a set of states and transitions. A
080 * transition has a <i>guard expression</i>, any number of <i>output
081 * actions</i>, and any number of <i>set actions</i>. It has an
082 * <i>initial state</i>, which is the unique state whose
083 * <i>isInitialState</i> parameter is true.  The states and
084 * transitions can have <i>refinements</i>, which are composite
085 * actors. In outline, a firing of this director is a sequence of
086 * steps:</p>
087 *
088 * <ol>
089 * <li>Read inputs.
090 * <li>Evaluate the guards of preemptive transition out of the current state.
091 * <li>If no preemptive transition is enabled:
092 * <ol>
093 * <li>Fire the refinements of the current state (if any).
094 * <li>Evaluate guards on non-preemptive transitions out of the current state.
095 * </ol>
096 * <li>Choose a transition whose guard is true.
097 * <li>Execute the output actions of the chosen transition.
098 * <li>Fire the transition refinements of the chosen transition.
099 * </ol>
100 * <p>In postfire, the following steps are performed:</p>
101 * <ol>
102 * <li>Postfire the refinements of the current state if they were fired.
103 * <li>Initialize the refinements of the destination state if the transition is a reset
104 * transition.
105 * <li>Execute the set actions of the chosen transition.
106 * <li>Postfire the transition refinements of the chosen transition.
107 * <li>Change the current state to the destination of the chosen transition.
108 * </ol>
109
110 * <p>Since this director makes no persistent state changes in its fire()
111 * method, it conforms with the <i>actor abstract
112 * semantics</i>. Assuming the state and transition refinements also
113 * conform, this director can be used inside any Ptolemy II actor
114 * model of computation. How it behaves in each domain, however, can
115 * be somewhat subtle, particularly with domains that have fixed-point
116 * semantics and when nondeterministic transitions are used. The
117 * details are given below.</p>
118 *
119 * <p> When a modal model is fired, this director first transfers the
120 * input tokens from the outside domain to the mode controller and the
121 * refinement of its current state. The preemptive transitions from
122 * the current state of the mode controller are examined. If there is
123 * more than one transition enabled, and any of the enabled
124 * transitions is not marked nondeterministic, an exception is
125 * thrown. If there is exactly one preemptive transition enabled then
126 * it is chosen. The choice actions (outputActions) contained by the
127 * transition are executed. Any output token produced by the mode
128 * controller is transferred to both the output ports of the modal
129 * model and the input ports of the mode controller. Then the
130 * refinements associated with the enabled transition are
131 * executed. Any output token produced by the refinements is
132 * transferred to both the output ports of the modal model and the
133 * input ports of the mode controller. The refinements of the current
134 * state will not be fired.</p>
135 *
136 * <p> If no preemptive transition is enabled, the refinements of the
137 * current state are fired. Any output token produced by the
138 * refinements is transferred to both the output ports of the modal
139 * model and the input ports of the mode controller. After this, the
140 * non-preemptive transitions from the current state of the mode
141 * controller are examined. If there is more than one transition
142 * enabled, and any of the enabled transitions is not marked
143 * nondeterministic, an exception is thrown. If there is exactly one
144 * non-preemptive transition enabled then it is chosen and the choice
145 * actions contained by the transition are executed. Any output token
146 * produced by the mode controller is transferred to the output ports
147 * of the modal model and the input ports of the mode
148 * controller. Then, the refinements of the enabled transition are
149 * executed. Any output token produced by the refinements is
150 * transferred to both the output ports of the modal model and the
151 * input ports of the mode controller.</p>
152 *
153 * <p> In a firing, it is possible that the current state refinement
154 * produces an output, and a transition that is taken also produces an
155 * output on the same port. In this case, only the second of these
156 * outputs will appear on the output of the composite actor containing
157 * this director.  However, the first of these output values, the one
158 * produced by the refinement, may affect whether the transition is
159 * taken. That is, it can affect the guard. If in addition a
160 * transition refinement writes to the output, then that value will be
161 * produced, overwriting the value produced either by the state
162 * refinement or the output action on the transition.</p>
163 *
164 * <p> At the end of one firing, the modal model transfers its outputs
165 * to the outside model. The mode controller does not change state
166 * during successive firings in one iteration of the top level in
167 * order to support upper level domains that iterate to a fixed
168 * point.</p>
169 *
170 * <p> When the modal model is postfired, the chosen transition of the
171 * latest firing is committed. The commit actions contained by the
172 * transition are executed and the current state of the mode
173 * controller is set to the destination state of the transition.</p>
174 *
175 * <p>FIXME: If a state has multiple refinements, they are fired in the order defined.
176 * If they write to the same output, then the "last one wins." It will be its value
177 * produced. It might make more sense to require them to be consistent, giving something
178 * closer to SR semantics. The same argument could apply when both a refinement and
179 * a transition produce outputs.</p>
180 *
181 * @author Xiaojun Liu, Haiyang Zheng, Edward A. Lee, Christian Motika
182 * @version $Id$
183 * @since Ptolemy II 8.0
184 * @Pt.ProposedRating Yellow (hyzheng)
185 * @Pt.AcceptedRating Red (hyzheng)
186 * @see FSMActor
187 */
188public class FSMDirector extends Director implements ExplicitChangeContext,
189        QuasiTransparentDirector, SuperdenseTimeDirector {
190    /**
191     * Construct a director in the default workspace with an empty
192     * string as its name. The director is added to the list of
193     * objects in the workspace. Increment the version number of the
194     * workspace.
195     *  @exception NameDuplicationException If construction of Time objects fails.
196     *  @exception IllegalActionException If construction of Time objects fails.
197     */
198    public FSMDirector()
199            throws IllegalActionException, NameDuplicationException {
200        super();
201        _createAttribute();
202    }
203
204    /**
205     * Construct a director in the workspace with an empty name. The
206     * director is added to the list of objects in the
207     * workspace. Increment the version number of the workspace.
208     * @param workspace The workspace of this director.
209     *  @exception NameDuplicationException If construction of Time objects fails.
210     *  @exception IllegalActionException If construction of Time objects fails.
211     */
212    public FSMDirector(Workspace workspace)
213            throws IllegalActionException, NameDuplicationException {
214        super(workspace);
215        _createAttribute();
216    }
217
218    /**
219     * Construct a director in the given container with the given
220     * name. The container argument must not be null, or a
221     * NullPointerException will be thrown. If the name argument is
222     * null, then the name is set to the empty string. Increment the
223     * version number of the workspace.
224     *
225     * @param container Container of this director.
226     * @param name Name of this director.
227     * @exception IllegalActionException If the name has a period in it, or the director
228     * is not compatible with the specified container.
229     * @exception NameDuplicationException If the container not a CompositeActor and the
230     * name collides with an entity in the container.
231     */
232    public FSMDirector(CompositeEntity container, String name)
233            throws IllegalActionException, NameDuplicationException {
234        super(container, name);
235        _createAttribute();
236    }
237
238    ///////////////////////////////////////////////////////////////////
239    ////                         public variables                  ////
240
241    /**
242     * Attribute specifying the name of the mode controller in the
243     * container of this director. This director must have a mode
244     * controller that has the same container as this director,
245     * otherwise an IllegalActionException will be thrown when action
246     * methods of this director are called.
247     */
248    public StringAttribute controllerName = null;
249
250    ///////////////////////////////////////////////////////////////////
251    ////                         public methods                    ////
252
253    /**
254     * React to a change in an attribute. If the changed attribute is
255     * the <i>controllerName</i> attribute, then make note that this
256     * has changed.
257     * @param attribute The attribute that changed.
258     * @exception IllegalActionException If thrown by the superclass attributeChanged()
259     * method.
260     */
261    @Override
262    public void attributeChanged(Attribute attribute)
263            throws IllegalActionException {
264        super.attributeChanged(attribute);
265
266        if (attribute == controllerName) {
267            _controllerVersion = -1;
268        }
269    }
270
271    /**
272     * Clone the director into the specified workspace. This calls the
273     * base class and then sets the attribute public members to refer
274     * to the attributes of the new director.
275     *
276     * @param workspace The workspace for the new director.
277     * @return A new director.
278     * @exception CloneNotSupportedException If a derived class contains an attribute that
279     * cannot be cloned.
280     */
281    @Override
282    public Object clone(Workspace workspace) throws CloneNotSupportedException {
283        FSMDirector newObject = (FSMDirector) super.clone(workspace);
284        // Protected variables.
285        newObject._currentLocalReceiverMap = null;
286        newObject._localReceiverMaps = new HashMap();
287
288        // Private variables.
289        newObject._controller = null;
290        newObject._controllerVersion = -1;
291        newObject._localReceiverMapsVersion = -1;
292
293        return newObject;
294    }
295
296    /**
297     * Return a default dependency to use between input input ports
298     * and output ports. This overrides the base class so that if
299     * there is an executive director, then we get the default
300     * dependency from it.
301     *
302     * @see Dependency
303     * @see CausalityInterface
304     * @see Actor#getCausalityInterface()
305     * @return A default dependency between input ports and output ports.
306     */
307    @Override
308    public Dependency defaultDependency() {
309        Director executiveDirector = ((Actor) getContainer())
310                .getExecutiveDirector();
311        if (isEmbedded() && executiveDirector != null) {
312            return executiveDirector.defaultDependency();
313        }
314        return BooleanDependency.OTIMES_IDENTITY;
315    }
316
317    /**
318     * Fire the modal model for one iteration. If there is a
319     * preemptive transition enabled, execute its choice actions
320     * (outputActions). Otherwise, fire the refinement of the current
321     * state.  After this firing, if there is a transition enabled,
322     * execute its choice actions. If any tokens are produced during
323     * this iteration, they are sent to the output ports of the
324     * model model and also the input ports of the mode controller.
325     *
326     * @exception IllegalActionException If there is more than one
327     *                transition enabled and nondeterminism is not
328     *                permitted, or there is no controller, or it is
329     *                thrown by any choice action.
330     */
331    @Override
332    public void fire() throws IllegalActionException {
333        FSMActor controller = getController();
334        State currentState = controller.currentState();
335        if (_debugging) {
336            _debug("*** Firing " + getFullName(), " at time " + getModelTime());
337            _debug("Current state is:", currentState.getName());
338        }
339        controller.fire();
340    }
341
342    /**
343     * Schedule a firing of the given actor at the given time
344     * and microstep. If there exists an executive
345     * director, this method delegates to the fireAt() method of the executive director by
346     * requesting a firing of the container of this director at the given time adjusted by the
347     * current offset between local time and the environment time.
348     *
349     * <p> If there is no executive director, then the request results
350     * in model time of this director being set to the specified
351     * time. The reason for this latter behavior is to support models
352     * where FSM is at the top level. A director inside the state
353     * refinements could be timed, and expects time to advance in its
354     * environment between firings. It typically makes a call to
355     * fireAt() at the conclusion of each iteration to specify the
356     * time value it expects to next see. Such directors can thus be
357     * used inside top-level FSM models. For example, the DEDirector
358     * and SDFDirector behave exactly this way.</p>
359     *
360     * @param actor The actor scheduled to be fired.
361     * @param time The scheduled time.
362     * @param microstep The microstep.
363     * @return The time at which the actor passed as an argument will be fired.
364     * @exception IllegalActionException If thrown by the executive director.
365     */
366    @Override
367    public Time fireAt(Actor actor, Time time, int microstep)
368            throws IllegalActionException {
369        Actor container = (Actor) getContainer();
370        if (container != null) {
371            Director director = container.getExecutiveDirector();
372            if (isEmbedded() && director != null) {
373                if (_debugging) {
374                    _debug("**** Requesting that enclosing director refire me at "
375                            + time + " with microstep " + microstep);
376                }
377                // Translate the local time into an environment time.
378                Time environmentTime = localClock
379                        .getEnvironmentTimeForLocalTime(time);
380                Time result = director.fireAt(container, environmentTime,
381                        microstep);
382
383                // Translate the response from the environment into a local time.
384                return localClock.getLocalTimeForEnvironmentTime(result);
385            }
386
387        }
388        setModelTime(time);
389        return time;
390    }
391
392    /**
393     * Return the mode controller of this director. The name of the
394     * mode controller is specified by the <i>controllerName</i>
395     * attribute. The mode controller must have the same container as
396     * this director. This method is read-synchronized on the
397     * workspace.
398     *
399     * @return The mode controller of this director.
400     * @exception IllegalActionException If no controller is found.
401     */
402    public FSMActor getController() throws IllegalActionException {
403        if (_controllerVersion == workspace().getVersion()) {
404            return _controller;
405        }
406
407        try {
408            workspace().getReadAccess();
409
410            String name = controllerName.getExpression();
411
412            if (name == null) {
413                throw new IllegalActionException(this,
414                        "No name for mode " + "controller is set.");
415            }
416
417            Nameable container = getContainer();
418
419            if (!(container instanceof CompositeActor)) {
420                throw new IllegalActionException(this, "No controller found.");
421            }
422
423            CompositeActor cont = (CompositeActor) container;
424            Entity entity = cont.getEntity(name);
425
426            if (entity == null) {
427                throw new IllegalActionException(this,
428                        "No controller found " + "with name " + name);
429            }
430
431            if (!(entity instanceof FSMActor)) {
432                throw new IllegalActionException(this, entity,
433                        "mode controller must be an instance of FSMActor.");
434            }
435
436            _controller = (FSMActor) entity;
437            _controllerVersion = workspace().getVersion();
438            return _controller;
439        } finally {
440            workspace().doneReading();
441        }
442    }
443
444    /**
445     * Return the explicit change context. In this case, the change context returned is the
446     * composite actor controlled by this director.
447     *
448     * @return The explicit change context.
449     */
450    @Override
451    public Entity getContext() {
452        return (Entity) getContainer();
453    }
454
455    /**
456     * Override the base class so that if any outgoing transition has
457     * a guard that evaluates to true, then return the current
458     * time. Otherwise, delegate to the enclosing director.
459     */
460    @Override
461    public Time getModelNextIterationTime() {
462        try {
463            FSMActor controller = getController();
464            List transitionList = controller.currentState().outgoingPort
465                    .linkedRelationList();
466            // First check preemptive transitions, then non-preemptive ones.
467            List enabledTransitions = controller
468                    .enabledTransitions(transitionList, true, false);
469            if (enabledTransitions.size() > 0) {
470                return getModelTime();
471            }
472            enabledTransitions = controller.enabledTransitions(transitionList,
473                    false, false);
474            if (enabledTransitions.size() > 0) {
475                return getModelTime();
476            }
477            // The result returned below needs to be adjusted by the current offset.
478            Time result = localClock.getLocalTimeForEnvironmentTime(
479                    super.getModelNextIterationTime());
480            return result;
481        } catch (IllegalActionException e) {
482            // Any exception here should have shown up before now.
483            throw new InternalErrorException(e);
484        }
485    }
486
487    /**
488     * Return a list of variables that are modified in a modal
489     * model. The variables are assumed to have a change context of
490     * the container of this director. This class returns all
491     * variables that are assigned in the actions of transitions.
492     *
493     * @return A list of variables.
494     * @exception IllegalActionException If no controller can be
495     *                found, or the variables to be assigned by the
496     *                actions can not be found.
497     */
498    @Override
499    public List getModifiedVariables() throws IllegalActionException {
500        List list = new LinkedList();
501
502        // Collect assignments from FSM transitions
503        for (Iterator states = getController().entityList().iterator(); states
504                .hasNext();) {
505            State state = (State) states.next();
506
507            for (Iterator transitions = state.outgoingPort.linkedRelationList()
508                    .iterator(); transitions.hasNext();) {
509                Transition transition = (Transition) transitions.next();
510
511                for (Iterator actions = transition.choiceActionList()
512                        .iterator(); actions.hasNext();) {
513                    AbstractActionsAttribute action = (AbstractActionsAttribute) actions
514                            .next();
515
516                    for (Iterator names = action.getDestinationNameList()
517                            .iterator(); names.hasNext();) {
518                        String name = (String) names.next();
519                        NamedObj object = action.getDestination(name);
520
521                        if (object instanceof Variable) {
522                            list.add(object);
523                        }
524                    }
525                }
526
527                for (Iterator actions = transition.commitActionList()
528                        .iterator(); actions.hasNext();) {
529                    AbstractActionsAttribute action = (AbstractActionsAttribute) actions
530                            .next();
531
532                    for (Iterator names = action.getDestinationNameList()
533                            .iterator(); names.hasNext();) {
534                        String name = (String) names.next();
535                        NamedObj object = action.getDestination(name);
536
537                        if (object instanceof Variable) {
538                            list.add(object);
539                        }
540                    }
541                }
542            }
543        }
544
545        return list;
546    }
547
548    /**
549     * Return the parse tree evaluator used to evaluate guard
550     * expressions. In this base class, an instance of {@link
551     * ParseTreeEvaluator} is returned. The derived classes may need
552     * to override this method to return different parse tree
553     * evaluators.
554     *
555     * @return ParseTreeEvaluator used to evaluate guard expressions.
556     */
557    public ParseTreeEvaluator getParseTreeEvaluator() {
558        return new ParseTreeEvaluator();
559    }
560
561    /**
562     * Return a superdense time index for the current time. This
563     * method delegates to the executive director, if there is one
564     * that implements SuperdenseTimeDirector, and returns current
565     * time with index 1 otherwise.
566     *
567     * @return A superdense time index.
568     * @see #setIndex(int)
569     * @see ptolemy.actor.SuperdenseTimeDirector
570     */
571    @Override
572    public int getIndex() {
573        Director executiveDirector = ((Actor) getContainer())
574                .getExecutiveDirector();
575        if (isEmbedded()
576                && executiveDirector instanceof SuperdenseTimeDirector) {
577            return ((SuperdenseTimeDirector) executiveDirector).getIndex()
578                    + _indexOffset;
579        }
580        return 1;
581    }
582
583    /**
584     * Return true if the model errors are handled. Otherwise, return
585     * false and the model errors are passed to the higher level in
586     * hierarchy.
587     *
588     * <p> In this method, model errors including
589     * multipleEnabledTransitionException and
590     * InvariantViolationException are handled.</p>
591     *
592     * <p> In the current design, if multiple enabled transitions are
593     * detected, an exception will be thrown. For future designs,
594     * different ways to handle this situation will be introduced
595     * here.</p>
596     *
597     * <p> When an invariant is violated, this method checks whether
598     * there exists an enabled (non-preemptive) transition. If there
599     * is one, the model error is ignored and this director will
600     * handle the enabled transition later. Otherwise, an exception
601     * will be thrown.</p>
602     *
603     * @param context The context where the model error happens.
604     * @param exception An exception that represents the model error.
605     * @return True if the error has been handled, false if the model error is passed
606     * to the higher level.
607     * @exception IllegalActionException If multiple enabled transition is detected,
608     * or mode controller can not be found, or can not read outputs from refinements.
609     */
610    @Override
611    public boolean handleModelError(NamedObj context,
612            IllegalActionException exception) throws IllegalActionException {
613        // NOTE: Besides throwing exception directly, we can handle
614        // multiple enabled transitions in different ways by the derived
615        // sub classes.
616        if (exception instanceof MultipleEnabledTransitionsException) {
617            throw exception;
618        }
619
620        // If the exception is an InvariantViolationException
621        // exception, check if any transition is enabled.
622        // FIXME: This whole mechanism needs to be checked...
623        if (exception instanceof InvariantViolationException) {
624            FSMActor controller = getController();
625            controller.readOutputsFromRefinement();
626
627            State currentState = controller.currentState();
628            // FIXME: Need to understand how error transitions work
629            // in combination with immediate transitions and model errors.
630            // Note that this only makes sense for non-preemptive transitions.
631            List enabledTransitions = controller.enabledTransitions(
632                    currentState.outgoingPort.linkedRelationList(), false,
633                    false);
634
635            if (enabledTransitions.size() == 0) {
636                ModelErrorHandler container = getContainer();
637
638                if (container != null) {
639                    // We can not call the handleModelError() method of the
640                    // super class, because the container will call this
641                    // method again and it will lead to a dead loop.
642                    // return container.handleModelError(context, exception);
643                    throw exception;
644                }
645            }
646
647            if (_debugging && _verbose) {
648                _debug("ModelError " + exception.getMessage() + " is handled.");
649            }
650
651            return true;
652        }
653
654        // else delegate the exception to upper level.
655        return false;
656    }
657
658    /** Return true if all state refinements have directors that
659     *  implement the strict actor semantics.
660     *  @return True if the director exports strict actor semantics.
661     */
662    @Override
663    public boolean implementsStrictActorSemantics() {
664        // Iterate over the state refinements.
665        try {
666            FSMActor controller = getController();
667            List<State> states = controller.entityList();
668            for (State state : states) {
669                TypedActor[] refinements;
670                try {
671                    refinements = state.getRefinement();
672                } catch (IllegalActionException e) {
673                    throw new InternalErrorException(e);
674                }
675                if (refinements != null) {
676                    for (TypedActor refinement : refinements) {
677                        Director director = refinement.getDirector();
678                        // Added director != this since it might be
679                        // possible that the refinement is a Modal
680                        // Model without its own director. In this
681                        // case director == this, and the call
682                        // director.implementsStrictActorSemantics()
683                        // would lead to a infinite loop.
684                        if (director != null && director != this
685                                && !director.implementsStrictActorSemantics()) {
686                            return false;
687                        }
688                    }
689                }
690            }
691        } catch (IllegalActionException e1) {
692            throw new InternalErrorException(e1);
693        }
694        return true;
695    }
696
697    /**
698     * Initialize the mode controller and all the refinements by
699     * calling the initialize() method in the super class. Build the
700     * local maps for receivers. Suspend all the refinements of states
701     * that are not the current state.
702     *
703     * @exception IllegalActionException If thrown by the initialize()
704     *                method of the super class, or can not find mode
705     *                controller, or can not find refinement of the
706     *                current state.
707     */
708    @Override
709    public void initialize() throws IllegalActionException {
710        super.initialize();
711        // Transitions may produce initial outputs that need to be transferred
712        // to the outside. Make sure to do this before resetting receivers.
713        transferOutputs();
714
715        resetOutputReceivers();
716
717        // Suspend the refinements of all non-initial states at the start time.
718        // NOTE: Perhaps this could be avoided by doing a suspend in the loop
719        // over refinements above.
720        List<State> states = getController().entityList();
721        for (State state : states) {
722            if (((BooleanToken) state.isInitialState.getToken())
723                    .booleanValue()) {
724                continue;
725            }
726            TypedActor[] refinements = state.getRefinement();
727            if (refinements != null) {
728                for (TypedActor refinement : refinements) {
729                    Director refinementDirector = refinement.getDirector();
730                    if (refinementDirector != this) {
731                        refinementDirector.suspend();
732                    }
733                }
734            }
735        }
736    }
737
738    /**
739     * Indicate that a schedule for the model may no longer be valid,
740     * if there is a schedule. This method should be called when
741     * topology changes are made, or for that matter when any change
742     * that may invalidate the schedule is made. In this class,
743     * delegate to the executive director.  This is because changes in
744     * the FSM may affect the causality interface of the FSM, which
745     * may be used in scheduling by the enclosing director.
746     */
747    @Override
748    public void invalidateSchedule() {
749        Director executiveDirector = ((Actor) getContainer())
750                .getExecutiveDirector();
751        if (isEmbedded() && executiveDirector != null) {
752            executiveDirector.invalidateSchedule();
753        }
754    }
755
756    /**
757     * Return false. This director checks inputs to see whether they
758     * are known before evaluating guards, so it can fired even if it
759     * has unknown inputs.
760     *
761     * @return False.
762     * @exception IllegalActionException
763     *                Not thrown in this base class.
764     */
765    @Override
766    public boolean isStrict() throws IllegalActionException {
767        return false;
768        /*
769         * NOTE: This used to return a value as follows based on the
770         * causality interface. But this is conservative and prevents
771         * using the director in some models. Actor container =
772         * (Actor)getContainer(); CausalityInterface causality =
773         * container.getCausalityInterface(); int numberOfOutputs =
774         * container.outputPortList().size(); Collection<IOPort>
775         * inputs = container.inputPortList(); for (IOPort input :
776         * inputs) { // If the input is also output, skip it. // This
777         * is the output of a refinement. if (input.isOutput()) {
778         * continue; } try { if
779         * (causality.dependentPorts(input).size() < numberOfOutputs)
780         * { return false; } } catch (IllegalActionException e) {
781         * throw new InternalErrorException(e); } } return true;
782         */
783    }
784
785    /**
786     * Return a receiver that is a one-place buffer. A token put into
787     * the receiver will override any token already in the receiver.
788     *
789     * @return A receiver that is a one-place buffer.
790     */
791    @Override
792    public Receiver newReceiver() {
793        return new FSMReceiver();
794    }
795
796    /**
797     * Invoke postfire() on any state refinements that were fired,
798     * then execute the commit actions contained by the last chosen
799     * transition, if any, and finally set the current state
800     * to the destination state of the transition. This will return
801     * false if any refinement that is postfired returns false.
802     * <p>
803     * If any transition was taken in this iteration, and if there is
804     * an executive director, and if there is a transition from the
805     * new state that is currently enabled, then this method calls
806     * fireAtCurrentTime(Actor) on that executive director (this call
807     * occurs indirectly in the FSMActor controller). If there is an
808     * enabled transition, then the current state is transient, and we
809     * will want to spend zero time in it.
810     *
811     * @return True if the mode controller wishes to be scheduled for another iteration.
812     * @exception IllegalActionException
813     *                If thrown by any commit action or there is no controller.
814     */
815    @Override
816    public boolean postfire() throws IllegalActionException {
817        boolean result = true;
818        if (_debugging) {
819            _debug("*** postfire called at time: ", getModelTime().toString());
820        }
821        FSMActor controller = getController();
822        result &= controller.postfire();
823        _currentLocalReceiverMap = (Map) _localReceiverMaps
824                .get(controller.currentState());
825
826        // Reset all the receivers on the inside of output ports.
827        // NOTE: This only has an effect for FSMReceiver.
828        _resetOutputReceivers();
829
830        return result && !_stopRequested && !_finishRequested;
831    }
832
833    /** Check whether contained refinements have a director.
834     *  @exception IllegalActionException If a contained refinement
835     *  does not have a director.
836     */
837    @Override
838    public void preinitialize() throws IllegalActionException {
839        Iterator<?> actors = ((CompositeActor) getContainer()).deepEntityList()
840                .iterator();
841        while (actors.hasNext()) {
842            Actor actor = (Actor) actors.next();
843            if (!(actor instanceof Refinement)
844                    && actor.getContainer() instanceof Refinement) {
845                throw new IllegalActionException(actor.getContainer(),
846                        "Refinement is missing a director!");
847            }
848        }
849
850        super.preinitialize();
851    }
852
853    /**
854     * Return true if the mode controller is ready to fire. If this
855     * model is not at the top level and the current time of this
856     * director lags behind that of the executive director, update the
857     * current time to that of the executive director. Record whether
858     * the refinements of the current state of the mode controller are
859     * ready to fire.
860     *
861     * @exception IllegalActionException
862     *                If there is no controller.
863     */
864    @Override
865    public boolean prefire() throws IllegalActionException {
866        if (_debugging) {
867            _debug("Prefire called at time: " + getModelTime());
868        }
869        super.prefire();
870        return getController().prefire();
871    }
872
873    /** Rebuild the output receivers map and reset the output receivers.
874     *  @exception IllegalActionException If there is no mode
875     *  controller, or can not find refinements for states or if
876     *  getting the receivers fails.
877     */
878    public void resetOutputReceivers() throws IllegalActionException {
879        _buildLocalReceiverMaps();
880        _resetOutputReceivers();
881    }
882
883    /**
884     * If the container is not null, register this director as the model error handler.
885     *
886     * @param container The proposed container.
887     * @exception IllegalActionException If the action would result in
888     *                a recursive containment structure, or if this
889     *                entity and container are not in the same
890     *                workspace, or if the protected method
891     *                _checkContainer() throws it, or if a contained
892     *                Settable becomes invalid and the error handler
893     *                throws it.
894     * @exception NameDuplicationException If the name of this entity
895     * collides with a name already in the container.
896     */
897    @Override
898    public void setContainer(NamedObj container)
899            throws IllegalActionException, NameDuplicationException {
900        super.setContainer(container);
901
902        if (container != null) {
903            container.setModelErrorHandler(this);
904        }
905    }
906
907    /**
908     * Set the superdense time index by delegating to the directors of
909     * the refinements of the current state, if any. This should only
910     * be called by an enclosing director.
911     *
912     * @exception IllegalActionException
913     *                Not thrown in this base class.
914     * @see #getIndex()
915     * @see ptolemy.actor.SuperdenseTimeDirector
916     */
917    @Override
918    public void setIndex(int index) throws IllegalActionException {
919        // FIXME: Is this right?
920        Actor[] actors = _controller.currentState().getRefinement();
921        if (actors != null) {
922            for (int i = 0; i < actors.length; ++i) {
923                Director destinationDirector = actors[i].getDirector();
924                // If the refinement doesn't have a director, then the
925                // destinationDirector would be this one! This is an error,
926                // but we tolerate it here.
927                if (destinationDirector != this
928                        && destinationDirector instanceof SuperdenseTimeDirector) {
929                    ((SuperdenseTimeDirector) destinationDirector)
930                            .setIndex(index);
931                }
932            }
933        }
934    }
935
936    /**
937     * Transfer data from the input port of the container to the ports
938     * connected to the inside of the input port and on the mode
939     * controller or the refinement of its current state. This method
940     * will transfer exactly one token on each input channel that has
941     * at least one token available.  The port argument must be an
942     * opaque input port. If any channel of the input port has no
943     * data, then that channel is ignored. Any token left not consumed
944     * in the ports to which data are transferred is discarded.
945     *
946     * @param port
947     *            The input port to transfer tokens from.
948     * @return True if at least one data token is transferred.
949     * @exception IllegalActionException
950     *                If the port is not an opaque input port.
951     */
952    @Override
953    public boolean transferInputs(IOPort port) throws IllegalActionException {
954        if (!port.isInput() || !port.isOpaque()) {
955            throw new IllegalActionException(this, port,
956                    "transferInputs: port argument is not an opaque"
957                            + "input port.");
958        }
959
960        boolean transferredToken = false;
961        // NOTE: The following method does quite a song and dance with the "local
962        // receivers map" to avoid putting the input data into all
963        // refinements. Is this worth it? A much simpler design would
964        // just make the input data available to all refinements, whether
965        // they run or not.
966        Receiver[][] insideReceivers = _currentLocalReceivers(port);
967
968        // If the port is not connected, then the input is known to be absent
969        // and we can safely send a clear to the inside.
970        int numberOfSources = port.numberOfSources();
971        if (numberOfSources < insideReceivers.length) {
972            // At least one inside channel has no viable sources.
973            for (int i = numberOfSources; i < insideReceivers.length; i++) {
974                for (Receiver receiver : insideReceivers[i]) {
975                    receiver.clear();
976                }
977            }
978        }
979
980        for (int i = 0; i < port.getWidth(); i++) {
981            try {
982                if (port.isKnown(i)) {
983                    if (port.hasToken(i)) {
984                        Token t = port.get(i);
985                        // FindBugs: insideReceivers can't be null;
986                        if (i < insideReceivers.length
987                                && insideReceivers[i] != null) {
988                            for (int j = 0; j < insideReceivers[i].length; j++) {
989                                insideReceivers[i][j].put(t);
990                                if (_debugging) {
991                                    _debug(getFullName(),
992                                            "transferring input " + t + " from "
993                                                    + port.getFullName()
994                                                    + " to "
995                                                    + insideReceivers[i][j]
996                                                            .getContainer()
997                                                            .getFullName());
998                                }
999                            }
1000                            transferredToken = true;
1001                        }
1002                    } else {
1003                        /** Port does not have a token. */
1004                        // FindBugs: insideReceivers can't be null;
1005                        if (insideReceivers[i] != null) {
1006                            for (int j = 0; j < insideReceivers[i].length; j++) {
1007                                if (_debugging) {
1008                                    _debug(getName(),
1009                                            "input port has no token. Clearing "
1010                                                    + insideReceivers[i][j]
1011                                                            .getContainer()
1012                                                            .getFullName());
1013                                }
1014                                insideReceivers[i][j].clear();
1015                            }
1016                        }
1017                    }
1018                } else {
1019                    /** Port status is not known. */
1020                    if (_debugging) {
1021                        _debug("Input port status is not known. Resetting inside receivers of "
1022                                + port.getName());
1023                    }
1024                    // FindBugs: insideReceivers can't be null;
1025                    if (insideReceivers[i] != null) {
1026                        for (int j = 0; j < insideReceivers[i].length; j++) {
1027                            insideReceivers[i][j].reset();
1028                        }
1029                    }
1030                }
1031            } catch (NoTokenException ex) {
1032                // this shouldn't happen.
1033                throw new InternalErrorException(
1034                        "Director.transferInputs: Internal error: "
1035                                + ex.getMessage());
1036            }
1037        }
1038        return transferredToken;
1039    }
1040
1041    ///////////////////////////////////////////////////////////////////
1042    ////                         protected methods                 ////
1043
1044    /**
1045     * Build for each state of the mode controller the map from input ports of the modal model to
1046     * the local receivers when the mode controller is in that state. This method is
1047     * read-synchronized on the workspace.
1048     *
1049     * @exception IllegalActionException
1050     *                If there is no mode controller, or can not find refinements for states.
1051     */
1052    protected void _buildLocalReceiverMaps() throws IllegalActionException {
1053        try {
1054            workspace().getReadAccess();
1055
1056            FSMActor controller = getController();
1057
1058            // Remove any existing maps.
1059            _localReceiverMaps.clear();
1060
1061            // Create a map for each state of the mode controller.
1062            Iterator states = controller.entityList().iterator();
1063            State state = null;
1064
1065            while (states.hasNext()) {
1066                state = (State) states.next();
1067                _localReceiverMaps.put(state, new HashMap());
1068            }
1069
1070            CompositeActor comp = (CompositeActor) getContainer();
1071            Iterator inPorts = comp.inputPortList().iterator();
1072            List resultsList = new LinkedList();
1073
1074            while (inPorts.hasNext()) {
1075                IOPort port = (IOPort) inPorts.next();
1076                Receiver[][] allReceivers = port.deepGetReceivers();
1077                states = controller.entityList().iterator();
1078
1079                while (states.hasNext()) {
1080                    state = (State) states.next();
1081
1082                    TypedActor[] actors = state.getRefinement();
1083                    Receiver[][] allReceiversArray = new Receiver[allReceivers.length][0];
1084
1085                    for (int i = 0; i < allReceivers.length; ++i) {
1086                        resultsList.clear();
1087
1088                        for (int j = 0; j < allReceivers[i].length; ++j) {
1089                            Receiver receiver = allReceivers[i][j];
1090                            Nameable cont = receiver.getContainer()
1091                                    .getContainer();
1092
1093                            if (cont == controller) {
1094                                resultsList.add(receiver);
1095                            } else {
1096                                // check transitions
1097                                Iterator transitions = state
1098                                        .nonpreemptiveTransitionList()
1099                                        .iterator();
1100
1101                                while (transitions.hasNext()) {
1102                                    Transition transition = (Transition) transitions
1103                                            .next();
1104                                    _checkActorsForReceiver(
1105                                            transition.getRefinement(), cont,
1106                                            receiver, resultsList);
1107                                }
1108
1109                                // check refinements
1110                                List stateList = new LinkedList();
1111                                stateList.add(state);
1112                                transitions = state.preemptiveTransitionList()
1113                                        .iterator();
1114
1115                                while (transitions.hasNext()) {
1116                                    Transition transition = (Transition) transitions
1117                                            .next();
1118                                    stateList
1119                                            .add(transition.destinationState());
1120                                    _checkActorsForReceiver(
1121                                            transition.getRefinement(), cont,
1122                                            receiver, resultsList);
1123                                }
1124
1125                                Iterator nextStates = stateList.iterator();
1126
1127                                while (nextStates.hasNext()) {
1128                                    actors = ((State) nextStates.next())
1129                                            .getRefinement();
1130                                    _checkActorsForReceiver(actors, cont,
1131                                            receiver, resultsList);
1132                                }
1133                            }
1134                        }
1135
1136                        allReceiversArray[i] = new Receiver[resultsList.size()];
1137
1138                        Object[] receivers = resultsList.toArray();
1139
1140                        for (int j = 0; j < receivers.length; ++j) {
1141                            allReceiversArray[i][j] = (Receiver) receivers[j];
1142                        }
1143                    }
1144
1145                    Map m = (HashMap) _localReceiverMaps.get(state);
1146                    m.put(port, allReceiversArray);
1147                }
1148            }
1149
1150            _localReceiverMapsVersion = workspace().getVersion();
1151            _currentLocalReceiverMap = (Map) _localReceiverMaps
1152                    .get(controller.currentState());
1153        } finally {
1154            workspace().doneReading();
1155        }
1156    }
1157
1158    /**
1159     * Return the receivers contained by ports connected to the inside
1160     * of the given input port and on the mode controller or the
1161     * refinement of its current state.
1162     *
1163     * @param port
1164     *            An input port of the container of this director.
1165     * @return The receivers that currently get inputs from the given port.
1166     * @exception IllegalActionException
1167     *                If there is no controller.
1168     */
1169    protected Receiver[][] _currentLocalReceivers(IOPort port)
1170            throws IllegalActionException {
1171        if (_localReceiverMapsVersion != workspace().getVersion()) {
1172            _buildLocalReceiverMaps();
1173        }
1174
1175        return (Receiver[][]) _currentLocalReceiverMap.get(port);
1176    }
1177
1178    /**
1179     * Return the last chosen transitions.
1180     *
1181     * @return The last chosen transitions, or null if there has been none.
1182     * @exception IllegalActionException
1183     *                If there is no controller.
1184     */
1185    protected Map<State, Transition> _getLastChosenTransition()
1186            throws IllegalActionException {
1187        FSMActor controller = getController();
1188        if (controller != null) {
1189            return controller._lastChosenTransitions;
1190        } else {
1191            return null;
1192        }
1193    }
1194
1195    /** Return the list used to keep track of refinements that have been
1196     *  fired. This is protected so that FSMDirector can mirror it with
1197     *  its own protected method so that subclasses of FSMDirector can
1198     *  access it.
1199     *  @return A list of actors to postfire.
1200     *  @exception IllegalActionException If can't get the controller.
1201     */
1202    protected List<Actor> _getStateRefinementsToPostfire()
1203            throws IllegalActionException {
1204        FSMActor controller = getController();
1205        return controller._getStateRefinementsToPostfire();
1206    }
1207
1208    /** Return the list used to keep track of refinements that have been
1209     *  fired. This is protected so that FSMDirector can mirror it with
1210     *  its own protected method so that subclasses of FSMDirector can
1211     *  access it.
1212     *  @return A list of actors to postfire.
1213     *  @exception IllegalActionException If can't get the controller.
1214     */
1215    protected List<Actor> _getTransitionRefinementsToPostfire()
1216            throws IllegalActionException {
1217        FSMActor controller = getController();
1218        return controller._getTransitionRefinementsToPostfire();
1219    }
1220
1221    /**
1222     * Set the value of the shadow variables for input ports of the controller actor.
1223     *
1224     * @exception IllegalActionException
1225     *                If a shadow variable cannot take the token read from its corresponding channel
1226     *                (should not occur).
1227     */
1228    protected void _readInputs() throws IllegalActionException {
1229        FSMActor controller = getController();
1230
1231        if (controller != null) {
1232            controller.readInputs();
1233        }
1234    }
1235
1236    /**
1237     * Set the value of the shadow variables for input ports of the
1238     * controller actor that are defined by output ports of the
1239     * refinement.
1240     *
1241     * @exception IllegalActionException
1242     *                If a shadow variable cannot take the token read from its corresponding channel
1243     *                (should not occur).
1244     */
1245    protected void _readOutputsFromRefinement() throws IllegalActionException {
1246        FSMActor controller = getController();
1247
1248        if (controller != null) {
1249            controller.readOutputsFromRefinement();
1250        }
1251    }
1252
1253    /**
1254     * Set the map from input ports to boolean flags indicating
1255     * whether a channel is connected to an output port of the
1256     * refinement of the current state. This method is called by
1257     * HDFFSMDirector.
1258     *
1259     * @exception IllegalActionException If the refinement specified
1260     *                for one of the states is not valid, or if there
1261     *                is no controller.
1262     */
1263    protected void _setCurrentConnectionMap() throws IllegalActionException {
1264        FSMActor controller = getController();
1265
1266        if (controller != null) {
1267            controller._setCurrentConnectionMap();
1268        } else {
1269            throw new IllegalActionException(this, "No controller!");
1270        }
1271    }
1272
1273    /**
1274     * Set the current state of this actor.
1275     *
1276     * @param state
1277     *            The state to set.
1278     * @exception IllegalActionException
1279     *                If there is no controller.
1280     */
1281    protected void _setCurrentState(State state) throws IllegalActionException {
1282        FSMActor controller = getController();
1283
1284        if (controller != null) {
1285            controller._currentState = state;
1286        } else {
1287            throw new IllegalActionException(this, "No controller!");
1288        }
1289    }
1290
1291    /**
1292     * Transfer at most one data token from the given output port of
1293     * the container to the ports it is connected to on the
1294     * outside. If the receiver is known to be empty, then send a
1295     * clear. If the receiver status is not known, do nothing.
1296     *
1297     * @param port The port to transfer tokens from.
1298     * @return True if the port has an inside token that was
1299     *         successfully transferred. Otherwise return false (or
1300     *         throw an exception).
1301     * @exception IllegalActionException
1302     *                If the port is not an opaque output port.
1303     *
1304     */
1305    @Override
1306    protected boolean _transferOutputs(IOPort port)
1307            throws IllegalActionException {
1308        boolean result = false;
1309        if (_debugging) {
1310            _debug("Calling transferOutputs on port: " + port.getFullName());
1311        }
1312
1313        if (!port.isOutput() || !port.isOpaque()) {
1314            throw new IllegalActionException(this, port,
1315                    "Attempted to transferOutputs on a port that "
1316                            + "is not an opaque input port.");
1317        }
1318
1319        for (int i = 0; i < port.getWidthInside(); i++) {
1320            try {
1321                if (port.isKnownInside(i)) {
1322                    if (port.hasTokenInside(i)) {
1323                        Token t = port.getInside(i);
1324                        if (_debugging) {
1325                            _debug(getName(), "transferring output " + t
1326                                    + " from " + port.getName());
1327                        }
1328                        port.send(i, t);
1329                        // mark this port as we sent a token to
1330                        // prevent sending a clear afterwards in this
1331                        // fixed point iteration
1332                        result = true;
1333                    } else {
1334                        // Set the port to be absent only if it is not known
1335                        // or not connected.
1336                        if (port.getWidth() <= i || !port.isKnown(i)) {
1337                            if (_debugging) {
1338                                _debug(getName(),
1339                                        "sending clear from " + port.getName());
1340                            }
1341                            // only send a clear (=absent) to the
1342                            // port, iff it is ensured that in the
1343                            // current state, there might not be an
1344                            // enabled transition (now or later) that
1345                            // then would produce a token on that port
1346                            FSMActor controller = getController();
1347                            // If a transition has been chosen, then it is safe to set
1348                            // the outputs absent. If no transition has been chosen,
1349                            // then we have to check to make sure that sometime later
1350                            // in the fixed-point iteration, it will not be possible
1351                            // for a transition to become enabled that might send data
1352                            // on this port.
1353                            if (controller._lastChosenTransitions.size() > 0
1354                                    && !controller.foundUnknown()
1355                                    || controller._isSafeToClear(port, i,
1356                                            controller._currentState, false,
1357                                            null)) {
1358                                port.send(i, null);
1359                            }
1360                        }
1361                    }
1362                }
1363            } catch (NoTokenException ex) {
1364                // this shouldn't happen.
1365                throw new InternalErrorException(this, ex, null);
1366            }
1367        }
1368        return result;
1369    }
1370
1371    ///////////////////////////////////////////////////////////////////
1372    ////                         protected variables               ////
1373
1374    /** Map from input ports of the modal model to the local receivers
1375     *  for the current state.
1376     */
1377    protected Map _currentLocalReceiverMap = null;
1378
1379    /** The _indexOffset is set by FSMActor during initialization of
1380     *  destination refinements upon committing to a reset transition
1381     *  in order to ensure that the destination refinement views its
1382     *  index as one larger than the current index.
1383     */
1384    protected int _indexOffset = 0;
1385
1386    /** Record for each state of the mode controller the map from input
1387     *  ports of the modal model to the local receivers when the mode
1388     *  controller is in that state.
1389     */
1390    protected Map _localReceiverMaps = new HashMap();
1391
1392    ///////////////////////////////////////////////////////////////////
1393    ////                         private methods                   ////
1394
1395    private void _checkActorsForReceiver(TypedActor[] actors, Nameable cont,
1396            Receiver receiver, List resultsList) {
1397        if (actors != null) {
1398            for (int k = 0; k < actors.length; ++k) {
1399                if (cont == actors[k]) {
1400                    if (!resultsList.contains(receiver)) {
1401                        resultsList.add(receiver);
1402                        break;
1403                    }
1404                }
1405            }
1406        }
1407    }
1408
1409    /** Create the controllerName attribute. */
1410    private void _createAttribute() {
1411        try {
1412            controllerName = new StringAttribute(this, "controllerName");
1413        } catch (NameDuplicationException ex) {
1414            throw new InternalErrorException(
1415                    getName() + "Cannot create " + "controllerName attribute.");
1416        } catch (IllegalActionException ex) {
1417            throw new InternalErrorException(
1418                    getName() + "Cannot create " + "controllerName attribute.");
1419        }
1420    }
1421
1422    /** Reset the output receivers, which are the inside receivers of
1423     *  the output ports of the container.
1424     *  @exception IllegalActionException If getting the receivers fails.
1425     */
1426    private void _resetOutputReceivers() throws IllegalActionException {
1427        List<IOPort> outputs = ((Actor) getContainer()).outputPortList();
1428        for (IOPort output : outputs) {
1429            if (_debugging) {
1430                _debug("Resetting inside receivers of output port: "
1431                        + output.getName());
1432            }
1433            Receiver[][] receivers = output.getInsideReceivers();
1434            if (receivers != null) {
1435                for (Receiver[] receiver : receivers) {
1436                    if (receiver != null) {
1437                        for (int j = 0; j < receiver.length; j++) {
1438                            if (receiver[j] instanceof FSMReceiver) {
1439                                receiver[j].reset();
1440                            }
1441                        }
1442                    }
1443                }
1444            }
1445        }
1446    }
1447
1448    ///////////////////////////////////////////////////////////////////
1449    ////                         private variables                 ////
1450
1451    /** Cached reference to mode controller. */
1452    private FSMActor _controller = null;
1453
1454    /** Version of cached reference to mode controller. */
1455    private long _controllerVersion = -1;
1456
1457    /** Version of the local receiver maps. */
1458    private long _localReceiverMapsVersion = -1;
1459}