Schedule stateTransitionSchedule = new Schedule();
Schedule waveformGeneratorSchedule = new Schedule();
// Get the composite actor that contains the CT director,
// which contains this scheduler.
CompositeActor container = (CompositeActor) getContainer()
.getContainer();
boolean isCTCompositeActor = container instanceof CTCompositeActor;
// Examine and propagate the signal types of the container ports.
// FIXME: the following implementation is not a good solution.
// Signal types can also be derived in a similar way as the
// function dependencies analysis.
Iterator containerInPorts = container.inputPortList().iterator();
while (containerInPorts.hasNext()) {
IOPort inPort = (IOPort) containerInPorts.next();
if (!isCTCompositeActor) {
// If the container is not a CT composite actor, this
// actor is not embedded inside a CT or HS model. In
// most cases, the outside model is a discrete model.
// For example, a DE model. However, there is a
// possibility that the outside model is a Giotto or
// other domains that have their receivers with a
// "state" semantics. Solution: Set signal types
// according the receiver types. If the receiver is a
// state receiver, the signal type is set to
// "CONTINUOUS", otherwise, "DISCRETE".
// NOTE: The implicit assumption is that all the receivers
// belonging to the same IO port have the same signal type.
Receiver[][] localReceivers = inPort.getReceivers();
try {
Receiver localReceiver = localReceivers[0][0];
if (localReceiver instanceof StateReceiver) {
_signalTypeMap.setType(inPort, CONTINUOUS);
} else {
_signalTypeMap.setType(inPort, DISCRETE);
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalActionException(this, ex,
"The container \"" + container.getFullName()
+ "\" is a \"" + container.getClass().getName()
+ "\", which is not a CT Composite actor, yet "
+ "the port \"" + inPort.getFullName()
+ "\" appears to have no receivers? "
+ "Perhaps the port is not connected?");
}
} else {
// If the container is a CT composite actor,
// examine the "signalType" parameter specified manually
// by model designers, or by the upper level in
// hierarchy during its schedule construction.
// If no such parameter exists, defalut the signal type of
// the input ports to be "CONTINUOUS".
Parameter signalType = (Parameter) inPort
.getAttribute("signalType");
if (signalType != null) {
String type = ((StringToken) signalType.getToken())
.stringValue();
type = type.trim().toUpperCase();
if (type.equals("CONTINUOUS")) {
_signalTypeMap.setType(inPort, CONTINUOUS);
} else if (type.equals("DISCRETE")) {
_signalTypeMap.setType(inPort, DISCRETE);
} else {
throw new IllegalActionException(
inPort,
"Unrecognized signal type. "
+ "It should be a string of "
+ "either \"CONTINUOUS\" or \"DISCRETE\".");
}
} else {
// The default signal type of the input ports of a
// CTCompositeActor is continuous.
// FIXME: This is not accurate. There is a possibility
// that one of the input ports receives a discrete input
// while another input port receives a continuous input.
// FIXME: We may need iterations to solve this problem.
// Because there are only three types: CONTINUOUS, DISCRETE,
// and UNKNONW, where the UNKNOWN is the bottom. Two
// iterations are sufficient to solve the signal type of
// all ports.
_signalTypeMap.setType(inPort, CONTINUOUS);
}
}
// Propagate the signal types of the input ports of the contained
// actors.
_signalTypeMap.propagateTypeInside(inPort);
}
// Iterate all contained actors to classify each actor and
// resolve the signal types of its ports.
Iterator allActors = container.deepEntityList().iterator();
while (allActors.hasNext()) {
Actor a = (Actor) allActors.next();
if (_debugging & _verbose) {
_debug("Examine " + ((Nameable) a).getFullName()
+ " for signal types.");
}
// Now classify actors by their implemented interfaces.
if (a instanceof CompositeActor) {
if (a instanceof CTCompositeActor) {
// the actor is a CT subsystem
// We do not blindly add a CT sub system into the list
// of dynamic actors or the list of waveform generators.
// Instead, we check whether it contains some dynamic
// actors or waveform generators inside.
// The reason is that if a CT subsystem does not contain
// any of the above two actors, it cannot be treated as
// a source actor.
if (((CTCompositeActor) a).containsDynamicActors()) {
dynamicActors.add(a);
}
arithmeticActors.add(a);
ctSubsystems.add(a);
statefulActorSchedule.add(new Firing(a));
if (((CTCompositeActor) a).containsWaveformGenerators()) {
waveformGenerators.add(a);
}
eventGenerators.add(a);
} else {
// The actor is a subsystem but not a CT one,
// it can only be an arithmetic actor or a sink actor.
// We will clarify this actor based on the completely
// resolved signal type of its input and output ports later.
// Right now, we simply save it for future processing.
nonCTSubsystems.add(a);
}
} else {
// the actor is an atomic actor
if (a instanceof CTStatefulActor) {
statefulActorSchedule.add(new Firing(a));
}
if (a instanceof CTWaveformGenerator) {
waveformGenerators.add(a);
} else if (a instanceof CTEventGenerator) {
eventGenerators.add(a);
} else if (a instanceof CTDynamicActor) {
dynamicActors.add(a);
} else {
arithmeticActors.add(a);
}
}
// Now resolve signal types to find the continuous and
// discrete clusters of actors.
if (a instanceof SequenceActor) {
// Set all ports of a sequence actor with signal type "DISCRETE"
if (predecessorList(a).isEmpty()) {
throw new NotSchedulableException(((Nameable) a).getName()
+ " is a SequenceActor, which cannot be a"
+ " source actor in the CT domain.");
}
Iterator ports = ((Entity) a).portList().iterator();
while (ports.hasNext()) {
IOPort port = (IOPort) ports.next();
_signalTypeMap.setType(port, DISCRETE);
if (port.isOutput()) {
_signalTypeMap.propagateType(port);
}
}
} else if ((a instanceof CompositeActor)
&& !(a instanceof CTCompositeActor)) {
// This actor is an opaque composite actor but not a
// CT Composite actor.
// NOTE: For its output ports, we can tell the signal type
// from their receiver types. However, the signal types of
// its input ports have to be derived from the output ports
// of some other actors that reside at the same hierarchical
// level. So we only handle output ports here.
Iterator ports = ((Entity) a).portList().iterator();
while (ports.hasNext()) {
IOPort port = (IOPort) ports.next();
if (port.isOutput()) {
Receiver[][] insideReceivers = port
.getInsideReceivers();
// NOTE: The assumption is that all the receivers
// belonging to the same IO port have the same
// signal type, so if there is at least one
// receiver, then use its type. If there are no
// receivers, assume the type is continuous.
if (insideReceivers.length > 0
&& insideReceivers[0] != null
&& insideReceivers[0].length > 0) {
Receiver insideReceiver = insideReceivers[0][0];
if (insideReceiver instanceof StateReceiver) {
_signalTypeMap.setType(port, CONTINUOUS);
} else {
_signalTypeMap.setType(port, DISCRETE);
}
} else {
_signalTypeMap.setType(port, CONTINUOUS);
}
_signalTypeMap.propagateType(port);
}
}
} else {
// The signal types of the rest ports are obtained
// from the "signalType" parameter.
Iterator ports = ((Entity) a).portList().iterator();
while (ports.hasNext()) {
IOPort port = (IOPort) ports.next();
Parameter signalType = (Parameter) port
.getAttribute("signalType");
if (signalType != null) {
String type = ((StringToken) signalType.getToken())
.stringValue();
type = type.trim().toUpperCase();
if (type.equals("CONTINUOUS")) {
_signalTypeMap.setType(port, CONTINUOUS);
if (port.isOutput()) {
_signalTypeMap.propagateType(port);
}
} else if (type.equals("DISCRETE")) {
_signalTypeMap.setType(port, DISCRETE);
if (port.isOutput()) {
_signalTypeMap.propagateType(port);
}
} else {
throw new InvalidStateException(port,
" signalType not understandable.");
}
} else if (a instanceof CTCompositeActor) {
// Assume all the ports of a CTCompositeActor
// to be continuous unless otherwise specified.
// NOTE: this is a conservative approximation.
if (_signalTypeMap.getType(port) == UNKNOWN) {
_signalTypeMap.setType(port, CONTINUOUS);
}
if (port.isOutput()) {
_signalTypeMap.propagateType(port);
}
}
}
// NOTE: If it is a domain polymorphic source, unless its
// outputs are declared as DISCRETE, assume their signal
// types to CONTINUOUS. For example, the Const actor.
// FIXME: Should the Sequence actor implement the SequenceActor
// interface?
if (predecessorList(a).isEmpty()) {
ports = ((Entity) a).portList().iterator();
while (ports.hasNext()) {
IOPort port = (IOPort) ports.next();
if (_signalTypeMap.getType(port) == UNKNOWN) {
_signalTypeMap.setType(port, CONTINUOUS);
if (port.isOutput()) {
_signalTypeMap.propagateType(port);
}
}
}
}
}
}
// Done with classification and port signal type assignment
// of the sources, waveform generators, event generators,
// sequence actors, and dynamic actors.
// In the following, we first try to resolve the signal types of
// the ports of the rest actors including nonCTSubsystems
// by propagating known signal types.
// First make sure that there is no causality loop of arithmetic
// actors. This makes the graph reachability algorithms terminate.
DirectedAcyclicGraph arithmeticGraph = _toGraph(arithmeticActors);
if (!arithmeticGraph.isAcyclic()) {
Object[] cycleNodes = arithmeticGraph.cycleNodes();
LinkedList nodesAsList = new LinkedList();
StringBuffer inCycle = new StringBuffer("Cycle includes: ");
for (int i = 0; i < cycleNodes.length; i++) {
inCycle.append(((NamedObj) cycleNodes[i]).getFullName());
if (i < cycleNodes.length - 1) {
inCycle.append(", ");
}
nodesAsList.add(cycleNodes[i]);
}
throw new NotSchedulableException(nodesAsList, null,
"Algebraic loop. " + inCycle.toString());
}
// We do not allow loops of dynamic actors, either.
DirectedAcyclicGraph dynamicGraph = _toGraph(dynamicActors);
// FIXME: Why is this disallowed? If we change this, change the class
// comment (at the end) also.
if (!dynamicGraph.isAcyclic()) {
throw new NotSchedulableException(
"Loops of dynamic actors (e.g. integrators) "
+ "are not allowed in the CT domain. You may insert a "
+ "Scale actor with factor 1.");
}
// Now we propagate the signal types by topological sort.
// Notice that signal types of the output ports of the dynamic actors,
// source actors, waveform generators, event generators, and sequence
// actors have already been propagated by one step.
// So, we start with arithmetic actors.
Object[] sortedArithmeticActors = arithmeticGraph.topologicalSort();
for (int i = 0; i < sortedArithmeticActors.length; i++) {
Actor actor = (Actor) sortedArithmeticActors[i];
// Note that the signal type of the input ports should be set
// already. If all input ports are CONTINUOUS, and the
// output ports are UNKNOWN, then all output ports should be
// CONTINUOUS. If all input ports are DISCRETE, and the
// output ports are UNKNOWN, then all output ports should be
// DISCRETE. If some input ports are continuous and some
// input ports are discrete, then the output port type must
// be set manually, which means they can not been resolved.
Iterator inputPorts = actor.inputPortList().iterator();
CTReceiver.SignalType knownInputType = UNKNOWN;
boolean needManuallySetType = true;
while (inputPorts.hasNext()) {
IOPort inputPort = (IOPort) inputPorts.next();
if (inputPort.getWidth() != 0) {
CTReceiver.SignalType inputType = _signalTypeMap
.getType(inputPort);
if (inputType == UNKNOWN) {
throw new NotSchedulableException("Cannot resolve "
+ "signal type for port "
+ inputPort.getFullName()
+ ". If you are certain about the signal type"
+ ", you can set them manually.\n"
+ " To do this, you can add a parameter "
+ "called \'signalType\' with value "
+ "\'\"CONTINUOUS\"\' or \'\"DISCRETE\"\'"
+ " to a port.");
} else if (knownInputType == UNKNOWN) {
knownInputType = inputType;
needManuallySetType = false;
} else if (knownInputType != inputType) {
needManuallySetType = true;
break;
}
}
}
Iterator outputPorts = actor.outputPortList().iterator();
while (outputPorts.hasNext()) {
IOPort outputPort = (IOPort) outputPorts.next();
if (outputPort.getWidth() != 0) {
CTReceiver.SignalType outputType = _signalTypeMap
.getType(outputPort);
if (outputType == UNKNOWN) {
if (needManuallySetType) {
throw new NotSchedulableException(
"Cannot resolve "
+ "signal type for port "
+ outputPort.getFullName()
+ ".\n To set the signal type manually, "
+ "add a parameter with name \'signalType\'"
+ " and a string value \'\"CONTINUOUS\"\' "
+ "or \'\"DISCRETE\"\'.");
} else {
_signalTypeMap.setType(outputPort, knownInputType);
}
}
_signalTypeMap.propagateType(outputPort);
}
}
}
// Set the "signalType" parameters in the model
// to display the signal types.
_setPortSignalTypes(_signalTypeMap);
// Output the signal type resolution result to the debugger.
if (_debugging) {
_debug("Resolved signal types: {\n" + _signalTypeMap.toString()
+ "}");
}
// Now the signal types of all ports are resolved and stored in the
// SignalTypes table. We classify continuous and discrete actors.
// NOTE: An actor is continuous if it has continuous ports;
// an actor is discrete if it has discrete ports. Under this
// rule, the set of continuous actors and discrete actors may have
// a non-empty intersection set.
discreteActors = _signalTypeMap.getDiscreteActors();
continuousActors = _signalTypeMap.getContinuousActors();
// NOTE: There is a situation that the signal types of all the input
// and output ports of a CT composite actor are derived as "DISCRETE".
// In such case, we still need to include this actor into the set of
// continuous actors, because there may be some continuous actors
// hidden inside that CT composite actor.
// To avoid introducing duplication:
continuousActors.removeAll(ctSubsystems);
continuousActors.addAll(ctSubsystems);
// NOTE: There is a situation that the signal types of all the input
// and output ports of a CT composite actor are derived as "CONTINUOUS".
// In such case, we still need to include this actor into the set of
// discrete actors, because there may be some discrete actors
// hidden inside that CT composite actor.
// To avoid introducing duplication:
discreteActors.removeAll(ctSubsystems);
discreteActors.addAll(ctSubsystems);
// FIXME: the following statement does not make sense.
// At this point, since the ports of all actors have their signal types
// resolved, a nonCTSubsystem will be clarified based on
// the signal types of its input and output ports.
Iterator subsystems = nonCTSubsystems.iterator();
while (subsystems.hasNext()) {
CompositeActor subsystem = (CompositeActor) subsystems.next();
if (discreteActors.contains(subsystem)
&& continuousActors.contains(subsystem)) {
// NOTE:
// For a non-CT composite actor, it can not be a