001/* An actor that produces an event at the specified time.
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.Director;
031import ptolemy.actor.SuperdenseTimeDirector;
032import ptolemy.actor.TypedAtomicActor;
033import ptolemy.actor.TypedIOPort;
034import ptolemy.actor.util.Time;
035import ptolemy.data.BooleanToken;
036import ptolemy.data.DoubleToken;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.Workspace;
043
044///////////////////////////////////////////////////////////////////
045//// SingleEvent
046
047/**
048 This actor produces an event with the specified value at the
049 specified time.  In its initialize() method, it requests a firing
050 at the specified time.  If the <i>time</i>
051 parameter changes before that time is reached, then the event is
052 effectively canceled.  No event will be produced.
053 <p>
054 If used with a director that implements SuperdenseTimeDirector, then
055 this actor will produce its output event at microstep 1.
056 If it is fired at microstep 0 at the specified model time,
057 then it requests a refiring at the current time.
058 This ensures piecewise continuity.
059 <p>
060 Note that you do not really need this actor. The
061 {@link DiscreteClock} actor can produce any finite sequence of
062 events by setting is <i>period</i> to Infinity.
063
064 @author Edward A. Lee
065 @version $Id$
066 @since Ptolemy II 10.0
067 @Pt.ProposedRating Yellow (eal)
068 @Pt.AcceptedRating Red (liuj)
069 */
070public class SingleEvent extends TypedAtomicActor {
071    /** Construct an actor with the specified container and name.
072     *  @param container The container.
073     *  @param name The name of this actor.
074     *  @exception IllegalActionException If the entity cannot be contained
075     *   by the proposed container.
076     *  @exception NameDuplicationException If the container already has an
077     *   actor with this name.
078     */
079    public SingleEvent(CompositeEntity container, String name)
080            throws NameDuplicationException, IllegalActionException {
081        super(container, name);
082        output = new TypedIOPort(this, "output", false, true);
083        time = new Parameter(this, "time", new DoubleToken(0.0));
084        time.setTypeEquals(BaseType.DOUBLE);
085        value = new Parameter(this, "value", new BooleanToken(true));
086        output.setTypeSameAs(value);
087    }
088
089    ///////////////////////////////////////////////////////////////////
090    ////                      ports and parameters                 ////
091
092    /** The output port.  The type of this port is linked to the type
093     *  of the <i>value</i> parameter.
094     */
095    public TypedIOPort output = null;
096
097    /** The time at which to produce the output.  This has type double,
098     *  with default value 0.0.
099     *  If the value is negative, then no output will be produced.
100     */
101    public Parameter time;
102
103    /** The value produced at the output.  This can have any type,
104     *  and it defaults to a boolean token with value <i>true</i>.
105     */
106    public Parameter value;
107
108    ///////////////////////////////////////////////////////////////////
109    ////                         public methods                    ////
110
111    /** Clone the actor into the specified workspace. This calls the
112     *  base class and then links the type of the <i>value</i> parameter
113     *  to the output.
114     *  @param workspace The workspace for the new object.
115     *  @return A new actor.
116     *  @exception CloneNotSupportedException If a derived class has
117     *   has an attribute that cannot be cloned.
118     */
119    @Override
120    public Object clone(Workspace workspace) throws CloneNotSupportedException {
121        SingleEvent newObject = (SingleEvent) super.clone(workspace);
122        newObject.output.setTypeSameAs(newObject.value);
123        return newObject;
124    }
125
126    /** If the current time matches the value of the <i>time</i> parameter,
127     *  then produce an output token with value given by the <i>value</i>
128     *  parameter.
129     */
130    @Override
131    public void fire() throws IllegalActionException {
132        double eventTimeValue = ((DoubleToken) time.getToken()).doubleValue();
133        Director director = getDirector();
134        int microstep = 1;
135        if (director instanceof SuperdenseTimeDirector) {
136            microstep = ((SuperdenseTimeDirector) director).getIndex();
137        }
138        Time eventTime = new Time(director, eventTimeValue);
139        Time currentTime = director.getModelTime();
140        if (currentTime.equals(eventTime)) {
141            if (microstep >= 1) {
142                // If an output has been produced previously, then
143                // produce it again only if the microstep matches.
144                if (_outputProduced < 0 || _outputProduced == microstep) {
145                    output.send(0, value.getToken());
146                    _outputProduced = microstep;
147                } else {
148                    output.sendClear(0);
149                }
150            } else {
151                // Request a refiring at the next microstep.
152                // This ensures that the output is piecewise continuous.
153                // This doesn't really need to be postponed to postfire()
154                // because it is generally harmless to make this request more
155                // than once.
156                _fireAt(currentTime);
157                // Send clear to the output, since we know it is absent.
158                output.sendClear(0);
159            }
160        } else {
161            // Send clear to the output, since we know it is absent.
162            output.sendClear(0);
163        }
164
165        super.fire();
166    }
167
168    /** Request firing at the time given by the <i>time</i> parameter.
169     *  If the time is negative, then do nothing.
170     *  @exception IllegalActionException If there is no director.
171     */
172    @Override
173    public void initialize() throws IllegalActionException {
174        super.initialize();
175
176        _outputProduced = -1;
177
178        double eventTime = ((DoubleToken) time.getToken()).doubleValue();
179
180        if (eventTime >= 0.0) {
181            _fireAt(eventTime);
182        }
183    }
184
185    ///////////////////////////////////////////////////////////////////
186    ////                         private variables                 ////
187
188    /** Indicator that the event has been produced at a microstep. */
189    private int _outputProduced;
190}