001/* A clock source for sequence-capable domains. 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.TypedAtomicActor; 031import ptolemy.actor.TypedIOPort; 032import ptolemy.actor.util.Time; 033import ptolemy.data.ArrayToken; 034import ptolemy.data.DoubleToken; 035import ptolemy.data.IntToken; 036import ptolemy.data.Token; 037import ptolemy.data.expr.Parameter; 038import ptolemy.data.type.ArrayType; 039import ptolemy.data.type.BaseType; 040import ptolemy.kernel.CompositeEntity; 041import ptolemy.kernel.util.Attribute; 042import ptolemy.kernel.util.IllegalActionException; 043import ptolemy.kernel.util.InternalErrorException; 044import ptolemy.kernel.util.NameDuplicationException; 045import ptolemy.kernel.util.Workspace; 046 047/////////////////////////////////////////////////////////////////// 048//// SequentialClock 049 050/** 051 A clock source for sequence-capable domains. This actor is considerably 052 simpler than the Clock actor. On each firing, it produces the next value 053 from its <i>values</i> parameter, and schedules another firing at 054 a future time determined by the <i>offsets</i> and <i>period</i> parameters. 055 <p> 056 This actor can be used in the DE domain 057 to generate a sequence of events at regularly spaced 058 intervals. It cannot be used in CT, because CT will invoke it at times 059 where it has not requested a firing, and it will inappropriately advance 060 to the next output value. 061 <p> 062 At the beginning of each time interval of length given by <i>period</i>, 063 it initiates a sequence of output events with values given by 064 <i>values</i> and offset into the period given by <i>offsets</i>. 065 These parameters contain arrays, which are required to have the same length. 066 The <i>offsets</i> array must be nondecreasing and nonnegative, 067 or an exception will be thrown when it is set. 068 Moreover, its largest entry must be smaller than <i>period</i> 069 or an exception will be thrown by the fire() method. 070 <p> 071 The <i>values</i> parameter by default 072 contains an array of IntTokens with values 1 and 0. The default 073 <i>offsets</i> array is {0.0, 1.0}. Thus, the default output will be 074 alternating 1 and 0 with 50% duty cycle. The default period 075 is 2.0. 076 <p> 077 The actor uses the fireAt() method of the director to request 078 firing at the beginning of each period plus each of the offsets. 079 It assumes that all of its firings are in response to such 080 requests. 081 <p> 082 The type of the output can be any token type. This type is inferred from the 083 element type of the <i>values</i> parameter. 084 <p> 085 This actor is a timed source; the untimed version is Pulse. 086 087 @author Edward A. Lee 088 @version $Id$ 089 @since Ptolemy II 1.0 090 @Pt.ProposedRating Yellow (eal) 091 @Pt.AcceptedRating Red (yuhong) 092 @deprecated Use Clock instead. 093 */ 094@Deprecated 095public class SequentialClock extends TypedAtomicActor implements SequenceActor { 096 // NOTE: This cannot extend Source, because it doesn't have a trigger 097 // input. This is too bad, since it results in a lot of duplicated 098 // code with Clock. 099 100 /** Construct an actor with the specified container and name. 101 * @param container The container. 102 * @param name The name of this actor. 103 * @exception IllegalActionException If the entity cannot be contained 104 * by the proposed container. 105 * @exception NameDuplicationException If the container already has an 106 * actor with this name. 107 */ 108 public SequentialClock(CompositeEntity container, String name) 109 throws NameDuplicationException, IllegalActionException { 110 super(container, name); 111 112 output = new TypedIOPort(this, "output", false, true); 113 114 period = new Parameter(this, "period", new DoubleToken(2.0)); 115 period.setTypeEquals(BaseType.DOUBLE); 116 117 offsets = new Parameter(this, "offsets"); 118 offsets.setExpression("{0.0, 1.0}"); 119 offsets.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 120 121 // Call this so that we don't have to copy its code here... 122 attributeChanged(offsets); 123 124 // set the values parameter 125 IntToken[] defaultValues = new IntToken[2]; 126 defaultValues[0] = new IntToken(1); 127 defaultValues[1] = new IntToken(0); 128 129 ArrayToken defaultValueToken = new ArrayToken(BaseType.INT, 130 defaultValues); 131 values = new Parameter(this, "values", defaultValueToken); 132 133 // set type constraint 134 output.setTypeAtLeast(ArrayType.elementType(values)); 135 136 // Call this so that we don't have to copy its code here... 137 attributeChanged(values); 138 } 139 140 /////////////////////////////////////////////////////////////////// 141 //// ports and parameters //// 142 143 /** The output port. The type of this port is determined by from 144 * the <i>values</i> parameter. 145 */ 146 public TypedIOPort output = null; 147 148 /** The offsets at which the specified values will be produced. 149 * This parameter must contain an array of doubles, and it defaults 150 * to {0.0, 1.0}. 151 */ 152 public Parameter offsets; 153 154 /** The period of the output waveform. 155 * This parameter must contain a DoubleToken, and defaults to 2.0. 156 */ 157 public Parameter period; 158 159 /** The values that will be produced at the specified offsets. 160 * This parameter must contain an ArrayToken, and defaults to {1, 0}. 161 */ 162 public Parameter values; 163 164 /////////////////////////////////////////////////////////////////// 165 //// public methods //// 166 167 /** If the argument is the <i>offsets</i> parameter, check that the 168 * array is nondecreasing and has the right dimension; if the 169 * argument is <i>period</i>, check that it is positive. Other 170 * sanity checks with <i>period</i> and <i>values</i> are done in 171 * the fire() method. 172 * @param attribute The attribute that changed. 173 * @exception IllegalActionException If the offsets array is not 174 * nondecreasing and nonnegative, or it is not a row vector. 175 */ 176 @Override 177 public void attributeChanged(Attribute attribute) 178 throws IllegalActionException { 179 if (attribute == offsets) { 180 ArrayToken offsetsValue = (ArrayToken) offsets.getToken(); 181 _offsets = new double[offsetsValue.length()]; 182 183 double previous = 0.0; 184 185 for (int i = 0; i < offsetsValue.length(); i++) { 186 _offsets[i] = ((DoubleToken) offsetsValue.getElement(i)) 187 .doubleValue(); 188 189 // Check nondecreasing property. 190 if (_offsets[i] < previous) { 191 throw new IllegalActionException(this, 192 "Value of offsets is not nondecreasing " 193 + "and nonnegative."); 194 } 195 196 previous = _offsets[i]; 197 } 198 } else if (attribute == period) { 199 double periodValue = ((DoubleToken) period.getToken()) 200 .doubleValue(); 201 202 if (periodValue <= 0.0) { 203 throw new IllegalActionException(this, 204 "Period is required to be positive. " 205 + "Period given: " + periodValue); 206 } 207 } else { 208 super.attributeChanged(attribute); 209 } 210 } 211 212 /** Clone the actor into the specified workspace. This calls the 213 * base class and then sets the parameter public members to refer 214 * to the parameters of the new actor. 215 * @param workspace The workspace for the new object. 216 * @return A new actor. 217 * @exception CloneNotSupportedException If a derived class contains 218 * an attribute that cannot be cloned. 219 */ 220 @Override 221 public Object clone(Workspace workspace) throws CloneNotSupportedException { 222 SequentialClock newObject = (SequentialClock) super.clone(workspace); 223 try { 224 newObject.output 225 .setTypeAtLeast(ArrayType.elementType(newObject.values)); 226 } catch (IllegalActionException e) { 227 throw new InternalErrorException(e); 228 } 229 return newObject; 230 } 231 232 /** Output the current value of the clock. 233 * @exception IllegalActionException If the <i>values</i> and 234 * <i>offsets</i> parameters do not have the same length, or if 235 * the value in the offsets parameter is encountered that is greater 236 * than the period, or if there is no director. 237 */ 238 @Override 239 public void fire() throws IllegalActionException { 240 super.fire(); 241 output.send(0, _currentValue); 242 } 243 244 /** Schedule the first firing and initialize local variables. 245 * @exception IllegalActionException If the parent class throws it, 246 * or if the <i>values</i> parameter is not a row vector, or if the 247 * fireAt() method of the director throws it, or if the director does not 248 * agree to fire the actor at the specified time. 249 */ 250 @Override 251 public synchronized void initialize() throws IllegalActionException { 252 super.initialize(); 253 254 _firstFiring = true; 255 _phase = 0; 256 257 // Note: SequentialClock getCurrentTime() calls don't have to rewritten 258 // for DT because this actor is a pure source without any trigger. 259 // All calls to getCurrentTime will return the global time of 260 // the system. 261 // Schedule the first firing. 262 Time currentTime = getDirector().getModelTime(); 263 Time nextFiringTime = currentTime.add(_offsets[0]); 264 265 // NOTE: This must be the last line, because it could result 266 // in an immediate iteration. 267 _fireAt(nextFiringTime); 268 } 269 270 /** Update the state of the actor and schedule the next firing, 271 * if appropriate. 272 * @exception IllegalActionException If the director throws it when 273 * scheduling the next firing, or if the length of the values and 274 * offsets parameters don't match. 275 */ 276 @Override 277 public boolean postfire() throws IllegalActionException { 278 if (!super.postfire()) { 279 return false; 280 } 281 double periodValue = ((DoubleToken) period.getToken()).doubleValue(); 282 283 // Set the cycle start time here rather than in initialize 284 // so that we at least start out well aligned. 285 if (_firstFiring) { 286 _cycleStartTime = getDirector().getModelTime(); 287 _firstFiring = false; 288 } 289 290 // Increment to the next phase. 291 _phase++; 292 293 if (_phase >= _offsets.length) { 294 _phase = 0; 295 _cycleStartTime = _cycleStartTime.add(periodValue); 296 } 297 298 if (_offsets[_phase] >= periodValue) { 299 throw new IllegalActionException(this, 300 "Offset number " + _phase + " with value " 301 + _offsets[_phase] + " must be less than the " 302 + "period, which is " + periodValue); 303 } 304 305 Time nextIterationTime = _cycleStartTime.add(_offsets[_phase]); 306 _fireAt(nextIterationTime); 307 308 return true; 309 } 310 311 /** Set the current value. 312 * @return True. 313 * @exception IllegalActionException If there is no director. 314 */ 315 @Override 316 public boolean prefire() throws IllegalActionException { 317 if (!super.prefire()) { 318 return false; 319 } 320 ArrayToken val = (ArrayToken) values.getToken(); 321 322 if (val == null || val.length() <= _phase) { 323 throw new IllegalActionException(this, 324 "Offsets and values parameters lengths do not match."); 325 } 326 327 _currentValue = val.getElement(_phase); 328 return true; 329 } 330 331 /////////////////////////////////////////////////////////////////// 332 //// private variables //// 333 // The following are all transient because they need not be cloned. 334 // Either the clone method or the initialize() method sets them. 335 // The current value of the clock output. 336 private transient Token _currentValue; 337 338 // The most recent cycle start time. 339 private transient Time _cycleStartTime; 340 341 // Indicator of the first firing cycle. 342 private boolean _firstFiring = true; 343 344 // Cache of offsets array value. 345 private transient double[] _offsets; 346 347 // The phase of the next output. 348 private transient int _phase; 349}