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}