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