001/* This director extends FSMDirector by consuming only input tokens 002 that are needed in the current state. 003 004 Copyright (c) 2004-2014 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.HashSet; 031import java.util.Iterator; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035 036import ptolemy.actor.Actor; 037import ptolemy.actor.CompositeActor; 038import ptolemy.actor.IOPort; 039import ptolemy.actor.TypedActor; 040import ptolemy.actor.util.DFUtilities; 041import ptolemy.data.expr.ASTPtAssignmentNode; 042import ptolemy.data.expr.ASTPtRootNode; 043import ptolemy.data.expr.ParseTreeFreeVariableCollector; 044import ptolemy.data.expr.ParserScope; 045import ptolemy.data.expr.PtParser; 046import ptolemy.kernel.CompositeEntity; 047import ptolemy.kernel.util.IllegalActionException; 048import ptolemy.kernel.util.NameDuplicationException; 049import ptolemy.kernel.util.Workspace; 050 051/////////////////////////////////////////////////////////////////// 052//// NonStrictFSMDirector 053 054/** 055 This director extends FSMDirector by consuming only input tokens that 056 are needed in the current state. An input port will consume at most one 057 token if: 058 <p> 059 1. The port is referred by any guard expression of the preemptive 060 transitions leaving the current state, the output actions 061 and/or set actions of the enabled transition. 062 <p> 063 2. No preemptive transition is enabled and the port is referred by 064 the refinements of the current state, any guard expression of the 065 nonpreemptive transitions leaving the current state, the output 066 actions and/or set actions of the enabled transition. 067 <p> 068 A port is said to be referred by a guard/output action/set action 069 expression of a transition if the port name appears in that expression. 070 A port is said to be referred by a state refinement if the it is 071 not a dangling port and has a consumption rate greater than zero in 072 the refinement. 073 <p> 074 FIXME: This is highly preliminary. Missing capabilities: 075 FIXME: Currently this director uses the default receiver of FSMDirector, 076 which is a mailbox, so there is no way to consume more than one token. 077 This director could use a different receiver and support a syntax in 078 the guard expression language to support consumption of more than one 079 token. 080 FIXME: This director does not support immediate transitions. 081 082 @author Ye Zhou 083 @version $Id$ 084 @since Ptolemy II 8.0 085 @Pt.ProposedRating Red (zhouye) 086 @Pt.AcceptedRating Red (cxh) 087 */ 088public class NonStrictFSMDirector 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 not a 099 * CompositeActor and the name collides with an entity in the container. 100 */ 101 public NonStrictFSMDirector(CompositeEntity container, String name) 102 throws IllegalActionException, NameDuplicationException { 103 super(container, name); 104 } 105 106 /////////////////////////////////////////////////////////////////// 107 //// public methods //// 108 109 /** Clone the actor into the specified workspace. This calls the 110 * base class and then sets the attribute public members to refer 111 * to the attributes of the new actor. 112 * @param workspace The workspace for the new actor. 113 * @return A new FSMActor. 114 * @exception CloneNotSupportedException If a derived class contains 115 * an attribute that cannot be cloned. 116 */ 117 @Override 118 public Object clone(Workspace workspace) throws CloneNotSupportedException { 119 NonStrictFSMDirector newObject = (NonStrictFSMDirector) super.clone( 120 workspace); 121 newObject._nonpreemptiveTransitionsInputs = new HashSet(); 122 newObject._outputActionReferredInputPorts = new HashSet(); 123 newObject._preemptiveTransitionsInputs = new HashSet(); 124 newObject._referredInputPorts = new HashSet(); 125 newObject._refinementReferredInputPorts = new HashSet(); 126 newObject._setActionReferredInputPorts = new HashSet(); 127 return newObject; 128 } 129 130 /** Fire the modal model. The preemptive transitions from the current 131 * state are examined. If there is more than one transition enabled, 132 * an exception is thrown. If there is exactly one preemptive transition 133 * enabled, then it is chosen. Get additional input ports referred by 134 * the output actions and set actions of the enabled transition and 135 * executed the output actions. The refinements of the current state will 136 * not be fired. 137 * <p> 138 * If no preemptive transition is enabled, get additional input ports 139 * referred by the refinements of the current state and transfer at most 140 * one token from these input ports. Fire the refinements. After this, 141 * get additional input ports referred by the nonpreemptive transitions 142 * from the current state and transfer at most one token from these input 143 * ports. Then examine the nonpreemptive transitions. If there is more 144 * than one transition enabled, an exception is thrown. If there is 145 * exactly one nonpreemptive transition enabled, then it is chosen. Get 146 * additional input ports referred by the output actions and set actions 147 * of the enabled transition and executed the output actions. 148 * @exception IllegalActionException If the super class throws it. 149 */ 150 @Override 151 public void fire() throws IllegalActionException { 152 FSMActor controller = getController(); 153 controller.readInputs(); 154 155 CompositeActor container = (CompositeActor) getContainer(); 156 List inputPortList = container.inputPortList(); 157 State currentState = controller.currentState(); 158 List transitionList = currentState.outgoingPort.linkedRelationList(); 159 160 // Choose a preemptive transition 161 List enabledTransitions = controller.enabledTransitions(transitionList, 162 true, false); 163 164 // Ensure that if there are multiple enabled transitions, all of them 165 // must be nondeterministic. 166 if (enabledTransitions.size() > 1) { 167 Iterator transitions = enabledTransitions.iterator(); 168 169 while (transitions.hasNext()) { 170 Transition enabledTransition = (Transition) transitions.next(); 171 172 if (!enabledTransition.isNondeterministic()) { 173 throw new MultipleEnabledTransitionsException( 174 controller.currentState(), 175 "Multiple enabled transitions found but " 176 + enabledTransition.getName() 177 + " is deterministic."); 178 } 179 } 180 } 181 182 Transition enabledTransition = null; 183 184 // Randomly choose one transition from the list of the 185 // enabled trnasitions. 186 int length = enabledTransitions.size(); 187 188 if (length != 0) { 189 // Since the size of the list of enabled transitions usually (almost 190 // always) is less than the maximum value of integer. We can safely 191 // do the cast from long to int in the following statement. 192 int randomChoice = (int) Math.floor(Math.random() * length); 193 194 // There is tiny chance that randomChoice equals length. 195 // When this happens, we deduct 1 from the randomChoice. 196 if (randomChoice == length) { 197 randomChoice--; 198 } 199 200 enabledTransition = (Transition) enabledTransitions 201 .get(randomChoice); 202 } 203 204 controller.setLastChosenTransition(enabledTransition); 205 206 if (enabledTransition == null) { 207 // Get the inputs needed by the refinement. 208 Actor[] actors = currentState.getRefinement(); 209 getRefinementReferredInputPorts(currentState); 210 211 // Transfer additional inputs needed by the refinement. 212 for (int i = 0; i < inputPortList.size(); i++) { 213 IOPort port = (IOPort) inputPortList.get(i); 214 215 if (_refinementReferredInputPorts.contains(port) 216 && !_referredInputPorts.contains(port)) { 217 super.transferInputs(port); 218 controller.readInputs(); 219 _referredInputPorts.add(port); 220 } 221 } 222 223 // Fire the refinement. 224 if (actors != null) { 225 for (int i = 0; i < actors.length; ++i) { 226 if (_stopRequested) { 227 break; 228 } 229 230 if (actors[i].prefire()) { 231 actors[i].fire(); 232 actors[i].postfire(); 233 } 234 } 235 } 236 237 controller.readOutputsFromRefinement(); 238 239 // Get inputs needed by the nonpreemptive transitions. 240 getNonpreemptiveTransitionsReferredInputPorts(currentState); 241 242 // Transfer additional inputs needed by the refinement. 243 for (int i = 0; i < inputPortList.size(); i++) { 244 IOPort port = (IOPort) inputPortList.get(i); 245 246 if (_nonpreemptiveTransitionsInputs.contains(port) 247 && !_referredInputPorts.contains(port)) { 248 super.transferInputs(port); 249 controller.readInputs(); 250 _referredInputPorts.add(port); 251 } 252 } 253 254 // Choose an error transition 255 enabledTransitions = controller.enabledTransitions( 256 currentState.errorTransitionList(), false, false); 257 if (enabledTransitions.size() == 0) { 258 //choose a nonpremptive transition 259 enabledTransitions = controller 260 .enabledTransitions(transitionList, false, false); 261 } 262 263 // Ensure that if there are multiple enabled transitions, all of them 264 // must be nondeterministic. 265 if (enabledTransitions.size() > 1) { 266 Iterator transitions = enabledTransitions.iterator(); 267 268 while (transitions.hasNext()) { 269 Transition transition = (Transition) transitions.next(); 270 271 if (!transition.isNondeterministic()) { 272 throw new MultipleEnabledTransitionsException( 273 controller.currentState(), 274 "Multiple enabled transitions found but " 275 + transition.getName() 276 + " is deterministic."); 277 } 278 } 279 } 280 281 // Randomly choose one transition from the list of the 282 // enabled trnasitions. 283 length = enabledTransitions.size(); 284 285 if (length != 0) { 286 // Since the size of the list of enabled transitions usually (almost 287 // always) is less than the maximum value of integer. We can safely 288 // do the cast from long to int in the following statement. 289 int randomChoice = (int) Math.floor(Math.random() * length); 290 291 // There is tiny chance that randomChoice equals length. 292 // When this happens, we deduct 1 from the randomChoice. 293 if (randomChoice == length) { 294 randomChoice--; 295 } 296 297 enabledTransition = (Transition) enabledTransitions 298 .get(randomChoice); 299 } 300 controller.setLastChosenTransition(enabledTransition); 301 } 302 303 if (enabledTransition != null) { 304 // Get additional inputs needed for output actions and set actions 305 // of the enabled transition. 306 getOutputActionsReferredInputPorts(enabledTransition); 307 getSetActionsReferredInputPorts(enabledTransition); 308 309 for (int i = 0; i < inputPortList.size(); i++) { 310 IOPort port = (IOPort) inputPortList.get(i); 311 312 if (_outputActionReferredInputPorts.contains(port) 313 && !_referredInputPorts.contains(port)) { 314 super.transferInputs(port); 315 controller.readInputs(); 316 _referredInputPorts.add(port); 317 } 318 } 319 320 controller.readInputs(); 321 322 // execute output actions. 323 Iterator actions = enabledTransition.choiceActionList().iterator(); 324 325 while (actions.hasNext()) { 326 Action action = (Action) actions.next(); 327 action.execute(); 328 } 329 330 // Get additional input ports needed by set actions of 331 // the enabeld transition. 332 for (int i = 0; i < inputPortList.size(); i++) { 333 IOPort port = (IOPort) inputPortList.get(i); 334 335 if (_setActionReferredInputPorts.contains(port) 336 && !_referredInputPorts.contains(port)) { 337 super.transferInputs(port); 338 controller.readInputs(); 339 _referredInputPorts.add(port); 340 } 341 } 342 343 controller.readInputs(); 344 } 345 346 controller.setLastChosenTransition(enabledTransition); 347 } 348 349 /** Given a state, get a set of input ports referred in the guards of 350 * the preemptive transitions leaving that state. 351 * @param state The given state. 352 * @exception IllegalActionException If there is no controller or 353 * if any guard expression is illegal. 354 */ 355 public void getNonpreemptiveTransitionsReferredInputPorts(State state) 356 throws IllegalActionException { 357 List nonpreemptiveTransitionList = state.nonpreemptiveTransitionList(); 358 359 _nonpreemptiveTransitionsInputs = getTransitionReferredInputPorts( 360 nonpreemptiveTransitionList); 361 } 362 363 /** Given a transition, get a set of input ports referred in the 364 * outputActions of that transition. 365 * @param transition The transition. 366 * @exception IllegalActionException If there is no controller or if 367 * the outputActions is illegal. 368 */ 369 public void getOutputActionsReferredInputPorts(Transition transition) 370 throws IllegalActionException { 371 _outputActionReferredInputPorts.clear(); 372 373 String string = transition.outputActions.getExpression(); 374 PtParser parser = new PtParser(); 375 ASTPtRootNode parseTree; 376 ParseTreeFreeVariableCollector variableCollector = new ParseTreeFreeVariableCollector(); 377 FSMActor controller = getController(); 378 ParserScope scope = controller.getPortScope(); 379 380 if (!string.equals("")) { 381 Map map = parser.generateAssignmentMap(string); 382 Set set /* Dead Local Store: = new HashSet()*/; 383 384 for (Iterator names = map.entrySet().iterator(); names.hasNext();) { 385 Map.Entry entry = (Map.Entry) names.next(); 386 ASTPtAssignmentNode node = (ASTPtAssignmentNode) entry 387 .getValue(); 388 parseTree = node.getExpressionTree(); 389 set = variableCollector.collectFreeVariables(parseTree, scope); 390 getReferredInputPorts(set, _outputActionReferredInputPorts); 391 } 392 } 393 } 394 395 /** Given a state, get a set of input ports referred in the guards of 396 * the preemptive transitions leaving that state. 397 * @param state The given state. 398 * @exception IllegalActionException If there is no controller or 399 * if any guard expression is illegal. 400 */ 401 public void getPreemptiveTransitionsReferredInputPorts(State state) 402 throws IllegalActionException { 403 List preemptiveTransitionList = state.preemptiveTransitionList(); 404 405 _preemptiveTransitionsInputs = getTransitionReferredInputPorts( 406 preemptiveTransitionList); 407 } 408 409 /** Given a set of ports, get those that are input ports of the container 410 * and put them in the indicated referred set. 411 * @param portSet The given set of ports 412 * @param referredInputPorts The referred set. 413 */ 414 public void getReferredInputPorts(Set portSet, Set referredInputPorts) { 415 CompositeActor container = (CompositeActor) getContainer(); 416 List inputPortList = container.inputPortList(); 417 418 for (int i = 0; i < inputPortList.size(); i++) { 419 IOPort inputPort = (IOPort) inputPortList.get(i); 420 421 if (portSet.contains(inputPort.getName())) { 422 referredInputPorts.add(inputPort); 423 } 424 } 425 } 426 427 /** Given a state, get a set of input ports referred by the refinements 428 * of that state. 429 * @param state The given state. 430 * @exception IllegalActionException If refinement with given name is not 431 * found, or if the port rate does not contain a valid expression. 432 */ 433 public void getRefinementReferredInputPorts(State state) 434 throws IllegalActionException { 435 _refinementReferredInputPorts.clear(); 436 437 TypedActor[] refinements = state.getRefinement(); 438 CompositeActor container = (CompositeActor) getContainer(); 439 440 if (refinements != null) { 441 for (TypedActor refinement : refinements) { 442 Iterator inputPorts = refinement.inputPortList().iterator(); 443 444 while (inputPorts.hasNext()) { 445 IOPort inputPort = (IOPort) inputPorts.next(); 446 447 if (inputPort.isOutsideConnected() 448 && DFUtilities.getRate(inputPort) > 0) { 449 Iterator inputPortsOutside = inputPort 450 .deepConnectedInPortList().iterator(); 451 452 while (inputPortsOutside.hasNext()) { 453 IOPort inputPortOutside = (IOPort) inputPortsOutside 454 .next(); 455 456 if (inputPortOutside.getContainer() == container 457 && !_refinementReferredInputPorts 458 .contains(inputPortOutside)) { 459 _refinementReferredInputPorts 460 .add(inputPortOutside); 461 } 462 } 463 } 464 } 465 } 466 } 467 } 468 469 /** Given a transition, get a set of input ports referred in the set 470 * actions of that transition. 471 * @param transition The given transition. 472 * @exception IllegalActionException If there is no controller or 473 * if any set action expression is illegal. 474 */ 475 public void getSetActionsReferredInputPorts(Transition transition) 476 throws IllegalActionException { 477 _setActionReferredInputPorts.clear(); 478 479 String string = transition.setActions.getExpression(); 480 PtParser parser = new PtParser(); 481 ASTPtRootNode parseTree; 482 ParseTreeFreeVariableCollector variableCollector = new ParseTreeFreeVariableCollector(); 483 FSMActor controller = getController(); 484 ParserScope scope = controller.getPortScope(); 485 486 if (!string.equals("")) { 487 Map map = parser.generateAssignmentMap(string); 488 Set set /* Dead Local Store: = new HashSet()*/; 489 490 for (Iterator names = map.entrySet().iterator(); names.hasNext();) { 491 Map.Entry entry = (Map.Entry) names.next(); 492 ASTPtAssignmentNode node = (ASTPtAssignmentNode) entry 493 .getValue(); 494 parseTree = node.getExpressionTree(); 495 set = variableCollector.collectFreeVariables(parseTree, scope); 496 getReferredInputPorts(set, _setActionReferredInputPorts); 497 } 498 } 499 } 500 501 /** Given a list of transitions, get a set of referred input ports 502 * in the guard expressions of all the transitions leaving this state. 503 * @param transitionList The list of Transitions. 504 * @return A set of input ports referred by the guard expressions 505 * of the given transition list. 506 * @exception IllegalActionException If there is no controller or if 507 * the guard expression is illegal. 508 */ 509 public Set getTransitionReferredInputPorts(List transitionList) 510 throws IllegalActionException { 511 Set transitionsReferredInputPorts = new HashSet(); 512 513 Iterator transitions = transitionList.iterator(); 514 515 while (transitions.hasNext()) { 516 Transition transition = (Transition) transitions.next(); 517 String string = transition.getGuardExpression(); 518 519 if (string.equals("") && !transition.isErrorTransition()) { 520 throw new IllegalActionException(this, "guard expression on " 521 + transition.getName() + "is null!"); 522 } 523 if (!transition.isErrorTransition()) { 524 PtParser parser = new PtParser(); 525 ASTPtRootNode parseTree = parser.generateParseTree(string); 526 ParseTreeFreeVariableCollector variableCollector = new ParseTreeFreeVariableCollector(); 527 FSMActor controller = getController(); 528 ParserScope scope = controller.getPortScope(); 529 Set set = variableCollector.collectFreeVariables(parseTree, 530 scope); 531 getReferredInputPorts(set, transitionsReferredInputPorts); 532 } 533 } 534 535 return transitionsReferredInputPorts; 536 } 537 538 /** Initialize the director. Get the referred input ports in the guard 539 * expressions of all preemptive transitions leaving the initial state. 540 * @exception IllegalActionException If the super class throws it. 541 */ 542 @Override 543 public void initialize() throws IllegalActionException { 544 super.initialize(); 545 546 FSMActor controller = getController(); 547 getPreemptiveTransitionsReferredInputPorts( 548 controller.getInitialState()); 549 _referredInputPorts.clear(); 550 _referredInputPorts.addAll(_preemptiveTransitionsInputs); 551 } 552 553 /** Call the postfire() method of the super class. Get the referred 554 * input ports in the guard expressions of all preemptive transitions 555 * that go out from the current state. 556 * @exception IllegalActionException If the super class throws it. 557 */ 558 @Override 559 public boolean postfire() throws IllegalActionException { 560 boolean postfireValue = super.postfire(); 561 FSMActor controller = getController(); 562 getPreemptiveTransitionsReferredInputPorts(controller.currentState()); 563 _referredInputPorts.clear(); 564 _referredInputPorts.addAll(_preemptiveTransitionsInputs); 565 return postfireValue; 566 } 567 568 /** Override the super class by only transferring inputs for those 569 * input ports that are referred by the guard expressions of the 570 * preemptive transitions leaving the current state. 571 */ 572 @Override 573 public boolean transferInputs(IOPort port) throws IllegalActionException { 574 if (_preemptiveTransitionsInputs.contains(port)) { 575 return super.transferInputs(port); 576 } else { 577 return true; 578 } 579 } 580 581 /////////////////////////////////////////////////////////////////// 582 //// private variables //// 583 // A set of input ports that are referred by the guard expressions 584 // of the nonpreemptive transitions leaving the current state. 585 private Set _nonpreemptiveTransitionsInputs = new HashSet(); 586 587 // A set of input ports that are referred by the output actions of 588 // the enabled transition. 589 private Set _outputActionReferredInputPorts = new HashSet(); 590 591 // A set of input ports that are referred by the guard expressions 592 // of the preemptive transitions leaving the current state. 593 private Set _preemptiveTransitionsInputs = new HashSet(); 594 595 // A set of input ports that are referred. 596 private Set _referredInputPorts = new HashSet(); 597 598 // A set of input ports that are referred by the refinements 599 // of the current state. 600 private Set _refinementReferredInputPorts = new HashSet(); 601 602 // A set of input ports that are referred by the set actions of 603 // the enabled transition. 604 private Set _setActionReferredInputPorts = new HashSet(); 605}