001/* A commutator that processes a single token per iteration, used in DDF doamin.
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.domains.ddf.lib;
029
030import ptolemy.actor.lib.SingleTokenCommutator;
031import ptolemy.data.ArrayToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.expr.Parameter;
034import ptolemy.data.type.ArrayType;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.Port;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.Settable;
042
043///////////////////////////////////////////////////////////////////
044//// DDFSingleTokenCommutator
045
046/**
047 The DDFSingleTokenCommutator has a multiport input port and an output
048 port.  The types of the ports are undeclared and will be resolved by
049 the type resolution mechanism, with the constraint that the output
050 type must be greater than or equal to the input type. On each call to
051 the fire() method, the actor reads one token from the current input
052 channel, and writes the token to the output port. Then in the postfire()
053 method, it will update token consumption rate of the input port so that
054 it will read token from the next channel in the next iteration.
055
056 @author Gang Zhou
057 @version $Id$
058 @since Ptolemy II 4.1
059 @Pt.ProposedRating Yellow (zgang)
060 @Pt.AcceptedRating Yellow (cxh)
061 */
062public class DDFSingleTokenCommutator extends SingleTokenCommutator {
063
064    // FIXME: This actor extends sr.lib.SingleTokenCommutator which is
065    // really wrong because it means that if we ship DDF, then we _must_
066    // ship SR.  One solution would be to move this actor elsewhere,
067    // say actor/lib.  It would be interesting to see what happens when
068    // this actor is used with SDF, perhaps we should throw an error
069    // automatically.
070
071    /** Construct an actor in the specified container with the specified name.
072     *  @param container The container.
073     *  @param name This is the name of this distributor within the container.
074     *  @exception NameDuplicationException If an actor
075     *   with an identical name already exists in the container.
076     *  @exception IllegalActionException If the actor cannot be contained
077     *   by the proposed container.
078     */
079    public DDFSingleTokenCommutator(CompositeEntity container, String name)
080            throws NameDuplicationException, IllegalActionException {
081        super(container, name);
082
083        input_tokenConsumptionRate = new Parameter(input,
084                "tokenConsumptionRate");
085        input_tokenConsumptionRate.setVisibility(Settable.NOT_EDITABLE);
086        input_tokenConsumptionRate.setTypeEquals(new ArrayType(BaseType.INT));
087    }
088
089    ///////////////////////////////////////////////////////////////////
090    ////                          parameters                       ////
091
092    /** This parameter provides token consumption rate for each input
093     *  channel. The type is array of ints.
094     */
095    public Parameter input_tokenConsumptionRate;
096
097    ///////////////////////////////////////////////////////////////////
098    ////                         public methods                    ////
099
100    /** Pre-calculate the rates to be set in the rate parameter of the
101     *  <i>input</i> port. Initialize the private variable _rateArray,
102     *  each element of which indicates the <i>input</i> port needs to
103     *  consume one token from a corresponding channel and no token from
104     *  the rest of the channels.
105     *  @param port The port that has connection changes.
106     */
107    @Override
108    public void connectionsChanged(Port port) {
109        super.connectionsChanged(port);
110
111        if (port == input) {
112            try {
113                _rateArray = new ArrayToken[input.getWidth()];
114
115                IntToken[] rate = new IntToken[input.getWidth()];
116
117                for (int i = 0; i < input.getWidth(); i++) {
118                    rate[i] = _zero;
119                }
120
121                for (int i = 0; i < input.getWidth(); i++) {
122                    rate[i] = _one;
123                    _rateArray[i] = new ArrayToken(BaseType.INT, rate);
124                    rate[i] = _zero;
125                }
126            } catch (IllegalActionException ex) {
127                throw new InternalErrorException(this, ex,
128                        "At this time IllegalActionExceptions are not allowed to happen.\n"
129                                + "Width inference should already have been done.");
130            }
131        }
132    }
133
134    /** Begin execution by setting rate parameter indicating it will
135     *  read the zeroth input channel.
136     *  @exception IllegalActionException If there is no director.
137     */
138    @Override
139    public void initialize() throws IllegalActionException {
140        super.initialize();
141
142        input_tokenConsumptionRate.setToken(_rateArray[0]);
143    }
144
145    /** Update rate parameter indicating the next input channel.
146     *  @return True if execution can continue into the next iteration.
147     *  @exception IllegalActionException If any called method throws
148     *   IllegalActionException.
149     */
150    @Override
151    public boolean postfire() throws IllegalActionException {
152        // Call postfire first so that current input position is updated.
153        boolean postfireReturn = super.postfire();
154
155        input_tokenConsumptionRate
156                .setToken(_rateArray[_getCurrentInputPosition()]);
157
158        return postfireReturn;
159    }
160
161    ///////////////////////////////////////////////////////////////////
162    ////                         private variables                 ////
163
164    /** A final static IntToken with value 1.
165     */
166    private final static IntToken _one = new IntToken(1);
167
168    /** A final static IntToken with value 0.
169     */
170    private final static IntToken _zero = new IntToken(0);
171
172    /** An array of ArrayTokens to be used to set tokenConsumptionRate
173     *  of the input port. Each ArrayToken indicates the <i>input</i>
174     *  port needs to consume one token from a corresponding channel and
175     *  no token from the rest of the channels. The array is initialized
176     *  in the method connectionsChanged().
177     */
178    private ArrayToken[] _rateArray;
179}