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
// waveform generator or an event generator.
// Because the transformation of different type of signals
// can only be made in CT models.
waveformGenerators.add(subsystem);
// remove the current subsystem from both the continuous
// and discrete actor clusters.
discreteActors.remove(subsystem);
continuousActors.remove(subsystem);
}
}
// Now, all actors are classified.
// Notice that by now, we have all the discrete actors, but
// they are not in the topological order. Sort them and
// create the discrete schedule.
DirectedAcyclicGraph discreteGraph = _toGraph(discreteActors);
Object[] discreteSorted = discreteGraph.topologicalSort();
for (int i = 0; i < discreteSorted.length; i++) {
Actor actor = (Actor) discreteSorted[i];
// We want to distinguish the waveform and event generators,
// which have at least one input or output declared as CONTINUOUS
// signal type, from purely discrete (continuous) actors, whose
// ports are all DISCRETE (CONTINUOUS).
if (continuousActors.contains(actor)) {
if (actor instanceof CTCompositeActor) {
// We add CT composite actors into the list of discrete
// actors because a CTComposite actor can be anything.
discreteActorSchedule.add(new Firing(actor));
} else {
// the following code removes event generators
// and waveform generators from the list of purely
// continuous actors.
// NOTE: we only remove actors that are declared to be
// event generators or waveform generators. Some actors,
// such as TriggeredContinuousClock actor, are still treated
// as purely continuous actors even though they have
// discrete inputs, because they produce continuous signals.
// TESTIT: Clock3, Clock5, and Clock6 in ct/lib/test/auto.
if ((actor instanceof CTEventGenerator)
|| (actor instanceof CTWaveformGenerator)) {
continuousActors.remove(actor);
}
}
continue;
}
// We add purely discrete actors (discrete -> discrete) into list.
discreteActorSchedule.add(new Firing(actor));
}
// Create the schedule for waveform generators.
Iterator generators = waveformGenerators.iterator();
while (generators.hasNext()) {
Actor generator = (Actor) generators.next();
waveformGeneratorSchedule.add(new Firing(generator));
}
// Schedule event generators so that they are executed topologically.
// Treat them as sink actors from the point of view of a
// continuous phase of execution.
if (!eventGenerators.isEmpty()) {
DirectedAcyclicGraph eventGraph = _toGraph(eventGenerators);
Object[] eventSorted = eventGraph.topologicalSort();
for (int i = 0; i < eventSorted.length; i++) {
Actor actor = (Actor) eventSorted[i];
// If this actor is both an event generator and
// a CT step size control actor, we need to create two
// firing objects for this actor for each schedule.
eventGeneratorSchedule.add(new Firing(actor));
if (actor instanceof CTStepSizeControlActor) {
// If this event generator is a step size control actor.
outputSSCActorSchedule.add(new Firing(actor));
}
}
}
// Actors remain in the continuousActors list are purely continuous
// actor. The normal CT scheduling algorithm applies to them.
// Add all continuous actors in the continuous actors schedule.
Iterator continuousIterator = continuousActors.iterator();
while (continuousIterator.hasNext()) {
Actor actor = (Actor) continuousIterator.next();
// only purely continuous actors (continuous -> continuous) and
// CT composite actors are added into the continuousActorSchedule
continuousActorSchedule.add(new Firing(actor));
}
// Continuous actors can be clarified into three kinds:
// dynamic actors, state transition actors, and output actors
// (a.k.a sink actors).
// We first schedule dynamic actors and state transition actors.
// Create a list of state related actors to record dynamic actors and
// state transition actors. These actors are distinguished from output
// actors.
LinkedList stateRelatedActors = new LinkedList();
// Manipulate on the arithmeticGraph and the dynamicGraph within
// the continuous actors.
arithmeticGraph = _toArithmeticGraph(continuousActors);
if (!dynamicActors.isEmpty()) {
Object[] dynamicArray = dynamicActors.toArray();
// Dynamic actors are reverse ordered in the schedule.
Object[] xSorted = dynamicGraph.topologicalSort(dynamicArray);
for (int i = 0; i < xSorted.length; i++) {
Actor dynamicActor = (Actor) xSorted[i];
// Looping on add(0, a) will reverse the order.
dynamicActorSchedule.add(0, new Firing(dynamicActor));
stateRelatedActors.add(dynamicActor);
if (dynamicActor instanceof CTStepSizeControlActor) {
stateSSCActorSchedule.add(new Firing(dynamicActor));
}
// find state transition actors
Object[] fx;
fx = arithmeticGraph.backwardReachableNodes(dynamicActor);
Object[] fxSorted = arithmeticGraph.topologicalSort(fx);
for (int fxi = 0; fxi < fxSorted.length; fxi++) {
Actor actor = (Actor) fxSorted[fxi];
if (stateTransitionActors.contains(actor)) {
continue;
}
stateTransitionActors.add(actor);
stateRelatedActors.add(actor);
if (actor instanceof CTStepSizeControlActor) {
stateSSCActorSchedule.add(new Firing(actor));
}
}
// A CTCompositeActor can also be served as a state transition
// actor. To preserve topological order, append it to the
// end of found state transition actors.
// FIXME: is this necessary? get a two cascaded integrators
// as a test.
if ((dynamicActor instanceof CTCompositeActor)
&& !stateTransitionActors.contains(dynamicActor)) {
stateTransitionActors.add(dynamicActor);
}
}
}
// Create StateTransitionActorSchedule.
Iterator stActors = stateTransitionActors.iterator();
while (stActors.hasNext()) {
Actor stActor = (Actor) stActors.next();
stateTransitionSchedule.add(new Firing(stActor));
}
// Construct a list of sink actors.
sinkActors = (LinkedList) continuousActors.clone();
sinkActors.removeAll(stateRelatedActors);
// NOTE: Sink actors also include all the CT subsystems.
sinkActors.removeAll(ctSubsystems);
sinkActors.addAll(ctSubsystems);
// Create a schedule for the sink actors.
if (!sinkActors.isEmpty()) {
arithmeticGraph = _toArithmeticGraph(sinkActors);
Object[] sinkArray = sinkActors.toArray();
Object[] gxSorted = arithmeticGraph.topologicalSort(sinkArray);
for (int i = 0; i < gxSorted.length; i++) {
Actor a = (Actor) gxSorted[i];
outputSchedule.add(new Firing(a));
if (!eventGenerators.contains(a)
&& a instanceof CTStepSizeControlActor) {
outputSSCActorSchedule.add(new Firing(a));
}
}
}
// Create the CTSchedule. Note it must be done in this order.