001/* An actor that outputs a sequence with a given step in values.
002
003 Copyright (c) 1998-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.Manager;
031import ptolemy.actor.parameters.PortParameter;
032import ptolemy.data.BooleanToken;
033import ptolemy.data.Token;
034import ptolemy.data.expr.Parameter;
035import ptolemy.kernel.CompositeEntity;
036import ptolemy.kernel.util.Attribute;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.InternalErrorException;
039import ptolemy.kernel.util.NameDuplicationException;
040import ptolemy.kernel.util.Workspace;
041
042///////////////////////////////////////////////////////////////////
043//// Ramp
044
045/**
046 Produce an output token on each firing with a value that is
047 incremented by the specified step each iteration. The
048 first output is given by the <i>init</i> parameter, and the
049 increment may be given either by the <i>step</i> parameter or by
050 the associated <i>step</i> port. Note that the increment will show
051 up in the output only on the next iteration. If you need it to show
052 up on the current iteration, use the
053 {@link ptolemy.actor.lib.Accumulator} actor.
054 The type of the output is determined by the constraint that it must
055 be greater than or equal to the types of the parameter (and/or the
056 <i>step</i> port, if it is connected).
057 Thus, this actor is
058 polymorphic in the sense that its output data type can be that
059 of any token type that supports addition.
060
061 @see Accumulator
062 @author Yuhong Xiong, Edward A. Lee
063 @version $Id$
064 @since Ptolemy II 0.2
065 @Pt.ProposedRating Green (eal)
066 @Pt.AcceptedRating Green (bilung)
067 */
068public class Ramp extends SequenceSource {
069    /** Construct an actor with the given container and name.
070     *  In addition to invoking the base class constructors, construct
071     *  the <i>init</i> and <i>step</i> parameter and the <i>step</i>
072     *  port. Initialize <i>init</i>
073     *  to IntToken with value 0, and <i>step</i> to IntToken with value 1.
074     *  @param container The container.
075     *  @param name The name of this actor.
076     *  @exception IllegalActionException If the actor cannot be contained
077     *   by the proposed container.
078     *  @exception NameDuplicationException If the container already has an
079     *   actor with this name.
080     */
081    public Ramp(CompositeEntity container, String name)
082            throws NameDuplicationException, IllegalActionException {
083        super(container, name);
084        init = new PortParameter(this, "init");
085        init.setExpression("0");
086        new Parameter(init.getPort(), "_showName", BooleanToken.TRUE);
087        step = new PortParameter(this, "step");
088        step.setExpression("1");
089        new Parameter(step.getPort(), "_showName", BooleanToken.TRUE);
090
091        // set the type constraints.
092        output.setTypeAtLeast(init);
093        output.setTypeAtLeast(step);
094
095        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-20\" "
096                + "width=\"60\" height=\"40\" " + "style=\"fill:white\"/>\n"
097                + "<polygon points=\"-20,10 20,-10 20,10\" "
098                + "style=\"fill:grey\"/>\n" + "</svg>\n");
099        _resultArray = new Token[1];
100
101        // Show the firingCountLimit parameter last.
102        firingCountLimit.moveToLast();
103    }
104
105    ///////////////////////////////////////////////////////////////////
106    ////                     ports and parameters                  ////
107
108    /** The value produced by the ramp on its first iteration.
109     *  If this value is changed during execution, then the new
110     *  value will be the output on the next iteration.
111     *  The default value of this parameter is the integer 0.
112     */
113    public PortParameter init;
114
115    /** The amount by which the ramp output is incremented on each iteration.
116     *  The default value of this parameter is the integer 1.
117     */
118    public PortParameter step;
119
120    ///////////////////////////////////////////////////////////////////
121    ////                         public methods                    ////
122
123    /** If the argument is the <i>init</i> parameter, then reset the
124     *  state to the specified value.
125     *  @param attribute The attribute that changed.
126     *  @exception IllegalActionException If <i>init</i> cannot be evaluated
127     *   or cannot be converted to the output type, or if the superclass
128     *   throws it.
129     */
130    @Override
131    public void attributeChanged(Attribute attribute)
132            throws IllegalActionException {
133        if (attribute == init) {
134            // If type resolution has happened (the model is running),
135            // then update the current state.
136            Manager manager = getManager();
137            if (manager != null) {
138                Manager.State state = manager.getState();
139                if (state == Manager.ITERATING || state == Manager.PAUSED
140                        || state == Manager.PAUSED_ON_BREAKPOINT) {
141                    _stateToken = output.getType().convert(init.getToken());
142                }
143            }
144        } else {
145            super.attributeChanged(attribute);
146        }
147    }
148
149    /** Clone the actor into the specified workspace. This calls the
150     *  base class and then sets the <code>init</code> and <code>step</code>
151     *  public members to the parameters of the new actor.
152     *  @param workspace The workspace for the new object.
153     *  @return A new actor.
154     *  @exception CloneNotSupportedException If a derived class contains
155     *   an attribute that cannot be cloned.
156     */
157    @Override
158    public Object clone(Workspace workspace) throws CloneNotSupportedException {
159        Ramp newObject = (Ramp) super.clone(workspace);
160
161        // set the type constraints.
162        newObject.output.setTypeAtLeast(newObject.init);
163        newObject.output.setTypeAtLeast(newObject.step);
164        _resultArray = new Token[1];
165        return newObject;
166    }
167
168    /** Send the current value of the state of this actor to the output.
169     *  @exception IllegalActionException If calling send() or super.fire()
170     *  throws it.
171     */
172    @Override
173    public void fire() throws IllegalActionException {
174        init.update();
175        super.fire();
176        output.send(0, _stateToken);
177    }
178
179    /** Set the state to equal the value of the <i>init</i> parameter.
180     *  The state is incremented by the value of the <i>step</i>
181     *  parameter on each iteration (in the postfire() method).
182     *  @exception IllegalActionException If the parent class throws it.
183     */
184    @Override
185    public void initialize() throws IllegalActionException {
186        super.initialize();
187        _stateToken = output.getType().convert(init.getToken());
188    }
189
190    /** Invoke a specified number of iterations of this actor. Each
191     *  iteration updates the state of the actor by adding the
192     *  value of the <i>step</i> parameter to the state and sending
193     *  the value of the state to the output. The iteration count
194     *  is also incremented by the value of <i>count</i>, and if
195     *  the result is greater than or equal to <i>firingCountLimit</i>
196     *  then return STOP_ITERATING.
197     *  <p>
198     *  This method should be called instead of the usual prefire(),
199     *  fire(), postfire() methods when this actor is used in a
200     *  domain that supports vectorized actors.  This leads to more
201     *  efficient execution.
202     *  @param count The number of iterations to perform.
203     *  @return COMPLETED if the actor was successfully iterated the
204     *   specified number of times. Otherwise, if the maximum
205     *   iteration count has been reached, return STOP_ITERATING.
206     *  @exception IllegalActionException If iterating cannot be
207     *  performed.
208     */
209    @Override
210    public int iterate(int count) throws IllegalActionException {
211        // Check whether we need to reallocate the output token array.
212        if (count > _resultArray.length) {
213            _resultArray = new Token[count];
214        }
215
216        // Consume any trigger inputs.
217        // NOTE: It might seem that using trigger.numberOfSources() is
218        // correct here, but it is not. It is possible for channels
219        // to be connected, for example, to other output ports or
220        // even back to this same trigger port, in which case higher
221        // numbered channels will not have their inputs read.
222        for (int i = 0; i < trigger.getWidth(); i++) {
223            if (trigger.hasToken(i, count)) {
224                trigger.get(i, count);
225            }
226        }
227
228        for (int i = 0; i < count; i++) {
229            _resultArray[i] = _stateToken;
230
231            try {
232                step.update();
233                init.update();
234                _stateToken = _stateToken.add(step.getToken());
235            } catch (IllegalActionException ex) {
236                throw new InternalErrorException(this, ex,
237                        "Should not be thrown because we have already "
238                                + "verified that the tokens can be added");
239            }
240        }
241
242        output.send(0, _resultArray, count);
243
244        if (_firingCountLimit != 0) {
245            _iterationCount += count;
246
247            if (_iterationCount >= _firingCountLimit) {
248                return STOP_ITERATING;
249            }
250        }
251
252        return COMPLETED;
253    }
254
255    /** Update the state of the actor by adding the value of the
256     *  <i>step</i> parameter to the state.  Also, increment the
257     *  iteration count, and if the result is equal to
258     *  <i>firingCountLimit</i>, then
259     *  return false.
260     *  @return False if the number of iterations matches the number requested.
261     *  @exception IllegalActionException If the firingCountLimit parameter
262     *   has an invalid expression.
263     */
264    @Override
265    public boolean postfire() throws IllegalActionException {
266        step.update();
267        _stateToken = _stateToken.add(step.getToken());
268        return super.postfire();
269    }
270
271    ///////////////////////////////////////////////////////////////////
272    ////                         private variables                 ////
273    private Token _stateToken = null;
274
275    private Token[] _resultArray;
276}