001/* An actor that hold the last event and outputs a constant signal.
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.domains.continuous.lib;
029
030import java.util.HashSet;
031import java.util.Set;
032
033import ptolemy.actor.lib.Transformer;
034import ptolemy.data.Token;
035import ptolemy.data.expr.Parameter;
036import ptolemy.domains.continuous.kernel.ContinuousDirector;
037import ptolemy.graph.Inequality;
038import ptolemy.kernel.CompositeEntity;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.InternalErrorException;
041import ptolemy.kernel.util.NameDuplicationException;
042
043///////////////////////////////////////////////////////////////////
044//// ZeroOrderHold
045
046/**
047 Convert discrete events at the input to a continuous-time
048 signal at the output by holding the value of the discrete
049 event until the next discrete event arrives. Specifically,
050 on each firing, if an input is present, then record the
051 value of the input. Then produce the recorded value.
052 Prior to receipt of the first input, output the token
053 given by <i>defaultValue</i>, if one is given.
054 This actor will throw an exception if the input is not
055 purely discrete. Specifically, this means that when the input
056 is present, the step size of the solver has to be 0.0.
057
058 @author Edward A. Lee, Haiyang Zheng
059 @version $Id$
060 @since Ptolemy II 6.0
061 @Pt.ProposedRating Green (hyzheng)
062 @Pt.AcceptedRating Red (hyzheng)
063 */
064public class ZeroOrderHold extends Transformer {
065    /** Construct an actor in the specified container with the specified
066     *  name.  The name must be unique within the container or an exception
067     *  is thrown. The container argument must not be null, or a
068     *  NullPointerException will be thrown.
069     *
070     *  @param container The subsystem that this actor is lived in
071     *  @param name The actor's name
072     *  @exception IllegalActionException If the entity cannot be contained
073     *   by the proposed container.
074     *  @exception NameDuplicationException If name coincides with
075     *   an entity already in the container.
076     */
077    public ZeroOrderHold(CompositeEntity container, String name)
078            throws IllegalActionException, NameDuplicationException {
079        super(container, name);
080        defaultValue = new Parameter(this, "defaultValue");
081
082        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-20\" "
083                + "width=\"60\" height=\"40\" " + "style=\"fill:white\"/>\n"
084                + "<polyline points=\"-25,10 -15,10 -15,-10 5,-10\"/>\n"
085                + "<polyline points=\"5,-10 5,0 15,0 15,10 25,10\"/>\n"
086                + "</svg>\n");
087    }
088
089    ///////////////////////////////////////////////////////////////////
090    ////                     ports and parameters                       ////
091
092    /** Default output before any input has received.
093     *  The default is empty, indicating
094     *  that no output is produced until an input is received.
095     *  The type of the output is set to at least the type of
096     *  this parameter (and also at least the type of the input).
097     */
098    public Parameter defaultValue;
099
100    ///////////////////////////////////////////////////////////////////
101    ////                         public methods                    ////
102
103    /** Output the latest token consumed from the consumeCurrentEvents()
104     *  call.
105     *  @exception IllegalActionException If the token cannot be sent.
106     */
107    @Override
108    public void fire() throws IllegalActionException {
109        super.fire();
110
111        if (input.hasToken(0)) {
112            ContinuousDirector dir = (ContinuousDirector) getDirector();
113            double stepSize = dir.getCurrentStepSize();
114            if (stepSize != 0.0 && !_firstFiring) {
115                throw new IllegalActionException(this,
116                        "Signal at the input port is not purely discrete.");
117            }
118            _lastToken = input.get(0);
119            if (_debugging) {
120                _debug("Input value " + _lastToken + " read at time "
121                        + dir.getModelTime() + " and microstep "
122                        + dir.getIndex());
123            }
124        }
125        if (_lastToken != null) {
126            output.send(0, _lastToken);
127            if (_debugging) {
128                ContinuousDirector dir = (ContinuousDirector) getDirector();
129                _debug("Output value " + _lastToken + " sent at time "
130                        + dir.getModelTime() + " and microstep "
131                        + dir.getIndex());
132            }
133        }
134    }
135
136    /** Initialize token. If there is no input, the initial token is
137     *  a Double Token with the value specified by the defaultValue parameter.
138     *  @exception IllegalActionException If thrown by the super class.
139     */
140    @Override
141    public void initialize() throws IllegalActionException {
142        super.initialize();
143        _lastToken = defaultValue.getToken();
144        _firstFiring = true;
145    }
146
147    /** Override the base class to record that the first firing
148     *  has completed.
149     *  @return True if execution can continue into the next iteration.
150     *  @exception IllegalActionException If the base class throws it.
151     */
152    @Override
153    public boolean postfire() throws IllegalActionException {
154        _firstFiring = false;
155        return super.postfire();
156    }
157
158    ///////////////////////////////////////////////////////////////////
159    ////                         protected methods                 ////
160
161    /**
162     * Return a constraint that requires the type of the <i>defaultValue</i>
163     * parameter to be less than or equal to the type of the <i>output</i>
164     * port.
165     */
166    @Override
167    protected Set<Inequality> _customTypeConstraints() {
168        Set<Inequality> result = new HashSet<Inequality>();
169        try {
170            // type of initialValue <= type of output
171            if (defaultValue.getToken() != null) {
172                result.add(new Inequality(defaultValue.getTypeTerm(),
173                        output.getTypeTerm()));
174            }
175        } catch (IllegalActionException ex) {
176            // Errors in the defaultValue parameter should already
177            // have been caught in getAttribute() method of the base
178            // class.
179            throw new InternalErrorException("Bad defaultValue!\n" + ex);
180        }
181
182        return result;
183    }
184
185    ///////////////////////////////////////////////////////////////////
186    ////                         private variables                 ////
187
188    /** Saved token. */
189    private Token _lastToken;
190
191    /** Indicator that this is the first firing after initialize(). */
192    private boolean _firstFiring;
193}