001/* An up-down counter.
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.TypedAtomicActor;
031import ptolemy.actor.TypedIOPort;
032import ptolemy.data.BooleanToken;
033import ptolemy.data.IntToken;
034import ptolemy.data.Token;
035import ptolemy.data.expr.Parameter;
036import ptolemy.data.expr.SingletonParameter;
037import ptolemy.data.type.BaseType;
038import ptolemy.kernel.CompositeEntity;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.StringAttribute;
042
043///////////////////////////////////////////////////////////////////
044//// Counter
045
046/**
047 This actor implements an up-down counter of received tokens.  Whenever
048 a token is received from the <i>increment</i> input, the internal
049 counter is incremented.  Whenever a token is received from the
050 <i>decrement</i> port, the internal counter is decremented.  Whenever
051 a token is received from either input port, a token is created on the
052 output port with the integer value of the current count.  At most one
053 token will be consumed from each input during each firing.  If a token
054 is present on both input ports during any firing, then the increment
055 and the decrement will cancel out, and only one output token will be
056 produced. If any firing a <i>reset</i> input is present and true,
057 then the count will be reset.
058
059 @author Steve Neuendorffer
060 @version $Id$
061 @since Ptolemy II 2.0
062 @Pt.ProposedRating Yellow (neuendor)
063 @Pt.AcceptedRating Yellow (neuendor)
064 */
065public class Counter extends TypedAtomicActor {
066    /** Construct an actor with the given container and name.
067     *  @param container The container.
068     *  @param name The name of this actor.
069     *  @exception IllegalActionException If the actor cannot be contained
070     *   by the proposed container.
071     *  @exception NameDuplicationException If the container already has an
072     *   actor with this name.
073     */
074    public Counter(CompositeEntity container, String name)
075            throws NameDuplicationException, IllegalActionException {
076        super(container, name);
077        increment = new TypedIOPort(this, "increment", true, false);
078        increment.setTypeEquals(BaseType.GENERAL);
079        new Parameter(increment, "_showName", BooleanToken.TRUE);
080        decrement = new TypedIOPort(this, "decrement", true, false);
081        decrement.setTypeEquals(BaseType.GENERAL);
082        new Parameter(decrement, "_showName", BooleanToken.TRUE);
083        output = new TypedIOPort(this, "output", false, true);
084        output.setTypeEquals(BaseType.INT);
085        reset = new TypedIOPort(this, "reset", true, false);
086        reset.setTypeEquals(BaseType.BOOLEAN);
087        new SingletonParameter(reset, "_showName").setToken(BooleanToken.TRUE);
088        StringAttribute cardinal = new StringAttribute(reset, "_cardinal");
089        cardinal.setExpression("SOUTH");
090    }
091
092    ///////////////////////////////////////////////////////////////////
093    ////                     ports and parameters                  ////
094
095    /** The increment port. If this input port
096     *  receives a token, then the counter is incremented.  The port
097     *  has type general.
098     */
099    public TypedIOPort increment;
100
101    /** The decrement port. If this input port
102     *  receives a token, then the counter is decremented.  The port
103     *  has type general.
104     */
105    public TypedIOPort decrement;
106
107    /** The output port with type IntToken.
108     */
109    public TypedIOPort output;
110
111    /** The reset input port. This is of type boolean. */
112    public TypedIOPort reset;
113
114    ///////////////////////////////////////////////////////////////////
115    ////                         public methods                    ////
116
117    /** Consume at most one token from each input and update the
118     *  counter appropriately. Send the current value of the counter
119     *  to the output.  If there are no input tokens available, no
120     *  output will be produced.  If a token is consumed from only the
121     *  <i>increment</i> port the output value will be one more than
122     *  the previous output value.  If a token consumed from only the
123     *  <i>decrement</i> port the output value will be one less than
124     *  the previous output value.  If a token is consumed from both
125     *  input ports, then the output value will be the same as the
126     *  previous output value.  If the fire method is invoked multiple
127     *  times in one iteration, then only the input read on the last
128     *  invocation in the iteration will affect future outputs of the
129     *  counter.
130     *
131     *  @exception IllegalActionException If there is no director.
132     */
133    @Override
134    public void fire() throws IllegalActionException {
135        super.fire();
136        _latestCount = _count;
137        boolean consumed = false;
138
139        // Check the increment port.
140        for (int i = 0; i < increment.getWidth(); i++) {
141            if (increment.hasToken(i)) {
142                increment.get(i);
143                _latestCount++;
144                consumed = true;
145            }
146        }
147
148        // Check the decrement port.
149        for (int i = 0; i < decrement.getWidth(); i++) {
150            if (decrement.hasToken(i)) {
151                decrement.get(i);
152                _latestCount--;
153                consumed = true;
154            }
155        }
156
157        if (reset.getWidth() > 0) {
158            if (reset.hasToken(0)) {
159                if (((BooleanToken) reset.get(0)).booleanValue()) {
160                    _latestCount = 0;
161                    consumed = true;
162                }
163            }
164        }
165
166        // Produce an output if we consumed an input or got a reset.
167        if (consumed) {
168            Token out = new IntToken(_latestCount);
169            output.send(0, out);
170        }
171    }
172
173    /** Reset the count of inputs to zero.
174     *  @exception IllegalActionException If the parent class throws it.
175     */
176    @Override
177    public void initialize() throws IllegalActionException {
178        super.initialize();
179        _count = 0;
180    }
181
182    /** Record the most recent output count as the actual count.
183     *  @exception IllegalActionException If the base class throws it.
184     */
185    @Override
186    public boolean postfire() throws IllegalActionException {
187        _count = _latestCount;
188        return super.postfire();
189    }
190
191    ///////////////////////////////////////////////////////////////////
192    ////                         private members                   ////
193    private int _count = 0;
194
195    private int _latestCount = 0;
196}