001/* Split a stream into two according to a boolean selector parameter.
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 the BooleanSwitch actor.
028 */
029package ptolemy.actor.lib;
030
031import ptolemy.actor.Director;
032import ptolemy.actor.TypedAtomicActor;
033import ptolemy.actor.TypedIOPort;
034import ptolemy.actor.parameters.PortParameter;
035import ptolemy.data.BooleanToken;
036import ptolemy.data.IntToken;
037import ptolemy.data.Token;
038import ptolemy.data.expr.Parameter;
039import ptolemy.data.type.BaseType;
040import ptolemy.kernel.CompositeEntity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044import ptolemy.kernel.util.Settable;
045import ptolemy.kernel.util.StringAttribute;
046import ptolemy.kernel.util.Workspace;
047
048///////////////////////////////////////////////////////////////////
049//// ConfigurationSwitch
050
051/**
052 Split an input stream onto two output ports depending on a
053 boolean selector parameter.  The value of the <i>selector</i> parameter specifies the
054 output port that should be written to in this and subsequent iterations.
055 In each iteration, at most one token on each channel of the <i>input</i> port
056 is read and sent to the corresponding channel of the
057 <i>trueOutput</i> port or the <i>falseOutput</i> port, depending on the
058 most value of the <i>selector</i> parameter.
059 If the input has width greater than an output port, then
060 some input tokens will be discarded (those on input channels for which
061 there is no corresponding output channel).
062 Because tokens are
063 immutable, the same Token is sent to the output, rather than a copy.
064 The <i>input</i> port may receive Tokens of any type.
065
066 <p>Note that the this actor may be used in Synchronous Dataflow (SDF)
067 models, but only under certain circumstances. It specifies an output
068 production rate of zero on the output port not used, so downstream
069 actors will not be fired.
070
071 @author Charles Shelton
072 @version $Id$
073 @since Ptolemy II 8.0
074 @Pt.ProposedRating Green (cshelton)
075 @Pt.AcceptedRating Red (cshelton)
076 */
077public class ConfigurationSwitch extends TypedAtomicActor {
078    /** Construct an actor in the specified container with the specified
079     *  name.
080     *  @param container The container.
081     *  @param name The name of this actor within the container.
082     *  @exception IllegalActionException If the actor cannot be contained
083     *   by the proposed container.
084     *  @exception NameDuplicationException If the name coincides with
085     *   an actor already in the container.
086     */
087    public ConfigurationSwitch(CompositeEntity container, String name)
088            throws IllegalActionException, NameDuplicationException {
089        super(container, name);
090
091        input = new TypedIOPort(this, "input", true, false);
092        input.setMultiport(true);
093
094        // Default selector value to false
095        selector = new PortParameter(this, "selector", new BooleanToken(false));
096        selector.setTypeEquals(BaseType.BOOLEAN);
097
098        // Put the selector input on the bottom of the actor.
099        StringAttribute selectorCardinal = new StringAttribute(
100                selector.getPort(), "_cardinal");
101        selectorCardinal.setExpression("SOUTH");
102
103        trueOutput = new TypedIOPort(this, "trueOutput", false, true);
104        falseOutput = new TypedIOPort(this, "falseOutput", false, true);
105        trueOutput.setTypeAtLeast(input);
106        falseOutput.setTypeAtLeast(input);
107        trueOutput.setMultiport(true);
108        falseOutput.setMultiport(true);
109        trueOutput.setWidthEquals(input, true);
110        falseOutput.setWidthEquals(input, true);
111
112        // For the benefit of the DDF and SDF director, this actor sets
113        // consumption rate values.
114        trueOutput_tokenProductionRate = new Parameter(trueOutput,
115                "tokenProductionRate", _zero);
116        trueOutput_tokenProductionRate.setVisibility(Settable.NOT_EDITABLE);
117        trueOutput_tokenProductionRate.setTypeEquals(BaseType.INT);
118
119        falseOutput_tokenProductionRate = new Parameter(falseOutput,
120                "tokenProductionRate", _one);
121        falseOutput_tokenProductionRate.setVisibility(Settable.NOT_EDITABLE);
122        falseOutput_tokenProductionRate.setTypeEquals(BaseType.INT);
123
124        /** Make the icon show T and F for trueOutput and falseOutput.
125         */
126        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-50\" y=\"-20\" "
127                + "width=\"100\" height=\"40\" " + "style=\"fill:white\"/>\n"
128                + "<text x=\"39\" y=\"-3\" " + "style=\"font-size:14\">\n"
129                + "T \n" + "</text>\n" + "<text x=\"39\" y=\"15\" "
130                + "style=\"font-size:14\">\n" + "F \n" + "</text>\n"
131                + "</svg>\n");
132    }
133
134    ///////////////////////////////////////////////////////////////////
135    ////                     ports and parameters                  ////
136
137    /** PortParameter that selects one of the two input ports.  The type is
138     *  BooleanToken that defaults to false.
139     */
140    public PortParameter selector;
141
142    /** The input port.  The type can be anything. This is a multiport,
143     *  and input tokens on all channels are routed to corresponding
144     *  channels on the output port, if there are such channels.
145     */
146    public TypedIOPort input;
147
148    /** Output for tokens on the true path.  The type is at least the
149     *  type of the input.
150     */
151    public TypedIOPort trueOutput;
152
153    /** Output for tokens on the false path.  The type is at least the
154     *  type of the input.
155     */
156    public TypedIOPort falseOutput;
157
158    /** This parameter provides token consumption rate for <i>trueOutput</i>.
159     *  The type is int and it defaults to zero.
160     */
161    public Parameter trueOutput_tokenProductionRate;
162
163    /** This parameter provides token consumption rate for <i>falseOutput</i>.
164     *  The type is int and it defaults to one.
165     */
166    public Parameter falseOutput_tokenProductionRate;
167
168    ///////////////////////////////////////////////////////////////////
169    ////                         public methods                    ////
170
171    /** React to a change in an attribute.  This method is called by
172     *  a contained attribute when its value changes.  In this base class,
173     *  the method does nothing.  In derived classes, this method may
174     *  throw an exception, indicating that the new attribute value
175     *  is invalid.  It is up to the caller to restore the attribute
176     *  to a valid value if an exception is thrown.
177     *  @param attribute The attribute that changed.
178     *  @exception IllegalActionException If the change is not acceptable
179     *   to this container (not thrown in this base class).
180     */
181    @Override
182    public void attributeChanged(Attribute attribute)
183            throws IllegalActionException {
184        if (attribute == selector) {
185            boolean previousSelector = _selector;
186            _selector = ((BooleanToken) selector.getToken()).booleanValue();
187            if (_selector != previousSelector) {
188                if (_selector) {
189                    trueOutput_tokenProductionRate.setToken(_one);
190                    falseOutput_tokenProductionRate.setToken(_zero);
191                } else {
192                    trueOutput_tokenProductionRate.setToken(_zero);
193                    falseOutput_tokenProductionRate.setToken(_one);
194                }
195                Director director = getDirector();
196                if (director != null) {
197                    director.invalidateSchedule();
198                }
199            }
200        }
201    }
202
203    /** Clone this actor into the specified workspace. The new actor is
204     *  <i>not</i> added to the directory of that workspace (you must do this
205     *  yourself if you want it there).
206     *  The result is a new actor with the same ports as the original, but
207     *  no connections and no container.  A container must be set before
208     *  much can be done with this actor.
209     *
210     *  @param workspace The workspace for the cloned object.
211     *  @exception CloneNotSupportedException If cloned ports cannot have
212     *   as their container the cloned entity (this should not occur), or
213     *   if one of the attributes cannot be cloned.
214     *  @return A new ComponentEntity.
215     */
216    @Override
217    public Object clone(Workspace workspace) throws CloneNotSupportedException {
218        ConfigurationSwitch newObject = (ConfigurationSwitch) super.clone(
219                workspace);
220
221        newObject.trueOutput.setTypeAtLeast(newObject.input);
222        newObject.falseOutput.setTypeAtLeast(newObject.input);
223        newObject.trueOutput.setWidthEquals(newObject.input, true);
224        newObject.falseOutput.setWidthEquals(newObject.input, 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>input</i> port on the <i>trueOutput</i> port,
232     *  otherwise output the token on the <i>falseOutput</i> port.
233     *  @exception IllegalActionException If there is no director.
234     */
235    @Override
236    public void fire() throws IllegalActionException {
237        super.fire();
238        for (int i = 0; i < input.getWidth(); i++) {
239            if (input.hasToken(i)) {
240                Token token = input.get(i);
241
242                if (_selector) {
243                    if (i < trueOutput.getWidth()) {
244                        trueOutput.send(i, token);
245                    }
246                } else {
247                    if (i < falseOutput.getWidth()) {
248                        falseOutput.send(i, token);
249                    }
250                }
251            }
252        }
253    }
254
255    ///////////////////////////////////////////////////////////////////
256    ////                         private variables                 ////
257
258    /** Cached value of selector. */
259    private boolean _selector = false;
260
261    /** A final static IntToken with value 0. */
262    private final static IntToken _zero = new IntToken(0);
263
264    /** A final static IntToken with value 1. */
265    private final static IntToken _one = new IntToken(1);
266}