001/* An actor that delays the input by the amount specified through another port. 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.lib.Transformer; 031import ptolemy.actor.parameters.PortParameter; 032import ptolemy.actor.util.CalendarQueue; 033import ptolemy.actor.util.Time; 034import ptolemy.actor.util.TimedEvent; 035import ptolemy.data.DoubleToken; 036import ptolemy.data.Token; 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//// VariableDelay 046 047/** 048 This actor delays its inputs by a variable delay. 049 It works in a similar way as the TimedDelay actor except that the 050 amount of time delayed is specified by an incoming token through 051 the delay port (a parameter port). If the delay is zero, then this 052 actor only increments the microstep, not the model time. 053 If a negative delay is specified, then an exception is thrown. 054 055 @deprecated Use ptolemy.actor.lib.TimeDelay. 056 @see ptolemy.domains.de.lib.TimedDelay 057 @author Jie Liu, Haiyang Zheng 058 @version $Id$ 059 @since Ptolemy II 1.0 060 @Pt.ProposedRating Green (hyzheng) 061 @Pt.AcceptedRating Yellow (hyzheng) 062 */ 063@Deprecated 064public class VariableDelay extends Transformer { 065 066 // NOTE: This actor has alot copies from TimeDelay, but because it has 067 // a PortParameter named delay and TimeDelay has just a parameter, 068 // subclassing does not work well. 069 070 /** Construct an actor with the specified container and name. 071 * @param container The composite entity to contain this one. 072 * @param name The name of this actor. 073 * @exception IllegalActionException If the entity cannot be contained 074 * by the proposed container. 075 * @exception NameDuplicationException If the container already has an 076 * actor with this name. 077 */ 078 public VariableDelay(CompositeEntity container, String name) 079 throws NameDuplicationException, IllegalActionException { 080 super(container, name); 081 082 delay = new PortParameter(this, "delay"); 083 delay.setExpression("1.0"); 084 delay.setTypeEquals(BaseType.DOUBLE); 085 086 output.setTypeSameAs(input); 087 088 _delay = 1.0; 089 } 090 091 /////////////////////////////////////////////////////////////////// 092 //// ports and parameters //// 093 094 // FIXME: VariableDelay.delay overrides TimedDelay.delay. 095 /** The amount specifying delay. Its default value is 1.0. 096 */ 097 public PortParameter delay; 098 099 /////////////////////////////////////////////////////////////////// 100 //// public methods //// 101 102 /** If the attribute is <i>delay</i>, then ensure that the value 103 * is non-negative. 104 * <p>NOTE: the newDelay may be 0.0, which may change the causality 105 * property of the model. We leave the model designers to decide 106 * whether the zero delay is really what they want. 107 * @param attribute The attribute that changed. 108 * @exception IllegalActionException If the delay is negative. 109 */ 110 @Override 111 public void attributeChanged(Attribute attribute) 112 throws IllegalActionException { 113 if (attribute == delay) { 114 double newDelay = ((DoubleToken) delay.getToken()).doubleValue(); 115 116 if (newDelay < 0.0) { 117 throw new IllegalActionException(this, 118 "Cannot have negative delay: " + newDelay); 119 } else { 120 _delay = newDelay; 121 } 122 } else { 123 super.attributeChanged(attribute); 124 } 125 } 126 127 /** Clone the actor into the specified workspace. Set a type 128 * constraint that the output type is the same as the that of input. 129 * @param workspace The workspace for the new object. 130 * @return A new actor. 131 * @exception CloneNotSupportedException If a derived class has 132 * has an attribute that cannot be cloned. 133 */ 134 @Override 135 public Object clone(Workspace workspace) throws CloneNotSupportedException { 136 VariableDelay newObject = (VariableDelay) super.clone(workspace); 137 newObject.output.setTypeSameAs(newObject.input); 138 return newObject; 139 } 140 141 /** Declare that the <i>output</i> 142 * does not depend on the <i>input</i> and <i>delay</i> in a firing. 143 * @exception IllegalActionException If the causality interface 144 * cannot be computed. 145 * @see #getCausalityInterface() 146 */ 147 @Override 148 public void declareDelayDependency() throws IllegalActionException { 149 // Declare that output does not immediately depend on the delay input 150 // and the input port, 151 // though there is no lower bound on the time delay. 152 _declareDelayDependency(delay.getPort(), output, 0.0); 153 _declareDelayDependency(input, output, 0.0); 154 } 155 156 /** Update the delay parameter from the delay port and ensure the delay 157 * is not negative. Call the fire method of super class to consume 158 * inputs and generate outputs. 159 * @exception IllegalActionException If the super class throws it, 160 * or a negative delay is received. 161 */ 162 @Override 163 public void fire() throws IllegalActionException { 164 super.fire(); 165 166 delay.update(); 167 _delay = ((DoubleToken) delay.getToken()).doubleValue(); 168 169 if (_delay < 0) { 170 throw new IllegalActionException(this, 171 "Cannot have negative delay: " + _delay); 172 } 173 174 // produce output 175 // NOTE: The amount of delay may be zero. 176 // In this case, if there is already some token scheduled to 177 // be produced at the current time before the current input 178 // arrives, that token is produced. While the current input 179 // is delayed to the next available firing at the current time. 180 // 181 // If we observe events in the queue that have expired, 182 // discard them here. 183 Time currentTime = getDirector().getModelTime(); 184 _currentOutput = null; 185 186 if (_delayedOutputTokens.size() == 0) { 187 output.send(0, null); 188 return; 189 } 190 191 while (_delayedOutputTokens.size() > 0) { 192 TimedEvent earliestEvent = (TimedEvent) _delayedOutputTokens.get(); 193 Time eventTime = earliestEvent.timeStamp; 194 195 int comparison = eventTime.compareTo(currentTime); 196 if (comparison == 0) { 197 _currentOutput = (Token) earliestEvent.contents; 198 output.send(0, _currentOutput); 199 break; 200 } else if (comparison > 0) { 201 // It is not yet time to produce an output. 202 output.send(0, null); 203 break; 204 } 205 // If we get here, then we have passed the time of the delayed 206 // output. We simply discard it and check the next one in the queue. 207 _delayedOutputTokens.take(); 208 } 209 } 210 211 /** Initialize the states of this actor. 212 * @exception IllegalActionException If a derived class throws it. 213 */ 214 @Override 215 public void initialize() throws IllegalActionException { 216 super.initialize(); 217 _currentOutput = null; 218 _delayedOutputTokens = new CalendarQueue( 219 new TimedEvent.TimeComparator()); 220 } 221 222 /** Return false indicating that this actor can be fired even if 223 * the inputs are unknown. 224 * @return False. 225 */ 226 @Override 227 public boolean isStrict() { 228 return false; 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 // consume input 260 if (input.hasToken(0)) { 261 _delayedOutputTokens.put(new TimedEvent(delayToTime, input.get(0))); 262 _fireAt(delayToTime); 263 } 264 265 return super.postfire(); 266 } 267 268 /////////////////////////////////////////////////////////////////// 269 //// protected variables //// 270 271 /** Current output. */ 272 protected Token _currentOutput; 273 274 /** The amount of delay. */ 275 protected double _delay; 276 277 /** A local event queue to store the delayed output tokens. */ 278 protected CalendarQueue _delayedOutputTokens; 279 280}