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}