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}