001/* A composite actor that executes a submodel in fire().
002
003 Copyright (c) 2003-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
028 */
029package ptolemy.actor.lib.hoc;
030
031import java.util.Iterator;
032
033import ptolemy.actor.TypedIOPort;
034import ptolemy.data.IntToken;
035import ptolemy.data.expr.Parameter;
036import ptolemy.data.expr.Variable;
037import ptolemy.data.type.BaseType;
038import ptolemy.kernel.CompositeEntity;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.InternalErrorException;
042import ptolemy.kernel.util.NameDuplicationException;
043import ptolemy.kernel.util.Workspace;
044
045///////////////////////////////////////////////////////////////////
046//// RunCompositeActor
047
048/**
049 This is a composite actor that can execute the contained model
050 completely, as if it were a top-level model, on each firing.
051 This can be used to define an actor whose firing behavior
052 is given by a complete execution of a submodel.
053 <p>
054 An instance of this actor can have ports added to it.  If it has
055 input ports, then on each firing, before executing the referenced
056 model, this actor will read an input token from the input port, if
057 there is one, and use it to set the value of a top-level parameter
058 in the contained model that has the same name as the port, if there
059 is one.  The simplest way to ensure that there is a matching parameter
060 is to use a PortParameter for inputs.  However, this actor will work
061 also for ordinary ports. In this case, if this actor has a
062 parameter with the same name as the port, and it is an instance
063 of Variable (or its derived class Parameter), then the token
064 read at the input is moved into it using its setToken() method.
065 Otherwise, if it is an instance of Settable, then a string representation
066 of the token is copied using the setExpression() method.
067 Input ports should not be multiports, and if they are, then
068 all but the first channel will be ignored.
069 <p>
070 If this actor has output ports and the contained model is executed,
071 then upon completion of that execution, if this actor has parameters
072 whose names match those of the output ports, then the final value of
073 those parameters is sent to the output ports. If such a parameter is an
074 instance of Variable (or its derived class Parameter), then its
075 contained token is sent to the output token. Otherwise, if it is an
076 instance of Settable, then a string token is produced on the output
077 with its value equal to that returned by getExpression() of the
078 Settable. Output ports should not be multiports. If they are,
079 then all but the first channel will be ignored.
080 A typical use of this actor will use the SetVariable actor
081 inside to define the value of the output port.
082 <p>
083 In preinitialize(), type constraints are set up so that input
084 and output ports with (name) matching parameters are constrained
085 to have compatible types. Note that if the ports or parameters
086 are changed during execution, then it will be necessary to set
087 up matching type constraints by hand.  Since this isn't possible
088 to do from Vergil, the ports and parameters of this actor
089 should not be changed using Vergil during execution.
090 <p>
091 This actor also overrides the requestChange() method and the
092 executeChangeRequests() method to execute the given change. It does not
093 delegate the change request to the container, but executes the request
094 immediately or records it, depending on whether setDeferringChangeRequests()
095 has been called with a true argument.
096
097 @author Edward A. Lee, Yang Zhao, Elaine Cheong
098 @version $Id$
099 @since Ptolemy II 4.0
100 @see ModelReference
101 @see ptolemy.actor.lib.SetVariable
102 @Pt.ProposedRating Yellow (eal)
103 @Pt.AcceptedRating Red (eal)
104 */
105public class RunCompositeActor extends LifeCycleManager {
106    /** Construct an actor in the default workspace with no
107     *  container and an empty string as its name. Add the actor to the
108     *  workspace directory.  You should set the local director or
109     *  executive director before attempting to send data to the actor or
110     *  to execute it. Increment the version number of the workspace.
111     *  @exception NameDuplicationException If there is already a parameter with
112     *   name firingCountLimit.
113     *  @exception IllegalActionException If the firingCountLimit cannot be
114     *   initialized.
115     */
116    public RunCompositeActor()
117            throws IllegalActionException, NameDuplicationException {
118        super();
119        _init();
120    }
121
122    /** Construct a RunCompositeActor in the specified workspace with
123     *  no container and an empty string as a name. You can then change
124     *  the name with setName(). If the workspace argument is null, then
125     *  use the default workspace.  You should set the local director or
126     *  executive director before attempting to send data to the actor
127     *  or to execute it. Add the actor to the workspace directory.
128     *  Increment the version number of the workspace.
129     *  @param workspace The workspace that will list the actor.
130     *  @exception NameDuplicationException If there is already a parameter with
131     *   name firingCountLimit.
132     *  @exception IllegalActionException If the firingCountLimit cannot be
133     *   initialized.
134     */
135    public RunCompositeActor(Workspace workspace)
136            throws IllegalActionException, NameDuplicationException {
137        super(workspace);
138        _init();
139    }
140
141    /** Construct a RunCompositeActor with a name and a container.
142     *  The container argument must not be null, or a
143     *  NullPointerException will be thrown.  This actor will use the
144     *  workspace of the container for synchronization and version counts.
145     *  If the name argument is null, then the name is set to the empty string.
146     *  Increment the version of the workspace.  This actor will have no
147     *  local director initially, and its executive director will be simply
148     *  the director of the container.
149     *
150     *  @param container The container.
151     *  @param name The name of this actor.
152     *  @exception IllegalActionException If the container is incompatible
153     *   with this actor.
154     *  @exception NameDuplicationException If the name coincides with
155     *   an actor already in the container.
156     */
157    public RunCompositeActor(CompositeEntity container, String name)
158            throws IllegalActionException, NameDuplicationException {
159        super(container, name);
160        _init();
161    }
162
163    ///////////////////////////////////////////////////////////////////
164    ////                         parameters                        ////
165
166    /** Indicator to run the contained model a limited number of times.
167     *  If this parameter has a value greater than zero, then after
168     *  executing the inside model the specified number of times,
169     *  {@link #postfire()} will return false. This is an int that
170     *  defaults to 0, which means that there is no limit.
171     */
172    public Parameter firingCountLimit;
173
174    ///////////////////////////////////////////////////////////////////
175    ////                         public methods                    ////
176
177    /** Run a complete execution of the contained model.  A complete
178     *  execution consists of invocation of super.initialize(),
179     *  repeated invocations of super.prefire(), super.fire(), and
180     *  super.postfire(), followed by super.wrapup().  The invocations
181     *  of prefire(), fire(), and postfire() are repeated until either
182     *  the model indicates it is not ready to execute (prefire()
183     *  returns false), or it requests a stop (postfire() returns
184     *  false or stop() is called).  Before running the complete
185     *  execution, this method calls the director's transferInputs()
186     *  method to read any available inputs.  After running the
187     *  complete execution, it calls transferOutputs().  The subclass
188     *  of this can set the <i>_isSubclassOfRunCompositeActor</i> to be
189     *  true to call the fire method of the superclass of this.
190     *  @exception IllegalActionException If there is no director, or if
191     *   the director's action methods throw it.
192     */
193    @Override
194    public void fire() throws IllegalActionException {
195        // Note that super.fire() is not called here probably because
196        // CompositeActor.fire() transfers inputs.
197
198        // FIXME: CompositeActor.fire() invokes the piggyback.fire(),
199        // this method does not.
200
201        if (_debugging) {
202            _debug("---- calling fire(), which will execute a subsystem.");
203        }
204
205        _executeInsideModel();
206    }
207
208    /** Initialize this actor, which in this case, does nothing.  The
209     *  initialization of the submodel is accomplished in fire().  The
210     *  subclass of this can set the
211     *  <i>_isSubclassOfRunCompositeActor</i> to be true to call the
212     *  initialize method of the superclass of this.
213     *  @exception IllegalActionException Not thrown in this base class,
214     *  but declared so the subclasses can throw it.
215     */
216    @Override
217    public void initialize() throws IllegalActionException {
218        // FIXME: Why does this method not call super.initialize()?
219        // CompositeActor.initialize() invokes the initialize method
220        // of the local director and resets the receivers.  Are
221        // these steps not necessary here?
222        if (_debugging) {
223            _debug("Called initialize(), which does nothing.");
224        }
225        _iteration = 0;
226    }
227
228    /** Return true, indicating that execution can continue.  The
229     *  subclass of this can set the
230     *  <i>_isSubclassOfRunCompositeActor</i> to be true to call the
231     *  postfire method of the superclass of this.
232     *  @exception IllegalActionException Not thrown in this base class,
233     *  but declared so the subclasses can throw it.
234     */
235    @Override
236    public boolean postfire() throws IllegalActionException {
237        int limitValue = ((IntToken) firingCountLimit.getToken()).intValue();
238        if (limitValue > 0) {
239            _iteration++;
240            if (_iteration >= limitValue) {
241                if (_debugging) {
242                    _debug("Called postfire(), which returns false.");
243                }
244                return false;
245            }
246        }
247        if (_debugging) {
248            _debug("Called postfire(), which returns true.");
249        }
250        // FIXME: Why does calling super.postfire() cause a causality loop exception with test/auto/RunCompositeActor3.xml
251        // FIXME: Shouldn't piggybacked methods be called here?
252        //return super.postfire();
253        return true;
254    }
255
256    /** Return true, indicating that this actor is always ready to fire.
257     *  @exception IllegalActionException Not thrown in this base class,
258     *  but declared so the subclasses can throw it.
259     */
260    @Override
261    public boolean prefire() throws IllegalActionException {
262        // FIXME: Why is super.prefire() not called here, which calls
263        // CompositeActor.prefire().  CompositeActor.prefire() invokes
264        // prefire() on the director and the piggybacked methods,
265        // which does not happen here.
266        if (_debugging) {
267            _debug("Called prefire(), which returns true.");
268        }
269
270        return true;
271    }
272
273    /** Override the base class to set type constraints between the
274     *  output ports and parameters of this actor whose name matches
275     *  the output port. If there is no such parameter, then create
276     *  an instance of Variable with a matching name and set up the
277     *  type constraints to that instance.  The type of the output
278     *  port is constrained to be at least that of the parameter
279     *  or variable.
280     *  @exception IllegalActionException If there is no director, or if
281     *   the director's preinitialize() method throws it, or if this actor
282     *   is not opaque.
283     */
284    @Override
285    public void preinitialize() throws IllegalActionException {
286        super.preinitialize();
287
288        Iterator ports = outputPortList().iterator();
289
290        while (ports.hasNext()) {
291            TypedIOPort port = (TypedIOPort) ports.next();
292
293            // Ensure that the production rate is one.
294            // FIXME: This may not be right if there is no
295            // actual source of data for this port (e.g. no
296            // SetVariable actor).
297            Variable rate = (Variable) port.getAttribute("tokenProductionRate");
298
299            if (rate == null) {
300                try {
301                    rate = new Variable(port, "tokenProductionRate");
302                } catch (NameDuplicationException e) {
303                    throw new InternalErrorException(e);
304                }
305            }
306
307            rate.setToken(new IntToken(1));
308
309            String portName = port.getName();
310            Attribute attribute = getAttribute(portName);
311
312            if (attribute == null) {
313                try {
314                    workspace().getWriteAccess();
315                    attribute = new Variable(this, portName);
316                } catch (NameDuplicationException ex) {
317                    throw new InternalErrorException(ex);
318                } finally {
319                    workspace().doneWriting();
320                }
321            }
322
323            // attribute is now assured to be non-null.
324            if (attribute instanceof Variable) {
325                port.setTypeAtLeast((Variable) attribute);
326            } else {
327                // Assume the port type must be a string.
328                port.setTypeEquals(BaseType.STRING);
329            }
330        }
331    }
332
333    /** Override the base class to do nothing.
334     *  @exception IllegalActionException Not thrown in this base class,
335     *  but declared so the subclasses can throw it.
336     */
337    @Override
338    public void wrapup() throws IllegalActionException {
339        if (_debugging) {
340            _debug("Called wrapup(), which does nothing.");
341        }
342    }
343
344    ///////////////////////////////////////////////////////////////////
345    ////                         protected methods                 ////
346
347    /** Set the class name and create the parameters.
348     *  @exception NameDuplicationException If there is already a parameter with
349     *   name firingCountLimit.
350     *  @exception IllegalActionException If the firingCountLimit cannot be
351     *   initialized.
352     */
353    protected void _init()
354            throws IllegalActionException, NameDuplicationException {
355        // By default, when exporting MoML, the class name is whatever
356        // the Java class is, which in this case is RunCompositeActor.
357        // In derived classes, however, we usually do not want to identify
358        // the class name as that of the derived class, but rather want
359        // to identify it as RunCompositeActor.  This way, the MoML
360        // that is exported does not depend on the presence of the
361        // derived class Java definition. Thus, we force the class name
362        // here to be RunCompositeActor.
363        setClassName("ptolemy.actor.lib.hoc.RunCompositeActor");
364
365        firingCountLimit = new Parameter(this, "firingCountLimit");
366        firingCountLimit.setTypeEquals(BaseType.INT);
367        firingCountLimit.setExpression("0");
368    }
369
370    ///////////////////////////////////////////////////////////////////
371    ////                         private variables                 ////
372
373    /** The count of iterations. */
374    private int _iteration;
375}