001/* A MultirateFSM director that extends FSMDirector by supporting production
002 and consumption of multiple tokens on a port in a firing.
003
004 Copyright (c) 2004-2015 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 */
028package ptolemy.domains.modal.kernel;
029
030import java.util.Iterator;
031import java.util.LinkedList;
032import java.util.List;
033
034import ptolemy.actor.Actor;
035import ptolemy.actor.CompositeActor;
036import ptolemy.actor.Director;
037import ptolemy.actor.IOPort;
038import ptolemy.actor.NoTokenException;
039import ptolemy.actor.Receiver;
040import ptolemy.actor.TypedActor;
041import ptolemy.actor.TypedCompositeActor;
042import ptolemy.actor.parameters.ParameterPort;
043import ptolemy.actor.sched.StaticSchedulingDirector;
044import ptolemy.actor.util.ConstVariableModelAnalysis;
045import ptolemy.actor.util.DFUtilities;
046import ptolemy.actor.util.DependencyDeclaration;
047import ptolemy.actor.util.Time;
048import ptolemy.data.Token;
049import ptolemy.data.expr.Variable;
050import ptolemy.domains.sdf.kernel.SDFReceiver;
051import ptolemy.kernel.ComponentEntity;
052import ptolemy.kernel.CompositeEntity;
053import ptolemy.kernel.util.IllegalActionException;
054import ptolemy.kernel.util.InternalErrorException;
055import ptolemy.kernel.util.NameDuplicationException;
056
057/**
058 This director extends FSMDirector by supporting production and consumption
059 of multiple tokens on a port in a firing. This director assumes that every
060 state has exactly one refinement, unless the state has an immediate
061 transition whose guard evaluates to true in the iteration in which the
062 state is entered. In addition, preemptive transitions are not allowed.
063 Hence, each time when a modal model is fired,
064 the current state always has a state refinement that is fired and
065 will consume and produce outputs.
066 <p>
067 The number of tokens to be transferred from an input port of the modal model
068 is at most the token consumption rate inferred by the inside port of the
069 current state refinement. The number of tokens to be transferred from an
070 output port of the state refinement is exactly the token production rate
071 inferred by the state refinement. If there are not enough tokens available
072 from the refinement, an exception is thrown. The default token consumption
073 and production rate of a port is 1.
074 <p>
075 When a state transition occurs, this director compares the port rates of
076 the destination state refinement with that of the current state refinement.
077 If the rates are different, then it invalidate the schedule of the executive
078 director of the modal model and updates the port rates of the modal model to be
079 the port rates of the destination state refinement.
080
081 @author Ye Zhou and Edward A. Lee
082 @version $Id$
083 @since Ptolemy II 8.0
084 @Pt.ProposedRating Red (hyzheng)
085 @Pt.AcceptedRating Red (hyzheng)
086 @see FSMDirector
087 */
088public class MultirateFSMDirector extends FSMDirector {
089    /** Construct a director in the given container with the given name.
090     *  The container argument must not be null, or a
091     *  NullPointerException will be thrown.
092     *  If the name argument is null, then the name is set to the
093     *  empty string. Increment the version number of the workspace.
094     *  @param container Container of this director.
095     *  @param name Name of this director.
096     *  @exception IllegalActionException If the name has a period in it, or
097     *   the director is not compatible with the specified container.
098     *  @exception NameDuplicationException If the container is not a
099     *   CompositeActor and the name collides with an entity in the container.
100     */
101    public MultirateFSMDirector(CompositeEntity container, String name)
102            throws IllegalActionException, NameDuplicationException {
103        super(container, name);
104    }
105
106    ///////////////////////////////////////////////////////////////////
107    ////                         public methods                    ////
108
109    /** Fire the modal model.
110     *  If the refinement of the current state of the mode controller is
111     *  ready to fire, then fire the current refinement.
112     *  @exception IllegalActionException If there is no controller or
113     *   the current state has no or more than one refinement, or if
114     *   the current state has any preemptive transitions.
115     */
116    @Override
117    public void fire() throws IllegalActionException {
118        FSMActor controller = getController();
119        State currentState = controller.currentState();
120        Actor[] actors = currentState.getRefinement();
121        if (actors == null || actors.length != 1) {
122            throw new IllegalActionException(currentState,
123                    "Current state is required to have exactly one refinement: "
124                            + currentState.getName());
125        }
126        if (currentState.preemptiveTransitionList().size() > 0) {
127            throw new IllegalActionException(currentState,
128                    "Preemptive transitions are not allowed by MultirateFSMDirector: "
129                            + currentState.getName());
130        }
131        super.fire();
132    }
133
134    /** Override the base class to ignore the fireAt() call if the specified
135     *  actor is the controller and the time is the current time.
136     *  The controller calls fireAt()
137     *  if the destination state is enabled, but this director already handles
138     *  transient states.
139     *  @param actor The actor scheduled to be fired.
140     *  @param time The time at which to fire the actor.
141     *  @return If the argument is the controller, then return Time.NEGATIVE_INFINITY,
142     *   to indicate that the request is being ignored. Otherwise, return what the
143     *   superclass returns.
144     *  @exception IllegalActionException If thrown by the executive director.
145     */
146    @Override
147    public Time fireAt(Actor actor, Time time) throws IllegalActionException {
148        FSMActor controller = getController();
149        Time currentTime = getModelTime();
150        if (actor != controller || !currentTime.equals(time)) {
151            return super.fireAt(actor, time);
152        }
153        return Time.NEGATIVE_INFINITY;
154    }
155
156    /** Override the base class to ignore the fireAt() call if the specified
157     *  actor is the controller. The controller calls fireAtCurrentTime()
158     *  if the destination state is enabled, but this director already handles
159     *  transient states.
160     *  @param actor The actor scheduled to be fired.
161     *  @return If the argument is the controller, then return Time.NEGATIVE_INFINITY,
162     *   to indicate that the request is being ignored. Otherwise, return what the
163     *   superclass returns.
164     *  @exception IllegalActionException If thrown by the executive director.
165     */
166    @Override
167    public Time fireAtCurrentTime(Actor actor) throws IllegalActionException {
168        FSMActor controller = getController();
169        if (actor != controller) {
170            return super.fireAtCurrentTime(actor);
171        }
172        return Time.NEGATIVE_INFINITY;
173    }
174
175    /** Initialize the mode controller and all the refinements. Notify updates
176     *  of port rates to the upper level director, and invalidate the upper
177     *  level schedule.
178     *  @exception IllegalActionException If the refinement has no or more
179     *   than one refinement, or the initialize() method of one of the
180     *   associated actors throws it.
181     */
182    @Override
183    public void initialize() throws IllegalActionException {
184        try {
185            _inInitialize = true;
186            // Initialize all the refinements in the sub-layer
187            // FSMDirectors and recompute the schedule.
188            // Note that this will set the state to the
189            // initial state, or to states reached from that
190            // by immediate transitions.
191            super.initialize();
192
193            // Set the production and consumption rates of the ports
194            // according to the current refinement.
195            // This has to be after initialize because the initial
196            // immediate transitions may affect the rates.
197            _setProductionConsumptionRates();
198
199            invalidateSchedule();
200        } finally {
201            _inInitialize = false;
202        }
203    }
204
205    /** Return a new receiver of a type compatible with this director.
206     *  This returns an instance of SDFReceiver.
207     *  @return A new SDFReceiver.
208     */
209    @Override
210    public Receiver newReceiver() {
211        return new SDFReceiver();
212    }
213
214    /** Postfire the modal model and commit the transition.
215     *  @return True if the postfire() method of current state refinement
216     *   and that of the controller are both true.
217     *  @exception IllegalActionException If a refinement throws it, or
218     *   if there is no controller.
219     */
220    @Override
221    public boolean postfire() throws IllegalActionException {
222        return _doPostfire();
223    }
224
225    /** Preinitialize all actors deeply contained by the container
226     *  of this director. Find the "non-transient initial state", which is the
227     *  first non-transient state reached from the initial state. Propagate the
228     *  consumption and production rates of the non-transient initial state out
229     *  to corresponding ports of the container of this director.
230     *  @exception IllegalActionException If there is no controller, or if the
231     *   non-transient initial state has no or more than one refinement, or if
232     *   the preinitialize() method of one of the associated actors throws it.
233     */
234    @Override
235    public void preinitialize() throws IllegalActionException {
236        // The following is just a check to make sure the top-level
237        // director is not a MultirateFSMDirector. It will throw
238        // an exception if it is.
239        _getEnclosingDomainActor();
240
241        super.preinitialize();
242
243        // Note that the following is done again initialize().
244        // But it seems to be necessary to do it here the first time
245        // to cause the schedule to be computed. I.e, we might get
246        // the scheduler throwing an exception before we even get
247        // to initialize because the current state refinement is
248        // not compatible (e.g., in SDF, balance equations don't solve).
249        _setProductionConsumptionRates();
250    }
251
252    /** Return a boolean to indicate whether a ModalModel under control
253     *  of this director supports multirate firing.
254     *  @return True indicating a ModalModel under control of this director
255     *   does support multirate firing.
256     */
257    @Override
258    public boolean supportMultirateFiring() {
259        return true;
260    }
261
262    /** Transfer data from the input port of the container to the
263     *  ports connected to the inside of the input port and on the
264     *  mode controller or the refinement of its current state. This
265     *  method overrides the base class method by transferring at most
266     *  the number of tokens specified by the input consumption rate.
267     *  @param port The input port to transfer tokens from.
268     *  @return True if data are transferred.
269     *  @exception IllegalActionException If the port is not an opaque
270     *   input port.
271     */
272    @Override
273    public boolean transferInputs(IOPort port) throws IllegalActionException {
274        if (!port.isInput() || !port.isOpaque()) {
275            throw new IllegalActionException(this, port,
276                    "transferInputs: port argument is not an opaque"
277                            + "input port.");
278        }
279
280        boolean transferred = false;
281        Receiver[][] insideReceivers = _currentLocalReceivers(port);
282        int rate = DFUtilities.getTokenConsumptionRate(port);
283
284        for (int i = 0; i < port.getWidth(); i++) {
285            // For each channel
286            try {
287                if (insideReceivers != null && insideReceivers[i] != null) {
288                    for (int j = 0; j < insideReceivers[i].length; j++) {
289                        // Since we only transfer number of tokens
290                        // declared by the port rate, we should be
291                        // safe to clear the receivers.  Maybe we
292                        // should move this step to prefire() or
293                        // postfire(), as in FSMDirector. But if the
294                        // port consumes more tokens than the
295                        // refinement actually consumes (The SDF
296                        // sneaky trick), then perhaps we are in great
297                        // trouble.
298                        insideReceivers[i][j].clear();
299                    }
300
301                    // Transfer number of tokens at most the declared port rate.
302                    // Note: we don't throw exception if there are fewer tokens
303                    // available. The prefire() method of the refinement simply
304                    // return false.
305                    for (int k = 0; k < rate; k++) {
306                        if (port.hasToken(i)) {
307                            Token token = port.get(i);
308                            port.sendInside(i, token);
309                        }
310                    }
311
312                    // Successfully transferred data, so return true.
313                    transferred = true;
314                }
315            } catch (NoTokenException ex) {
316                // this shouldn't happen.
317                throw new InternalErrorException(
318                        "Director.transferInputs: Internal error: " + ex);
319            }
320        }
321
322        return transferred;
323    }
324
325    /** Transfer data from an output port of the current refinement actor
326     *  to the ports it is connected to on the outside. This method overrides
327     *  the base class method in that this method will transfer exactly <i>k</i>
328     *  tokens in the receivers, where <i>k</i> is the port rate if it is
329     *  declared by the port.
330     *  @param port The port to transfer tokens from.
331     *  @return True if data are transferred.
332     *  @exception IllegalActionException If the port is not an opaque
333     *   output port.
334     */
335    @Override
336    public boolean transferOutputs(IOPort port) throws IllegalActionException {
337        if (!port.isOutput() || !port.isOpaque()) {
338            throw new IllegalActionException(this, port,
339                    "MultirateFSMDirector: transferOutputs():"
340                            + "  port argument is not an opaque output port.");
341        }
342
343        boolean transferred = false;
344        int rate = DFUtilities.getRate(port);
345        Receiver[][] insideReceivers = port.getInsideReceivers();
346
347        for (int i = 0; i < port.getWidth(); i++) {
348            if (insideReceivers != null && insideReceivers[i] != null) {
349                for (int k = 0; k < rate; k++) {
350                    // Only transfer number of tokens declared by the port
351                    // rate. Throw exception if there are not enough tokens.
352                    try {
353                        Token token = port.getInside(i);
354                        port.send(i, token);
355                    } catch (NoTokenException ex) {
356                        // Do not throw an exception if we are in initialize
357                        // because in that case, these are initial tokens.
358                        if (!_inInitialize) {
359                            throw new InternalErrorException(
360                                    "Director.transferOutputs: "
361                                            + "Not enough tokens for port "
362                                            + port.getName() + " " + ex);
363                        }
364                    }
365                }
366            }
367
368            transferred = true;
369        }
370
371        return transferred;
372    }
373
374    ///////////////////////////////////////////////////////////////////
375    ////                         protected methods                 ////
376
377    /** Add a DependencyDeclaration (with the name
378     *  "_MultirateFSMRateDependencyDeclaration") to the variable with the
379     *  given name in the given port that declares the variable is dependent
380     *  on the given list of variables.  If a dependency declaration with that
381     *  name already exists, then simply set its dependents list to the given
382     *  list.
383     *  @param analysis The object that contains the dependency declarations.
384     *  @param port The IOPort to get rate variables from.
385     *  @param name The name of the IOPort.
386     *  @param dependents The dependents that the dependency declaration of the
387     *  given IO port depends on.
388     *  @exception IllegalActionException If a valid rate variable from the
389     *  given port can not be found, or the variable does not contain a
390     *  DependencyDeclaration attribute, or a new DependencyDeclaration object
391     *  can not be created, or can not associated with the analysis object the
392     *  newly created DependencyDeclaration object.
393     */
394    protected void _declareDependency(ConstVariableModelAnalysis analysis,
395            IOPort port, String name, List dependents)
396            throws IllegalActionException {
397        Variable variable = DFUtilities.getRateVariable(port, name);
398        DependencyDeclaration declaration = (DependencyDeclaration) variable
399                .getAttribute("_MultirateFSMRateDependencyDeclaration",
400                        DependencyDeclaration.class);
401
402        if (declaration == null) {
403            try {
404                declaration = new DependencyDeclaration(variable,
405                        "_MultirateFSMRateDependencyDeclaration");
406            } catch (NameDuplicationException ex) {
407                throw new InternalErrorException(variable, ex,
408                        "Failed to create DependencyDeclaration "
409                                + "_MultirateFSMRateDependencyDeclaration");
410            }
411        }
412
413        declaration.setDependents(dependents);
414        analysis.addDependencyDeclaration(declaration);
415    }
416
417    /** Declare the reconfiguration dependency in the given analysis
418     *  associated with the parameter name of the given port.
419     *  @param analysis The object that contains the dependency declarations.
420     *  @param port The IOPort to get refinement rate variables from.
421     *  @param parameterName The name of the rate variables.
422     *  @exception IllegalActionException If can not get the refinement rate
423     *  variables from the given port, or can not add the dependency declaration
424     *  of the given port to the analysis object, or a declared constant rate
425     *  variable does not contain a constant value.
426     */
427    protected void _declareReconfigurationDependencyForRefinementRateVariables(
428            ConstVariableModelAnalysis analysis, IOPort port,
429            String parameterName) throws IllegalActionException {
430        List refinementRateVariables = _getRefinementRateVariables(port,
431                parameterName);
432        _declareDependency(analysis, port, parameterName,
433                refinementRateVariables);
434
435        boolean isConstantAndIdentical = true;
436        Token value = null;
437
438        Iterator variables = refinementRateVariables.iterator();
439
440        while (variables.hasNext() && isConstantAndIdentical) {
441            Variable rateVariable = (Variable) variables.next();
442            isConstantAndIdentical = isConstantAndIdentical
443                    && analysis.getChangeContext(rateVariable) == null;
444
445            if (isConstantAndIdentical) {
446                Token newValue = analysis.getConstantValue(rateVariable);
447
448                if (value == null) {
449                    value = newValue;
450                } else {
451                    isConstantAndIdentical = isConstantAndIdentical
452                            && newValue.equals(value);
453                }
454            }
455        }
456
457        //if (!isConstantAndIdentical) {
458        // Has this as ChangeContext.
459        // System.out.println("Found rate parameter " + parameterName
460        //    + " of port " + port.getFullName() + " that changes.");
461        // FIXME: Declare this somehow so that we can check it.
462        //}
463    }
464
465    /** Postfire the modal model and commit the transition.
466     *  If a transition is taken, the update the token production and consumption
467     *  rates according to the new refinement.
468     *  @return True if the super class postfire() method returns true.
469     *  @exception IllegalActionException If there is no controller, or if the
470     *   destination state has no or more than one refinement, or if the state
471     *   refinement throws it.
472     */
473    protected boolean _doPostfire() throws IllegalActionException {
474        // Note: This is a protected method so that HDFFSMDirector can defer
475        // the actual work of postfire to occur between iterations.
476
477        // Commit the transition. This will postfire the refinement
478        // director.
479        boolean superPostfire = super.postfire();
480
481        if (getController().wasTransitionTaken()) {
482            _setProductionConsumptionRates();
483        }
484        return superPostfire && !_finishRequested;
485    }
486
487    /** If the container of this director does not have an MultirateFSMDirector
488     *  as its executive director, then return the container. Otherwise,
489     *  move up the hierarchy until we reach a container actor that does
490     *  not have a MultirateFSMDirector director for its executive director,
491     *  and return that container.
492     *  @return a composite actor that does not contain a MultirateFSMDirector
493     *  object.
494     *  @exception IllegalActionException If the top-level director is an
495     *   MultirateFSMDirector. This director is intended for use only inside
496     *   some other domain.
497     */
498    protected CompositeActor _getEnclosingDomainActor()
499            throws IllegalActionException {
500        if (isEmbedded()) {
501            // Keep moving up towards the toplevel of the hierarchy until
502            // we find an executive director that is not an instance of
503            // FSMDirector or until we reach the toplevel composite actor.
504            CompositeActor container = (CompositeActor) getContainer();
505            Director director = container.getExecutiveDirector();
506
507            while (director != null) {
508                if (director instanceof FSMDirector) {
509                    if (!director.isEmbedded()) {
510                        break;
511                    }
512                    // Move up another level in the hierarchy.
513                    container = (CompositeActor) container.getContainer();
514                    director = container.getExecutiveDirector();
515                } else {
516                    return container;
517                }
518            }
519        }
520
521        throw new IllegalActionException(this,
522                "MultirateFSMDirector director must be contained within another domain.");
523    }
524
525    /** Return the set of variables with the given parameter name that are
526     *  contained by ports connected to the given port on the inside.
527     *  @param port The given port.
528     *  @param parameterName The given parameter name.
529     *  @return A list of the variables with the given parameter name.
530     *  @exception IllegalActionException If can not get a rate variable
531     *  from the port that is connected to the given port from inside.
532     */
533    protected List _getRefinementRateVariables(IOPort port,
534            String parameterName) throws IllegalActionException {
535        List list = new LinkedList();
536
537        Iterator insidePorts = port.deepInsidePortList().iterator();
538
539        while (insidePorts.hasNext()) {
540            IOPort insidePort = (IOPort) insidePorts.next();
541            Variable variable = DFUtilities.getRateVariable(insidePort,
542                    parameterName);
543
544            if (variable != null) {
545                list.add(variable);
546            }
547        }
548
549        return list;
550    }
551
552    /** Set the production and consumption rates based on the
553     *  refinement of the current state, after descending through a hierarchy of
554     *  state machines.
555     *  @exception IllegalActionException If the initial state does not
556     *   have exactly one refinement.
557     */
558    protected void _setProductionConsumptionRates()
559            throws IllegalActionException {
560        FSMActor controller = getController();
561        State currentState = controller.currentState();
562
563        // Check that the state has a refinement.
564        TypedActor[] currentRefinements = currentState.getRefinement();
565        if (currentRefinements == null || currentRefinements.length != 1) {
566            throw new IllegalActionException(this,
567                    "Destination state (after any immediate transitions)"
568                            + " is required to have exactly one refinement: "
569                            + currentState.getName());
570        }
571
572        TypedCompositeActor currentRefinement = (TypedCompositeActor) currentRefinements[0];
573        Director refinementDir = currentRefinement.getDirector();
574        // If the refinement is a nested state machine, recursively descend until we
575        // find a refinement that is not.
576        while (refinementDir instanceof FSMDirector && refinementDir != this) {
577            controller = ((FSMDirector) refinementDir).getController();
578            currentState = controller.currentState();
579            currentRefinements = currentState.getRefinement();
580            if (currentRefinements == null || currentRefinements.length != 1) {
581                throw new IllegalActionException(this,
582                        "Initial state (after any immediate transitions)"
583                                + " is required to have exactly one refinement: "
584                                + currentState.getName());
585            }
586            // Set the refinement and its director.
587            currentRefinement = (TypedCompositeActor) currentRefinements[0];
588            refinementDir = currentRefinement.getDirector();
589        }
590
591        if (refinementDir instanceof StaticSchedulingDirector
592                && refinementDir != this) {
593            // Force the schedule to be computed, if necessary.
594            // Update the refinement's production and consumption rates.
595            refinementDir.invalidateSchedule();
596            ((StaticSchedulingDirector) refinementDir).getScheduler()
597                    .getSchedule();
598        }
599
600        // Record consumption and production rates in the ports of this actor.
601        boolean inputRateChanged = _updateInputTokenConsumptionRates(
602                currentRefinement);
603        boolean outputRateChanged = _updateOutputTokenProductionRates(
604                currentRefinement);
605        // Tell the upper level scheduler that the current schedule
606        // is no longer valid.
607        // FIXME: Apparently, this can't work because that
608        // director is in the middle of an iteration if a reset
609        // transition is being taken. Hopefully, the reconfiguration
610        // constraints set below will catch this.
611        if (inputRateChanged || outputRateChanged) {
612            CompositeActor actor = _getEnclosingDomainActor();
613            Director director = actor.getExecutiveDirector();
614            director.invalidateSchedule();
615        }
616
617        // Declare reconfiguration constraints on the ports of the
618        // actor.  The constraints indicate that the ports are
619        // reconfigured whenever any refinement rate parameter of
620        // a corresponding port is reconfigured.  Additionally,
621        // all rate parameters are reconfigured every time the
622        // controller makes a state transition, unless the
623        // corresponding refinement rate parameters are constant,
624        // and have the same value.  (Note that the controller
625        // itself makes transitions less often if its executive director
626        // is an HDFFSMDirector, which is a subclass of MultirateFSMDirector.
627        ConstVariableModelAnalysis analysis = ConstVariableModelAnalysis
628                .getAnalysis(this);
629        CompositeActor model = (CompositeActor) getContainer();
630
631        for (Iterator ports = model.portList().iterator(); ports.hasNext();) {
632            IOPort port = (IOPort) ports.next();
633
634            if (!(port instanceof ParameterPort)) {
635                if (port.isInput()) {
636                    _declareReconfigurationDependencyForRefinementRateVariables(
637                            analysis, port, "tokenConsumptionRate");
638                }
639
640                if (port.isOutput()) {
641                    _declareReconfigurationDependencyForRefinementRateVariables(
642                            analysis, port, "tokenProductionRate");
643                    _declareReconfigurationDependencyForRefinementRateVariables(
644                            analysis, port, "tokenInitProduction");
645                }
646            }
647        }
648    }
649
650    /** Extract the token consumption rates from the input ports of the current
651     *  refinement and update the rates of the input ports of the modal model
652     *  containing the refinement.
653     *  @param actor The current refinement.
654     *  @return True if any input token consumption rate is changed from its
655     *   previous value.
656     *  @exception IllegalActionException If can not find the controller, or
657     *  the port connections between controller and refinements are not
658     *  correct, or can not get valid token consumption rates for input ports.
659     */
660    protected boolean _updateInputTokenConsumptionRates(
661            TypedCompositeActor actor) throws IllegalActionException {
662        boolean inputRateChanged = false;
663
664        // Get the current refinement's container.
665        CompositeActor refineInPortContainer = (CompositeActor) actor
666                .getContainer();
667
668        // Get all of its input ports of the current refinement actor.
669        Iterator refineInPorts = actor.inputPortList().iterator();
670
671        while (refineInPorts.hasNext()) {
672            IOPort refineInPort = (IOPort) refineInPorts.next();
673
674            // Get all of the input ports this port is linked to on
675            // the outside (should only consist of 1 port).
676            Iterator inPortsOutside = refineInPort.deepConnectedInPortList()
677                    .iterator();
678
679            if (!inPortsOutside.hasNext()) {
680                throw new IllegalActionException("Current "
681                        + "state's refining actor has an input port not"
682                        + "connected to an input port of its container.");
683            }
684
685            while (inPortsOutside.hasNext()) {
686                IOPort inputPortOutside = (IOPort) inPortsOutside.next();
687
688                // Check if the current port is contained by the
689                // container of the current refinement.
690                ComponentEntity thisPortContainer = (ComponentEntity) inputPortOutside
691                        .getContainer();
692
693                // FIXME: Name match is an expensive check. Depth in hierarchy?
694                if (thisPortContainer.getFullName()
695                        .equals(refineInPortContainer.getFullName())) {
696                    // set the outside port rate equal to the port rate
697                    // of the refinement.
698                    int previousPortRate = DFUtilities
699                            .getTokenConsumptionRate(inputPortOutside);
700                    int portRateToSet = DFUtilities
701                            .getTokenConsumptionRate(refineInPort);
702
703                    if (previousPortRate != portRateToSet) {
704                        inputRateChanged = true;
705                    }
706                    // Do the following even if rates haven't changed to ensure that
707                    // the variables exist.
708
709                    DFUtilities.setTokenConsumptionRate(inputPortOutside,
710                            portRateToSet);
711
712                    // Also set the rate of the controller.
713                    FSMActor controller = getController();
714                    IOPort controllerPort = (IOPort) controller
715                            .getPort(inputPortOutside.getName());
716                    if (controllerPort != null) {
717                        DFUtilities.setTokenConsumptionRate(controllerPort,
718                                portRateToSet);
719                    }
720                }
721            }
722        }
723
724        return inputRateChanged;
725    }
726
727    /** Extract the token production rates from the output ports of the current
728     *  refinement and update the production and initial production rates of the
729     *  output ports of the modal model containing the refinement.
730     *  @param actor The current refinement.
731     *  @return True if any of the output token production rate is changed from
732     *   its previous value.
733     *  @exception IllegalActionException If we cannot get valid token
734     *  consumption rates for input ports.
735     */
736    protected boolean _updateOutputTokenProductionRates(
737            TypedCompositeActor actor) throws IllegalActionException {
738        boolean outputRateChanged = false;
739
740        // Get the current refinement's container.
741        CompositeActor refineOutPortContainer = (CompositeActor) actor
742                .getContainer();
743
744        // Get all of the current refinement's output ports.
745        Iterator refineOutPorts = actor.outputPortList().iterator();
746
747        while (refineOutPorts.hasNext()) {
748            IOPort refineOutPort = (IOPort) refineOutPorts.next();
749
750            Iterator outPortsOutside = refineOutPort.deepConnectedOutPortList()
751                    .iterator();
752
753            while (outPortsOutside.hasNext()) {
754                IOPort outputPortOutside = (IOPort) outPortsOutside.next();
755
756                // Check if the current port is contained by the
757                // container of the current refinment.
758                ComponentEntity thisPortContainer = (ComponentEntity) outputPortOutside
759                        .getContainer();
760
761                if (thisPortContainer.getFullName()
762                        .equals(refineOutPortContainer.getFullName())) {
763                    // set the outside port rate equal to the port rate
764                    // of the refinement.
765                    int previousPortRate = DFUtilities
766                            .getTokenProductionRate(outputPortOutside);
767                    int portRateToSet = DFUtilities
768                            .getTokenProductionRate(refineOutPort);
769                    int portInitRateToSet = DFUtilities
770                            .getTokenInitProduction(refineOutPort);
771
772                    if (previousPortRate != portRateToSet) {
773                        outputRateChanged = true;
774                    }
775                    // Do the following even if rates haven't changed to ensure that
776                    // the variables exist.
777                    DFUtilities.setTokenProductionRate(outputPortOutside,
778                            portRateToSet);
779                    DFUtilities.setTokenInitProduction(outputPortOutside,
780                            portInitRateToSet);
781
782                    // Also set the rate of the controller.
783                    // Note that we set the _consumption_ rate, because this an input port
784                    // (as well as an output port) for the controller.
785                    FSMActor controller = getController();
786                    IOPort controllerPort = (IOPort) controller
787                            .getPort(outputPortOutside.getName());
788                    if (controllerPort != null) {
789                        DFUtilities.setTokenConsumptionRate(controllerPort,
790                                portRateToSet);
791                    }
792                }
793            }
794        }
795
796        return outputRateChanged;
797    }
798
799    ///////////////////////////////////////////////////////////////////
800    ////                         private variables                 ////
801
802    /** Indicator that we are in initialize. */
803    private boolean _inInitialize;
804}