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