use of org.lflang.lf.Variable in project lingua-franca by lf-lang.
the class FedASTUtils method addNetworkOutputControlReaction.
/**
* Add a network control reaction for a given output port 'source' to
* source's parent reactor. This reaction will send a port absent
* message if the status of the output port is absent.
*
* @note Used in federated execution
*
* @param source The output port of the source federate
* @param instance The federate instance is used to keep track of all
* network reactions and some relevant triggers
* @param receivingPortID The ID of the receiving port
* @param channelIndex The channel index of the sending port, if it is a multiport.
* @param bankIndex The bank index of the sending federate, if it is a bank.
* @param receivingFedID The ID of destination federate.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param delay The delay value imposed on the connection using after
*/
private static void addNetworkOutputControlReaction(PortInstance source, FederateInstance instance, int receivingPortID, int bankIndex, int channelIndex, int receivingFedID, GeneratorBase generator, Delay delay) {
LfFactory factory = LfFactory.eINSTANCE;
Reaction reaction = factory.createReaction();
// Top-level reactor.
Reactor top = source.getParent().getParent().reactorDefinition;
// If the sender or receiver is in a bank of reactors, then we want
// these reactions to appear only in the federate whose bank ID matches.
generator.setReactionBankIndex(reaction, bankIndex);
// Add the output from the contained reactor as a source to
// the reaction to preserve precedence order.
VarRef newPortRef = factory.createVarRef();
newPortRef.setContainer(source.getParent().getDefinition());
newPortRef.setVariable(source.getDefinition());
reaction.getSources().add(newPortRef);
// Check whether the action already has been created.
if (instance.networkOutputControlReactionsTrigger == null) {
// The port has not been created.
String triggerName = "outputControlReactionTrigger";
// Find the trigger definition in the reactor definition, which could have been
// generated for another federate instance if there are multiple instances
// of the same reactor that are each distinct federates.
Optional<Action> optTriggerInput = top.getActions().stream().filter(I -> I.getName().equals(triggerName)).findFirst();
if (optTriggerInput.isEmpty()) {
// If no trigger with the name "outputControlReactionTrigger" is
// already added to the reactor definition, we need to create it
// for the first time. The trigger is a logical action.
Action newTriggerForControlReactionVariable = factory.createAction();
newTriggerForControlReactionVariable.setName(triggerName);
newTriggerForControlReactionVariable.setOrigin(ActionOrigin.LOGICAL);
top.getActions().add(newTriggerForControlReactionVariable);
// Now that the variable is created, store it in the federate instance
instance.networkOutputControlReactionsTrigger = newTriggerForControlReactionVariable;
} else {
// If the "outputControlReactionTrigger" trigger is already
// there, we can re-use it for this new reaction since a single trigger
// will trigger
// all network output control reactions.
instance.networkOutputControlReactionsTrigger = optTriggerInput.get();
}
}
// Add the trigger for all output control reactions to the list of triggers
VarRef triggerRef = factory.createVarRef();
triggerRef.setVariable(instance.networkOutputControlReactionsTrigger);
reaction.getTriggers().add(triggerRef);
// Generate the code
reaction.setCode(factory.createCode());
reaction.getCode().setBody(generator.generateNetworkOutputControlReactionBody(newPortRef, receivingPortID, receivingFedID, bankIndex, channelIndex, delay));
// Make the reaction unordered w.r.t. other reactions in the top level.
generator.makeUnordered(reaction);
// Insert the newly generated reaction after the generated sender and
// receiver top-level reactions.
top.getReactions().add(reaction);
// Add the network output control reaction to the federate instance's list
// of network reactions
instance.networkReactions.add(reaction);
}
use of org.lflang.lf.Variable in project lingua-franca by lf-lang.
the class FedASTUtils method findMaxSTP.
/**
* Find the maximum STP offset for the given 'port'.
*
* An STP offset predicate can be nested in contained reactors in
* the federate.
* @param port The port to generate the STP list for.
* @param generator The GeneratorBase instance used to perform some target-specific actions
* @param reactor The top-level reactor (not the federate reactor)
* @return The maximum STP as a TimeValue
*/
private static TimeValue findMaxSTP(Variable port, FederateInstance instance, GeneratorBase generator, Reactor reactor) {
// Find a list of STP offsets (if any exists)
List<Value> STPList = new LinkedList<>();
// First, check if there are any connections to contained reactors that
// need to be handled
List<Connection> connectionsWithPort = ASTUtils.allConnections(reactor).stream().filter(c -> c.getLeftPorts().stream().anyMatch((VarRef v) -> v.getVariable().equals(port))).collect(Collectors.toList());
// Find the list of reactions that have the port as trigger or source
// (could be a variable name)
List<Reaction> reactionsWithPort = ASTUtils.allReactions(reactor).stream().filter(r -> {
// Check the triggers of reaction r first
return r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == port;
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || // Then check the sources of reaction r
r.getSources().stream().anyMatch(s -> s.getVariable() == port);
}).collect(Collectors.toList());
// Find a list of STP offsets (if any exists)
if (generator.isFederatedAndDecentralized()) {
for (Reaction r : safe(reactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue().getParameter() != null) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(instance.instantiation);
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
// Check the children for STPs as well
for (Connection c : safe(connectionsWithPort)) {
VarRef childPort = c.getRightPorts().get(0);
Reactor childReactor = (Reactor) childPort.getVariable().eContainer();
// Find the list of reactions that have the port as trigger or
// source (could be a variable name)
List<Reaction> childReactionsWithPort = ASTUtils.allReactions(childReactor).stream().filter(r -> r.getTriggers().stream().anyMatch(t -> {
if (t instanceof VarRef) {
// Check if the variables match
return ((VarRef) t).getVariable() == childPort.getVariable();
} else {
// Not a network port (startup or shutdown)
return false;
}
}) || r.getSources().stream().anyMatch(s -> s.getVariable() == childPort.getVariable())).collect(Collectors.toList());
for (Reaction r : safe(childReactionsWithPort)) {
if (!instance.contains(r)) {
continue;
}
// If not, assume it is zero
if (r.getStp() != null) {
if (r.getStp().getValue() instanceof Parameter) {
List<Instantiation> instantList = new ArrayList<>();
instantList.add(childPort.getContainer());
STPList.addAll(ASTUtils.initialValue(r.getStp().getValue().getParameter(), instantList));
} else {
STPList.add(r.getStp().getValue());
}
}
}
}
}
return STPList.stream().map(ASTUtils::getLiteralTimeValue).filter(Objects::nonNull).reduce(TimeValue.ZERO, TimeValue::max);
}
use of org.lflang.lf.Variable in project lingua-franca by lf-lang.
the class ASTUtils method findConflictingConnectionsInModalReactors.
/**
* Find connections in the given resource that would be conflicting writes if they were not located in mutually
* exclusive modes.
*
* @param resource The AST.
* @return a list of connections being able to be transformed
*/
public static Collection<Connection> findConflictingConnectionsInModalReactors(Resource resource) {
var transform = new HashSet<Connection>();
var reactors = Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), Reactor.class);
for (Reactor reactor : reactors) {
if (!reactor.getModes().isEmpty()) {
// Only for modal reactors
var allWriters = HashMultimap.<Pair<Instantiation, Variable>, EObject>create();
// Collect destinations
for (var rea : allReactions(reactor)) {
for (var eff : rea.getEffects()) {
if (eff.getVariable() instanceof Port) {
allWriters.put(Tuples.pair(eff.getContainer(), eff.getVariable()), rea);
}
}
}
for (var con : ASTUtils.<Connection>collectElements(reactor, featurePackage.getReactor_Connections(), false, true)) {
for (var port : con.getRightPorts()) {
allWriters.put(Tuples.pair(port.getContainer(), port.getVariable()), con);
}
}
// Handle conflicting writers
for (var key : allWriters.keySet()) {
var writers = allWriters.get(key);
if (writers.size() > 1) {
// has multiple sources
var writerModes = HashMultimap.<Mode, EObject>create();
// find modes
for (var writer : writers) {
if (writer.eContainer() instanceof Mode) {
writerModes.put((Mode) writer.eContainer(), writer);
} else {
writerModes.put(null, writer);
}
}
// Conflicting connection can only be handled if..
if (// no writer is on root level (outside of modes) and...
!writerModes.containsKey(null) && writerModes.keySet().stream().map(m -> writerModes.get(m)).allMatch(// all writers in a mode are either...
writersInMode -> // the only writer or...
writersInMode.size() == 1 || // all are reactions and hence ordered
writersInMode.stream().allMatch(w -> w instanceof Reaction))) {
// Add connections to transform list
writers.stream().filter(w -> w instanceof Connection).forEach(c -> transform.add((Connection) c));
}
}
}
}
}
return transform;
}
use of org.lflang.lf.Variable in project lingua-franca by lf-lang.
the class FederateInstance method indexExcludedTopLevelReactions.
/**
* Build an index of reactions at the top-level (in the
* federatedReactor) that don't belong to this federate
* instance. This index is put in the excludeReactions
* class variable.
*
* @param federatedReactor The top-level federated reactor
*/
private void indexExcludedTopLevelReactions(Reactor federatedReactor) {
boolean inFederate = false;
if (excludeReactions != null) {
throw new IllegalStateException("The index for excluded reactions at the top level is already built.");
}
excludeReactions = new LinkedHashSet<Reaction>();
// Construct the set of excluded reactions for this federate.
// If a reaction is a network reaction that belongs to this federate, we
// don't need to perform this analysis.
Iterable<Reaction> reactions = IterableExtensions.filter(ASTUtils.allReactions(federatedReactor), it -> {
return !networkReactions.contains(it);
});
for (Reaction react : reactions) {
// Create a collection of all the VarRefs (i.e., triggers, sources, and effects) in the react
// signature that are ports that reference federates.
// We then later check that all these VarRefs reference this federate. If not, we will add this
// react to the list of reactions that have to be excluded (note that mixing VarRefs from
// different federates is not allowed).
List<VarRef> allVarRefsReferencingFederates = new ArrayList<VarRef>();
// Add all the triggers that are outputs
Stream<VarRef> triggersAsVarRef = react.getTriggers().stream().filter(it -> it instanceof VarRef).map(it -> (VarRef) it);
allVarRefsReferencingFederates.addAll(triggersAsVarRef.filter(it -> it.getVariable() instanceof Output).collect(Collectors.toList()));
// Add all the sources that are outputs
allVarRefsReferencingFederates.addAll(react.getSources().stream().filter(it -> it.getVariable() instanceof Output).collect(Collectors.toList()));
// Add all the effects that are inputs
allVarRefsReferencingFederates.addAll(react.getEffects().stream().filter(it -> it.getVariable() instanceof Input).collect(Collectors.toList()));
inFederate = containsAllVarRefs(allVarRefsReferencingFederates);
if (!inFederate) {
excludeReactions.add(react);
}
}
}
use of org.lflang.lf.Variable in project lingua-franca by lf-lang.
the class LFValidator method checkReaction.
@Check(CheckType.FAST)
public void checkReaction(Reaction reaction) {
if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
warning("Reaction has no trigger.", Literals.REACTION__TRIGGERS);
}
HashSet<Variable> triggers = new HashSet<>();
// Make sure input triggers have no container and output sources do.
for (TriggerRef trigger : reaction.getTriggers()) {
if (trigger instanceof VarRef) {
VarRef triggerVarRef = (VarRef) trigger;
triggers.add(triggerVarRef.getVariable());
if (triggerVarRef instanceof Input) {
if (triggerVarRef.getContainer() != null) {
error(String.format("Cannot have an input of a contained reactor as a trigger: %s.%s", triggerVarRef.getContainer().getName(), triggerVarRef.getVariable().getName()), Literals.REACTION__TRIGGERS);
}
} else if (triggerVarRef.getVariable() instanceof Output) {
if (triggerVarRef.getContainer() == null) {
error(String.format("Cannot have an output of this reactor as a trigger: %s", triggerVarRef.getVariable().getName()), Literals.REACTION__TRIGGERS);
}
}
}
}
// Also check that a source is not already listed as a trigger.
for (VarRef source : reaction.getSources()) {
if (triggers.contains(source.getVariable())) {
error(String.format("Source is already listed as a trigger: %s", source.getVariable().getName()), Literals.REACTION__SOURCES);
}
if (source.getVariable() instanceof Input) {
if (source.getContainer() != null) {
error(String.format("Cannot have an input of a contained reactor as a source: %s.%s", source.getContainer().getName(), source.getVariable().getName()), Literals.REACTION__SOURCES);
}
} else if (source.getVariable() instanceof Output) {
if (source.getContainer() == null) {
error(String.format("Cannot have an output of this reactor as a source: %s", source.getVariable().getName()), Literals.REACTION__SOURCES);
}
}
}
// Make sure output effects have no container and input effects do.
for (VarRef effect : reaction.getEffects()) {
if (effect.getVariable() instanceof Input) {
if (effect.getContainer() == null) {
error(String.format("Cannot have an input of this reactor as an effect: %s", effect.getVariable().getName()), Literals.REACTION__EFFECTS);
}
} else if (effect.getVariable() instanceof Output) {
if (effect.getContainer() != null) {
error(String.format("Cannot have an output of a contained reactor as an effect: %s.%s", effect.getContainer().getName(), effect.getVariable().getName()), Literals.REACTION__EFFECTS);
}
}
}
// // Report error if this reaction is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
Reactor reactor = ASTUtils.getEnclosingReactor(reaction);
boolean reactionInCycle = false;
for (NamedInstance<?> it : cycles) {
if (it.getDefinition().equals(reaction)) {
reactionInCycle = true;
}
}
if (reactionInCycle) {
// Report involved triggers.
List<CharSequence> trigs = new ArrayList<>();
for (TriggerRef t : reaction.getTriggers()) {
if (!(t instanceof VarRef)) {
continue;
}
VarRef tVarRef = (VarRef) t;
boolean triggerExistsInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(tVarRef.getVariable())) {
triggerExistsInCycle = true;
break;
}
}
if (triggerExistsInCycle) {
trigs.add(toText(tVarRef));
}
}
if (trigs.size() > 0) {
error(String.format("Reaction triggers involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", trigs)), Literals.REACTION__TRIGGERS);
}
// Report involved sources.
List<CharSequence> sources = new ArrayList<>();
for (VarRef t : reaction.getSources()) {
boolean sourceExistInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(t.getVariable())) {
sourceExistInCycle = true;
break;
}
}
if (sourceExistInCycle) {
sources.add(toText(t));
}
}
if (sources.size() > 0) {
error(String.format("Reaction sources involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", sources)), Literals.REACTION__SOURCES);
}
// Report involved effects.
List<CharSequence> effects = new ArrayList<>();
for (VarRef t : reaction.getEffects()) {
boolean effectExistInCycle = false;
for (NamedInstance<?> c : cycles) {
if (c.getDefinition().equals(t.getVariable())) {
effectExistInCycle = true;
break;
}
}
if (effectExistInCycle) {
effects.add(toText(t));
}
}
if (effects.size() > 0) {
error(String.format("Reaction effects involved in cyclic dependency in reactor %s: %s.", reactor.getName(), String.join(", ", effects)), Literals.REACTION__EFFECTS);
}
if (trigs.size() + sources.size() == 0) {
error(String.format("Cyclic dependency due to preceding reaction. Consider reordering reactions within reactor %s to avoid causality loop.", reactor.getName()), reaction.eContainer(), Literals.REACTOR__REACTIONS, reactor.getReactions().indexOf(reaction));
} else if (effects.size() == 0) {
error(String.format("Cyclic dependency due to succeeding reaction. Consider reordering reactions within reactor %s to avoid causality loop.", reactor.getName()), reaction.eContainer(), Literals.REACTOR__REACTIONS, reactor.getReactions().indexOf(reaction));
}
// Not reporting reactions that are part of cycle _only_ due to reaction ordering.
// Moving them won't help solve the problem.
}
// FIXME: improve error message.
}
Aggregations