001/* A type polymorphic boolean 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.BooleanToken;
033import ptolemy.data.IntToken;
034import ptolemy.data.expr.Parameter;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NameDuplicationException;
039import ptolemy.kernel.util.Settable;
040import ptolemy.kernel.util.StringAttribute;
041import ptolemy.kernel.util.Workspace;
042
043/**
044 A type polymorphic select with boolean valued control for use in
045 the DDF domain. In the first iteration, an input token at the
046 <i>control</i> port is read and its value is noted. In the second
047 iteration, if the <i>control</i> input read from the previous
048 iteration is true, then an input token at the <i>trueInput</i> port
049 is read and sent to the output. Likewise with a false <i>control</i>
050 input and the <i>falseInput</i> port. It alternates between these
051 two kinds of iterations until stopped. The <i>control</i> port must
052 receive Boolean Tokens. The <i>trueInput</i> and <i>falseInput</i>
053 ports may receive Tokens of any type. Because tokens are immutable,
054 the same Token is sent to the output, rather than a copy.
055 <p>
056 Note this actor sends an output token every two iterations. Contrast
057 this with BooleanSelect which sends an output token every iteration.
058
059 @author Gang Zhou
060 @version $Id$
061 @since Ptolemy II 4.1
062 @Pt.ProposedRating Yellow (zgang)
063 @Pt.AcceptedRating Yellow (cxh)
064 */
065public class DDFBooleanSelect extends TypedAtomicActor {
066    /** Construct an actor in the specified container with the specified
067     *  name.
068     *  @param container The container.
069     *  @param name The name of this actor within the container.
070     *  @exception IllegalActionException If the actor cannot be contained
071     *   by the proposed container.
072     *  @exception NameDuplicationException If the name coincides with
073     *   an actor already in the container.
074     */
075    public DDFBooleanSelect(CompositeEntity container, String name)
076            throws IllegalActionException, NameDuplicationException {
077        super(container, name);
078
079        trueInput = new TypedIOPort(this, "trueInput", true, false);
080        falseInput = new TypedIOPort(this, "falseInput", true, false);
081        control = new TypedIOPort(this, "control", true, false);
082        control.setTypeEquals(BaseType.BOOLEAN);
083        output = new TypedIOPort(this, "output", false, true);
084        output.setTypeAtLeast(trueInput);
085        output.setTypeAtLeast(falseInput);
086
087        trueInput_tokenConsumptionRate = new Parameter(trueInput,
088                "tokenConsumptionRate");
089        trueInput_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
090        trueInput_tokenConsumptionRate.setTypeEquals(BaseType.INT);
091
092        falseInput_tokenConsumptionRate = new Parameter(falseInput,
093                "tokenConsumptionRate");
094        falseInput_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
095        falseInput_tokenConsumptionRate.setTypeEquals(BaseType.INT);
096
097        control_tokenConsumptionRate = new Parameter(control,
098                "tokenConsumptionRate");
099        control_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
100        control_tokenConsumptionRate.setTypeEquals(BaseType.INT);
101
102        // Put the control input on the bottom of the actor.
103        StringAttribute controlCardinal = new StringAttribute(control,
104                "_cardinal");
105        controlCardinal.setExpression("SOUTH");
106    }
107
108    ///////////////////////////////////////////////////////////////////
109    ////                     ports and parameters                  ////
110
111    /** Input for tokens on the true path. The port type can be any type.
112     */
113    public TypedIOPort trueInput;
114
115    /** Input for tokens on the false path. The port type can be any type.
116     */
117    public TypedIOPort falseInput;
118
119    /** Input that selects one of the other input ports.  The type is
120     *  boolean.
121     */
122    public TypedIOPort control;
123
124    /** The output port.  The type is at least the type of
125     *  <i>trueInput</i> and <i>falseInput</i>
126     */
127    public TypedIOPort output;
128
129    /** This parameter provides token consumption rate for <i>trueInput</i>.
130     *  The type is int.
131     */
132    public Parameter trueInput_tokenConsumptionRate;
133
134    /** This parameter provides token consumption rate for <i>falseInput</i>.
135     *  The type is int.
136     */
137    public Parameter falseInput_tokenConsumptionRate;
138
139    /** This parameter provides token consumption rate for <i>control</i>.
140     *  The type is int.
141     */
142    public Parameter control_tokenConsumptionRate;
143
144    ///////////////////////////////////////////////////////////////////
145    ////                         public methods                    ////
146
147    /** Clone the actor into the specified workspace. This calls the
148     *  base class and then sets the type constraints.
149     *  @param workspace The workspace for the new object.
150     *  @return A new actor.
151     *  @exception CloneNotSupportedException If a derived class has
152     *   an attribute that cannot be cloned.
153     */
154    @Override
155    public Object clone(Workspace workspace) throws CloneNotSupportedException {
156        DDFBooleanSelect newObject = (DDFBooleanSelect) super.clone(workspace);
157        newObject.output.setTypeAtLeast(newObject.trueInput);
158        newObject.output.setTypeAtLeast(newObject.falseInput);
159        return newObject;
160    }
161
162    /** Fire the actor once. If the <i>control</i> port is not read in the
163     *  previous iteration, read a new token from the <i>control</i> port
164     *  and record the value of the token and this concludes the current
165     *  firing. Otherwise output the token consumed from the <i>trueInput</i>
166     *  port if the token read from the <i>control</i> port in the previous
167     *  firing is true. Likewise with a false <i>control</i> input and the
168     *  <i>falseInput</i> port. Then reset an internal variable so that
169     *  it will read from the <i>control</i> port in the next iteration.
170     *  @exception IllegalActionException If there is no director, and hence
171     *   no receivers have been created.
172     */
173    @Override
174    public void fire() throws IllegalActionException {
175        super.fire();
176
177        if (_isControlRead) {
178            if (_control) {
179                output.send(0, trueInput.get(0));
180            } else {
181                output.send(0, falseInput.get(0));
182            }
183
184            _isControlRead = false;
185        } else {
186            _control = ((BooleanToken) control.get(0)).booleanValue();
187            _isControlRead = true;
188        }
189    }
190
191    /** Initialize this actor and the rate parameters so that it will read
192     *  from the <i>control</i> port in the first iteration.
193     *  @exception IllegalActionException If setToken() throws it.
194     */
195    @Override
196    public void initialize() throws IllegalActionException {
197        super.initialize();
198        _isControlRead = false;
199        trueInput_tokenConsumptionRate.setToken(_zero);
200        falseInput_tokenConsumptionRate.setToken(_zero);
201        control_tokenConsumptionRate.setToken(_one);
202    }
203
204    /** Update rate parameters for the next iteration.
205     *  Then return whatever the superclass returns.
206     *  @return True if execution can continue into the next iteration.
207     *  @exception IllegalActionException If setToken() throws it.
208     */
209    @Override
210    public boolean postfire() throws IllegalActionException {
211        if (_isControlRead) {
212            if (_control) {
213                trueInput_tokenConsumptionRate.setToken(_one);
214                falseInput_tokenConsumptionRate.setToken(_zero);
215                control_tokenConsumptionRate.setToken(_zero);
216            } else {
217                trueInput_tokenConsumptionRate.setToken(_zero);
218                falseInput_tokenConsumptionRate.setToken(_one);
219                control_tokenConsumptionRate.setToken(_zero);
220            }
221        } else {
222            trueInput_tokenConsumptionRate.setToken(_zero);
223            falseInput_tokenConsumptionRate.setToken(_zero);
224            control_tokenConsumptionRate.setToken(_one);
225        }
226
227        return super.postfire();
228    }
229
230    /** Return false if the port to read from in the current
231     *  iteration does not have a token. If the <i>control</i> port is
232     *  not read in the previous iteration, the port to read from
233     *  in the current iteration is the <i>control</i> port. Otherwise,
234     *  it is the <i>trueInput</i> port or the <i>falseInput</i> port
235     *  depending on the <i>control</i> input value read in the
236     *  previous iteration.
237     *  @return False if there are not enough tokens to fire.
238     *  @exception IllegalActionException If the receivers do not support
239     *   the query, or if there is no director, and hence no receivers.
240     */
241    @Override
242    public boolean prefire() throws IllegalActionException {
243        if (_isControlRead) {
244            if (_control) {
245                if (!trueInput.hasToken(0)) {
246                    return false;
247                }
248            } else {
249                if (!falseInput.hasToken(0)) {
250                    return false;
251                }
252            }
253        } else {
254            if (!control.hasToken(0)) {
255                return false;
256            }
257        }
258
259        return super.prefire();
260    }
261
262    ///////////////////////////////////////////////////////////////////
263    ////                         private variables                 ////
264
265    /** The most recently read <i>control</i> token.
266     */
267    private boolean _control;
268
269    /** The boolean to determine whether to read from the <i>control<i>
270     *  port or the <i>trueInput</i>/<i>falseInput</i> port.
271     */
272    private boolean _isControlRead;
273
274    /** A final static IntToken with value 0.
275     */
276    private final static IntToken _zero = new IntToken(0);
277
278    /** A final static IntToken with value 1.
279     */
280    private final static IntToken _one = new IntToken(1);
281}