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>></b>: <i>left</i> > <i>right</i></li> 057 <li> <b>>=</b>: <i>left</i> >= <i>right</i></li> 058 <li> <b><</b>: <i>left</i> < <i>right</i></li> 059 <li> <b><=</b>: <i>left</i> <= <i>right</i></li> 060 <li> <b>==</b>: <i>left</i> == <i>right</i></li> 061 </ul> 062 063 <p>The default is ">". 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 (">"). 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 ">". 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}