001/* An actor that outputs the sum of the inputs so far. 002 003 Copyright (c) 2002-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.ScalarToken; 033import ptolemy.data.Token; 034import ptolemy.data.expr.Parameter; 035import ptolemy.data.expr.SingletonParameter; 036import ptolemy.data.type.BaseType; 037import ptolemy.kernel.CompositeEntity; 038import ptolemy.kernel.util.IllegalActionException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.StringAttribute; 041import ptolemy.kernel.util.Workspace; 042 043/////////////////////////////////////////////////////////////////// 044//// Accumulator 045 046/** 047 Output the initial value plus the sum of all the inputs since 048 the last time a true token was received at the reset port. 049 One output is produced each time the actor is fired. The 050 inputs and outputs can be any token type that supports addition. 051 The output type is constrained to be greater than or 052 equal to the input type and the type of the <i>init</i> parameter. 053 <p> 054 If the input and <i>init</i> data type are scalars, then you can 055 also set the <i>lowerBound</i> and <i>upperBound</i> parameters to 056 limit the range of the accumulated value. 057 058 @author Edward A. Lee 059 @version $Id$ 060 @since Ptolemy II 2.0 061 @Pt.ProposedRating Green (eal) 062 @Pt.AcceptedRating Yellow (neuendor) 063 */ 064public class Accumulator extends Transformer { 065 /** Construct an actor with the given container and name. 066 * @param container The container. 067 * @param name The name of this actor. 068 * @exception IllegalActionException If the actor cannot be contained 069 * by the proposed container. 070 * @exception NameDuplicationException If the container already has an 071 * actor with this name. 072 */ 073 public Accumulator(CompositeEntity container, String name) 074 throws NameDuplicationException, IllegalActionException { 075 super(container, name); 076 077 input.setMultiport(true); 078 079 reset = new TypedIOPort(this, "reset", true, false); 080 reset.setTypeEquals(BaseType.BOOLEAN); 081 reset.setMultiport(true); 082 new StringAttribute(reset, "_cardinal").setExpression("SOUTH"); 083 new SingletonParameter(reset, "_showName").setToken(BooleanToken.TRUE); 084 085 init = new Parameter(this, "init"); 086 init.setExpression("0"); 087 088 lowerBound = new Parameter(this, "lowerBound"); 089 lowerBound.setTypeSameAs(init); 090 091 upperBound = new Parameter(this, "upperBound"); 092 upperBound.setTypeSameAs(init); 093 094 // set the type constraints. 095 output.setTypeAtLeast(init); 096 output.setTypeAtLeast(input); 097 } 098 099 /////////////////////////////////////////////////////////////////// 100 //// ports and parameters //// 101 102 /** The lower bound. If this is set, then its type must be the 103 * same as that of the <i>init</i> parameter, and the output 104 * will be constrained to never drop below the lower bound. 105 * By default, this is not set, so there is no lower bound. 106 */ 107 public Parameter lowerBound; 108 109 /** The value produced by the actor on its first iteration. 110 * The default value of this parameter is the integer 0. 111 */ 112 public Parameter init; 113 114 /** If this port receives a True token on any channel, then the 115 * accumulator state will be reset to the initial value. 116 * This is a multiport and has type boolean. 117 */ 118 public TypedIOPort reset; 119 120 /** The upper bound. If this is set, then its type must be the 121 * same as that of the <i>init</i> parameter, and the output 122 * will be constrained to never rise above the upper bound. 123 * By default, this is not set, so there is no upper bound. 124 */ 125 public Parameter upperBound; 126 127 /////////////////////////////////////////////////////////////////// 128 //// public methods //// 129 130 /** Clone the actor into the specified workspace. This calls the 131 * base class and then sets up the type constraints. 132 * @param workspace The workspace for the new object. 133 * @return A new actor. 134 * @exception CloneNotSupportedException If a derived class contains 135 * an attribute that cannot be cloned. 136 */ 137 @Override 138 public Object clone(Workspace workspace) throws CloneNotSupportedException { 139 Accumulator newObject = (Accumulator) super.clone(workspace); 140 141 // set the type constraints. 142 newObject.lowerBound.setTypeSameAs(newObject.init); 143 newObject.upperBound.setTypeSameAs(newObject.init); 144 newObject.output.setTypeAtLeast(newObject.init); 145 newObject.output.setTypeAtLeast(newObject.input); 146 147 return newObject; 148 } 149 150 /** Consume at most one token from each channel of the <i>input</i> 151 * port, add it to the running sum, and produce the result at the 152 * <i>output</i> port. If there is no input token available, 153 * the current value of the running sum is produced at the output. 154 * If there is a true-valued token on the <i>reset</i> input, 155 * then the running sum is reset to the initial value before 156 * adding the input. 157 * @exception IllegalActionException If addition is not 158 * supported by the supplied tokens. 159 */ 160 @Override 161 public void fire() throws IllegalActionException { 162 super.fire(); 163 _latestSum = _sum; 164 165 // Check whether to reset. 166 for (int i = 0; i < reset.getWidth(); i++) { 167 if (reset.hasToken(i)) { 168 BooleanToken r = (BooleanToken) reset.get(i); 169 170 if (r.booleanValue()) { 171 // Being reset at this firing. 172 _latestSum = output.getType().convert(init.getToken()); 173 } 174 } 175 } 176 177 for (int i = 0; i < input.getWidth(); i++) { 178 if (input.hasToken(i)) { 179 Token in = input.get(i); 180 _latestSum = _latestSum.add(in); 181 } 182 } 183 // Check the bounds. 184 Token lowerBoundValue = lowerBound.getToken(); 185 if (lowerBoundValue != null) { 186 if (lowerBoundValue instanceof ScalarToken) { 187 if (((ScalarToken) lowerBoundValue) 188 .isGreaterThan((ScalarToken) _latestSum) 189 .booleanValue()) { 190 _latestSum = lowerBoundValue; 191 } 192 } else { 193 throw new IllegalActionException(this, 194 "lowerBound parameter only works with scalar values. Value given was: " 195 + lowerBoundValue); 196 } 197 } 198 Token upperBoundValue = upperBound.getToken(); 199 if (upperBoundValue != null) { 200 if (upperBoundValue instanceof ScalarToken) { 201 if (((ScalarToken) upperBoundValue) 202 .isLessThan((ScalarToken) _latestSum).booleanValue()) { 203 _latestSum = upperBoundValue; 204 } 205 } else { 206 throw new IllegalActionException(this, 207 "upperBound parameter only works with scalar values. Value given was: " 208 + upperBoundValue); 209 } 210 } 211 212 output.broadcast(_latestSum); 213 } 214 215 /** Reset the running sum to equal the value of <i>init</i>. 216 * @exception IllegalActionException If the parent class throws it. 217 */ 218 @Override 219 public void initialize() throws IllegalActionException { 220 super.initialize(); 221 _latestSum = _sum = output.getType().convert(init.getToken()); 222 } 223 224 /** Record the most recent input as part of the running average. 225 * Do nothing if there is no input. 226 * @exception IllegalActionException If the base class throws it. 227 */ 228 @Override 229 public boolean postfire() throws IllegalActionException { 230 _sum = _latestSum; 231 return super.postfire(); 232 } 233 234 /////////////////////////////////////////////////////////////////// 235 //// private members //// 236 237 /** The running sum. */ 238 private Token _sum; 239 240 /** The latest sum, prior to a state commit. */ 241 private Token _latestSum; 242}