001/* Generate discrete events at prespecified time instants. 002 003 Copyright (c) 2006-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.domains.continuous.lib; 029 030import ptolemy.actor.TypedAtomicActor; 031import ptolemy.actor.TypedIOPort; 032import ptolemy.actor.sched.FixedPointDirector; 033import ptolemy.actor.util.Time; 034import ptolemy.data.ArrayToken; 035import ptolemy.data.DoubleToken; 036import ptolemy.data.StringToken; 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//// EventSource 049 050/** 051 This actor outputs a set of events at a discrete set of time points. 052 It can be used to generate impulses in Continuous models. Events are 053 produced at superdense time index 1 or greater in order to ensure 054 that the output is piecewise continuous. 055 <p> 056 This actor only generates predictable events and that is why it does not 057 implement the ContinuousStepSizeControlActor interface. This actor requests a 058 refiring in its initialize() method to produce events. During its postfire() 059 method, it requests further firings to produce more events if necessary. 060 061 @author Haiyang Zheng 062 @version $Id$ 063 @since Ptolemy II 6.0 064 @Pt.ProposedRating Yellow (hyzheng) 065 @Pt.AcceptedRating Yellow (hyzheng) 066 @deprecated Use {@link ptolemy.actor.lib.DiscreteClock with period set to Infinity} 067 */ 068@Deprecated 069public class EventSource extends TypedAtomicActor { 070 /** Construct an actor in the specified container with the specified 071 * name. The name must be unique within the container or an exception 072 * is thrown. The container argument must not be null, or a 073 * NullPointerException will be thrown. 074 * 075 * @param container The container. 076 * @param name The actor's name 077 * @exception IllegalActionException If the entity cannot be contained 078 * by the proposed container. 079 * @exception NameDuplicationException If name coincides with 080 * an entity already in the container. 081 */ 082 public EventSource(CompositeEntity container, String name) 083 throws IllegalActionException, NameDuplicationException { 084 super(container, name); 085 086 // Create port and parameters. 087 output = new TypedIOPort(this, "output", false, true); 088 new Parameter(output, "signalType", new StringToken("DISCRETE")); 089 090 period = new Parameter(this, "period"); 091 period.setExpression("2.0"); 092 period.setTypeEquals(BaseType.DOUBLE); 093 094 offsets = new Parameter(this, "offsets"); 095 offsets.setExpression("{0.0, 1.0}"); 096 offsets.setTypeEquals(new ArrayType(BaseType.DOUBLE)); 097 098 // Call this so that we don't have to copy its code here... 099 attributeChanged(offsets); 100 101 // set the values parameter 102 values = new Parameter(this, "values"); 103 values.setExpression("{1, 0}"); 104 105 // set type constraints 106 output.setTypeAtLeast(ArrayType.elementType(values)); 107 108 // Call this so that we don't have to copy its code here... 109 attributeChanged(values); 110 } 111 112 /////////////////////////////////////////////////////////////////// 113 //// ports and parameters //// 114 115 /** The output port. The type of this port is determined by from 116 * the <i>values</i> parameter. 117 */ 118 public TypedIOPort output = null; 119 120 /** The offsets at which the specified values will be produced. 121 * This parameter must contain an array of doubles, and it defaults 122 * to {0.0, 1.0}. 123 */ 124 public Parameter offsets; 125 126 /** The period of the output events. 127 * This parameter must contain a DoubleToken, and defaults to 2.0. 128 */ 129 public Parameter period; 130 131 /** The values that will be produced at the specified offsets. 132 * This parameter must contain an ArrayToken, and defaults to {1, 0}. 133 */ 134 public Parameter values; 135 136 /////////////////////////////////////////////////////////////////// 137 //// public methods //// 138 139 /** If the argument is the <i>offsets</i> parameter, check that the 140 * array is nondecreasing and has the right dimension; if the 141 * argument is <i>period</i>, check that it is positive. Other 142 * sanity checks with <i>period</i> and <i>values</i> are done in 143 * the fire() method. 144 * @param attribute The attribute that changed. 145 * @exception IllegalActionException If the offsets array is not 146 * nondecreasing and nonnegative, or it is not a row vector. 147 */ 148 @Override 149 public void attributeChanged(Attribute attribute) 150 throws IllegalActionException { 151 if (attribute == offsets) { 152 ArrayToken offsetsValue = (ArrayToken) offsets.getToken(); 153 _offsets = new double[offsetsValue.length()]; 154 155 double previous = 0.0; 156 157 for (int i = 0; i < offsetsValue.length(); i++) { 158 _offsets[i] = ((DoubleToken) offsetsValue.getElement(i)) 159 .doubleValue(); 160 161 // Check nondecreasing property. 162 if (_offsets[i] < previous) { 163 throw new IllegalActionException(this, 164 "Value of offsets is not nondecreasing " 165 + "and nonnegative."); 166 } 167 168 previous = _offsets[i]; 169 } 170 } else if (attribute == period) { 171 double periodValue = ((DoubleToken) period.getToken()) 172 .doubleValue(); 173 174 if (periodValue <= 0.0) { 175 throw new IllegalActionException(this, 176 "Period is required to be positive. " 177 + "Period given: " + periodValue); 178 } 179 } else { 180 super.attributeChanged(attribute); 181 } 182 } 183 184 /** Clone the actor into the specified workspace. This calls the 185 * base class and then sets the parameter public members to refer 186 * to the parameters of the new actor. 187 * @param workspace The workspace for the new object. 188 * @return A new actor. 189 * @exception CloneNotSupportedException If a derived class contains 190 * an attribute that cannot be cloned. 191 */ 192 @Override 193 public Object clone(Workspace workspace) throws CloneNotSupportedException { 194 EventSource newObject = (EventSource) super.clone(workspace); 195 196 newObject._offsets = new double[_offsets.length]; 197 System.arraycopy(_offsets, 0, newObject._offsets, 0, _offsets.length); 198 199 try { 200 newObject.output 201 .setTypeAtLeast(ArrayType.elementType(newObject.values)); 202 } catch (IllegalActionException e) { 203 throw new InternalErrorException(e); 204 } 205 206 return newObject; 207 } 208 209 /** Emit the discrete event that happens at the current time. If there 210 * is no such events, do nothing. 211 * @exception IllegalActionException If the event cannot be sent. 212 */ 213 @Override 214 public void fire() throws IllegalActionException { 215 if (_readyToFire == _TIME_RIGHT) { 216 super.fire(); 217 output.send(0, ((ArrayToken) values.getToken()).getElement(_phase)); 218 } 219 } 220 221 /** Schedule the first firing and initialize local variables. 222 * @exception IllegalActionException If the parent class throws it, 223 * or if the <i>values</i> parameter is not a row vector, or if the 224 * fireAt() method of the director throws it, or if the director does not 225 * agree to fire the actor at the specified time. 226 */ 227 @Override 228 public synchronized void initialize() throws IllegalActionException { 229 super.initialize(); 230 _cycleStartTime = getDirector().getModelTime(); 231 _nextOutputIndex = 1; 232 _phase = 0; 233 234 // Schedule the first firing. 235 _nextOutputTime = _cycleStartTime.add(_offsets[0]); 236 _fireAt(_nextOutputTime); 237 } 238 239 /** Update the state of the actor and schedule the next firing, 240 * if the director is in the discrete phase. 241 * @exception IllegalActionException If the director throws it when 242 * scheduling the next firing, or if the length of the values and 243 * offsets parameters don't match, or if the director does not 244 * agree to fire the actor at the specified time. 245 */ 246 @Override 247 public boolean postfire() throws IllegalActionException { 248 if (_readyToFire == _TIME_NOT_RIGHT) { 249 return true; 250 } else if (_readyToFire == _TIME_RIGHT_INDEX_EARLY) { 251 // Request a refiring at the current time. 252 _fireAt(getDirector().getModelTime()); 253 return true; 254 } 255 // Advance to the next phase. 256 double periodValue = ((DoubleToken) period.getToken()).doubleValue(); 257 258 // Increment to the next phase. 259 _phase++; 260 261 if (_phase >= _offsets.length) { 262 _phase = 0; 263 _cycleStartTime = _cycleStartTime.add(periodValue); 264 } 265 266 if (_offsets[_phase] >= periodValue) { 267 throw new IllegalActionException(this, 268 "Offset number " + _phase + " with value " 269 + _offsets[_phase] + " must be less than the " 270 + "period, which is " + periodValue); 271 } 272 273 _nextOutputTime = _cycleStartTime.add(_offsets[_phase]); 274 _fireAt(_nextOutputTime); 275 return true; 276 } 277 278 /** Return true if this actor is scheduled to fire at the current time. 279 * @return True if this actor is scheduled to fire at the current time. 280 * @exception IllegalActionException If thrown by the super class. 281 */ 282 @Override 283 public boolean prefire() throws IllegalActionException { 284 FixedPointDirector director = (FixedPointDirector) getDirector(); 285 if (director.getModelTime().compareTo(_nextOutputTime) == 0) { 286 if (_nextOutputIndex <= director.getIndex()) { 287 _readyToFire = _TIME_RIGHT; 288 } else { 289 _readyToFire = _TIME_RIGHT_INDEX_EARLY; 290 } 291 } else { 292 _readyToFire = _TIME_NOT_RIGHT; 293 } 294 return super.prefire(); 295 } 296 297 /////////////////////////////////////////////////////////////////// 298 //// private variables //// 299 // The following are all transient because they need not be cloned. 300 // Either the clone method or the initialize() method sets them. 301 302 /** The most recent cycle start time. */ 303 private transient Time _cycleStartTime; 304 305 /** Cache of offsets array value. */ 306 private transient double[] _offsets; 307 308 /** The phase of the next output. */ 309 private transient int _phase; 310 311 /** The index of when the output should be emitted. */ 312 private transient int _nextOutputIndex; 313 314 /** The next time point when the output should be emitted. */ 315 private transient Time _nextOutputTime; 316 317 /** The flag indicating whether the time is right to produce output. */ 318 private transient int _readyToFire; 319 private static final int _TIME_RIGHT = 0; 320 private static final int _TIME_RIGHT_INDEX_EARLY = 1; 321 private static final int _TIME_NOT_RIGHT = 2; 322}