001/* A ComponentActor is outside component and inside actor. 002 003 Copyright (c) 1997-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 027 */ 028package ptolemy.component; 029 030import java.util.Iterator; 031 032import ptolemy.actor.Director; 033import ptolemy.actor.Executable; 034import ptolemy.actor.IOPort; 035import ptolemy.actor.IORelation; 036import ptolemy.actor.Manager; 037import ptolemy.actor.Receiver; 038import ptolemy.actor.TypedCompositeActor; 039import ptolemy.actor.TypedIOPort; 040import ptolemy.data.Token; 041import ptolemy.data.TupleToken; 042import ptolemy.kernel.ComponentEntity; 043import ptolemy.kernel.ComponentPort; 044import ptolemy.kernel.ComponentRelation; 045import ptolemy.kernel.CompositeEntity; 046import ptolemy.kernel.Entity; 047import ptolemy.kernel.Port; 048import ptolemy.kernel.Relation; 049import ptolemy.kernel.util.IllegalActionException; 050import ptolemy.kernel.util.InternalErrorException; 051import ptolemy.kernel.util.NameDuplicationException; 052import ptolemy.kernel.util.Workspace; 053 054/////////////////////////////////////////////////////////////////// 055//// ComponentActor 056 057/** 058 A Component is outside compatible with components and inside compatable 059 with actors. 060 061 @author Yang Zhao 062 @version $Id$ 063 @since Ptolemy II 11.0 064 @Pt.ProposedRating yellow (ellen_zh) 065 @Pt.AcceptedRating red (cxh) 066 */ 067public class ComponentActor extends TypedCompositeActor implements Component { 068 /** Construct an entity with the given name contained by the specified 069 * entity. The container argument must not be null, or a 070 * NullPointerException will be thrown. This entity will use the 071 * workspace of the container for synchronization and version counts. 072 * If the name argument is null, then the name is set to the empty string. 073 * Increment the version of the workspace. 074 * This constructor write-synchronizes on the workspace. 075 * @param container The container entity. 076 * @param name The name of the entity. 077 * @exception IllegalActionException If the entity cannot be contained 078 * by the proposed container. 079 * @exception NameDuplicationException If the name coincides with 080 * an entity already in the container. 081 */ 082 public ComponentActor(CompositeEntity container, String name) 083 throws IllegalActionException, NameDuplicationException { 084 super(container, name); 085 setContainer(container); 086 input = new IOMethodPort(this, "input", true, false); 087 088 output = new IOMethodPort(this, "output", false, true); 089 _addIcon(); 090 } 091 092 /////////////////////////////////////////////////////////////////// 093 //// ports and parameters //// 094 public IOMethodPort input; 095 096 public IOMethodPort output; 097 098 /////////////////////////////////////////////////////////////////// 099 //// public methods //// 100 101 /** Clone the actor into the specified workspace. The new object is 102 * <i>not</i> added to the directory of that workspace (you must do this 103 * yourself if you want it there). 104 * The result is a composite actor with clones of the ports of the 105 * original actor, the contained actors, and the contained relations. 106 * The ports of the returned actor are not connected to anything. 107 * The connections of the relations are duplicated in the new composite, 108 * unless they cross levels, in which case an exception is thrown. 109 * The local director is cloned, if there is one. 110 * The executive director is not cloned. 111 * NOTE: This will not work if there are level-crossing transitions. 112 * 113 * @param workspace The workspace for the cloned object. 114 * @exception CloneNotSupportedException If the actor contains 115 * level crossing transitions so that its connections cannot be cloned, 116 * or if one of the attributes cannot be cloned. 117 * @return A new CompositeActor. 118 */ 119 @Override 120 public Object clone(Workspace workspace) throws CloneNotSupportedException { 121 ComponentActor newObject = (ComponentActor) super.clone(workspace); 122 //newObject._inputPortsVersion = -1; 123 //newObject._outputPortsVersion = -1; 124 return newObject; 125 } 126 127 /** Invalidate the schedule and type resolution and create 128 * new receivers if the specified port is an opaque 129 * output port. Also, notify the containers of any ports 130 * deeply connected on the inside by calling their connectionsChanged() 131 * methods, since their width may have changed. 132 * @param port The port that has connection changes. 133 */ 134 135 //FIXME: the reason that i need overwrite this method is because 136 // it checked whether the container is compositeActor to avoid 137 // infinite loop. But here it should be componentActor. We should change 138 // the check to refer to a super interface of this two... 139 @Override 140 public void connectionsChanged(Port port) { 141 if (_debugging) { 142 _debug("Connections changed on port: " + port.getName()); 143 } 144 145 super.connectionsChanged(port); 146 147 if (port instanceof ComponentPort) { 148 // NOTE: deepInsidePortList() is not the right thing here 149 // since it will return the same port if it is opaque. 150 Iterator<?> insidePorts = ((ComponentPort) port).insidePortList() 151 .iterator(); 152 153 try { 154 _inConnectionsChanged = true; 155 156 while (insidePorts.hasNext()) { 157 ComponentPort insidePort = (ComponentPort) insidePorts 158 .next(); 159 Entity portContainer = (Entity) insidePort.getContainer(); 160 161 // Avoid an infinite loop where notifications are traded. 162 if (!(portContainer instanceof ComponentActor) 163 || !((ComponentActor) portContainer)._inConnectionsChanged) { 164 portContainer.connectionsChanged(insidePort); 165 } 166 } 167 } finally { 168 _inConnectionsChanged = false; 169 } 170 } 171 172 if (port instanceof IOPort) { 173 IOPort castPort = (IOPort) port; 174 175 if (castPort.isOpaque()) { 176 Manager manager = getManager(); 177 178 if (castPort.isOutput() && getDirector() != null 179 && manager != null && manager.getState() != Manager.IDLE 180 && manager.getState() != Manager.INFERING_WIDTHS 181 && manager.getState() != Manager.PREINITIALIZING) { 182 183 // Note that even if castPort is opaque, we still have to 184 // check for director above. 185 try { 186 castPort.createReceivers(); 187 } catch (IllegalActionException ex) { 188 // Should never happen. 189 throw new InternalErrorException(this, ex, 190 "Cannot create receivers"); 191 } 192 } 193 194 if (castPort.isInput() && getExecutiveDirector() != null 195 && manager != null && manager.getState() != Manager.IDLE 196 && manager.getState() != Manager.INFERING_WIDTHS 197 && manager.getState() != Manager.PREINITIALIZING) { 198 try { 199 castPort.createReceivers(); 200 } catch (IllegalActionException ex) { 201 // Should never happen. 202 throw new InternalErrorException(this, ex, 203 "Cannot create receivers"); 204 } 205 } 206 207 // Invalidate the local director schedule and types 208 if (getDirector() != null) { 209 getDirector().invalidateSchedule(); 210 getDirector().invalidateResolvedTypes(); 211 } 212 } 213 } 214 } 215 216 /** If this actor is opaque, transfer any data from the input ports 217 * of this composite to the ports connected on the inside, and then 218 * invoke the fire() method of its local director. 219 * The transfer is accomplished by calling the transferInputs() method 220 * of the local director (the exact behavior of which depends on the 221 * domain). If the actor is not opaque, throw an exception. 222 * This method is read-synchronized on the workspace, so the 223 * fire() method of the director need not be (assuming it is only 224 * called from here). After the fire() method of the director returns, 225 * send any output data created by calling the local director's 226 * transferOutputs method. 227 * 228 * @exception IllegalActionException If there is no director, or if 229 * the director's fire() method throws it, or if the actor is not 230 * opaque. 231 */ 232 @Override 233 public void fire() throws IllegalActionException { 234 super.fire(); 235 if (_debugging) { 236 _debug("Called fire()"); 237 } 238 239 try { 240 _workspace.getReadAccess(); 241 242 if (!isOpaque()) { 243 throw new IllegalActionException(this, 244 "Cannot fire a non-opaque actor."); 245 } 246 247 if (_stopRequested) { 248 return; 249 } 250 251 Director director = getDirector(); 252 director.fire(); 253 } finally { 254 _workspace.doneReading(); 255 } 256 } 257 258 /** Load the generated class and search for its fire method. 259 */ 260 @Override 261 public void preinitialize() throws IllegalActionException { 262 super.preinitialize(); 263 } 264 265 /** Execute the component, which in this base class means doing 266 * nothing and returning immediately. 267 * This is invoked after preinitialize() 268 * and initialize(), and may be invoked repeatedly. 269 * @exception IllegalActionException If the run cannot be completed 270 * (not thrown in this base class). 271 */ 272 @Override 273 public void run() throws IllegalActionException { 274 } 275 276 /* (non-Javadoc) 277 * @see ptolemy.kernel.Component#initialize() 278 */ 279 @Override 280 public void initialize() throws IllegalActionException { 281 super.initialize(); 282 } 283 284 /* (non-Javadoc) 285 * @see ptolemy.kernel.Component#wrapup() 286 */ 287 @Override 288 public void wrapup() throws IllegalActionException { 289 // TODO Auto-generated method stub 290 super.wrapup(); 291 } 292 293 /////////////////////////////////////////////////////////////////// 294 //// protected methods //// 295 296 /** Invoke one iteration and transfer the token to a method call. 297 */ 298 protected TupleToken _executeInside() { 299 if (_debugging) { 300 _debug("execute inside"); 301 } 302 303 System.out.println("call execute inside"); 304 305 try { 306 int iter = iterate(1); 307 308 //System.out.println("the iterate return is: " + iter); 309 if (iter == Executable.COMPLETED) { 310 return output.call(); 311 } else { 312 return TupleToken.VOID; 313 } 314 } catch (Exception ex) { 315 // this shouldn't happen. 316 throw new InternalErrorException(this, ex, null); 317 } 318 } 319 320 /////////////////////////////////////////////////////////////////// 321 //// private methods //// 322 323 /* Create receivers for each input port. 324 * @exception IllegalActionException If any port throws it. 325 */ 326 327 //FIXME: how should I modify this mehtod. 328 // public void createReceivers() throws IllegalActionException { 329 // Iterator ports = portList().iterator(); 330 // 331 // while (ports.hasNext()) { 332 // IOMethodPort onePort = (IOMethodPort) ports.next(); 333 // onePort.createReceivers(); 334 // } 335 // } 336 @Override 337 protected void _addRelation(ComponentRelation relation) 338 throws IllegalActionException, NameDuplicationException { 339 } 340 341 // Indicator that we are in the connectionsChanged method. 342 private boolean _inConnectionsChanged = false; 343 344 /////////////////////////////////////////////////////////////////// 345 //// private methods //// 346 private void _addIcon() { 347 _attachText("_iconDescription", 348 "<svg>\n" + "<rect x=\"-30\" y=\"-20\" width=\"60\" " 349 + "height=\"40\" style=\"fill:white\"/>\n" 350 + "<polygon points=\"-20,-10 20,0 -20,10\" " 351 + "style=\"fill:blue\"/>\n" + "</svg>\n"); 352 } 353 354 /////////////////////////////////////////////////////////////////// 355 //// inner class //// 356 // A class that encapsulates the declared and resolved types of a 357 // field and implements the InequalityTerm interface. 358 private class IOMethodPort extends TypedIOPort implements Method { 359 /** Construct a port with the given name contained by the specified 360 * entity. The container argument must not be null, or a 361 * NullPointerException will be thrown. This port will use the 362 * workspace of the container for synchronization and version counts. 363 * If the name argument is null, then the name is set to the empty 364 * string. Increment the version of the workspace. 365 * @param container The container entity. 366 * @param name The name of the port. 367 * @exception IllegalActionException If the port is not of an acceptable 368 * class for the container. 369 * @exception NameDuplicationException If the name coincides with 370 * a port already in the container. 371 */ 372 public IOMethodPort(ComponentEntity container, String name) 373 throws IllegalActionException, NameDuplicationException { 374 super(container, name); 375 } 376 377 /** Construct an IOPort with a container and a name that is 378 * either an input, an output, or both, depending on the third 379 * and fourth arguments. The specified container must implement 380 * the Actor interface or an exception will be thrown. 381 * 382 * @param container The container actor. 383 * @param name The name of the port. 384 * @param isInput True if this is to be an input port. 385 * @param isOutput True if this is to be an output port. 386 * @exception IllegalActionException If the port is not of an acceptable 387 * class for the container, or if the container does not implement the 388 * Actor interface. 389 * @exception NameDuplicationException If the name coincides with 390 * a port already in the container. 391 */ 392 public IOMethodPort(ComponentEntity container, String name, 393 boolean isInput, boolean isOutput) 394 throws IllegalActionException, NameDuplicationException { 395 this(container, name); 396 setInput(isInput); 397 setOutput(isOutput); 398 } 399 400 /////////////////////////////////////////////////////////////// 401 //// public inner methods //// 402 403 /** Override the base class to attach an empty properties token. 404 * @see ptolemy.domains.de.kernel.DEReceiver#put(ptolemy.data.Token) 405 */ 406 public synchronized TupleToken call() { 407 //FIXME: we assume only one token is going to be outputed. 408 //what is the correct sematics here? 409 System.out.println("try to call outside"); 410 411 if (isOutput()) { 412 try { 413 // FIXME: This loop will only go through 414 // the once and then return. 415 for (int i = 0; i < getWidthInside(); /*i++*/) { 416 if (hasTokenInside(i)) { 417 //System.out.println("has token to transfer to a method call outside"); 418 Token t = getInside(i); 419 Token[] tokens = new Token[1]; 420 tokens[0] = t; 421 422 Iterator<?> ports = this.deepConnectedPortList() 423 .iterator(); 424 MethodCallPort port = (MethodCallPort) ports.next(); 425 426 //System.out.println("get the connected method call port"); 427 return port.call(new TupleToken(tokens)); 428 } else { 429 return TupleToken.VOID; 430 } 431 } 432 } catch (Exception ex) { 433 // this shouldn't happen. 434 throw new InternalErrorException(this, ex, null); 435 } 436 } else { 437 // The port provided should over write this method. 438 return TupleToken.VOID; 439 } 440 441 return TupleToken.VOID; 442 } 443 444 /** 445 * @exception IllegalActionException If the transaction fails (e.g. 446 * the data type is incompatible). 447 */ 448 @Override 449 public synchronized TupleToken call(TupleToken token) 450 throws IllegalActionException { 451 if (isInput()) { 452 int l = token.length(); 453 454 //Assume only one port is connected to this. 455 Iterator<?> ports = this.deepInsidePortList().iterator(); 456 IOPort port = (IOPort) ports.next(); 457 Receiver[][] receivers = port.getReceivers(); 458 459 for (int i = 0; i < l; i++) { 460 Token t = token.getElement(i); 461 462 //assume not multiple port. 463 receivers[0][0].put(t); 464 } 465 466 return _executeInside(); 467 } else { 468 return TupleToken.VOID; 469 } 470 } 471 472 /** Create new receivers for this port, replacing any that may 473 * previously exist, and validate any instances of Settable that 474 * this port may contain. This method should only be called on 475 * opaque ports. 476 * <p> 477 * If the port is an input port, receivers are created as necessary 478 * for each relation connecting to the port from the outside. 479 * If the port is an output port, receivers are created as necessary 480 * for each relation connected to the port from the inside. Note that 481 * only composite entities will have relations connecting to ports 482 * from the inside. 483 * <p> 484 * Note that it is perfectly allowable for a zero width output port to 485 * have insideReceivers. This can be used to allow a model to be 486 * embedded in a container that does not connect the port to anything. 487 * <p> 488 * This method is <i>not</i> write-synchronized on the workspace, so the 489 * caller should be. 490 * @exception IllegalActionException If this port is not 491 * an opaque input port or if there is no director. 492 */ 493 @Override 494 public void createReceivers() throws IllegalActionException { 495 boolean output = isOutput(); 496 497 if (output) { 498 Iterator<?> insideRelations = insideRelationList().iterator(); 499 500 if (insideRelations.hasNext()) { 501 _insideReceivers = new Receiver[1][1]; 502 _insideReceivers[0][0] = _newInsideReceiver(); 503 } 504 } 505 } 506 507 /** Override the base class to return the inside receiver. 508 * @return The local inside receivers, or an empty array if there are 509 * none. 510 */ 511 @Override 512 public Receiver[][] getInsideReceivers() { 513 return _insideReceivers; 514 } 515 516 @Override 517 public Receiver[][] getReceivers() { 518 return _insideReceivers; 519 } 520 521 /** FIXME... see IORelation.deepReceivers(). 522 * 523 * @param relation Relations that are linked on the outside or inside. 524 * @param occurrence The occurrence number that we are interested in, 525 * starting at 0. 526 * @return The local receivers, or an empty array if there are none. 527 * @exception IllegalActionException If the relation is not linked 528 * from the outside. 529 */ 530 @Override 531 public Receiver[][] getReceivers(IORelation relation, int occurrence) 532 throws IllegalActionException { 533 return _insideReceivers; 534 } 535 536 /** Override parent method to ensure compatibility of the relation 537 * and validity of the width of the port. 538 * If this port is not a multiport, then the width of the 539 * relation is required to be specified to be one. This method 540 * allows level-crossing links. 541 * This method is <i>not</i> synchronized on the 542 * workspace, so the caller should be. 543 * 544 * @param relation The relation to link to on the inside. 545 * @exception IllegalActionException If this port has no container or 546 * the relation is not an IORelation, or the port already linked to a 547 * relation and is not a multiport, or the relation has width 548 * not exactly one and the port is not a multiport, or the 549 * relation is incompatible with this port, or the port is not 550 * in the same workspace as the relation. 551 */ 552 @Override 553 protected void _checkLiberalLink(Relation relation) 554 throws IllegalActionException { 555 } 556 557 /** Override parent method to ensure compatibility of the relation 558 * and validity of the width of the port. 559 * If this port is not a multiport, then the width of the 560 * relation is required to be specified to be one. 561 * This method is <i>not</i> synchronized on the 562 * workspace, so the caller should be. 563 * 564 * @param relation The relation to link to. 565 * @exception IllegalActionException If this port has no container or 566 * the relation is not an IORelation, or the port already linked to a 567 * relation and is not a multiport, or if the relation has width 568 * not exactly one and the port is not a multiport, or the port is 569 * not in the same workspace as the relation. 570 */ 571 @Override 572 protected void _checkLink(Relation relation) 573 throws IllegalActionException { 574 } 575 576 // Lists of local receivers, indexed by relation. 577 private Receiver[][] _insideReceivers; 578 579 //private boolean _isProviedPort = false; 580 } 581}