001/* An actor that compares two doubles.
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.logic;
029
030import ptolemy.actor.TypedAtomicActor;
031import ptolemy.actor.TypedIOPort;
032import ptolemy.data.BooleanToken;
033import ptolemy.data.ScalarToken;
034import ptolemy.data.expr.Parameter;
035import ptolemy.data.type.BaseType;
036import ptolemy.kernel.CompositeEntity;
037import ptolemy.kernel.util.Attribute;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.StringAttribute;
042
043// NOTE: If you update the list of comparisons, then you will want
044// to update the list in actor/lib/logic/logic.xml.
045///////////////////////////////////////////////////////////////////
046//// Comparator
047
048/**
049 Compare two double-valued inputs, and output the boolean result
050 of the comparison.
051
052 <p>The exact comparison performed is given by the
053 <i>comparison</i> attribute, which can take any of the following
054 values:</p>
055 <ul>
056 <li> <b>&gt;</b>: <i>left</i> &gt; <i>right</i></li>
057 <li> <b>&gt;=</b>: <i>left</i> &gt;= <i>right</i></li>
058 <li> <b>&lt;</b>: <i>left</i> &lt; <i>right</i></li>
059 <li> <b>&lt;=</b>: <i>left</i> &lt;= <i>right</i></li>
060 <li> <b>==</b>: <i>left</i> == <i>right</i></li>
061 </ul>
062
063 <p>The default is "&gt;".  The input ports are named <i>left</i> and
064 <i>right</i> to indicate which side of the comparison operator their
065 value appears on.</p>
066
067 <p> The <i>tolerance</i> parameter, which defaults to zero, defines
068 an error tolerance.  That is, the actor may produce true even if
069 the specified test is not exactly satisfied, but rather is almost
070 satisfied, within the specified tolerance.</p>
071
072 <p> Note that this actor will work with any data type that can be
073 losslessly converted to doubles, such as integers.</p>
074
075 @author Edward A. Lee
076 @version $Id$
077 @since Ptolemy II 1.0
078 @Pt.ProposedRating Green (eal)
079 @Pt.AcceptedRating Green (neuendor)
080 */
081public class Comparator extends TypedAtomicActor {
082    /** Construct an actor with the given container and name.  Set the
083     *  comparison to the default ("&gt;").  Set the types of
084     *  the input ports to double, and the type of the output port
085     *  to boolean.
086     *  @param container The container.
087     *  @param name The name of this actor.
088     *  @exception IllegalActionException If the actor cannot be contained
089     *   by the proposed container.
090     *  @exception NameDuplicationException If the container already has an
091     *   actor with this name.
092     */
093    public Comparator(CompositeEntity container, String name)
094            throws NameDuplicationException, IllegalActionException {
095        super(container, name);
096
097        // Parameters
098        comparison = new StringAttribute(this, "comparison");
099        comparison.setExpression(">");
100
101        tolerance = new Parameter(this, "tolerance");
102        tolerance.setExpression("0.0");
103
104        // Ports
105        left = new TypedIOPort(this, "left", true, false);
106        right = new TypedIOPort(this, "right", true, false);
107        output = new TypedIOPort(this, "output", false, true);
108        left.setTypeEquals(BaseType.DOUBLE);
109        right.setTypeEquals(BaseType.DOUBLE);
110        output.setTypeEquals(BaseType.BOOLEAN);
111
112        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-15\" "
113                + "width=\"60\" height=\"30\" " + "style=\"fill:white\"/>\n"
114                + "<polyline points=\"-30,-10, -10,-10, -10,0\" "
115                + "style=\"stroke:grey\"/>\n"
116                + "<polyline points=\"-30,10, 10,10, 10,0\" "
117                + "style=\"stroke:grey\"/>\n" + "</svg>\n");
118    }
119
120    ///////////////////////////////////////////////////////////////////
121    ////                     ports and parameters                  ////
122
123    /** The left input port, which has type double. */
124    public TypedIOPort left;
125
126    /** The right input port, which has type double. */
127    public TypedIOPort right;
128
129    /** The output port, which has type boolean. */
130    public TypedIOPort output;
131
132    /** The comparison operator.  This is a string-valued attribute
133     *  that defaults to "&gt;".
134     */
135    public StringAttribute comparison;
136
137    /** The tolerance for the comparison. This has type double,
138     *  and defaults to 0.0.
139     */
140    public Parameter tolerance;
141
142    ///////////////////////////////////////////////////////////////////
143    ////                         public methods                    ////
144
145    /** Override the base class to determine which comparison is being
146     *  specified.  Read the value of the comparison attribute and set
147     *  the cached value appropriately.
148     *  @param attribute The attribute that changed.
149     *  @exception IllegalActionException If the comparison is not recognized.
150     */
151    @Override
152    public void attributeChanged(Attribute attribute)
153            throws IllegalActionException {
154        if (attribute == tolerance) {
155            _tolerance = ((ScalarToken) tolerance.getToken()).doubleValue();
156        } else if (attribute == comparison) {
157            String comparisonName = comparison.getExpression().trim();
158
159            if (comparisonName.equals(">")) {
160                _comparison = _GT;
161            } else if (comparisonName.equals(">=")) {
162                _comparison = _GE;
163            } else if (comparisonName.equals("<")) {
164                _comparison = _LT;
165            } else if (comparisonName.equals("<=")) {
166                _comparison = _LE;
167            } else if (comparisonName.equals("==")) {
168                _comparison = _EQ;
169            } else {
170                throw new IllegalActionException(this,
171                        "Unrecognized comparison: " + comparisonName);
172            }
173        } else {
174            super.attributeChanged(attribute);
175        }
176    }
177
178    /** Consume exactly one input token from each input port,
179     *  and compute the specified comparison. This method assumes
180     *  that both ports have an input, as checked by prefire().
181     *  @exception IllegalActionException If there is no director.
182     */
183    @Override
184    public void fire() throws IllegalActionException {
185        super.fire();
186        BooleanToken result = BooleanToken.FALSE;
187        double leftIn = ((ScalarToken) left.get(0)).doubleValue();
188        double rightIn = ((ScalarToken) right.get(0)).doubleValue();
189
190        switch (_comparison) {
191        case _GT:
192
193            if (leftIn + _tolerance > rightIn) {
194                result = BooleanToken.TRUE;
195            }
196
197            break;
198
199        case _GE:
200
201            if (leftIn + _tolerance >= rightIn) {
202                result = BooleanToken.TRUE;
203            }
204
205            break;
206
207        case _LT:
208
209            if (leftIn < rightIn + _tolerance) {
210                result = BooleanToken.TRUE;
211            }
212
213            break;
214
215        case _LE:
216
217            if (leftIn <= rightIn + _tolerance) {
218                result = BooleanToken.TRUE;
219            }
220
221            break;
222
223        case _EQ:
224
225            if (leftIn <= rightIn + _tolerance
226                    && leftIn >= rightIn - _tolerance) {
227                result = BooleanToken.TRUE;
228            }
229
230            break;
231
232        default:
233            throw new InternalErrorException(
234                    "Invalid value for _comparison private variable. "
235                            + "Comparator actor (" + getFullName() + ")"
236                            + " on comparison type " + _comparison);
237        }
238
239        output.send(0, result);
240    }
241
242    /** Check that each input port has at least one token, and if
243     *  so, return the result of the superclass prefire() method.
244     *  Otherwise, return false.
245     *  @return True if there inputs available on both input ports.
246     *  @exception IllegalActionException If the base class throws it.
247     */
248    @Override
249    public boolean prefire() throws IllegalActionException {
250        if (!left.hasToken(0) || !right.hasToken(0)) {
251            return false;
252        }
253
254        return super.prefire();
255    }
256
257    ///////////////////////////////////////////////////////////////////
258    ////                         private variables                 ////
259    // An indicator for the comparison to compute.
260    private int _comparison;
261
262    // The cached value of the tolerance parameter.
263    private double _tolerance;
264
265    // Constants used for more efficient execution.
266    private static final int _LT = 0;
267
268    private static final int _LE = 1;
269
270    private static final int _GT = 2;
271
272    private static final int _GE = 3;
273
274    private static final int _EQ = 4;
275}