001/* An actor that finds a destination to send data to.
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.domains.de.demo.SmartSender;
030
031import java.util.Iterator;
032import java.util.List;
033
034import ptolemy.actor.Director;
035import ptolemy.actor.IOPort;
036import ptolemy.actor.TypedAtomicActor;
037import ptolemy.actor.TypedIOPort;
038import ptolemy.data.DoubleToken;
039import ptolemy.data.IntToken;
040import ptolemy.data.expr.Parameter;
041import ptolemy.data.type.BaseType;
042import ptolemy.kernel.CompositeEntity;
043import ptolemy.kernel.Entity;
044import ptolemy.kernel.Port;
045import ptolemy.kernel.util.ChangeRequest;
046import ptolemy.kernel.util.IllegalActionException;
047import ptolemy.kernel.util.NameDuplicationException;
048
049///////////////////////////////////////////////////////////////////
050//// SmartSender
051
052/**
053 This actor adaptively establishes connections by searching for an
054 unused input port in the model and connecting to it. If the output
055 is connected to something (the width of the output port is greater
056 than zero), then the actor sends an integer on the output port and
057 requests a refiring at a time in the future determined by the
058 <i>firingPeriod</i> parameter. The value of the output is simply
059 the count of the firing, starting at 1.
060 <p>
061 If the output is not connected to anything, then the actor will
062 attempt to connect it. It does this by issuing a change request
063 that, when executed, will search for an unused input port (any
064 unused input port) in and actor in the same container as this actor,
065 and then will connect to it.
066 <p>
067 Note that getWidth() is used rather than numberOfSinks() to determine
068 whether the output is connected. This way, this actors search for an
069 input port can be silenced by just connecting it to a relation.
070 <p>
071 This actor is an illustration of the capability actors can have to affect
072 their environment, to detect faults (in this case, missing connections),
073 and to repair the model.  It is designed to be used in the DE domain,
074 or any domain that respects fireAt() calls.
075
076 @author Edward A. Lee
077 @see IOPort#getWidth()
078 @version $Id$
079 @since Ptolemy II 4.0
080 @Pt.ProposedRating Yellow (eal)
081 @Pt.AcceptedRating Red (eal)
082 */
083public class SmartSender extends TypedAtomicActor {
084    /** Create a new actor in the specified container with the specified
085     *  name.  The name must be unique within the container or an exception
086     *  is thrown. The container argument must not be null, or a
087     *  NullPointerException will be thrown.
088     *  @param container The container.
089     *  @param name The name of this actor within the container.
090     *  @exception IllegalActionException If this actor cannot be contained
091     *   by the proposed container (see the setContainer() method).
092     *  @exception NameDuplicationException If the name coincides with
093     *   an entity already in the container.
094     */
095    public SmartSender(CompositeEntity container, String name)
096            throws NameDuplicationException, IllegalActionException {
097        super(container, name);
098
099        output = new TypedIOPort(this, "output");
100        output.setOutput(true);
101        output.setTypeEquals(BaseType.INT);
102
103        firingPeriod = new Parameter(this, "firingPeriod");
104        firingPeriod.setExpression("0.1");
105        firingPeriod.setTypeEquals(BaseType.DOUBLE);
106    }
107
108    ///////////////////////////////////////////////////////////////////
109    ////                         ports and parameters              ////
110
111    /** The output port, which has type int. */
112    public TypedIOPort output;
113
114    /** The period at which this actor will execute.  This is a double
115     *  with a default value of 0.1.
116     */
117    public Parameter firingPeriod;
118
119    ///////////////////////////////////////////////////////////////////
120    ////                         public methods                    ////
121
122    /** If the output port is connected, then send a count of the firing
123     *  to the output; otherwise, issue a change request that will search
124     *  for an input port to connect to.
125     *  @exception IllegalActionException If there is no director or if
126     *   producing the output causes an exception.
127     */
128    @Override
129    public void fire() throws IllegalActionException {
130        super.fire();
131
132        double firingPeriodValue = ((DoubleToken) firingPeriod.getToken())
133                .doubleValue();
134        Director director = getDirector();
135        _fireAt(director.getModelTime().add(firingPeriodValue));
136
137        if (output.isOutsideConnected()) {
138            output.send(0, new IntToken(_count++));
139        } else {
140            ChangeRequest request = new ChangeRequest(this,
141                    "Find a destination") {
142                @Override
143                protected void _execute() throws IllegalActionException {
144                    CompositeEntity container = (CompositeEntity) getContainer();
145                    List entityList = container.entityList();
146                    Iterator entities = entityList.iterator();
147
148                    while (entities.hasNext()) {
149                        Entity entity = (Entity) entities.next();
150                        Iterator ports = entity.portList().iterator();
151
152                        while (ports.hasNext()) {
153                            Port port = (Port) ports.next();
154
155                            if (port instanceof IOPort
156                                    && ((IOPort) port).isInput()
157                                    && !((IOPort) port).isOutsideConnected()) {
158                                container.connect(output, (IOPort) port);
159                                return;
160                            }
161                        }
162                    }
163                }
164            };
165
166            requestChange(request);
167        }
168    }
169
170    /** Initialize this actor, which in this case requests a firing at
171     *  the current time.
172     *  @exception IllegalActionException If a derived class throws it.
173     */
174    @Override
175    public void initialize() throws IllegalActionException {
176        super.initialize();
177
178        Director director = getDirector();
179        director.fireAtCurrentTime(this);
180
181        _count = 1;
182    }
183
184    ///////////////////////////////////////////////////////////////////
185    ////                         private members                   ////
186
187    /** Count of the number of firings. */
188    private int _count = 1;
189}