001/* Merge streams according to a boolean control signal.
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 (This is similar to Select and BooleanMultiplexor and could be
028 design/code reviewed at the same time.
029 */
030package ptolemy.actor.lib;
031
032import ptolemy.actor.TypedAtomicActor;
033import ptolemy.actor.TypedIOPort;
034import ptolemy.data.BooleanToken;
035import ptolemy.data.IntToken;
036import ptolemy.data.expr.Parameter;
037import ptolemy.data.type.BaseType;
038import ptolemy.kernel.CompositeEntity;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.Settable;
042import ptolemy.kernel.util.StringAttribute;
043import ptolemy.kernel.util.Workspace;
044
045///////////////////////////////////////////////////////////////////
046//// BooleanSelect
047
048/**
049 Conditionally merge the streams at two input ports
050 depending on the value of the boolean control input.
051 In the first firing, this actor consumes a token from the
052 <i>control</i> input port.
053 The token at the <i>control</i> input specifies the
054 input port that should be read from in the next firing.
055 If the <i>control</i>
056 token is false, then the <i>falseInput</i> port is used,
057 otherwise the <i>trueInput</i> port is used. In the next
058 firing, tokens are consumed from the specified
059 port and sent to the <i>output</i> port.
060 <p>
061 The actor is able to fire if either it needs a new control
062 token and there is a token on the <i>control</i> port, or
063 it has read a control token and there is a token on every
064 channel of the specified input port.
065 <p>
066 If the input port that is read has width greater than an output port, then
067 some input tokens will be discarded (those on input channels for which
068 there is no corresponding output channel).
069 <p>
070 Because tokens are immutable, the same Token is sent
071 to the output, rather than a copy.  The <i>trueInput</i> and
072 <i>falseInput</i> port may receive Tokens of any type.
073 <p>
074 This actor is designed to be used with the DDF or PN director.
075 It should not be used with
076 SDF because the number of tokens it consumes is not fixed.
077 It probably also does not make sense to use it
078 with SR or DE, because it takes two firings to transfer
079 a token to the output. In those domains,
080 {@link ptolemy.actor.lib.BooleanMultiplexor} makes more sense.
081 Unlike BooleanMultiplexor actor, this actor
082 does not discard input tokens on the port that it does not read.
083
084 @author Steve Neuendorffer, Adam Cataldo, Edward A. Lee, Gang Zhou
085 @version $Id$
086 @since Ptolemy II 2.0
087 @Pt.ProposedRating Green (neuendor)
088 @Pt.AcceptedRating Red (neuendor)
089 */
090public class BooleanSelect extends TypedAtomicActor {
091    /** Construct an actor in the specified container with the specified
092     *  name.
093     *  @param container The container.
094     *  @param name The name of this actor within the container.
095     *  @exception IllegalActionException If the actor cannot be contained
096     *   by the proposed container.
097     *  @exception NameDuplicationException If the name coincides with
098     *   an actor already in the container.
099     */
100    public BooleanSelect(CompositeEntity container, String name)
101            throws IllegalActionException, NameDuplicationException {
102        super(container, name);
103
104        trueInput = new TypedIOPort(this, "trueInput", true, false);
105        trueInput.setMultiport(true);
106        falseInput = new TypedIOPort(this, "falseInput", true, false);
107        falseInput.setMultiport(true);
108        control = new TypedIOPort(this, "control", true, false);
109        control.setTypeEquals(BaseType.BOOLEAN);
110        output = new TypedIOPort(this, "output", false, true);
111        output.setTypeAtLeast(trueInput);
112        output.setTypeAtLeast(falseInput);
113        output.setMultiport(true);
114        output.setWidthEquals(trueInput, true);
115        output.setWidthEquals(falseInput, true);
116
117        // Put the control input on the bottom of the actor.
118        StringAttribute controlCardinal = new StringAttribute(control,
119                "_cardinal");
120        controlCardinal.setExpression("SOUTH");
121
122        // For the benefit of the DDF director, this actor sets
123        // consumption rate values.
124        trueInput_tokenConsumptionRate = new Parameter(trueInput,
125                "tokenConsumptionRate");
126        trueInput_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
127        trueInput_tokenConsumptionRate.setTypeEquals(BaseType.INT);
128
129        falseInput_tokenConsumptionRate = new Parameter(falseInput,
130                "tokenConsumptionRate");
131        falseInput_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
132        falseInput_tokenConsumptionRate.setTypeEquals(BaseType.INT);
133
134        control_tokenConsumptionRate = new Parameter(control,
135                "tokenConsumptionRate");
136        control_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
137        control_tokenConsumptionRate.setTypeEquals(BaseType.INT);
138
139        /** Make the icon show T, F, and C for trueInput, falseInput
140         *  and control.
141         */
142        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-20\" y=\"-20\" "
143                + "width=\"40\" height=\"40\" " + "style=\"fill:white\"/>\n"
144                + "<text x=\"-17\" y=\"-3\" " + "style=\"font-size:14\">\n"
145                + "T \n" + "</text>\n" + "<text x=\"-17\" y=\"15\" "
146                + "style=\"font-size:14\">\n" + "F \n" + "</text>\n"
147                + "<text x=\"-5\" y=\"16\" " + "style=\"font-size:14\">\n"
148                + "C \n" + "</text>\n" + "</svg>\n");
149    }
150
151    ///////////////////////////////////////////////////////////////////
152    ////                     ports and parameters                  ////
153
154    /** Input for tokens on the true path.  The type can be anything.
155     */
156    public TypedIOPort trueInput;
157
158    /** Input for tokens on the false path.  The type can be anything.
159     */
160    public TypedIOPort falseInput;
161
162    /** Input that selects one of the other input ports.  The type is
163     *  BooleanToken.
164     */
165    public TypedIOPort control;
166
167    /** The output port.  The type is at least the type of
168     *  <i>trueInput</i> and <i>falseInput</i>
169     */
170    public TypedIOPort output;
171
172    /** This parameter provides token consumption rate for <i>trueInput</i>.
173     *  The type is int.
174     */
175    public Parameter trueInput_tokenConsumptionRate;
176
177    /** This parameter provides token consumption rate for <i>falseInput</i>.
178     *  The type is int.
179     */
180    public Parameter falseInput_tokenConsumptionRate;
181
182    /** This parameter provides token consumption rate for <i>control</i>.
183     *  The type is int.
184     */
185    public Parameter control_tokenConsumptionRate;
186
187    ///////////////////////////////////////////////////////////////////
188    ////                         public methods                    ////
189
190    /** Clone this actor into the specified workspace. The new actor is
191     *  <i>not</i> added to the directory of that workspace (you must do this
192     *  yourself if you want it there).
193     *  The result is a new actor with the same ports as the original, but
194     *  no connections and no container.  A container must be set before
195     *  much can be done with this actor.
196     *
197     *  @param workspace The workspace for the cloned object.
198     *  @exception CloneNotSupportedException If cloned ports cannot have
199     *   as their container the cloned entity (this should not occur), or
200     *   if one of the attributes cannot be cloned.
201     *  @return A new ComponentEntity.
202     */
203    @Override
204    public Object clone(Workspace workspace) throws CloneNotSupportedException {
205        BooleanSelect newObject = (BooleanSelect) super.clone(workspace);
206        newObject._control = null;
207        newObject._controlUsed = false;
208        newObject.output.setTypeAtLeast(newObject.trueInput);
209        newObject.output.setTypeAtLeast(newObject.falseInput);
210        newObject.output.setWidthEquals(newObject.trueInput, true);
211        newObject.output.setWidthEquals(newObject.falseInput, true);
212
213        return newObject;
214    }
215
216    /** Read a token from the control port or from the input designated
217     *  by the previously read input from the control port.  In the
218     *  latter case, send to the token read to the output. In the former
219     *  case, send nothing to the output.
220     *  @exception IllegalActionException If there is no director.
221     */
222    @Override
223    public void fire() throws IllegalActionException {
224        super.fire();
225        if (_control == null) {
226            _control = (BooleanToken) control.get(0);
227            _controlUsed = false;
228        } else {
229            if (_control.booleanValue()) {
230                for (int i = 0; i < trueInput.getWidth(); i++) {
231                    if (output.getWidth() > i) {
232                        output.send(i, trueInput.get(i));
233                    }
234                }
235            } else {
236                for (int i = 0; i < falseInput.getWidth(); i++) {
237                    if (output.getWidth() > i) {
238                        output.send(i, falseInput.get(i));
239                    }
240                }
241            }
242            _controlUsed = true;
243        }
244    }
245
246    /** Initialize this actor so that the <i>falseInput</i> is read
247     *  from until a token arrives on the <i>control</i> input.
248     *  @exception IllegalActionException If the parent class throws it.
249     */
250    @Override
251    public void initialize() throws IllegalActionException {
252        super.initialize();
253        _control = null;
254        _controlUsed = false;
255        trueInput_tokenConsumptionRate.setToken(_zero);
256        falseInput_tokenConsumptionRate.setToken(_zero);
257        control_tokenConsumptionRate.setToken(_one);
258    }
259
260    /** Return true, unless stop() has been called, in which case,
261     *  return false.
262     *  @return True if execution can continue into the next iteration.
263     *  @exception IllegalActionException If the base class throws it.
264     */
265    @Override
266    public boolean postfire() throws IllegalActionException {
267        // If on this iteration the control token was used, set it to null.
268        if (_controlUsed) {
269            _control = null;
270            trueInput_tokenConsumptionRate.setToken(_zero);
271            falseInput_tokenConsumptionRate.setToken(_zero);
272            control_tokenConsumptionRate.setToken(_one);
273        } else {
274            if (_control == null) {
275                _control = (BooleanToken) control.get(0);
276            }
277            if (_control.booleanValue()) {
278                trueInput_tokenConsumptionRate.setToken(_one);
279                falseInput_tokenConsumptionRate.setToken(_zero);
280                control_tokenConsumptionRate.setToken(_zero);
281            } else {
282                trueInput_tokenConsumptionRate.setToken(_zero);
283                falseInput_tokenConsumptionRate.setToken(_one);
284                control_tokenConsumptionRate.setToken(_zero);
285            }
286        }
287        return super.postfire();
288    }
289
290    /** If the mode is to read a control token, then return true
291     *  if the <i>control</i> input has a token. Otherwise, return
292     *  true if every channel of the input port specified by the most
293     *  recently read control input has a token.
294     *  @return False if there are not enough tokens to fire.
295     *  @exception IllegalActionException If there is no director.
296     */
297    @Override
298    public boolean prefire() throws IllegalActionException {
299        boolean result = super.prefire();
300        if (_control == null) {
301            return result && control.hasToken(0);
302        } else {
303            if (_control.booleanValue()) {
304                for (int i = 0; i < trueInput.getWidth(); i++) {
305                    if (!trueInput.hasToken(i)) {
306                        return false;
307                    }
308                }
309            } else {
310                for (int i = 0; i < falseInput.getWidth(); i++) {
311                    if (!falseInput.hasToken(i)) {
312                        return false;
313                    }
314                }
315            }
316        }
317        return result;
318    }
319
320    ///////////////////////////////////////////////////////////////////
321    ////                         private variables                 ////
322
323    /** The most recently read control token. */
324    private BooleanToken _control = null;
325
326    /** Indicator that the control token was used in the fire method. */
327    private boolean _controlUsed = false;
328
329    /** A final static IntToken with value 0. */
330    private final static IntToken _zero = new IntToken(0);
331
332    /** A final static IntToken with value 1. */
333    private final static IntToken _one = new IntToken(1);
334}