001/* An actor that handles an HttpRequest by producing an output and waiting for an input.
002
003 Copyright (c) 1997-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 */
028
029package ptolemy.domains.de.test;
030
031import java.util.Collection;
032import java.util.LinkedList;
033
034import ptolemy.actor.IOPort;
035import ptolemy.actor.TypedAtomicActor;
036import ptolemy.actor.TypedIOPort;
037import ptolemy.actor.util.BreakCausalityInterface;
038import ptolemy.actor.util.CausalityInterface;
039import ptolemy.data.DoubleToken;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.CompositeEntity;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044
045/** A test actor illustrating a potential bug in DE in its
046 *  handling of superdense time. In particular,
047 *  the outputs of this actor have no causal dependence on the
048 *  inputs, and hence no delay is needed in a feedback loop
049 *  that feeds outputs back to the inputs. Inputs are read
050 *  and processed in postfire(). However, if the output is
051 *  fed directly back to an input, then the input used to be
052 *  read <i>in the same iteration</i> in which the output
053 *  was produced. If, however, there is an intervening actor
054 *  in the feedback loop, then the input would be read
055 *  <i>in the next iteration</i>. This leads to strange
056 *  behavior where the presence of even a no-op actor (like
057 *  a unit gain) changes the behavior of the model.
058 *  <p>
059 *  What is the solution here?
060 *  <p>
061 *  This actor first outputs nothing. In postfire(), it
062 *  reads inputs and sums them, and if any inputs were present,
063 *  it requests a new firing. In that firing, it produces the
064 *  calculated sum.
065 *  After 10 firings, it stops producing outputs.
066 *
067 *  @author Edward A. Lee
068 *  @version $Id$
069 *  @since Ptolemy II 10.0
070 *  @Pt.ProposedRating Red (ltrnc)
071 *  @Pt.AcceptedRating Red (ltrnc)
072 */
073public class SuperdenseTimeTest extends TypedAtomicActor {
074
075    /** Create an instance of the actor.
076     *  @param container The container
077     *  @param name The name.
078     *  @exception IllegalActionException If the superclass throws it.
079     *  @exception NameDuplicationException If the super
080     */
081    public SuperdenseTimeTest(CompositeEntity container, String name)
082            throws IllegalActionException, NameDuplicationException {
083        super(container, name);
084
085        // Ports
086        in1 = new TypedIOPort(this, "in1", true, false);
087        in1.setTypeEquals(BaseType.DOUBLE);
088        in2 = new TypedIOPort(this, "in2", true, false);
089        in2.setTypeEquals(BaseType.DOUBLE);
090        in3 = new TypedIOPort(this, "in3", true, false);
091        in3.setTypeEquals(BaseType.DOUBLE);
092        out = new TypedIOPort(this, "out", false, true);
093        out.setTypeEquals(BaseType.DOUBLE);
094
095        _inputPorts = new LinkedList<IOPort>();
096        _inputPorts.add(in1);
097        _inputPorts.add(in2);
098        _inputPorts.add(in3);
099    }
100
101    ///////////////////////////////////////////////////////////////////
102    ////                     ports and parameters                  ////
103
104    /** The inputs and output.
105     */
106    public TypedIOPort in1, in2, in3, out;
107
108    ///////////////////////////////////////////////////////////////////
109    ////                         public methods                    ////
110
111    /** Override the base class to return a causality interface that
112     *  indicates that no output depends (immediately) on
113     *  any input, and that also puts both input ports in an
114     *  equivalence class.
115     *  @return A representation of the dependencies between input ports
116     *   and output ports.
117     */
118    @Override
119    public CausalityInterface getCausalityInterface() {
120        if (_causalityInterface == null) {
121            _causalityInterface = new BreakCausalityInterface(this,
122                    getDirector().defaultDependency()) {
123                @Override
124                public Collection<IOPort> equivalentPorts(IOPort input) {
125                    return _inputPorts;
126                }
127            };
128        }
129        return _causalityInterface;
130    }
131
132    /** Respond to an HTTP request. If there is a
133     *  response at the input port, then record that
134     *  response and notify the servlet thread that a response
135     *  is ready. Otherwise, if the servlet has received
136     *  an HTTP request, then produce on the output ports
137     *  the details of the request.
138     *  @exception IllegalActionException If sending the
139     *   outputs fails.
140     */
141    @Override
142    public synchronized void fire() throws IllegalActionException {
143        // The methods of the servlet are invoked in another
144        // thread, so we synchronize on this actor for mutual exclusion.
145        super.fire();
146        if (_count < 10 && _value != null) {
147            out.send(0, _value);
148            _value = null;
149        }
150    }
151
152    /** Set the output value to 0.0.
153     *  @exception IllegalActionException If the superclass throws it.
154     */
155    @Override
156    public void initialize() throws IllegalActionException {
157        super.initialize();
158        _value = null;
159        _count = 0;
160    }
161
162    /** Read the inputs and sum them.
163     *  @return True if a stop has not been requested.
164     */
165    @Override
166    public synchronized boolean postfire() throws IllegalActionException {
167        if (_debugging) {
168            _debug("Starting postfire.");
169        }
170        double result = 0.0;
171        boolean foundOne = false;
172        if (in1.hasToken(0)) {
173            result += ((DoubleToken) in1.get(0)).doubleValue();
174            foundOne = true;
175        }
176        if (in2.hasToken(0)) {
177            result += ((DoubleToken) in2.get(0)).doubleValue();
178            foundOne = true;
179        }
180        if (in3.hasToken(0)) {
181            result += ((DoubleToken) in3.get(0)).doubleValue();
182            foundOne = true;
183        }
184        if (foundOne) {
185            _value = new DoubleToken(result);
186            getDirector().fireAtCurrentTime(this);
187        }
188        _count++;
189        return super.postfire();
190    }
191
192    ///////////////////////////////////////////////////////////////////
193    ////                         private variables                 ////
194
195    /** The causality interface. */
196    private CausalityInterface _causalityInterface;
197
198    /** Count of firings. */
199    private int _count;
200
201    /** A collection of the two input ports, for use in the causality interface. */
202    private Collection<IOPort> _inputPorts;
203
204    /** Value to produce in fire(). */
205    private DoubleToken _value;
206}