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}