001/* An actor that outputs the average of the inputs so far.
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.TypedIOPort;
031import ptolemy.data.BooleanToken;
032import ptolemy.data.IntToken;
033import ptolemy.data.Token;
034import ptolemy.data.expr.SingletonParameter;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.NameDuplicationException;
039import ptolemy.kernel.util.StringAttribute;
040
041///////////////////////////////////////////////////////////////////
042//// Average
043
044/**
045 <p>Output the average of the inputs after the last time a true token is
046 received at the reset port.
047 One output is produced each time the actor is fired.
048 The inputs and outputs can be any token type that
049 supports addition and division by an integer.  The output type is
050 constrained to be the same as the input type.
051 Note that if the input is an integer, then the output is an
052 integer, which may not be what you want. You may need to set
053 the input and output ports to double to force the result to be
054 a double.</p>
055 <p>
056 Note that the type system will fail to catch some errors. Static type
057 checking may result in a resolved type that does not support addition
058 and division.  In this case, a run-time error will occur.
059 </p>
060
061 @author Edward A. Lee, Jie Liu
062 @version $Id$
063 @since Ptolemy II 0.3
064 @Pt.ProposedRating Green (eal)
065 @Pt.AcceptedRating Green (bilung)
066 */
067public class Average extends Transformer {
068    /** Construct an actor with the given container and name.
069     *  @param container The container.
070     *  @param name The name of this actor.
071     *  @exception IllegalActionException If the actor cannot be contained
072     *   by the proposed container.
073     *  @exception NameDuplicationException If the container already has an
074     *   actor with this name.
075     */
076    public Average(CompositeEntity container, String name)
077            throws NameDuplicationException, IllegalActionException {
078        super(container, name);
079        reset = new TypedIOPort(this, "reset", true, false);
080        reset.setTypeEquals(BaseType.BOOLEAN);
081        new StringAttribute(reset, "_cardinal").setExpression("SOUTH");
082        new SingletonParameter(reset, "_showName").setToken(BooleanToken.TRUE);
083    }
084
085    ///////////////////////////////////////////////////////////////////
086    ////                     ports and parameters                  ////
087
088    /** The reset port of type BooleanToken. If this input port
089     *  receives a True token, then the averaging process will be
090     *  reset.
091     */
092    public TypedIOPort reset;
093
094    ///////////////////////////////////////////////////////////////////
095    ////                         public methods                    ////
096
097    /** Consume at most one token from the <i>input</i>
098     *  and compute the average of the input tokens so far. Send the
099     *  result to the output.  If there is no input token available,
100     *  no output will be produced.  If there is a true-valued token
101     *  on the <i>reset</i> input, then the average is reset, and
102     *  the output will be equal to the <i>input</i> token (if there
103     *  is one). If the fire method
104     *  is invoked multiple times in one iteration, then only the
105     *  input read on the last invocation in the iteration will affect
106     *  future averages.  Inputs that are read earlier in the iteration
107     *  are forgotten.
108     *  @exception IllegalActionException If addition or division by an
109     *   integer are not supported by the supplied tokens.
110     */
111    @Override
112    public void fire() throws IllegalActionException {
113        super.fire();
114        _latestSum = _sum;
115        _latestCount = _count;
116
117        // Check whether to reset.
118        for (int i = 0; i < reset.getWidth(); i++) {
119            if (reset.hasToken(i)) {
120                BooleanToken r = (BooleanToken) reset.get(i);
121
122                if (r.booleanValue()) {
123                    // Being reset at this firing.
124                    _latestSum = null;
125                    _latestCount = 0;
126                }
127            }
128        }
129
130        if (input.hasToken(0)) {
131            Token in = input.get(0);
132            _latestCount++;
133
134            if (_latestSum == null) {
135                _latestSum = in;
136            } else {
137                _latestSum = _latestSum.add(in);
138            }
139
140            Token out = _latestSum.divide(new IntToken(_latestCount));
141            output.broadcast(out);
142        }
143    }
144
145    /** Reset the count of inputs.
146     *  @exception IllegalActionException If the parent class throws it.
147     */
148    @Override
149    public void initialize() throws IllegalActionException {
150        super.initialize();
151        _count = 0;
152        _sum = null;
153    }
154
155    /** Record the most recent input as part of the running average.
156     *  Do nothing if there is no input.
157     *  @exception IllegalActionException If the base class throws it.
158     */
159    @Override
160    public boolean postfire() throws IllegalActionException {
161        _sum = _latestSum;
162        _count = _latestCount;
163        return super.postfire();
164    }
165
166    ///////////////////////////////////////////////////////////////////
167    ////                         private members                   ////
168    private Token _sum;
169
170    private Token _latestSum;
171
172    private int _count = 0;
173
174    private int _latestCount;
175}