/* An actor that compares two doubles.
Copyright (c) 1998-2007 The Regents of the University of California.
All rights reserved.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the above
copyright notice and the following two paragraphs appear in all copies
of this software.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.
PT_COPYRIGHT_VERSION_2
COPYRIGHTENDKEY
*/
package ptolemy.actor.lib.logic;
import ptolemy.actor.TypedAtomicActor;
import ptolemy.actor.TypedIOPort;
import ptolemy.data.BooleanToken;
import ptolemy.data.ScalarToken;
import ptolemy.data.expr.Parameter;
import ptolemy.data.type.BaseType;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InternalErrorException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.StringAttribute;
// NOTE: If you update the list of comparisons, then you will want
// to update the list in actor/lib/logic/logic.xml.
//////////////////////////////////////////////////////////////////////////
//// Comparator
/**
<p>Compare two double-valued inputs, and output the boolean result
of the comparison. The exact comparison performed is given by the
<i>comparison</i> attribute, which can take any of the following
values:
<ul>
<li> <b>></b>: <i>left</i> > <i>right</i></li>
<li> <b>>=</b>: <i>left</i> >= <i>right</i></li>
<li> <b><</b>: <i>left</i> < <i>right</i></li>
<li> <b><=</b>: <i>left</i> <= <i>right</i></li>
<li> <b>==</b>: <i>left</i> == <i>right</i></li>
</ul>
The default is ">".
The input ports are named <i>left</i> and <i>right</i> to indicate
which side of the comparison operator their value appears on.
</p>
<p>
The <i>tolerance</i> parameter, which defaults to zero, defines
an error tolerance. That is, the actor may produce true even if
the specified test is not exactly satisfied, but rather is almost
satisfied, within the specified tolerance.
</p>
<p>
Note that this actor will work with any data type that can be losslessly
converted to doubles, such as integers.
</p>
@author Edward A. Lee
@version $Id$
@since Ptolemy II 1.0
@Pt.ProposedRating Green (eal)
@Pt.AcceptedRating Green (neuendor)
*/
public class Comparator extends TypedAtomicActor {
/** Construct an actor with the given container and name. Set the
* comparison to the default (">"). Set the types of
* the input ports to double, and the type of the output port
* to boolean.
* @param container The container.
* @param name The name of this actor.
* @exception IllegalActionException If the actor cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the container already has an
* actor with this name.
*/
public Comparator(CompositeEntity container, String name)
throws NameDuplicationException, IllegalActionException {
super(container, name);
// Parameters
comparison = new StringAttribute(this, "comparison");
comparison.setExpression(">");
tolerance = new Parameter(this, "tolerance");
tolerance.setExpression("0.0");
// Ports
left = new TypedIOPort(this, "left", true, false);
right = new TypedIOPort(this, "right", true, false);
output = new TypedIOPort(this, "output", false, true);
left.setTypeEquals(BaseType.DOUBLE);
right.setTypeEquals(BaseType.DOUBLE);
output.setTypeEquals(BaseType.BOOLEAN);
_attachText("_iconDescription", "<svg>\n"
+ "<rect x=\"-30\" y=\"-15\" " + "width=\"60\" height=\"30\" "
+ "style=\"fill:white\"/>\n"
+ "<polyline points=\"-30,-10, -10,-10, -10,0\" "
+ "style=\"stroke:grey\"/>\n"
+ "<polyline points=\"-30,10, 10,10, 10,0\" "
+ "style=\"stroke:grey\"/>\n" + "</svg>\n");
}
///////////////////////////////////////////////////////////////////
//// ports and parameters ////
/** The left input port, which has type double. */
public TypedIOPort left;
/** The right input port, which has type double. */
public TypedIOPort right;
/** The output port, which has type boolean. */
public TypedIOPort output;
/** The comparison operator. This is a string-valued attribute
* that defaults to ">".
*/
public StringAttribute comparison;
/** The tolerance for the comparison. This has type double,
* and defaults to 0.0.
*/
public Parameter tolerance;
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Override the base class to determine which comparison is being
* specified. Read the value of the comparison attribute and set
* the cached value appropriately.
* @param attribute The attribute that changed.
* @exception IllegalActionException If the comparison is not recognized.
*/
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
if (attribute == tolerance) {
_tolerance = ((ScalarToken) tolerance.getToken()).doubleValue();
} else if (attribute == comparison) {
String comparisonName = comparison.getExpression().trim();
if (comparisonName.equals(">")) {
_comparison = _GT;
} else if (comparisonName.equals(">=")) {
_comparison = _GE;
} else if (comparisonName.equals("<")) {
_comparison = _LT;
} else if (comparisonName.equals("<=")) {
_comparison = _LE;
} else if (comparisonName.equals("==")) {
_comparison = _EQ;
} else {
throw new IllegalActionException(this,
"Unrecognized comparison: " + comparisonName);
}
} else {
super.attributeChanged(attribute);
}
}
/** Consume exactly one input token from each input port,
* and compute the specified comparison. This method assumes
* that both ports have an input, as checked by prefire().
* @exception IllegalActionException If there is no director.
*/
public void fire() throws IllegalActionException {
super.fire();
BooleanToken result = BooleanToken.FALSE;
double leftIn = ((ScalarToken) (left.get(0))).doubleValue();
double rightIn = ((ScalarToken) (right.get(0))).doubleValue();
switch (_comparison) {
case _GT:
if ((leftIn + _tolerance) > rightIn) {
result = BooleanToken.TRUE;
}
break;
case _GE:
if ((leftIn + _tolerance) >= rightIn) {
result = BooleanToken.TRUE;
}
break;
case _LT:
if (leftIn < (rightIn + _tolerance)) {
result = BooleanToken.TRUE;
}
break;
case _LE:
if (leftIn <= (rightIn + _tolerance)) {
result = BooleanToken.TRUE;
}
break;
case _EQ:
if ((leftIn <= (rightIn + _tolerance))
&& (leftIn >= (rightIn - _tolerance))) {
result = BooleanToken.TRUE;
}
break;
default:
throw new InternalErrorException(
"Invalid value for _comparison private variable. "
+ "Comparator actor (" + getFullName() + ")"
+ " on comparison type " + _comparison);
}
output.send(0, result);
}
/** Check that each input port has at least one token, and if
* so, return the result of the superclass prefire() method.
* Otherwise, return false.
* @return True if there inputs available on both input ports.
* @exception IllegalActionException If the base class throws it.
*/
public boolean prefire() throws IllegalActionException {
if (!left.hasToken(0) || !right.hasToken(0)) {
return false;
}
return super.prefire();
}
///////////////////////////////////////////////////////////////////
//// private variables ////
// An indicator for the comparison to compute.
private int _comparison;
// The cached value of the tolerance parameter.
private double _tolerance;
// Constants used for more efficient execution.
private static final int _LT = 0;
private static final int _LE = 1;
private static final int _GT = 2;
private static final int _GE = 3;
private static final int _EQ = 4;
}