001/* Split a stream into two 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 Switch and could be design/code reviewed at the same time.
028 */
029package ptolemy.actor.lib;
030
031import ptolemy.actor.TypedAtomicActor;
032import ptolemy.actor.TypedIOPort;
033import ptolemy.data.BooleanToken;
034import ptolemy.data.Token;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NameDuplicationException;
039import ptolemy.kernel.util.StringAttribute;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// BooleanSwitch
044
045/**
046 Split an input stream onto two output ports depending on a
047 boolean control input.  In an
048 iteration, if an input token is available at the <i>control</i> input,
049 that token is read, and its value is noted.  Its value specifies the
050 output port that should be written to in this and subsequent iterations,
051 until another <i>control</i> input is provided. If no <i>control</i>
052 input is provided, then the inputs are routed to the <i>falseOutput</i> port.
053 In each iteration, at most one token on each channel of the <i>input</i> port
054 is read and sent to the corresponding channel of the
055 <i>trueOutput</i> port or the <i>falseOutput</i> port, depending on the
056 most recently received <i>control</i> input.
057 If the input has width greater than an output port, then
058 some input tokens will be discarded (those on input channels for which
059 there is no corresponding output channel).
060 Because tokens are
061 immutable, the same Token is sent to the output, rather than a copy.
062 The <i>input</i> port may receive Tokens of any type.
063
064 <p>Note that the this actor may be used in Synchronous Dataflow (SDF)
065 models, but only under certain circumstances. Specifically, downstream
066 actors will be fired whether a token is sent to them or not.
067 This will only work if the downstream actors specifically check to
068 see whether input tokens are available.
069
070 @author Steve Neuendorffer
071 @version $Id$
072 @since Ptolemy II 2.0
073 @Pt.ProposedRating Green (neuendor)
074 @Pt.AcceptedRating Red (neuendor)
075 */
076public class BooleanSwitch extends TypedAtomicActor {
077    /** Construct an actor in the specified container with the specified
078     *  name.
079     *  @param container The container.
080     *  @param name The name of this actor within the container.
081     *  @exception IllegalActionException If the actor cannot be contained
082     *   by the proposed container.
083     *  @exception NameDuplicationException If the name coincides with
084     *   an actor already in the container.
085     */
086    public BooleanSwitch(CompositeEntity container, String name)
087            throws IllegalActionException, NameDuplicationException {
088        super(container, name);
089
090        input = new TypedIOPort(this, "input", true, false);
091        input.setMultiport(true);
092
093        control = new TypedIOPort(this, "control", true, false);
094        control.setTypeEquals(BaseType.BOOLEAN);
095        trueOutput = new TypedIOPort(this, "trueOutput", false, true);
096        falseOutput = new TypedIOPort(this, "falseOutput", false, true);
097        trueOutput.setTypeAtLeast(input);
098        falseOutput.setTypeAtLeast(input);
099        trueOutput.setMultiport(true);
100        falseOutput.setMultiport(true);
101        trueOutput.setWidthEquals(input, true);
102        falseOutput.setWidthEquals(input, true);
103
104        // Put the control input on the bottom of the actor.
105        StringAttribute controlCardinal = new StringAttribute(control,
106                "_cardinal");
107        controlCardinal.setExpression("SOUTH");
108    }
109
110    ///////////////////////////////////////////////////////////////////
111    ////                     ports and parameters                  ////
112
113    /** Input that selects one of the other input ports.  The type is
114     *  boolean.
115     */
116    public TypedIOPort control;
117
118    /** The input port.  The type can be anything. This is a multiport,
119     *  and input tokens on all channels are routed to corresponding
120     *  channels on the output port, if there are such channels.
121     */
122    public TypedIOPort input;
123
124    /** Output for tokens on the true path.  The type is at least the
125     *  type of the input.
126     */
127    public TypedIOPort trueOutput;
128
129    /** Output for tokens on the false path.  The type is at least the
130     *  type of the input.
131     */
132    public TypedIOPort falseOutput;
133
134    ///////////////////////////////////////////////////////////////////
135    ////                         public methods                    ////
136
137    /** Clone this actor into the specified workspace. The new actor is
138     *  <i>not</i> added to the directory of that workspace (you must do this
139     *  yourself if you want it there).
140     *  The result is a new actor with the same ports as the original, but
141     *  no connections and no container.  A container must be set before
142     *  much can be done with this actor.
143     *
144     *  @param workspace The workspace for the cloned object.
145     *  @exception CloneNotSupportedException If cloned ports cannot have
146     *   as their container the cloned entity (this should not occur), or
147     *   if one of the attributes cannot be cloned.
148     *  @return A new ComponentEntity.
149     */
150    @Override
151    public Object clone(Workspace workspace) throws CloneNotSupportedException {
152        BooleanSwitch newObject = (BooleanSwitch) super.clone(workspace);
153        newObject.trueOutput.setTypeAtLeast(newObject.input);
154        newObject.falseOutput.setTypeAtLeast(newObject.input);
155        newObject.trueOutput.setWidthEquals(newObject.input, true);
156        newObject.falseOutput.setWidthEquals(newObject.input, true);
157
158        return newObject;
159    }
160
161    /** Read a token from each input port.  If the token from the
162     *  <i>control</i> input is true, then output the token consumed from the
163     *  <i>input</i> port on the <i>trueOutput</i> port,
164     *  otherwise output the token on the <i>falseOutput</i> port.
165     *
166     *  @exception IllegalActionException If there is no director.
167     */
168    @Override
169    public void fire() throws IllegalActionException {
170        super.fire();
171        if (control.hasToken(0)) {
172            _control = ((BooleanToken) control.get(0)).booleanValue();
173        }
174
175        for (int i = 0; i < input.getWidth(); i++) {
176            if (input.hasToken(i)) {
177                Token token = input.get(i);
178
179                if (_control) {
180                    if (i < trueOutput.getWidth()) {
181                        trueOutput.send(i, token);
182                    }
183                } else {
184                    if (i < falseOutput.getWidth()) {
185                        falseOutput.send(i, token);
186                    }
187                }
188            }
189        }
190    }
191
192    /** Initialize this actor so that the <i>falseOutput</i> is written
193     *  to until a token arrives on the <i>control</i> input.
194     *  @exception IllegalActionException If the parent class throws it.
195     */
196    @Override
197    public void initialize() throws IllegalActionException {
198        super.initialize();
199        _control = false;
200    }
201
202    ///////////////////////////////////////////////////////////////////
203    ////                         private variables                 ////
204    // The most recently read control token.
205    private boolean _control = false;
206}