/* Events in the Ptera controller. @Copyright (c) 2008-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.ptera.kernel; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import ptolemy.actor.Initializable; import ptolemy.actor.TypedActor; import ptolemy.actor.util.Time; import ptolemy.data.ArrayToken; import ptolemy.data.BooleanToken; import ptolemy.data.IntToken; import ptolemy.data.RecordToken; import ptolemy.data.Token; import ptolemy.data.expr.Constants; import ptolemy.data.expr.Parameter; import ptolemy.data.expr.ParserScope; import ptolemy.data.expr.Variable; import ptolemy.data.type.BaseType; import ptolemy.data.type.Type; import ptolemy.domains.modal.kernel.State; import ptolemy.domains.modal.modal.RefinementExtender; import ptolemy.domains.ptera.kernel.PteraDirector.TimedEvent; 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.NamedObj; import ptolemy.kernel.util.Settable; import ptolemy.kernel.util.Workspace; /** A Ptera event is contained by a Ptera controller in a Ptera modal model. Each event can be placed in the Ptera controller's event queue when it is scheduled. When the model time reaches the time of the scheduled event, the event is removed from the event queue and is processed. Actions can be associated with each event, so that processing an event causes those actions to be executed.
An event may schedule another event after a 0 or greater delay of model time. This scheduling relation may be guarded by a Boolean expression. When an event schedules another, processing the first one causes the second one to be placed in the same Ptera controller's event queue. An event may also cancel another event that was previously placed in the same Ptera controller's event queue but has not been processed yet.
If the fireOnInput
parameter of an event is set to true, then the
event is also placed in its Ptera controller's input event queue when
scheduled. When the Ptera controller receives an input at any of its input
ports, all the events in its input event queue are removed and processed. Those
events can use the value of that input in their guards or actions.
An event may also define 0 or more formal parameters. When it is scheduled, on the scheduling relation, values to those parameters must be provided.
When an event is processed, if there are actions defined for it, then those actions are executed. This happens before the refinements of the event is fired, if any.
One or more variable names can be listed in the monitoredVariables
parameter, separated by commas. If any such variable names are specified, when
the model is executed, changes on those variables are captured by the event.
This means, if the event is scheduled in the Ptera controller's event queue and
a change is made on the variable (e.g., by the user or by actors such as {@link
ptolemy.actor.lib.SetVariable}), then the event is processed even though its
scheduled time has not arrive yet.
This class extends the {@link State} class in FSM, but it implements a model of
computation whose semantics is completely different from FSM. Extending from
FSM only because this makes it possible to reuse the user interface implemented
for FSM.
@author Thomas Huining Feng
@version $Id: Event.java 70402 2014-10-23 00:52:20Z cxh $
@since Ptolemy II 8.0
@Pt.ProposedRating Yellow (tfeng)
@Pt.AcceptedRating Red (tfeng)
*/
public class Event extends State implements Initializable {
/** Construct an event with the given name contained by the specified
* composite entity. The container argument must not be null, or a
* NullPointerException will be thrown. This event will use the
* workspace of the container for synchronization and version counts.
* If the name argument is null, then the name is set to the empty
* string.
* Increment the version of the workspace.
* This constructor write-synchronizes on the workspace.
*
* @param container The container.
* @param name The name of the state.
* @exception IllegalActionException If the state cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the name coincides with
* that of an entity already in the container.
*/
public Event(CompositeEntity container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
RefinementExtender refinementExtender = new RefinementExtender(this,
uniqueName("refinementExtender"));
refinementExtender.description.setExpression("Ptera Refinement");
refinementExtender.setPersistent(false);
refinementExtender.className.setExpression("ptolemy.domains.ptera"
+ ".kernel.PteraController");
refinementName.setVisibility(Settable.NONE);
//isInitialState.setDisplayName("isInitialEvent");
//isFinalState.setDisplayName("isFinalEvent");
isInitialState.setVisibility(Settable.NONE);
isInitialState.setPersistent(false);
isFinalState.setVisibility(Settable.NONE);
isFinalState.setPersistent(false);
isInitialEvent = new Parameter(this, "isInitialEvent");
isInitialEvent.setTypeEquals(BaseType.BOOLEAN);
isInitialEvent.setExpression("false");
isFinalEvent = new Parameter(this, "isFinalEvent");
isFinalEvent.setTypeEquals(BaseType.BOOLEAN);
isFinalEvent.setExpression("false");
isEndingEvent = new Parameter(this, "isEndingEvent");
isEndingEvent.setTypeEquals(BaseType.BOOLEAN);
isEndingEvent.setExpression("false");
parameters = new ParametersAttribute(this, "parameters");
actions = new ActionsAttribute(this, "actions");
Variable variable = new Variable(actions, "_textHeightHint");
variable.setExpression("5");
variable.setPersistent(false);
parameters.setExpression("()");
}
/** Add the specified object to the list of objects whose
* preinitialize(), initialize(), and wrapup()
* methods should be invoked upon invocation of the corresponding
* methods of this object.
* @param initializable The object whose methods should be invoked.
* @see #removeInitializable(Initializable)
*/
@Override
public void addInitializable(Initializable initializable) {
if (_initializables == null) {
_initializables = new LinkedList
* The difference between an ending event and a final event is that the
* latter also clears the submodel's local event queue, whereas the former
* doesn't. It just triggers the outgoing scheduling relations.
*
* @return true if this event is an ending event, or false otherwise.
* @exception IllegalActionException If the expression of the
* isEndingEvent parameter cannot be parsed or cannot be evaluated, or if
* the result of evaluation violates type constraints, or if the result of
* evaluation is null and there are variables that depend on this one.
*/
public boolean isEndingEvent() throws IllegalActionException {
return ((BooleanToken) isEndingEvent.getToken()).booleanValue();
}
/** Return whether this event is a final event, so that its execution causes
* the event queue of the Ptera director to be cleared.
*
* @return true if this event is a final event, or false otherwise.
* @exception IllegalActionException If the expression of the isFinalEvent
* parameter cannot be parsed or cannot be evaluated, or if the result of
* evaluation violates type constraints, or if the result of evaluation is
* null and there are variables that depend on this one.
*/
public boolean isFinalEvent() throws IllegalActionException {
return ((BooleanToken) isFinalEvent.getToken()).booleanValue();
}
/** Return whether this event is an initial event, so that it is
* automatically scheduled at model time 0 in the Ptera director's event
* queue.
*
* @return true if this event is an initial event, or false otherwise.
* @exception IllegalActionException If the expression of the
* isInitialEvent parameter cannot be parsed or cannot be evaluated, or if
* the result of evaluation violates type constraints, or if the result of
* evaluation is null and there are variables that depend on this one.
*/
public boolean isInitialEvent() throws IllegalActionException {
return ((BooleanToken) isInitialEvent.getToken()).booleanValue();
}
/** This method should be invoked exactly once per execution
* of a model, before any of these other methods are invoked.
* For actors, this is invoked prior to type resolution and
* may trigger changes in the topology, changes in the
* type constraints.
*
* @exception IllegalActionException If initializing is not permitted.
*/
@Override
public void preinitialize() throws IllegalActionException {
if (_initializables != null) {
for (Initializable initializable : _initializables) {
initializable.preinitialize();
}
}
}
/** Continue the processing of this event with the given arguments from the
* previous fire() or refire(). The number of arguments
* provided must be equal to the number of formal parameters defined for
* this event, and their types must match. The actions of this event are
* executed.
*
* @param arguments The arguments used to process this event, which must be
* either an ArrayToken or a RecordToken.
* @param data The refiring data structure returned by the previous fire()
* or refire().
* @return A refiring data structure that contains a non-negative double
* number if refire() should be called after that amount of model time, or
* null if refire() need not be called.
* @exception IllegalActionException If the number of the arguments or
* their types do not match, the actions cannot be executed, or any
* expression (such as guards and arguments to the next events) cannot be
* evaluated.
* @see #fire(Token)
*/
public RefiringData refire(Token arguments, RefiringData data)
throws IllegalActionException {
_debug(new PteraDebugEvent(this, "Refire."));
return null;
}
/** Remove the specified object from the list of objects whose
* preinitialize(), initialize(), and wrapup()
* methods should be invoked upon invocation of the corresponding
* methods of this object. If the specified object is not
* on the list, do nothing.
* @param initializable The object whose methods should no longer be invoked.
* @see #addInitializable(Initializable)
*/
@Override
public void removeInitializable(Initializable initializable) {
if (_initializables != null) {
_initializables.remove(initializable);
if (_initializables.size() == 0) {
_initializables = null;
}
}
}
/** Schedule the next events by evaluating all scheduling relations from
* this event. This method uses the argument values passed to this event by
* the previous invocation to {@link #fire(Token)}. If {@link #fire(Token)}
* has never been called, it uses a default scope in which no argument
* value has been given.
*
* This method searches for all the events that are scheduled or
* cancelled by this event. For each scheduling relation from this event,
* the guard is tested. If it is true, the ending event (which could be the
* same as this event) is scheduled to occur after the specified amount of
* delay. Arguments to that event, if any, are also computed at this time.
* For each cancelling edge from this event, the ending event is cancelled
* in the containing Ptera controller's event queue, if it is in it.
*
* All the scheduling relations from this events are tested with their
* guards. If a scheduling relation's guard returns true, then the event
* that it points to is scheduled to occur after the amount of model time
* specified by the scheduling relation's delay parameter.
*
* @exception IllegalActionException If the scheduling relations cannot be
* evaluated.
*/
public void scheduleEvents() throws IllegalActionException {
PteraController controller = getController();
if (controller == null) {
throw new IllegalActionException(this, "To schedule events, the "
+ "container must be a PteraController.");
}
PteraDirector director = controller.director;
Listparameters
attribute, update the parser scope for the
* actions so that the parameters' names can be referred to in those
* actions. If it is monitoredVariables
, register this event
* as a value listener of all the monitored variables. If the changed
* attribute is isInitialState
, do nothing. This is because
* the Ptera controller need not be updated with this attribute is set. If
* the changed attribute is among the other attributes, then the superclass
* is called.
*
* @param attribute The attribute that changed.
* @exception IllegalActionException If thrown by the superclass
* attributeChanged() method, or the parser scope cannot be updated.
*/
@Override
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
if (attribute != isInitialState) {
super.attributeChanged(attribute);
}
if (attribute == parameters) {
List