/* An actor that produces a copy of the most recent input each time the trigger input receives an event. Copyright (c) 1998-2014 The Regents of the University of California. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PT_COPYRIGHT_VERSION_2 COPYRIGHTENDKEY */ package ptolemy.domains.de.lib; import java.util.Set; import ptolemy.actor.TypedIOPort; import ptolemy.actor.lib.Sampler; import ptolemy.actor.lib.Transformer; import ptolemy.data.Token; import ptolemy.data.expr.Parameter; import ptolemy.graph.Inequality; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.util.Attribute; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.InternalErrorException; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.StringAttribute; import ptolemy.kernel.util.Workspace; /////////////////////////////////////////////////////////////////// //// MostRecent /** Output the most recent input token when the trigger port receives a token. If no token has been received on the input port when a token is received on the trigger port, then the value of the initialValue parameter is produced. If, however, the initialValue parameter contains no value, then no output is produced. The inputs can be of any token type, but the output port is constrained to be of a type at least that of the input port and the initialValue parameter (if it has a value).
Both the input port and the output port are multiports. Generally, their widths should match. Otherwise, if the width of the input is greater than the width of the output, the extra input tokens will not appear on any output, although they will be consumed from the input port. If the width of the output is greater than that of the input, then the last few channels of the output will never emit tokens.
The trigger port is a multiport. Whenever a trigger is received on any channel the actor fires and produces an output. Multiple triggers with the same timestamp are considered as one trigger.
Note: If the width of the input changes during execution, then the most recent inputs are forgotten, as if the execution of the model were starting over.
This actor is similar to the Inhibit actor in that it modifies a stream of events based on the presence or absence of events from another input. This actor reacts to the presence of the other event, whereas Inhibit reacts to the absence of it.
This actor is different from the Register actor in that the input
tokens are consumed from the input ports before the outputs are generated.
Note that this actor is also different from the
{@link Sampler} actor, which produces the current input on the
output when a trigger input is present, rather than the most
recently received input signal.
@author Jie Liu, Edward A. Lee, Steve Neuendorffer, Elaine Cheong
@version $Id: MostRecent.java 69608 2014-07-30 18:15:49Z cxh $
@since Ptolemy II 10.0
@Pt.ProposedRating Yellow (eal)
@Pt.AcceptedRating Yellow (eal)
@see ptolemy.domains.de.lib.Inhibit
@see ptolemy.domains.de.lib.Register
*/
public class MostRecent extends Transformer {
/** Construct an actor with the given container and name.
* @param container The container.
* @param name The name of this actor.
* @exception IllegalActionException If the actor cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the container already has an
* actor with this name.
*/
public MostRecent(CompositeEntity container, String name)
throws NameDuplicationException, IllegalActionException {
super(container, name);
input.setMultiport(true);
output.setMultiport(true);
output.setTypeAtLeast(input);
trigger = new TypedIOPort(this, "trigger", true, false);
trigger.setMultiport(true);
// Width constraint. Not bidirectional to not break any existing models.
output.setWidthEquals(input, false);
// Leave type undeclared.
initialValue = new Parameter(this, "initialValue");
_attachText("_iconDescription", "\n");
StringAttribute cardinality = new StringAttribute(trigger, "_cardinal");
cardinality.setExpression("SOUTH");
}
///////////////////////////////////////////////////////////////////
//// ports and parameters ////
/** The trigger port, which has undeclared type. If this port
* receives a token, then the most recent token from the
* input port will be emitted on the output port.
*/
public TypedIOPort trigger;
/** The value that is output when no input has yet been received.
* If this is changed during execution, then the output will match
* the new value until another input is received.
* The type should be the same as the input port.
* @see #typeConstraints()
*/
public Parameter initialValue;
///////////////////////////////////////////////////////////////////
//// public methods ////
/** If the initialValue parameter is the argument, then
* reset the current output to match the new value.
* @param attribute The attribute that changed.
* @exception IllegalActionException If the change is not acceptable
* to this container (not thrown in this base class).
*/
@Override
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
if (attribute == initialValue) {
if (initialValue.getToken() != null) {
int width = 1;
// Calling input.getWidth() when the model is not being
// executed can cause Exceptions because the width cannot
// be resolved. This method is called also, for instance, when a
// class containing a MostRecent actor is saved. In this case,
// the model is not executed and the width is irrelevant.
if (_initializeDone) {
width = input.getWidth();
}
if (width < 1) {
width = 1;
}
_lastInputs = new Token[width];
for (int i = 0; i < width; i++) {
_lastInputs[i] = initialValue.getToken();
}
} else {
_lastInputs = null;
}
} else {
super.attributeChanged(attribute);
}
}
/** Clone the actor into the specified workspace. This calls the
* base class and then sets the ports.
* @param workspace The workspace for the new object.
* @return A new actor.
* @exception CloneNotSupportedException If a derived class has
* has an attribute that cannot be cloned.
*/
@Override
public Object clone(Workspace workspace) throws CloneNotSupportedException {
MostRecent newObject = (MostRecent) super.clone(workspace);
newObject.output.setTypeAtLeast(newObject.input);
// Width constraint. Not bidirectional to not break any existing models.
newObject.output.setWidthEquals(newObject.input, false);
// This is not strictly needed (since it is always recreated
// in preinitialize) but it is safer.
newObject._lastInputs = null;
return newObject;
}
/** Consume all the tokens in the input ports and record them.
* If there is a token in the trigger port, emit the most
* recent token from the input port. If there has been no
* input token, but the initialValue parameter has been
* set, emit the value of the initialValue parameter.
* Otherwise, emit nothing.
* @exception IllegalActionException If there is no director.
*/
@Override
public void fire() throws IllegalActionException {
super.fire();
int inputWidth = input.getWidth();
int outputWidth = output.getWidth();
int commonWidth = Math.min(inputWidth, outputWidth);
// If the initialValue parameter was not set, or if the
// width of the input has changed.
if (_lastInputs == null || _lastInputs.length != inputWidth) {
_lastInputs = new Token[inputWidth];
}
readInputs(commonWidth, inputWidth);
sendOutputIfTriggered(commonWidth);
}
/** If there is no input on the trigger port, return
* false, indicating that this actor does not want to fire.
* This has the effect of leaving input values in the input
* ports, if there are any.
* @exception IllegalActionException If there is no director.
*/
@Override
public boolean prefire() throws IllegalActionException {
super.prefire();
// If the trigger input is not connected, never fire.
if (trigger.isOutsideConnected()) {
boolean hasToken = false;
for (int j = 0; j < trigger.getWidth(); j++) {
if (trigger.hasToken(j)) {
hasToken = true;
break;
}
}
return hasToken;
} else {
return false;
}
}
/** Clear the cached input tokens.
* @exception IllegalActionException If there is no director.
*/
@Override
public void initialize() throws IllegalActionException {
if (initialValue.getToken() != null) {
_lastInputs = new Token[input.getWidth()];
for (int i = 0; i < input.getWidth(); i++) {
_lastInputs[i] = initialValue.getToken();
}
} else {
_lastInputs = null;
}
_initializeDone = true;
super.initialize();
}
/** Wrapup and reset variables.
*/
@Override
public void wrapup() throws IllegalActionException {
super.wrapup();
_initializeDone = false;
}
/** Override the method in the base class so that the type
* constraint for the initialValue parameter will be set
* if it contains a value.
* @return a list of Inequality objects.
* @see ptolemy.graph.Inequality
*/
/* public Set