001/* A pulse source. 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.data.ArrayToken; 031import ptolemy.data.BooleanToken; 032import ptolemy.data.IntToken; 033import ptolemy.data.Token; 034import ptolemy.data.expr.Parameter; 035import ptolemy.data.type.ArrayType; 036import ptolemy.data.type.BaseType; 037import ptolemy.kernel.CompositeEntity; 038import ptolemy.kernel.util.Attribute; 039import ptolemy.kernel.util.IllegalActionException; 040import ptolemy.kernel.util.InternalErrorException; 041import ptolemy.kernel.util.NameDuplicationException; 042import ptolemy.kernel.util.Workspace; 043 044/////////////////////////////////////////////////////////////////// 045//// Pulse 046 047/** 048 Produce a pulse with a shape specified by the parameters. 049 The <i>values</i> parameter contains an ArrayToken, which specifies 050 the sequence of values to produce at the output. The <i>indexes</i> 051 parameter contains an array of integers, which specifies when those values 052 should be produced. The array in the <i>indexes</i> parameter 053 must have the same length as that in the 054 <i>values</i> parameter or an exception will be thrown by the fire() method. 055 Also, the <i>indexes</i> array must be increasing and non-negative, 056 or an exception will be thrown when it is set. 057 <p> 058 Eventually, this actor will support various kinds of interpolation. 059 For now, it outputs a zero (of the same type as the values) whenever 060 the iteration count does not match an index in <i>indexes</i>. 061 <p> 062 The default for the <i>values</i> parameter is 063 an integer vector of form {1, 0}. 064 The default indexes array is {0, 1}. 065 Thus, the default output sequence will be 1, 0, 0, ... 066 <p> 067 However, the Pulse actor has a <I>repeat</i> parameter. When set to 068 true, the defined sequence is repeated indefinitely. Otherwise, the 069 default sequence of zero values result. 070 <p> 071 The type of the output can be any token type. This type is inferred 072 from the element type of the <i>values</i> parameter. 073 <p>The Ptolemy Expression language has several constructs that are 074 useful for creating arrays for use as values or indexes: 075 <dl> 076 <dt><code>[0:1:100].toArray()</code> 077 <dd>Matlab style array construction that creates an array of 100 elements, 078 0 through 99. 079 <dt><code>repeat(100, {1}(0))</code> 080 <dd>Creat a sequence of one hundred 1's. 081 </dl> 082 <p> 083 NOTE: A reset input for this actor would be useful. This would reset 084 the iterations count, to cause the pulse to emerge again. 085 086 @author Edward A. Lee 087 @version $Id$ 088 @since Ptolemy II 0.2 089 @Pt.ProposedRating Yellow (eal) 090 @Pt.AcceptedRating Yellow (cxh) 091 */ 092public class Pulse extends SequenceSource { 093 /** Construct an actor with the specified container and name. 094 * @param container The container. 095 * @param name The name of this actor. 096 * @exception IllegalActionException If the entity cannot be contained 097 * by the proposed container. 098 * @exception NameDuplicationException If the container already has an 099 * actor with this name. 100 */ 101 public Pulse(CompositeEntity container, String name) 102 throws NameDuplicationException, IllegalActionException { 103 super(container, name); 104 105 indexes = new Parameter(this, "indexes"); 106 indexes.setExpression("{0, 1}"); 107 indexes.setTypeEquals(new ArrayType(BaseType.INT)); 108 109 // Call this so that we don't have to copy its code here... 110 attributeChanged(indexes); 111 112 // set values parameter 113 values = new Parameter(this, "values"); 114 values.setExpression("{1, 0}"); 115 116 // Set the Repeat Flag. 117 repeat = new Parameter(this, "repeat", new BooleanToken(false)); 118 repeat.setTypeEquals(BaseType.BOOLEAN); 119 attributeChanged(repeat); 120 121 // set type constraint 122 output.setTypeAtLeast(ArrayType.elementType(values)); 123 124 // Call this so that we don't have to copy its code here... 125 attributeChanged(values); 126 127 // Show the firingCountLimit parameter last. 128 firingCountLimit.moveToLast(); 129 } 130 131 /////////////////////////////////////////////////////////////////// 132 //// ports and parameters //// 133 134 /** The indexes at which the specified values will be produced. 135 * This parameter is an array of integers, with default value {0, 1}. 136 */ 137 public Parameter indexes; 138 139 /** The flag that indicates whether the pulse sequence needs to be 140 * repeated. This is a boolean, and defaults to false. 141 */ 142 public Parameter repeat; 143 144 /** The values that will be produced at the specified indexes. 145 * This parameter is an array, with default value {1, 0}. 146 */ 147 public Parameter values; 148 149 /////////////////////////////////////////////////////////////////// 150 //// public methods //// 151 152 /** If the attribute being changed is <i>indexes</i>, then check 153 * that it is increasing and nonnegative. 154 * @param attribute The attribute that changed. 155 * @exception IllegalActionException If the indexes vector is not 156 * increasing and nonnegative, or the indexes is not a row vector. 157 */ 158 @Override 159 public void attributeChanged(Attribute attribute) 160 throws IllegalActionException { 161 if (attribute == indexes) { 162 ArrayToken indexesValue = (ArrayToken) indexes.getToken(); 163 _indexes = new int[indexesValue.length()]; 164 165 int previous = 0; 166 167 for (int i = 0; i < indexesValue.length(); i++) { 168 _indexes[i] = ((IntToken) indexesValue.getElement(i)) 169 .intValue(); 170 171 // Check nondecreasing property. 172 if (_indexes[i] < previous) { 173 throw new IllegalActionException(this, 174 "Value of indexes is not nondecreasing " 175 + "and nonnegative."); 176 } 177 178 previous = _indexes[i]; 179 } 180 } else if (attribute == values) { 181 try { 182 ArrayToken valuesArray = (ArrayToken) values.getToken(); 183 Token prototype = valuesArray.getElement(0); 184 _zero = prototype.zero(); 185 } catch (ArrayIndexOutOfBoundsException ex) { 186 throw new IllegalActionException(this, 187 "Cannot set values to an empty array."); 188 } catch (ClassCastException ex) { 189 throw new IllegalActionException(this, 190 "Cannot set values to something that is not an array: " 191 + values.getToken()); 192 } 193 } else if (attribute == repeat) { 194 _repeatFlag = ((BooleanToken) repeat.getToken()).booleanValue(); 195 } else { 196 super.attributeChanged(attribute); 197 } 198 } 199 200 /** Clone the actor into the specified workspace. This overrides the 201 * base class to handle type constraints. 202 * @param workspace The workspace for the new object. 203 * @return A new actor. 204 * @exception CloneNotSupportedException If a derived class contains 205 * an attribute that cannot be cloned. 206 */ 207 @Override 208 public Object clone(Workspace workspace) throws CloneNotSupportedException { 209 Pulse newObject = (Pulse) super.clone(workspace); 210 try { 211 newObject.output 212 .setTypeAtLeast(ArrayType.elementType(newObject.values)); 213 } catch (IllegalActionException e) { 214 throw new InternalErrorException(e); 215 } 216 217 newObject._indexes = new int[_indexes.length]; 218 System.arraycopy(_indexes, 0, newObject._indexes, 0, _indexes.length); 219 220 try { 221 ArrayToken valuesArray = (ArrayToken) newObject.values.getToken(); 222 Token prototype = valuesArray.getElement(0); 223 newObject._zero = prototype.zero(); 224 } catch (Exception ex) { 225 throw new InternalErrorException(ex); 226 } 227 return newObject; 228 } 229 230 /** Output a value if the count of iterations matches one of the entries 231 * in the indexes array. 232 * Otherwise output a zero token with the same type as the values in 233 * the value array. 234 * @exception IllegalActionException If the values and indexes parameters 235 * do not have the same length, or if there is no director. 236 */ 237 @Override 238 public void fire() throws IllegalActionException { 239 super.fire(); 240 241 int currentIndex = 0; 242 ArrayToken val = (ArrayToken) values.getToken(); 243 244 if (_indexColCount < _indexes.length) { 245 if (val.length() != _indexes.length) { 246 throw new IllegalActionException(this, 247 "Parameters values and indexes have " 248 + "different lengths. Length of values = " 249 + val.length() + ". Length of indexes = " 250 + _indexes.length + "."); 251 } 252 253 currentIndex = _indexes[_indexColCount]; 254 255 if (_iterationCount == currentIndex) { 256 // Got a match with an index. 257 output.send(0, val.getElement(_indexColCount)); 258 _match = true; 259 return; 260 } 261 } else { 262 if (_repeatFlag) { 263 // Repeat the pulse sequence again. 264 _iterationCount = 0; 265 _indexColCount = 0; 266 267 currentIndex = _indexes[_indexColCount]; 268 269 if (_iterationCount == currentIndex) { 270 output.send(0, val.getElement(_indexColCount)); 271 _match = true; 272 } 273 274 return; 275 } 276 } 277 278 output.send(0, _zero); 279 _match = false; 280 } 281 282 /** Set the iteration count to zero. 283 * @exception IllegalActionException If the parent class throws it. 284 */ 285 @Override 286 public void initialize() throws IllegalActionException { 287 super.initialize(); 288 _iterationCount = 0; 289 _indexColCount = 0; 290 } 291 292 /** Update the iteration counters until they exceed the values 293 * in the indexes array. 294 * @exception IllegalActionException If the expression of indexes 295 * is not valid. 296 */ 297 @Override 298 public boolean postfire() throws IllegalActionException { 299 // We stop incrementing after reaching the top of the indexes 300 // vector to avoid possibility of overflow. 301 if (_iterationCount <= _indexes[_indexes.length - 1]) { 302 ++_iterationCount; 303 } 304 305 if (_match) { 306 ++_indexColCount; 307 } 308 309 return super.postfire(); 310 } 311 312 /** Start an iteration. 313 * @exception IllegalActionException If the base class throws it. 314 */ 315 @Override 316 public boolean prefire() throws IllegalActionException { 317 _match = false; 318 return super.prefire(); 319 } 320 321 /////////////////////////////////////////////////////////////////// 322 //// private variables //// 323 // Count of the iterations. This stops incrementing when 324 // we exceed the top of the indexes vector. 325 private int _iterationCount = 0; 326 327 // Index of the next output in the values array. 328 private int _indexColCount = 0; 329 330 // Cache of indexes array value. 331 private transient int[] _indexes; 332 333 // Zero token of the same type as in the values array. 334 private Token _zero; 335 336 // Indicator of whether the iterations count matches one of the indexes. 337 private boolean _match = false; 338 339 // Flag to indicate whether or not to repeat the sequence. 340 private boolean _repeatFlag; 341}