001/* Generate discrete events by sampling a CT 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.actor.lib;
029
030import ptolemy.actor.TypedIOPort;
031import ptolemy.data.Token;
032import ptolemy.kernel.CompositeEntity;
033import ptolemy.kernel.util.IllegalActionException;
034import ptolemy.kernel.util.NameDuplicationException;
035import ptolemy.kernel.util.StringAttribute;
036import ptolemy.kernel.util.Workspace;
037
038///////////////////////////////////////////////////////////////////
039//// Sampler
040
041/**
042 This actor generates discrete events by sampling the input signal whenever
043 the <i>trigger</i> input is present.
044 This sampler will send to the output whatever input occurs
045 at a sample time; if the input is absent, the output will be
046 absent.
047 <p>
048 This actor has multiport inputs and outputs. Signals in
049 each input channel are sampled and produced to corresponding output
050 channel. When there are multiple inputs, the first non-absent input
051 from each channel is read, and the output is produced at the first
052 microstep after the last of the inputs became non-absent.
053
054 @author Edward A. Lee
055 @version $Id$
056 @since Ptolemy II 8.0
057 @Pt.ProposedRating Yellow (eal)
058 @Pt.AcceptedRating Red (cxh)
059 */
060public class Sampler extends Transformer {
061    /** Construct an actor in the specified container with the specified
062     *  name.  The name must be unique within the container or an exception
063     *  is thrown. The container argument must not be null, or a
064     *  NullPointerException will be thrown.
065     *  The actor can be either dynamic, or not.  It must be set at the
066     *  construction time and can't be changed thereafter.
067     *  A dynamic actor will produce a token at its initialization phase.
068     *  @param container The container of this actor.
069     *  @param name The actor's name
070     *  @exception IllegalActionException If the entity cannot be contained
071     *   by the proposed container.
072     *  @exception NameDuplicationException If name coincides with
073     *   an entity already in the container.
074     */
075    public Sampler(CompositeEntity container, String name)
076            throws IllegalActionException, NameDuplicationException {
077        super(container, name);
078        input.setMultiport(true);
079        output.setMultiport(true);
080        output.setWidthEquals(input, true);
081
082        output.setTypeSameAs(input);
083
084        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-20\" "
085                + "width=\"60\" height=\"40\" " + "style=\"fill:white\"/>\n"
086                + "<polyline points=\"-30,0 -20,0 -10,0 10,-7\"/>\n"
087                + "<polyline points=\"10,0 30,0\"/>\n" + "</svg>\n");
088
089        trigger = new TypedIOPort(this, "trigger", true, false);
090        trigger.setMultiport(true);
091
092        // Width constraint. Not bidirectional to not break any existing models.
093        output.setWidthEquals(input, true);
094
095        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-20\" "
096                + "width=\"60\" height=\"40\" " + "style=\"fill:white\"/>\n"
097                + "<polyline points=\"0,20 0,0\"/>\n"
098                + "<polyline points=\"-30,-0 -10,0 10,-7\"/>\n"
099                + "<polyline points=\"10,0 30,0\"/>\n" + "</svg>\n");
100
101        StringAttribute cardinality = new StringAttribute(trigger, "_cardinal");
102        cardinality.setExpression("SOUTH");
103    }
104
105    ///////////////////////////////////////////////////////////////////
106    ////                     ports and parameters                  ////
107
108    /** The trigger port, which has undeclared type. If this port
109     *  receives a token, then the most recent token from the
110     *  <i>input</i> port will be emitted on the <i>output</i> port.
111     */
112    public TypedIOPort trigger;
113
114    ///////////////////////////////////////////////////////////////////
115    ////                         public methods                    ////
116
117    /** Clone this actor into the specified workspace. The new actor is
118     *  <i>not</i> added to the directory of that workspace (you must do this
119     *  yourself if you want it there).
120     *  The result is a new actor with the same ports as the original, but
121     *  no connections and no container.  A container must be set before
122     *  much can be done with this actor.
123     *
124     *  @param workspace The workspace for the cloned object.
125     *  @exception CloneNotSupportedException If cloned ports cannot have
126     *   as their container the cloned entity (this should not occur), or
127     *   if one of the attributes cannot be cloned.
128     *  @return A new ComponentEntity.
129     */
130    @Override
131    public Object clone(Workspace workspace) throws CloneNotSupportedException {
132        Sampler newObject = (Sampler) super.clone(workspace);
133        newObject.output.setTypeSameAs(newObject.input);
134        newObject.output.setWidthEquals(newObject.input, true);
135
136        return newObject;
137    }
138
139    /** Generate an output if the current time is one of the sampling
140     *  times. The value of the event is the value of the input signal at the
141     *  current time.
142     *  @exception IllegalActionException If the transfer of tokens failed.
143     */
144    @Override
145    public void fire() throws IllegalActionException {
146        super.fire();
147
148        boolean hasTrigger = false;
149        for (int i = 0; i < trigger.getWidth(); i++) {
150            if (trigger.hasToken(i)) {
151                trigger.get(i);
152                hasTrigger = true;
153            }
154        }
155
156        // Read the input and send it to the output if a trigger has arrived.
157        int width = Math.min(input.getWidth(), output.getWidth());
158        for (int i = 0; i < input.getWidth(); i++) {
159            if (input.hasToken(i)) {
160                Token token = input.get(i);
161                if (hasTrigger) {
162                    if (_debugging) {
163                        _debug("Sampled input value " + token + " at time "
164                                + getDirector().getModelTime());
165                    }
166                    if (i < width) {
167                        output.send(i, token);
168                    }
169                }
170            }
171        }
172    }
173}