001/* An actor that delays the input by the specified amount. 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.domains.de.lib; 029 030import ptolemy.actor.Director; 031import ptolemy.actor.util.CalendarQueue; 032import ptolemy.actor.util.Time; 033import ptolemy.actor.util.TimedEvent; 034import ptolemy.data.DoubleToken; 035import ptolemy.data.Token; 036import ptolemy.data.expr.Parameter; 037import ptolemy.data.type.BaseType; 038import ptolemy.kernel.CompositeEntity; 039import ptolemy.kernel.util.Attribute; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.NameDuplicationException; 042import ptolemy.kernel.util.Workspace; 043 044/////////////////////////////////////////////////////////////////// 045//// TimedDelay 046 047/** 048 This actor delays the input by a specified amount of time. The amount 049 of the time is required to be non-negative and has a default value 1.0. 050 The input and output types are unconstrained, except that the output type 051 must be the same as that of the input. 052 <p> 053 This actor keeps a local FIFO queue to store all received but not processed 054 inputs. The behavior of this actor on each firing is to read a token from 055 the input, if there is one, store it into the local queue, and output the 056 previously received token that is scheduled to be produced at the current 057 time. 058 <p> During the postfire() method, this actor schedules itself to fire again 059 to produce the just received token on the corresponding output channel after 060 the appropriate time delay. Note that if the value of delay is 0.0, the 061 actor schedules itself to fire at the current model time. 062 <p> 063 Occasionally, this actor is useful with the 064 delay parameter set to 0.0. The time stamp of the output will 065 equal that of the input, but there is a "microstep" delay. 066 The discrete-event domain in Ptolemy II has a "super dense" model 067 of time, meaning that a signal from one actor to another can 068 contain multiple events with the same time stamp. These events 069 are "simultaneous," but nonetheless 070 have a well-defined sequential ordering determined by the order 071 in which they are produced. 072 If \textit{delay} is 0.0, then the fire() method of this actor 073 always produces on its output port the event consumed in the 074 \textit{previous iteration} with the same time stamp, if there 075 was one. If there wasn't such a previous iteration, then it 076 produces no output. Its postfire() method consumes and 077 records the input for use in the next iteration, if there 078 is such an input, and also requests a refiring at the current 079 time. This refire request triggers the next iteration (at 080 the same time stamp), on which the output is produced. 081 <p> 082 A consequence of this strategy is that this actor is 083 able to produce an output (or assert that there is no output) before the 084 input with the same time is known. Hence, it can be used to break 085 causality loops in feedback systems. The DE director will leverage this when 086 determining the precedences of the actors. It is sometimes useful to think 087 of this zero-valued delay as an infinitesimal delay. 088 089 @deprecated Use actor.lib.TimeDelay. 090 @see ptolemy.domains.de.lib.VariableDelay 091 @see ptolemy.domains.de.lib.Server 092 @author Edward A. Lee, Lukito Muliadi, Haiyang Zheng 093 @version $Id$ 094 @since Ptolemy II 1.0 095 @Pt.ProposedRating Green (hyzheng) 096 @Pt.AcceptedRating Yellow (hyzheng) 097 */ 098@Deprecated 099public class TimedDelay extends DETransformer { 100 /** Construct an actor with the specified container and name. 101 * Constrain that the output type to be the same as the input type. 102 * @param container The composite entity to contain this one. 103 * @param name The name of this actor. 104 * @exception IllegalActionException If the entity cannot be contained 105 * by the proposed container. 106 * @exception NameDuplicationException If the container already has an 107 * actor with this name. 108 */ 109 public TimedDelay(CompositeEntity container, String name) 110 throws NameDuplicationException, IllegalActionException { 111 super(container, name); 112 113 // NOTE: The _init method is used to allow classes that extend 114 // this class to reconfig their settings. This may not be a 115 // good pattern. 116 _init(); 117 output.setTypeSameAs(input); 118 } 119 120 /////////////////////////////////////////////////////////////////// 121 //// ports and parameters //// 122 123 /** The amount of delay. The default for this parameter is 1.0. 124 * This parameter must contain a DoubleToken 125 * with a non-negative value, or an exception will be thrown when 126 * it is set. 127 */ 128 public Parameter delay; 129 130 /////////////////////////////////////////////////////////////////// 131 //// public methods //// 132 133 /** If the attribute is <i>delay</i>, then ensure that the value 134 * is non-negative. 135 * <p>NOTE: the newDelay may be 0.0, which may change the causality 136 * property of the model. We leave the model designers to decide 137 * whether the zero delay is really what they want. 138 * @param attribute The attribute that changed. 139 * @exception IllegalActionException If the delay is negative. 140 */ 141 @Override 142 public void attributeChanged(Attribute attribute) 143 throws IllegalActionException { 144 if (attribute == delay) { 145 double newDelay = ((DoubleToken) delay.getToken()).doubleValue(); 146 147 if (newDelay < 0.0) { 148 throw new IllegalActionException(this, 149 "Cannot have negative delay: " + newDelay); 150 } else { 151 _delay = newDelay; 152 } 153 } else { 154 super.attributeChanged(attribute); 155 } 156 } 157 158 /** Clone the actor into the specified workspace. Set a type 159 * constraint that the output type is the same as the that of input. 160 * @param workspace The workspace for the new object. 161 * @return A new actor. 162 * @exception CloneNotSupportedException If a derived class has 163 * has an attribute that cannot be cloned. 164 */ 165 @Override 166 public Object clone(Workspace workspace) throws CloneNotSupportedException { 167 TimedDelay newObject = (TimedDelay) super.clone(workspace); 168 newObject.output.setTypeSameAs(newObject.input); 169 return newObject; 170 } 171 172 /** Declare that the <i>output</i> 173 * does not depend on the <i>input</i> in a firing. 174 * @exception IllegalActionException If the causality interface 175 * cannot be computed. 176 * @see #getCausalityInterface() 177 */ 178 @Override 179 public void declareDelayDependency() throws IllegalActionException { 180 _declareDelayDependency(input, output, _delay); 181 } 182 183 /** Read one token from the input. Send out a token that is scheduled 184 * to produce at the current time to the output. 185 * @exception IllegalActionException If there is no director, or the 186 * input can not be read, or the output can not be sent. 187 */ 188 @Override 189 public void fire() throws IllegalActionException { 190 super.fire(); 191 192 // consume input 193 if (input.hasToken(0)) { 194 _currentInput = input.get(0); 195 } else { 196 _currentInput = null; 197 } 198 199 // produce output 200 // NOTE: The amount of delay may be zero. 201 // In this case, if there is already some token scheduled to 202 // be produced at the current time before the current input 203 // arrives, that token is produced. While the current input 204 // is delayed to the next available firing at the current time. 205 Time currentTime = getDirector().getModelTime(); 206 _currentOutput = null; 207 208 if (_delayedOutputTokens.size() > 0) { 209 TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get(); 210 Time eventTime = earliestEvent.timeStamp; 211 212 if (eventTime.equals(currentTime)) { 213 _currentOutput = (Token) earliestEvent.contents; 214 output.send(0, _currentOutput); 215 } 216 } 217 } 218 219 /** Initialize the states of this actor. 220 * @exception IllegalActionException If a derived class throws it. 221 */ 222 @Override 223 public void initialize() throws IllegalActionException { 224 super.initialize(); 225 _currentInput = null; 226 _currentOutput = null; 227 _delayedOutputTokens = new CalendarQueue( 228 new TimedEvent.TimeComparator()); 229 } 230 231 /** Process the current input if it has not been processed. Schedule 232 * a firing to produce the earliest output token. 233 * @exception IllegalActionException If scheduling to refire cannot 234 * be performed or the superclass throws it. 235 */ 236 @Override 237 public boolean postfire() throws IllegalActionException { 238 Time currentTime = getDirector().getModelTime(); 239 Time delayToTime = currentTime.add(_delay); 240 241 // Remove the token that is sent at the current time. 242 if (_delayedOutputTokens.size() > 0) { 243 if (_currentOutput != null) { 244 _delayedOutputTokens.take(); 245 } 246 } 247 248 // Handle the refiring of the multiple tokens 249 // that are scheduled to be produced at the same time. 250 if (_delayedOutputTokens.size() > 0) { 251 TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get(); 252 Time eventTime = earliestEvent.timeStamp; 253 254 if (eventTime.equals(currentTime)) { 255 _fireAt(currentTime); 256 } 257 } 258 259 // Process the current input. 260 if (_currentInput != null) { 261 _delayedOutputTokens 262 .put(new TimedEvent(delayToTime, _currentInput)); 263 _fireAt(delayToTime); 264 } 265 266 return super.postfire(); 267 } 268 269 /////////////////////////////////////////////////////////////////// 270 //// protected method //// 271 272 /** Initialize the delay parameter. 273 * @exception IllegalActionException If delay parameter cannot be set. 274 * @exception NameDuplicationException If there already is a parameter 275 * named "delay". 276 */ 277 protected void _init() 278 throws IllegalActionException, NameDuplicationException { 279 delay = new Parameter(this, "delay", new DoubleToken(1.0)); 280 delay.setTypeEquals(BaseType.DOUBLE); 281 _delay = ((DoubleToken) delay.getToken()).doubleValue(); 282 } 283 284 /////////////////////////////////////////////////////////////////// 285 //// protected variables //// 286 287 /** Current input. */ 288 protected Token _currentInput; 289 290 /** Current output. */ 291 protected Token _currentOutput; 292 293 /** The amount of delay. */ 294 protected double _delay; 295 296 /** A local event queue to store the delayed output tokens. */ 297 protected CalendarQueue _delayedOutputTokens; 298 299 /////////////////////////////////////////////////////////////////// 300 //// protected methods //// 301 302 /** Request a firing of this actor at the specified time 303 * and throw an exception if the director does not agree to 304 * do it at the requested time. This is a convenience method 305 * provided because many actors need it. 306 * <p> 307 * If the executive director is a Ptides director, use 308 * fireAt(Actor, Time, IOPort) method because the pure event this 309 * actor generates is always safe to process. 310 * </p> 311 * @param time The requested time. 312 * @exception IllegalActionException If the director does not 313 * agree to fire the actor at the specified time, or if there 314 * is no director. 315 */ 316 @Override 317 protected void _fireAt(Time time) throws IllegalActionException { 318 Director director = getDirector(); 319 if (director == null) { 320 throw new IllegalActionException(this, "No director."); 321 } 322 Time result = null; 323 result = director.fireAt(this, time); 324 if (!result.equals(time)) { 325 throw new IllegalActionException(this, 326 "Director is unable to fire the actor at the requested time: " 327 + time + ". It responds it will fire it at: " 328 + result); 329 } 330 } 331}