001/* Merge streams according to a boolean parameter value.
002
003 Copyright (c) 1997-2016 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 Adapted from the BooleanSelect actor.
028 */
029package ptolemy.actor.lib;
030
031import ptolemy.actor.Director;
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.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Settable;
043import ptolemy.kernel.util.Workspace;
044
045///////////////////////////////////////////////////////////////////
046//// ConfigurationSelect
047
048/**
049 Conditionally merge the streams at two input ports
050 depending on the value of the boolean parameter.
051 The token in the <i>selector</i> parameter specifies the
052 input port that should be read from in the next firing.
053 If the <i>selector</i> parameter
054 token is false, then the <i>falseInput</i> port is used,
055 otherwise the <i>trueInput</i> port is used. In the next
056 firing, tokens are consumed from the specified
057 port and sent to the <i>output</i> port.
058 <p>
059 The actor is able to fire if the <i>selector</i> parameter
060 is either has a true or false token and there is a token on every
061 channel of the specified input port.
062 <p>
063 If the input port that is read has width greater than an output port, then
064 some input tokens will be discarded (those on input channels for which
065 there is no corresponding output channel).
066 <p>
067 Because tokens are immutable, the same Token is sent
068 to the output, rather than a copy.  The <i>trueInput</i> and
069 <i>falseInput</i> port may receive Tokens of any type.
070 <p>
071 This actor is designed to be used with the DDF or PN director,
072 but it can also be used with SR, DE, and possibly other domains.
073 It should not be used with
074 SDF because the number of tokens it consumes is not fixed.
075 <p>
076 This actor is similar to the BooleanSelect actor, except that
077 it uses a parameter rather than a control port to determine which input
078 to use.
079
080 @author Charles Shelton and Edward A. Lee
081 @version $Id$
082 @since Ptolemy II 8.0
083 @Pt.ProposedRating Green (cshelton)
084 @Pt.AcceptedRating Red (cshelton)
085 */
086public class ConfigurationSelect extends TypedAtomicActor {
087    /** Construct an actor in the specified container with the specified
088     *  name.
089     *  @param container The container.
090     *  @param name The name of this actor within the container.
091     *  @exception IllegalActionException If the actor cannot be contained
092     *   by the proposed container.
093     *  @exception NameDuplicationException If the name coincides with
094     *   an actor already in the container.
095     */
096    public ConfigurationSelect(CompositeEntity container, String name)
097            throws IllegalActionException, NameDuplicationException {
098        super(container, name);
099
100        trueInput = new TypedIOPort(this, "trueInput", true, false);
101        trueInput.setMultiport(true);
102        falseInput = new TypedIOPort(this, "falseInput", true, false);
103        falseInput.setMultiport(true);
104
105        // Default selector value to false
106        selector = new Parameter(this, "selector", new BooleanToken(false));
107        selector.setTypeEquals(BaseType.BOOLEAN);
108
109        output = new TypedIOPort(this, "output", false, true);
110        output.setTypeAtLeast(trueInput);
111        output.setTypeAtLeast(falseInput);
112        output.setMultiport(true);
113        output.setWidthEquals(trueInput, true);
114        output.setWidthEquals(falseInput, true);
115
116        // For the benefit of the DDF and SDF director, this actor sets
117        // consumption rate values.
118        trueInput_tokenConsumptionRate = new Parameter(trueInput,
119                "tokenConsumptionRate", _zero);
120        trueInput_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
121        trueInput_tokenConsumptionRate.setTypeEquals(BaseType.INT);
122
123        falseInput_tokenConsumptionRate = new Parameter(falseInput,
124                "tokenConsumptionRate", _one);
125        falseInput_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
126        falseInput_tokenConsumptionRate.setTypeEquals(BaseType.INT);
127
128        /** Make the icon show T and F for trueInput and falseInput.
129         */
130        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-50\" y=\"-20\" "
131                + "width=\"100\" height=\"40\" " + "style=\"fill:white\"/>\n"
132                + "<text x=\"-47\" y=\"-3\" " + "style=\"font-size:14\">\n"
133                + "T \n" + "</text>\n" + "<text x=\"-47\" y=\"15\" "
134                + "style=\"font-size:14\">\n" + "F \n" + "</text>\n"
135                + "</svg>\n");
136    }
137
138    ///////////////////////////////////////////////////////////////////
139    ////                     ports and parameters                  ////
140
141    /** Input for tokens on the true path.  The type can be anything.
142     */
143    public TypedIOPort trueInput;
144
145    /** Input for tokens on the false path.  The type can be anything.
146     */
147    public TypedIOPort falseInput;
148
149    /** Parameter that selects one of the two input ports.  The type is
150     *  BooleanToken that defaults to false.
151     */
152    public Parameter selector;
153
154    /** The output port.  The type is at least the type of
155     *  <i>trueInput</i> and <i>falseInput</i>
156     */
157    public TypedIOPort output;
158
159    /** This parameter provides token consumption rate for <i>trueInput</i>.
160     *  The type is int and it defaults to zero.
161     */
162    public Parameter trueInput_tokenConsumptionRate;
163
164    /** This parameter provides token consumption rate for <i>falseInput</i>.
165     *  The type is int and it defaults to one.
166     */
167    public Parameter falseInput_tokenConsumptionRate;
168
169    ///////////////////////////////////////////////////////////////////
170    ////                         public methods                    ////
171
172    /** React to a change in an attribute.  This method is called by
173     *  a contained attribute when its value changes.  In this base class,
174     *  the method does nothing.  In derived classes, this method may
175     *  throw an exception, indicating that the new attribute value
176     *  is invalid.  It is up to the caller to restore the attribute
177     *  to a valid value if an exception is thrown.
178     *  @param attribute The attribute that changed.
179     *  @exception IllegalActionException If the change is not acceptable
180     *   to this container (not thrown in this base class).
181     */
182    @Override
183    public void attributeChanged(Attribute attribute)
184            throws IllegalActionException {
185        if (attribute == selector) {
186            boolean previousSelector = _selector;
187            _selector = ((BooleanToken) selector.getToken()).booleanValue();
188            if (_selector != previousSelector) {
189                if (_selector) {
190                    trueInput_tokenConsumptionRate.setToken(_one);
191                    falseInput_tokenConsumptionRate.setToken(_zero);
192                } else {
193                    trueInput_tokenConsumptionRate.setToken(_zero);
194                    falseInput_tokenConsumptionRate.setToken(_one);
195                }
196                Director director = getDirector();
197                if (director != null) {
198                    director.invalidateSchedule();
199                }
200            }
201        }
202    }
203
204    /** Clone this actor into the specified workspace. The new actor is
205     *  <i>not</i> added to the directory of that workspace (you must do this
206     *  yourself if you want it there).
207     *  The result is a new actor with the same ports as the original, but
208     *  no connections and no container.  A container must be set before
209     *  much can be done with this actor.
210     *
211     *  @param workspace The workspace for the cloned object.
212     *  @exception CloneNotSupportedException If cloned ports cannot have
213     *   as their container the cloned entity (this should not occur), or
214     *   if one of the attributes cannot be cloned.
215     *  @return A new ComponentEntity.
216     */
217    @Override
218    public Object clone(Workspace workspace) throws CloneNotSupportedException {
219        ConfigurationSelect newObject = (ConfigurationSelect) super.clone(
220                workspace);
221        newObject.output.setTypeAtLeast(newObject.trueInput);
222        newObject.output.setTypeAtLeast(newObject.falseInput);
223        newObject.output.setWidthEquals(newObject.trueInput, true);
224        newObject.output.setWidthEquals(newObject.falseInput, true);
225
226        return newObject;
227    }
228
229    /** Read a token from each input port.  If the
230     *  <i>selector</i> parameter is true, then output the token consumed from the
231     *  <i>trueInput</i> port, otherwise output the token from the
232     *  <i>falseInput</i> port.
233     *  @exception IllegalActionException If there is no director.
234     */
235    @Override
236    public void fire() throws IllegalActionException {
237        super.fire();
238        if (_selector) {
239            for (int i = 0; i < trueInput.getWidth(); i++) {
240                if (output.getWidth() > i) {
241                    output.send(i, trueInput.get(i));
242                }
243            }
244        } else {
245            for (int i = 0; i < falseInput.getWidth(); i++) {
246                if (output.getWidth() > i) {
247                    output.send(i, falseInput.get(i));
248                }
249            }
250        }
251    }
252
253    /** Return false if there are not available tokens on the input
254     *  port chosen by the current value of the selector. Otherwise,
255     *  return true.
256     *  @return False if there are not enough tokens to fire.
257     *  @exception IllegalActionException If there is no director.
258     */
259    @Override
260    public boolean prefire() throws IllegalActionException {
261        boolean result = super.prefire();
262        if (_selector) {
263            for (int i = 0; i < trueInput.getWidth(); i++) {
264                if (!trueInput.hasToken(i)) {
265                    return false;
266                }
267            }
268        } else {
269            for (int i = 0; i < falseInput.getWidth(); i++) {
270                if (!falseInput.hasToken(i)) {
271                    return false;
272                }
273            }
274        }
275        return result;
276    }
277
278    ///////////////////////////////////////////////////////////////////
279    ////                         private variables                 ////
280
281    /** Cached value of selector. */
282    private boolean _selector = false;
283
284    /** A final static IntToken with value 0. */
285    private final static IntToken _zero = new IntToken(0);
286
287    /** A final static IntToken with value 1. */
288    private final static IntToken _one = new IntToken(1);
289}