001/* An HybridModalDirector governs the execution of the discrete dynamics of a 002 hybrid system model. 003 004 Copyright (c) 2005-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.continuous.kernel; 029 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033 034import ptolemy.actor.Actor; 035import ptolemy.actor.CompositeActor; 036import ptolemy.actor.Director; 037import ptolemy.actor.continuous.ContinuousStatefulComponent; 038import ptolemy.actor.continuous.ContinuousStepSizeController; 039import ptolemy.data.expr.ParseTreeEvaluator; 040import ptolemy.domains.modal.kernel.FSMActor; 041import ptolemy.domains.modal.kernel.FSMDirector; 042import ptolemy.domains.modal.kernel.ParseTreeEvaluatorForGuardExpression; 043import ptolemy.domains.modal.kernel.RelationList; 044import ptolemy.domains.modal.kernel.State; 045import ptolemy.domains.modal.kernel.Transition; 046import ptolemy.kernel.CompositeEntity; 047import ptolemy.kernel.util.IllegalActionException; 048import ptolemy.kernel.util.InternalErrorException; 049import ptolemy.kernel.util.NameDuplicationException; 050import ptolemy.kernel.util.NamedObj; 051import ptolemy.kernel.util.Workspace; 052 053/////////////////////////////////////////////////////////////////// 054//// HybridModalDirector 055 056/** 057 An HybridModalDirector governs the execution of the discrete dynamics of a hybrid 058 system model. It extends ModalDirector by implementing the ContinuousStatefulComponent 059 and ContinuousStepSizeController interfaces by delegating the function of those 060 interfaces to the currently active state refinement. 061 <p> 062 Note that when a multi-step solver is used, the guards on the transitions are 063 only evaluated when either the step size is zero or the multi-step solver has 064 just completed its last step. The guards are not evaluated during intermediate 065 steps. 066 <p> 067 This director is based on HSFSMDirector by Xiaojun Liu and Haiyang Zheng. 068 069 @author Edward A. Lee, Haiyang Zheng 070 @version $Id$ 071 @since Ptolemy II 5.2 072 @Pt.ProposedRating Yellow (eal) 073 @Pt.AcceptedRating Red (liuxj) 074 */ 075public class HybridModalDirector extends FSMDirector 076 implements ContinuousStatefulComponent, ContinuousStepSizeController { 077 078 /** Construct a director in the given container with the given name. 079 * The container argument must not be null, or a 080 * NullPointerException will be thrown. 081 * If the name argument is null, then the name is set to the 082 * empty string. Increment the version number of the workspace. 083 * @param container Container of this director. 084 * @param name Name of this director. 085 * @exception IllegalActionException If the name has a period in it, or 086 * the director is not compatible with the specified container. 087 * @exception NameDuplicationException If the container is not a 088 * CompositeActor and the name collides with an entity in the container. 089 */ 090 public HybridModalDirector(CompositeEntity container, String name) 091 throws IllegalActionException, NameDuplicationException { 092 super(container, name); 093 // _verbose = true; 094 } 095 096 /////////////////////////////////////////////////////////////////// 097 //// public methods //// 098 099 /** Clone the actor into the specified workspace. This calls the 100 * base class and then sets the attribute public members to refer 101 * to the attributes of the new actor. 102 * @param workspace The workspace for the new actor. 103 * @return A new FSMActor. 104 * @exception CloneNotSupportedException If a derived class contains 105 * an attribute that cannot be cloned. 106 */ 107 @Override 108 public Object clone(Workspace workspace) throws CloneNotSupportedException { 109 HybridModalDirector newObject = (HybridModalDirector) super.clone( 110 workspace); 111 newObject._enclosingContinuousDirector = null; 112 newObject._enclosingContinuousDirectorVersion = -1; 113 return newObject; 114 } 115 116 /** Fire the model model for one iteration. Override the base class 117 * to avoid firing the controller during intermediate stages of a 118 * multi-step solver. 119 * @exception IllegalActionException If there is more than one 120 * transition enabled and nondeterminism is not permitted, 121 * or there is no controller, or it is thrown by any 122 * choice action. 123 */ 124 @Override 125 public void fire() throws IllegalActionException { 126 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 127 if (enclosingDirector != null 128 && enclosingDirector._isIntermediateStep()) { 129 FSMActor controller = getController(); 130 State currentState = controller.currentState(); 131 if (_debugging) { 132 _debug("*** Firing during intermediate stage of solver " 133 + getFullName(), " at time " + getModelTime()); 134 _debug("Current state is:", currentState.getName()); 135 } 136 Actor[] stateRefinements = currentState.getRefinement(); 137 138 if (stateRefinements != null) { 139 for (int i = 0; i < stateRefinements.length; ++i) { 140 if (_stopRequested) { 141 break; 142 } 143 if (stateRefinements[i].prefire()) { 144 if (_debugging) { 145 _debug("Fire state refinement:", 146 stateRefinements[i].getName()); 147 } 148 stateRefinements[i].fire(); 149 _getStateRefinementsToPostfire() 150 .add(stateRefinements[i]); 151 } 152 } 153 } 154 controller.readOutputsFromRefinement(); 155 } else { 156 super.fire(); 157 } 158 } 159 160 /** Return error tolerance used for detecting enabled transitions. 161 * If there is an enclosing continuous director, then get the 162 * error tolerance from that director. Otherwise, return 1e-4. 163 * @return The error tolerance used for detecting enabled transitions. 164 */ 165 public final double getErrorTolerance() { 166 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 167 if (enclosingDirector == null) { 168 return 1e-4; 169 } 170 return enclosingDirector.getErrorTolerance(); 171 } 172 173 /** Return the parse tree evaluator used to evaluate guard expressions. 174 * In this class, an instance 175 * of {@link ParseTreeEvaluatorForGuardExpression} is returned. 176 * The parse tree evaluator is set to construction mode. 177 * @return ParseTreeEvaluator used to evaluate guard expressions. 178 */ 179 @Override 180 public ParseTreeEvaluator getParseTreeEvaluator() { 181 RelationList relationList = new RelationList(); 182 ParseTreeEvaluatorForGuardExpression evaluator = new ParseTreeEvaluatorForGuardExpression( 183 relationList, getErrorTolerance()); 184 evaluator.setConstructionMode(); 185 return evaluator; 186 } 187 188 /** Return true if all actors that were fired in the current iteration 189 * report that the step size is accurate and if no transition is enabled. 190 * @return True if the current step is accurate. 191 */ 192 @Override 193 public boolean isStepSizeAccurate() { 194 _lastDistanceToBoundary = 0.0; 195 _distanceToBoundary = 0.0; 196 boolean foundActor = false; 197 198 // Double iterator over two lists. 199 Iterator actors = null; 200 try { 201 actors = new ActorsFiredIterator(); 202 } catch (IllegalActionException e) { 203 throw new InternalErrorException(e); 204 } 205 while (actors.hasNext()) { 206 Actor actor = (Actor) actors.next(); 207 foundActor = true; 208 if (actor instanceof ContinuousStepSizeController) { 209 if (!((ContinuousStepSizeController) actor) 210 .isStepSizeAccurate()) { 211 return false; 212 } 213 } else if (actor instanceof CompositeActor) { 214 // Delegate to the director. 215 Director director = actor.getDirector(); 216 if (director instanceof ContinuousStepSizeController) { 217 if (!((ContinuousStepSizeController) director) 218 .isStepSizeAccurate()) { 219 return false; 220 } 221 } 222 } 223 } 224 // Next check for enabled transitions. Do this even if the above 225 // result is false, because as a side effect we calculate the 226 // data needed to suggest the next step size when refinedStepSize() 227 // is called. 228 // All non-preemptive and preemptive transitions are checked below, 229 // because even if a preemptive transition is enabled, the non-preemptive 230 // transitions never even get a chance to be evaluated. 231 // However, do this only if there is an enclosing 232 // ContinuousDirector. 233 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 234 if (enclosingDirector == null) { 235 return true; 236 } 237 try { 238 // Check whether there is any preemptive transition enabled. 239 FSMActor controller = getController(); 240 State currentState = controller.currentState(); 241 List transitionList = currentState.outgoingPort 242 .linkedRelationList(); 243 List preemptiveEnabledTransitions = controller 244 .enabledTransitions(transitionList, true, false); 245 246 // Check whether there is any non-preemptive transition enabled. 247 List nonpreemptiveEnabledTransitions = controller 248 .enabledTransitions(transitionList, false, false); 249 250 // Check whether there is any event detected for preemptive transitions. 251 Transition preemptiveTrWithEvent = _checkEvent( 252 currentState.preemptiveTransitionList()); 253 254 // Check whether there is any event detected for 255 // nonpreemptive transitions. 256 Transition nonPreemptiveTrWithEvent = _checkEvent( 257 currentState.nonpreemptiveTransitionList()); 258 259 if (_debugging && _verbose) { 260 if (preemptiveEnabledTransitions.size() != 0) { 261 _debug("Found enabled preemptive transitions."); 262 } 263 if (nonpreemptiveEnabledTransitions.size() != 0) { 264 _debug("Found enabled non-preemptive transitions."); 265 } 266 if (preemptiveTrWithEvent != null) { 267 _debug("Detected event for transition: " 268 + preemptiveTrWithEvent.getGuardExpression()); 269 } 270 if (nonPreemptiveTrWithEvent != null) { 271 _debug("Detected event for transition: " 272 + nonPreemptiveTrWithEvent.getGuardExpression()); 273 } 274 } 275 276 // If there is no transition enabled, the last step size is 277 // accurate for transitions. The states will be committed at 278 // the postfire method. 279 // Set the local variables to be used to suggest a step 280 // size in the next call to refinedStepSize(). 281 if (preemptiveEnabledTransitions.size() == 0 282 && nonpreemptiveEnabledTransitions.size() == 0 283 && preemptiveTrWithEvent == null 284 && nonPreemptiveTrWithEvent == null) { 285 _lastDistanceToBoundary = 0.0; 286 _distanceToBoundary = 0.0; 287 return true; 288 } else { 289 Transition enabledTransition = null; 290 291 // We check the maximum difference of the relations that change 292 // their status for step size refinement. 293 _distanceToBoundary = Double.MIN_VALUE; 294 295 Iterator iterator = preemptiveEnabledTransitions.iterator(); 296 297 while (iterator.hasNext()) { 298 Transition transition = (Transition) iterator.next(); 299 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 300 .getParseTreeEvaluator(); 301 RelationList relationList = parseTreeEvaluator 302 .getRelationList(); 303 304 double distanceToBoundary = relationList 305 .getMaximumDifference(); 306 // The distance to boundary is the difference between 307 // the value of a variable in a relation (comparison 308 // operation) and the threshold value against which it 309 // is being compared. The previous distance is the last 310 // committed distance. 311 if (distanceToBoundary > _distanceToBoundary) { 312 _distanceToBoundary = distanceToBoundary; 313 _lastDistanceToBoundary = relationList 314 .getPreviousMaximumDistance(); 315 enabledTransition = transition; 316 } 317 } 318 319 iterator = nonpreemptiveEnabledTransitions.iterator(); 320 321 while (iterator.hasNext()) { 322 Transition transition = (Transition) iterator.next(); 323 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 324 .getParseTreeEvaluator(); 325 RelationList relationList = parseTreeEvaluator 326 .getRelationList(); 327 328 double distanceToBoundary = relationList 329 .getMaximumDifference(); 330 331 if (distanceToBoundary > _distanceToBoundary) { 332 _distanceToBoundary = distanceToBoundary; 333 _lastDistanceToBoundary = relationList 334 .getPreviousMaximumDistance(); 335 enabledTransition = transition; 336 } 337 } 338 339 if (preemptiveTrWithEvent != null) { 340 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) preemptiveTrWithEvent 341 .getParseTreeEvaluator(); 342 RelationList relationList = parseTreeEvaluator 343 .getRelationList(); 344 double distanceToBoundary = relationList 345 .getMaximumDifference(); 346 347 if (distanceToBoundary > _distanceToBoundary) { 348 _distanceToBoundary = distanceToBoundary; 349 _lastDistanceToBoundary = relationList 350 .getPreviousMaximumDistance(); 351 enabledTransition = preemptiveTrWithEvent; 352 } 353 } 354 355 if (nonPreemptiveTrWithEvent != null) { 356 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) nonPreemptiveTrWithEvent 357 .getParseTreeEvaluator(); 358 RelationList relationList = parseTreeEvaluator 359 .getRelationList(); 360 double distanceToBoundary = relationList 361 .getMaximumDifference(); 362 363 if (distanceToBoundary > _distanceToBoundary) { 364 _distanceToBoundary = distanceToBoundary; 365 _lastDistanceToBoundary = relationList 366 .getPreviousMaximumDistance(); 367 enabledTransition = nonPreemptiveTrWithEvent; 368 } 369 } 370 371 if (_debugging && _verbose) { 372 if (enabledTransition != null) { 373 _debug("The guard " + enabledTransition.getGuardExpression() 374 + " has the biggest difference to boundary as " 375 + _distanceToBoundary); 376 } else { 377 _debug("No enabled transition."); 378 } 379 } 380 381 // If we are close enough, then the flipping of the guard is OK. 382 double errorTolerance = enclosingDirector.getErrorTolerance(); 383 if (_distanceToBoundary < errorTolerance) { 384 _distanceToBoundary = 0.0; 385 _lastDistanceToBoundary = 0.0; 386 return true; 387 } else { 388 // Return false only if there is a refinement. 389 if (!foundActor) { 390 return true; 391 } 392 return false; 393 } 394 } 395 } catch (Throwable throwable) { 396 // Can not evaluate guard expression. 397 throw new InternalErrorException(throwable); 398 } 399 } 400 401 /** Override the base class so that if there is no enabled transition 402 * then we record for each comparison operation in each 403 * guard expression the distance between the current value of the 404 * variable being compared and the threshold. 405 * @exception IllegalActionException If thrown by any commit action 406 * or there is no controller. 407 */ 408 @Override 409 public boolean postfire() throws IllegalActionException { 410 FSMActor controller = getController(); 411 State currentState = controller.currentState(); 412 Map<State, Transition> lastChosenTransitions = controller 413 .getLastChosenTransitions(); 414 if (lastChosenTransitions.size() == 0) { 415 // No transition was chosen. 416 // Record the current values on either side of each 417 // comparison operation (called a relation) in each guard. 418 Iterator iterator = currentState.nonpreemptiveTransitionList() 419 .listIterator(); 420 while (iterator.hasNext()) { 421 Transition transition = (Transition) iterator.next(); 422 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 423 .getParseTreeEvaluator(); 424 RelationList relationList = parseTreeEvaluator 425 .getRelationList(); 426 relationList.commitRelationValues(); 427 } 428 429 iterator = currentState.preemptiveTransitionList().listIterator(); 430 while (iterator.hasNext()) { 431 Transition transition = (Transition) iterator.next(); 432 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 433 .getParseTreeEvaluator(); 434 RelationList relationList = parseTreeEvaluator 435 .getRelationList(); 436 relationList.commitRelationValues(); 437 } 438 } else { 439 // It is important to clear the history information of the 440 // relation list since after this breakpoint, no history 441 // information is valid. 442 Iterator iterator = currentState.nonpreemptiveTransitionList() 443 .listIterator(); 444 while (iterator.hasNext()) { 445 Transition transition = (Transition) iterator.next(); 446 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 447 .getParseTreeEvaluator(); 448 RelationList relationList = parseTreeEvaluator 449 .getRelationList(); 450 relationList.resetRelationList(); 451 } 452 453 iterator = currentState.preemptiveTransitionList().listIterator(); 454 while (iterator.hasNext()) { 455 Transition transition = (Transition) iterator.next(); 456 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 457 .getParseTreeEvaluator(); 458 RelationList relationList = parseTreeEvaluator 459 .getRelationList(); 460 relationList.resetRelationList(); 461 } 462 } 463 return super.postfire(); 464 } 465 466 /** Override the base class to set current time to match that of 467 * the enclosing executive director, if there is one, regardless 468 * of whether that time is in the future or past. The superclass 469 * sets current time only if the local time is less than the 470 * environment time. 471 * Initialize the firing of the director by resetting all receivers to 472 * unknown. 473 * @return Whatever the superclass returns. 474 * @exception IllegalActionException If thrown by the superclass. 475 */ 476 @Override 477 public boolean prefire() throws IllegalActionException { 478 boolean result = super.prefire(); 479 // If any refinement is not ready to fire, stop prefiring the 480 // remaining actors, call super.prefire(), and return false; 481 State st = getController().currentState(); 482 Actor[] actors = st.getRefinement(); 483 if (actors != null) { 484 for (int i = 0; i < actors.length; ++i) { 485 if (_stopRequested) { 486 break; 487 } 488 if (_debugging) { 489 _debug("Prefire the refinement of the current state: ", 490 actors[i].getFullName()); 491 } 492 if (!actors[i].prefire()) { 493 result = false; 494 break; 495 } 496 } 497 } 498 return result; 499 } 500 501 /** Return the minimum of the step sizes suggested by any 502 * actors that were fired in the current iteration. 503 * @return The suggested refined step size. 504 * @exception IllegalActionException If the step size cannot be further refined. 505 */ 506 @Override 507 public double refinedStepSize() throws IllegalActionException { 508 double result = Double.POSITIVE_INFINITY; 509 Iterator actors = new ActorsFiredIterator(); 510 while (actors.hasNext()) { 511 Actor actor = (Actor) actors.next(); 512 if (actor instanceof ContinuousStepSizeController) { 513 double candidate = ((ContinuousStepSizeController) actor) 514 .refinedStepSize(); 515 if (candidate < result) { 516 result = candidate; 517 } 518 } else if (actor instanceof CompositeActor) { 519 // Delegate to the director. 520 Director director = actor.getDirector(); 521 if (director instanceof ContinuousStepSizeController) { 522 double candidate = ((ContinuousStepSizeController) director) 523 .refinedStepSize(); 524 if (candidate < result) { 525 result = candidate; 526 } 527 } 528 } 529 } 530 531 // If this is inside a ContinuousDirector and there was a guard that 532 // became enabled, then guess as to the new step size based on the 533 // linear interpolation performed in the last invocation of 534 // isStepSizeAccurate(). 535 if (_distanceToBoundary > 0.0) { 536 ContinuousDirector enclosingDirector = _enclosingContinuousDirector(); 537 if (enclosingDirector != null) { 538 double errorTolerance = enclosingDirector.getErrorTolerance(); 539 double currentStepSize = enclosingDirector.getCurrentStepSize(); 540 541 // Linear interpolation to refine the step size. 542 // The "distances" here are not in time, but in the value 543 // of continuous variables. The "last distance" is the distance 544 // as of the previous postfire(), i.e., the previously committed 545 // values of the continuous variables. 546 // Note the step size is refined such that the distanceToBoundary 547 // expected at the new step size is half of errorTolerance. 548 double refinedStepSize = currentStepSize 549 * (_lastDistanceToBoundary + errorTolerance / 2) 550 / (_lastDistanceToBoundary + _distanceToBoundary); 551 552 result = Math.min(result, refinedStepSize); 553 // Note: To see how well this algorithm is working, you can 554 // uncomment the following line. 555 // System.out.println("refined step size: " + result); 556 } 557 } 558 559 return result; 560 } 561 562 /** Roll back to committed state. 563 * This will roll back any actors that were fired in the current iteration. 564 * @exception IllegalActionException If the rollback attempts to go 565 * back further than the last committed time. 566 */ 567 @Override 568 public void rollBackToCommittedState() throws IllegalActionException { 569 Iterator actors = null; 570 try { 571 actors = new ActorsFiredIterator(); 572 } catch (IllegalActionException e1) { 573 throw new InternalErrorException(e1); 574 } 575 while (actors.hasNext()) { 576 Actor actor = (Actor) actors.next(); 577 if (actor instanceof ContinuousStatefulComponent) { 578 ((ContinuousStatefulComponent) actor) 579 .rollBackToCommittedState(); 580 } else if (actor instanceof CompositeActor) { 581 // Delegate to the director. 582 Director director = actor.getDirector(); 583 // On May 5, 2015, in Ptolemy Hackers, Stefan 584 // Resmerita wrote: "When rolling back the refinement 585 // of a state, this method delegates to the director 586 // of the refinement only if that one is a 587 // ContinuousDirector. Thus, models where the 588 // refinement director is a HybridModalDirector may 589 // not work. 590 591 // "This is illustrated in the attached example, which 592 // is a modified BouncingBall model where the 593 // refinement of the original "free" state has been 594 // moved one level down in FSM hierarchy. 595 596 // "At first sight, it would make sense to delegate 597 // rollback to a ContinuousStatefulComponent rather 598 // than a ContinuousDirector, but I'm unsure about 599 // possible side effects... " 600 601 if (director instanceof ContinuousStatefulComponent) { 602 ((ContinuousStatefulComponent) director) 603 .rollBackToCommittedState(); 604 } 605 } 606 } 607 // Reset the last chosen transition of the FSM controller to null 608 // because upon moving forward again we may choose a different 609 // transition. 610 try { 611 FSMActor controller = getController(); 612 if (controller != null) { 613 Map<State, Transition> chosenTransitions = controller 614 .getLastChosenTransitions(); 615 chosenTransitions.clear(); 616 } 617 } catch (IllegalActionException e) { 618 // This should not occur. 619 throw new InternalErrorException(e); 620 } 621 } 622 623 /** Return the minimum of the step sizes suggested by any 624 * actors that were fired in current iteration. 625 * @return The suggested next step size. 626 * @exception IllegalActionException If an actor requests an 627 * illegal step size. 628 */ 629 @Override 630 public double suggestedStepSize() throws IllegalActionException { 631 double result = Double.POSITIVE_INFINITY; 632 Iterator actors = new ActorsFiredIterator(); 633 while (actors.hasNext()) { 634 Actor actor = (Actor) actors.next(); 635 if (actor instanceof ContinuousStepSizeController) { 636 double candidate = ((ContinuousStepSizeController) actor) 637 .suggestedStepSize(); 638 if (candidate < result) { 639 result = candidate; 640 } 641 } else if (actor instanceof CompositeActor) { 642 // Delegate to the director. 643 Director director = actor.getDirector(); 644 if (director instanceof ContinuousStepSizeController) { 645 double candidate = ((ContinuousStepSizeController) director) 646 .suggestedStepSize(); 647 if (candidate < result) { 648 result = candidate; 649 } 650 } 651 } 652 } 653 return result; 654 } 655 656 /////////////////////////////////////////////////////////////////// 657 //// protected methods //// 658 659 /** Return the enclosing continuous director, or null if there 660 * is none. The enclosing continuous director is a director 661 * above this in the hierarchy, possibly separated by composite 662 * actors with other foreign directors. 663 * @return The enclosing ContinuousDirector, or null if there is none. 664 */ 665 protected ContinuousDirector _enclosingContinuousDirector() { 666 if (_enclosingContinuousDirectorVersion != _workspace.getVersion()) { 667 // Update the cache. 668 _enclosingContinuousDirector = null; 669 NamedObj container = getContainer(); 670 while (container != null) { 671 // On the first pass, the container will be the immediate 672 // container, whose director is this one, so update the 673 // container first. 674 container = container.getContainer(); 675 if (container instanceof Actor) { 676 Director director = ((Actor) container).getDirector(); 677 if (director instanceof ContinuousDirector) { 678 _enclosingContinuousDirector = (ContinuousDirector) director; 679 break; 680 } 681 } 682 } 683 _enclosingContinuousDirectorVersion = _workspace.getVersion(); 684 } 685 return _enclosingContinuousDirector; 686 } 687 688 /////////////////////////////////////////////////////////////////// 689 //// private methods //// 690 691 /** Return the first transition in the specified list that has 692 * an "event", which is a change in the boolean value of its guard 693 * since the last evaluation. 694 * @return A transition with a change in the value of the guard. 695 */ 696 private Transition _checkEvent(List transitionList) { 697 Iterator transitionRelations = transitionList.iterator(); 698 699 while (transitionRelations.hasNext() && !_stopRequested) { 700 Transition transition = (Transition) transitionRelations.next(); 701 ParseTreeEvaluatorForGuardExpression parseTreeEvaluator = (ParseTreeEvaluatorForGuardExpression) transition 702 .getParseTreeEvaluator(); 703 RelationList relationList = parseTreeEvaluator.getRelationList(); 704 if (relationList.hasEvent()) { 705 return transition; 706 } 707 } 708 return null; 709 } 710 711 /////////////////////////////////////////////////////////////////// 712 //// private variables //// 713 714 /** Local variable to indicate the distance to boundary. */ 715 private double _distanceToBoundary = 0.0; 716 717 /** The enclosing continuous director, if there is one. */ 718 private ContinuousDirector _enclosingContinuousDirector = null; 719 720 /** The version for __enclosingContinuousDirector. */ 721 private long _enclosingContinuousDirectorVersion = -1; 722 723 /** Local variable to indicate the last committed distance to boundary. */ 724 private double _lastDistanceToBoundary = 0.0; 725 726 /////////////////////////////////////////////////////////////////// 727 //// inner classes //// 728 729 /** Iterator over _stateRefinementsToPostfire followed by 730 * _transitionRefinementsToPostfire. 731 */ 732 private class ActorsFiredIterator implements Iterator { 733 public ActorsFiredIterator() throws IllegalActionException { 734 _iterator = _getStateRefinementsToPostfire().iterator(); 735 } 736 737 @Override 738 public boolean hasNext() { 739 if (_iterator.hasNext()) { 740 return true; 741 } 742 try { 743 _iterator = _getTransitionRefinementsToPostfire().iterator(); 744 } catch (IllegalActionException e) { 745 throw new InternalErrorException(e); 746 } 747 return _iterator.hasNext(); 748 } 749 750 @Override 751 public Object next() { 752 return _iterator.next(); 753 } 754 755 @Override 756 public void remove() { 757 // Ignore. 758 } 759 760 private Iterator _iterator; 761 } 762}