001/* An actor to compute the average of the input values received so far over
002model time.
003
004 Copyright (c) 2008-2014 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029package ptolemy.domains.de.lib;
030
031import ptolemy.actor.util.Time;
032import ptolemy.data.DoubleToken;
033import ptolemy.data.Token;
034import ptolemy.data.type.BaseType;
035import ptolemy.kernel.CompositeEntity;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038import ptolemy.kernel.util.Workspace;
039
040/**
041 An actor to compute the average of the input values received so far over model
042 time. This actor can fire whenever there is a token at its input port, or the
043 model time has advanced since last firing. It records the last token that it
044 received. In each firing, the produce of the last token and the amount of model
045 time increment is added to a sum variable. The average value received so far is
046 obtained by dividing the sum by the current model time. This average is sent to
047 the output port in each firing. If a token is available at the input port, this
048 actor reads that token and overwrites the last token with it.
049
050 @author tfeng
051 @version $Id$
052 @since Ptolemy II 8.0
053 @Pt.ProposedRating Yellow (tfeng)
054 @Pt.AcceptedRating Red (tfeng)
055 */
056public class AverageOverTime extends DETransformer {
057
058    /** Construct an actor with the given container and name.
059     *  @param container The container.
060     *  @param name The name of this actor.
061     *  @exception IllegalActionException If the actor cannot be contained
062     *   by the proposed container.
063     *  @exception NameDuplicationException If the container already has an
064     *   actor with this name.
065     */
066    public AverageOverTime(CompositeEntity container, String name)
067            throws NameDuplicationException, IllegalActionException {
068        super(container, name);
069
070        input.setTypeAtMost(BaseType.SCALAR);
071        output.setTypeEquals(BaseType.SCALAR.divide(BaseType.DOUBLE));
072    }
073
074    /** Clone this actor into the specified workspace. The new actor is
075     *  <i>not</i> added to the directory of that workspace (you must do this
076     *  yourself if you want it there).
077     *  The result is a new actor with the same ports as the original, but
078     *  no connections and no container.  A container must be set before
079     *  much can be done with this actor.
080     *
081     *  @param workspace The workspace for the cloned object.
082     *  @exception CloneNotSupportedException If cloned ports cannot have
083     *   as their container the cloned entity (this should not occur), or
084     *   if one of the attributes cannot be cloned.
085     *  @return A new AverageOverTime actor.
086     */
087    @Override
088    public Object clone(Workspace workspace) throws CloneNotSupportedException {
089        AverageOverTime newObject = (AverageOverTime) super.clone(workspace);
090        newObject._sum = null;
091        newObject._lastToken = null;
092        newObject._lastTime = null;
093
094        newObject.input.setTypeAtMost(BaseType.SCALAR);
095        newObject.output.setTypeEquals(BaseType.SCALAR.divide(BaseType.DOUBLE));
096
097        return newObject;
098    }
099
100    /** Fire this actor. If there is a least one token at the input port,
101     *  consume the first token. Output the current average value via the output
102     *  port.
103     *  @exception IllegalActionException If thrown when trying to read the
104     *  input token or to write the output token.
105     */
106    @Override
107    public void fire() throws IllegalActionException {
108        super.fire();
109
110        Time modelTime = getDirector().getModelTime();
111        DoubleToken currentTime = new DoubleToken(modelTime.getDoubleValue());
112        if (_lastToken != null) {
113            DoubleToken lastTime = new DoubleToken(_lastTime.getDoubleValue());
114            Token increase = _lastToken
115                    .multiply(currentTime.subtract(lastTime));
116            if (_sum == null) {
117                _sum = increase;
118            } else {
119                _sum = _sum.add(increase);
120            }
121        }
122        _lastTime = modelTime;
123        if (input.hasToken(0)) {
124            _lastToken = input.get(0);
125        }
126        if (_sum != null) {
127            output.broadcast(_sum.divide(currentTime));
128        }
129    }
130
131    /** Initialize this actor.
132     *
133     *  @exception IllegalActionException If the superclass throws it.
134     */
135    @Override
136    public void initialize() throws IllegalActionException {
137        super.initialize();
138
139        _sum = null;
140        _lastToken = null;
141        _lastTime = getDirector().getModelTime();
142    }
143
144    @Override
145    public boolean postfire() throws IllegalActionException {
146        boolean result = super.postfire();
147        _lastTime = getDirector().getModelTime();
148        return result;
149    }
150
151    /** Determine whether this actor can fire in the current iteration. Return
152     *  true if there is at least one token at the input port, or the model time
153     *  has advanced since last firing.
154     *
155     *  @exception IllegalActionException If the superclass throws it.
156     */
157    @Override
158    public boolean prefire() throws IllegalActionException {
159        boolean result = super.prefire();
160        if (!result) {
161            result = input.hasToken(0)
162                    || !_lastTime.equals(getDirector().getModelTime());
163        }
164        return result;
165    }
166
167    // The last time at which this actor was fired.
168    private Time _lastTime;
169
170    // The last received input token.
171    private Token _lastToken;
172
173    // The sum accumulated so far.
174    private Token _sum;
175}