001/* A type polymorphic select used in the DDF domain.
002
003 Copyright (c) 1998-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.domains.ddf.lib;
029
030import ptolemy.actor.TypedAtomicActor;
031import ptolemy.actor.TypedIOPort;
032import ptolemy.data.ArrayToken;
033import ptolemy.data.IntToken;
034import ptolemy.data.expr.Parameter;
035import ptolemy.data.type.ArrayType;
036import ptolemy.data.type.BaseType;
037import ptolemy.kernel.CompositeEntity;
038import ptolemy.kernel.Port;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.InternalErrorException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Settable;
043import ptolemy.kernel.util.StringAttribute;
044import ptolemy.kernel.util.Workspace;
045
046/**
047 A type polymorphic select, which routes specified input channels to
048 the output, used in the DDF domain. In the first iteration, an input
049 token at the <i>control</i> port is read and its value is recorded.
050 In the second iteration, an input token is read from the input port
051 channel specified by the most recently seen token at the <i>control</i>
052 port and sent to the output. It alternates between these two kinds of
053 iterations until stopped. The <i>control</i> port must receive IntTokens.
054 The input port may receive tokens of any type. Because tokens are
055 immutable, the same token is sent to the output, rather than a copy.
056 Note that as for any multiport, the channel number starts from 0 and
057 increments by 1 for each additional channel in the order the channel
058 is created (e.g., when a connection is drawn in Vergil).
059 <p>
060 Note this actor sends an output token every two iterations. Contrast
061 this with Select which sends an output token every iteration.
062
063 @author Gang Zhou
064 @version $Id$
065 @since Ptolemy II 4.1
066 @Pt.ProposedRating Yellow (zgang)
067 @Pt.AcceptedRating Yellow (cxh)
068 */
069public class DDFSelect extends TypedAtomicActor {
070    /** Construct an actor in the specified container with the specified
071     *  name.
072     *  @param container The container.
073     *  @param name The name of this actor within the container.
074     *  @exception IllegalActionException If the actor cannot be contained
075     *   by the proposed container.
076     *  @exception NameDuplicationException If the name coincides with
077     *   an actor already in the container.
078     */
079    public DDFSelect(CompositeEntity container, String name)
080            throws IllegalActionException, NameDuplicationException {
081        super(container, name);
082
083        input = new TypedIOPort(this, "input", true, false);
084        input.setMultiport(true);
085        control = new TypedIOPort(this, "control", true, false);
086        control.setTypeEquals(BaseType.INT);
087        output = new TypedIOPort(this, "output", false, true);
088        output.setTypeAtLeast(input);
089
090        input_tokenConsumptionRate = new Parameter(input,
091                "tokenConsumptionRate");
092        input_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
093        input_tokenConsumptionRate.setTypeEquals(new ArrayType(BaseType.INT));
094
095        control_tokenConsumptionRate = new Parameter(control,
096                "tokenConsumptionRate");
097        control_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
098        control_tokenConsumptionRate.setTypeEquals(BaseType.INT);
099
100        // Put the control input on the bottom of the actor.
101        StringAttribute controlCardinal = new StringAttribute(control,
102                "_cardinal");
103        controlCardinal.setExpression("SOUTH");
104    }
105
106    ///////////////////////////////////////////////////////////////////
107    ////                     ports and parameters                  ////
108
109    /** The input port.  The port type can be any type.
110     */
111    public TypedIOPort input;
112
113    /** The input port for control tokens, which specifies the input
114     *  channels to read the tokens from.  The type is int.
115     */
116    public TypedIOPort control;
117
118    /** The output port.  The type is at least the type of <i>input</i>.
119     */
120    public TypedIOPort output;
121
122    /** This parameter provides token consumption rate for <i>input</i>.
123     *  The type is array of ints.
124     */
125    public Parameter input_tokenConsumptionRate;
126
127    /** This parameter provides token consumption rate for <i>control</i>.
128     *  The type is int.
129     */
130    public Parameter control_tokenConsumptionRate;
131
132    ///////////////////////////////////////////////////////////////////
133    ////                         public methods                    ////
134
135    /** Clone the actor into the specified workspace. This calls the
136     *  base class and then sets the type constraints.
137     *  @param workspace The workspace for the new object.
138     *  @return A new actor.
139     *  @exception CloneNotSupportedException If a derived class has
140     *   an attribute that cannot be cloned.
141     */
142    @Override
143    public Object clone(Workspace workspace) throws CloneNotSupportedException {
144        DDFSelect newObject = (DDFSelect) super.clone(workspace);
145        newObject.output.setTypeAtLeast(newObject.input);
146        return newObject;
147    }
148
149    /** Pre-calculate the rates to be set in the rate parameter of the
150     *  <i>input</i> port. Initialize the private variables _rateZero,
151     *  which indicates the <i>input</i> port does not consume any token
152     *  from any channel, and _rateArray, each element of which indicates
153     *  the <i>input</i> port needs to consume one token from a corresponding
154     *  channel and no token from the rest of the channels.
155     *  @param port The port that has connection changes.
156     */
157    @Override
158    public void connectionsChanged(Port port) {
159        super.connectionsChanged(port);
160
161        if (port == input) {
162            try {
163                _rateArray = new ArrayToken[input.getWidth()];
164
165                IntToken[] rate = new IntToken[input.getWidth()];
166
167                for (int i = 0; i < input.getWidth(); i++) {
168                    rate[i] = _zero;
169                }
170
171                _rateZero = new ArrayToken(BaseType.INT, rate);
172
173                for (int i = 0; i < input.getWidth(); i++) {
174                    rate[i] = _one;
175                    _rateArray[i] = new ArrayToken(rate);
176                    rate[i] = _zero;
177                }
178            } catch (IllegalActionException ex) {
179                // shouldn't happen
180                throw new InternalErrorException(ex);
181            }
182        }
183    }
184
185    /** Fire the actor once. If the <i>control</i> port is not read in
186     *  the previous iteration, read a new token from the <i>control</i>
187     *  port and record the value of the token and this concludes the
188     *  current firing. Otherwise output the token consumed from the
189     *  <i>input</i> port channel specified by the most recently seen
190     *  token on the <i>control</i> port.  Then reset an internal variable
191     *  so that it will read from the <i>control</i> port in the next
192     *  iteration.
193     *  @exception IllegalActionException If there is no director, and
194     *   hence no receivers have been created, or the value of the received
195     *   control token is out of range.
196     */
197    @Override
198    public void fire() throws IllegalActionException {
199        super.fire();
200
201        if (_isControlRead) {
202            output.send(0, input.get(_control));
203            _isControlRead = false;
204        } else {
205            _control = ((IntToken) control.get(0)).intValue();
206
207            if (_control >= 0 && _control < input.getWidth()) {
208                _isControlRead = true;
209            } else {
210                // If the value of the received control token is out of
211                // range, throw an IllegalActionException.
212                throw new IllegalActionException(this,
213                        "The width of the " + "input port is "
214                                + input.getWidth() + " , but "
215                                + "the value of the received control token: "
216                                + _control + " is out of range.");
217            }
218        }
219    }
220
221    /** Initialize this actor and rate parameters so that it will read
222     *  from the <i>control</i> port in the first iteration.
223     *  @exception IllegalActionException If setToken() throws it.
224     */
225    @Override
226    public void initialize() throws IllegalActionException {
227        super.initialize();
228
229        _isControlRead = false;
230
231        input_tokenConsumptionRate.setToken(_rateZero);
232        control_tokenConsumptionRate.setToken(_one);
233    }
234
235    /** Update rate parameters for the next iteration.
236     *  @return True if execution can continue into the next iteration.
237     *  @exception IllegalActionException If setToken() throws it.
238     */
239    @Override
240    public boolean postfire() throws IllegalActionException {
241        if (_isControlRead) {
242            input_tokenConsumptionRate.setToken(_rateArray[_control]);
243            control_tokenConsumptionRate.setToken(_zero);
244        } else {
245            input_tokenConsumptionRate.setToken(_rateZero);
246            control_tokenConsumptionRate.setToken(_one);
247        }
248
249        return super.postfire();
250    }
251
252    /** Return false if the port or channel it needs to read from in the
253     *  following firing does not have a token.
254     *  Otherwise, return whatever the superclass returns.
255     *  @return True if there are enough tokens to fire.
256     *  @exception IllegalActionException If the receivers do not support
257     *   the query, or if there is no director, and hence no receivers.
258     */
259    @Override
260    public boolean prefire() throws IllegalActionException {
261        if (_isControlRead) {
262            if (!input.hasToken(_control)) {
263                return false;
264            }
265        } else {
266            if (!control.hasToken(0)) {
267                return false;
268            }
269        }
270
271        return super.prefire();
272    }
273
274    ///////////////////////////////////////////////////////////////////
275    ////                         private variables                 ////
276
277    /** The most recently read <i>control</i> token.
278     */
279    private int _control;
280
281    /** The boolean to determine whether to read from the <i>control</i>
282     *  port or from the <i>input</i> port.
283     */
284    private boolean _isControlRead;
285
286    /** A final static IntToken with value 1.
287     */
288    private final static IntToken _one = new IntToken(1);
289
290    /** A final static IntToken with value 0.
291     */
292    private final static IntToken _zero = new IntToken(0);
293
294    /** An array of ArrayTokens to be used to set tokenConsumptionRate
295     *  of the input port. Each ArrayToken indicates the <i>input</i>
296     *  port needs to consume one token from a corresponding channel and
297     *  no token from the rest of the channels. The array is initialized
298     *  in the method connectionsChanged().
299     */
300    private ArrayToken[] _rateArray;
301
302    /** An ArrayToken to be used to set tokenConsumptionRate of the input
303     *  port. It indicates the <i>input</i> port does not consume any token
304     *  from any channel. This variable is initialized in the method
305     *  connectionsChanged().
306     */
307    private ArrayToken _rateZero;
308}