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}