001/* A polymorphic distributor. 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 */ 028package ptolemy.actor.lib; 029 030import ptolemy.actor.IOPort; 031import ptolemy.data.IntToken; 032import ptolemy.data.Token; 033import ptolemy.data.expr.Parameter; 034import ptolemy.data.type.BaseType; 035import ptolemy.kernel.CompositeEntity; 036import ptolemy.kernel.Port; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.NameDuplicationException; 039import ptolemy.kernel.util.NamedObj; 040import ptolemy.kernel.util.Settable; 041import ptolemy.kernel.util.Workspace; 042 043/////////////////////////////////////////////////////////////////// 044//// Distributor 045 046/** 047 A polymorphic distributor, which splits an input stream into a set of 048 output streams. The distributor has an input port and an output port, 049 the latter of which is a multiport. 050 The types of the ports are undeclared and will be resolved by the type 051 resolution mechanism, with the constraint that the output type must be 052 greater than or equal to the input type. On each call to the fire method, the 053 actor reads at most <i>N</i> tokens from the input, where <i>N</i> is 054 the width of the output port times the <i>blockSize</i> parameter, 055 and writes <i>blockSize</i> tokens to each output channel, 056 in the order of the channels. If there are fewer than <i>N</i> tokens 057 at the input, then the all available input tokens are sent to the output 058 channels, and the fire() method returns. In the next iteration of this 059 actor, it will begin producing outputs on the first channel that did 060 not have enough tokens in the previous iteration. 061 <p> 062 For the benefit of domains like SDF, which need to know the token consumption 063 or production rate for all ports before they can construct a firing schedule, 064 this actor sets the tokenConsumptionRate parameter for the input port 065 to equal the number of output channels times the <i>blockSize</i> parameter, 066 and the output production rate is set to the <i>blockSize</i> parameter. 067 The consumption rate parameter is set each time that a link is established with 068 the input port, or when a link is removed. The director is notified 069 that the schedule is invalid, so that if the link is modified at 070 run time, the schedule will be recalculated if necessary. 071 072 @author Mudit Goel, Edward A. Lee 073 @version $Id$ 074 @since Ptolemy II 0.2 075 @Pt.ProposedRating Yellow (mudit) 076 @Pt.AcceptedRating Yellow (cxh) 077 */ 078public class Distributor extends Transformer implements SequenceActor { 079 /** Construct an actor in the specified container with the specified 080 * name. Create ports and make the input port a multiport. Create 081 * the actor parameters. 082 * 083 * @param container The container. 084 * @param name This is the name of this distributor within the container. 085 * @exception NameDuplicationException If an actor 086 * with an identical name already exists in the container. 087 * @exception IllegalActionException If the actor cannot be contained 088 * by the proposed container. 089 */ 090 public Distributor(CompositeEntity container, String name) 091 throws NameDuplicationException, IllegalActionException { 092 super(container, name); 093 094 blockSize = new Parameter(this, "blockSize"); 095 blockSize.setTypeEquals(BaseType.INT); 096 blockSize.setExpression("1"); 097 098 // These parameters are required for SDF 099 input_tokenConsumptionRate = new WidthDependentParameter(input, 100 "tokenConsumptionRate", new IntToken(0), output); 101 102 input_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE); 103 input_tokenConsumptionRate.setTypeEquals(BaseType.INT); 104 input_tokenConsumptionRate.setPersistent(false); 105 106 output_tokenProductionRate = new Parameter(output, 107 "tokenProductionRate"); 108 output_tokenProductionRate.setVisibility(Settable.NOT_EDITABLE); 109 output_tokenProductionRate.setTypeEquals(BaseType.INT); 110 output_tokenProductionRate.setExpression("blockSize"); 111 output_tokenProductionRate.setPersistent(false); 112 113 output.setMultiport(true); 114 } 115 116 /////////////////////////////////////////////////////////////////// 117 //// ports and parameters //// 118 119 /** The number of tokens produced on each output channel on each firing. 120 * This is an integer with default value 0. 121 */ 122 public Parameter blockSize; 123 124 /** The parameter controlling the input port consumption rate. 125 * This is an integer, initially with value 0. Whenever a connection 126 * is made to the output, the value of this parameter is changed to 127 * equal the width of the output times the <i>blockSize</i> parameter. 128 */ 129 public Parameter input_tokenConsumptionRate; 130 131 /** The parameter specifying the output port production rate. 132 * This is an integer, equal to the value of <i>blockSize</i>. 133 */ 134 public Parameter output_tokenProductionRate; 135 136 /////////////////////////////////////////////////////////////////// 137 //// public methods //// 138 139 /** Clone the actor into the specified workspace. This calls the base 140 * class method and sets the public variables to point to the new ports. 141 * @param workspace The workspace for the new object. 142 * @return A new actor. 143 * @exception CloneNotSupportedException If a derived class contains 144 * attributes that cannot be cloned. 145 */ 146 @Override 147 public Object clone(Workspace workspace) throws CloneNotSupportedException { 148 Distributor newObject = (Distributor) super.clone(workspace); 149 newObject.input_tokenConsumptionRate = (Parameter) newObject.input 150 .getAttribute("tokenConsumptionRate"); 151 ((WidthDependentParameter) newObject.input_tokenConsumptionRate) 152 .setPort(newObject.output); 153 return newObject; 154 } 155 156 /** Notify this entity that the links to the specified port have 157 * been altered. This sets the consumption rate of the input port 158 * and notifies the director that the schedule is invalid, if there 159 * is a director. This also sets the current output position to zero, 160 * which means that the next consumed input token will be produced 161 * on channel zero of the output. 162 */ 163 @Override 164 public void connectionsChanged(Port port) { 165 super.connectionsChanged(port); 166 167 if (port == output) { 168 _currentOutputPosition = 0; 169 170 // NOTE: schedule is invalidated automatically already 171 // by the changed connections. 172 } 173 } 174 175 /** Read at most <i>N</i> tokens from the input port, where <i>N</i> 176 * is the width of the output port times the <i>blockSize</i> parameter. 177 * Write <i>blockSize</i> tokens to each of the 178 * output channels. If there are not <i>N</i> tokens available, 179 * then read all the tokens and send them to the outputs. 180 * On the next iteration, the actor will pick up where it left off. 181 * @exception IllegalActionException If there is no director. 182 */ 183 @Override 184 public void fire() throws IllegalActionException { 185 super.fire(); 186 _tentativeOutputPosition = _currentOutputPosition; 187 188 int width = output.getWidth(); 189 int blockSizeValue = ((IntToken) blockSize.getToken()).intValue(); 190 191 for (int i = 0; i < width; i++) { 192 if (!input.hasToken(0, blockSizeValue)) { 193 break; 194 } 195 Token[] tokens = input.get(0, blockSizeValue); 196 output.send(_tentativeOutputPosition++, tokens, blockSizeValue); 197 198 if (_tentativeOutputPosition >= width) { 199 _tentativeOutputPosition = 0; 200 } 201 } 202 } 203 204 /** Begin execution by setting the current output channel to zero. 205 * @exception IllegalActionException If there is no director. 206 */ 207 @Override 208 public void initialize() throws IllegalActionException { 209 super.initialize(); 210 _currentOutputPosition = 0; 211 } 212 213 /** Update the output position to equal that determined by the most 214 * recent invocation of the fire() method. The output position is 215 * the channel number of the output port to which the next input 216 * will be sent. 217 * @exception IllegalActionException If there is no director. 218 */ 219 @Override 220 public boolean postfire() throws IllegalActionException { 221 _currentOutputPosition = _tentativeOutputPosition; 222 return super.postfire(); 223 } 224 225 /////////////////////////////////////////////////////////////////// 226 //// private variables //// 227 228 // The channel number for the next output. 229 private int _currentOutputPosition; 230 231 // The new channel number for the next output as determined by fire(). 232 private int _tentativeOutputPosition; 233 234 /** This parameter overrides the default behavior to always return the 235 * value of the blockSize parameter times the width of the input port. 236 */ 237 private class WidthDependentParameter extends Parameter { 238 public WidthDependentParameter(NamedObj container, String name, 239 Token token, IOPort port) 240 throws IllegalActionException, NameDuplicationException { 241 super(container, name, token); 242 _port = port; 243 setPersistent(false); 244 } 245 246 @Override 247 public ptolemy.data.Token getToken() throws IllegalActionException { 248 IntToken blockSizeValue = (IntToken) blockSize.getToken(); 249 setToken( 250 new IntToken(_port.getWidth() * blockSizeValue.intValue())); 251 return super.getToken(); 252 } 253 254 void setPort(IOPort port) { 255 _port = port; 256 } 257 258 private IOPort _port; 259 }; 260}