use of org.lflang.lf.Connection in project lingua-franca by lf-lang.
the class LFValidator method checkConnection.
@Check(CheckType.FAST)
public void checkConnection(Connection connection) {
// Report if connection is part of a cycle.
Set<NamedInstance<?>> cycles = this.info.topologyCycles();
for (VarRef lp : connection.getLeftPorts()) {
for (VarRef rp : connection.getRightPorts()) {
boolean leftInCycle = false;
for (NamedInstance<?> it : cycles) {
if ((lp.getContainer() == null && it.getDefinition().equals(lp.getVariable())) || (it.getDefinition().equals(lp.getVariable()) && it.getParent().equals(lp.getContainer()))) {
leftInCycle = true;
break;
}
}
for (NamedInstance<?> it : cycles) {
if ((rp.getContainer() == null && it.getDefinition().equals(rp.getVariable())) || (it.getDefinition().equals(rp.getVariable()) && it.getParent().equals(rp.getContainer()))) {
if (leftInCycle) {
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
String reactorName = reactor.getName();
error(String.format("Connection in reactor %s creates", reactorName) + String.format("a cyclic dependency between %s and %s.", toOriginalText(lp), toOriginalText(rp)), Literals.CONNECTION__DELAY);
}
}
}
}
}
// we leave type compatibility that language's compiler or interpreter.
if (isCBasedTarget()) {
Type type = (Type) null;
for (VarRef port : connection.getLeftPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
// method for AST types, so we have to manually check the types.
if (!sameType(type, ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__LEFT_PORTS);
}
}
}
}
for (VarRef port : connection.getRightPorts()) {
// error. Avoid a class cast exception.
if (port.getVariable() instanceof Port) {
if (type == null) {
type = ((Port) port.getVariable()).getType();
} else {
if (!sameType(type, type = ((Port) port.getVariable()).getType())) {
error("Types do not match.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
// Check whether the total width of the left side of the connection
// matches the total width of the right side. This cannot be determined
// here if the width is not given as a constant. In that case, it is up
// to the code generator to check it.
int leftWidth = 0;
for (VarRef port : connection.getLeftPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || leftWidth < 0) {
// Cannot determine the width of the left ports.
leftWidth = -1;
} else {
leftWidth += width;
}
}
int rightWidth = 0;
for (VarRef port : connection.getRightPorts()) {
// null args imply incomplete check.
int width = inferPortWidth(port, null, null);
if (width < 0 || rightWidth < 0) {
// Cannot determine the width of the left ports.
rightWidth = -1;
} else {
rightWidth += width;
}
}
if (leftWidth != -1 && rightWidth != -1 && leftWidth != rightWidth) {
if (connection.isIterated()) {
if (leftWidth == 0 || rightWidth % leftWidth != 0) {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not divide right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
} else {
// FIXME: The second argument should be Literals.CONNECTION, but
// stupidly, xtext will not accept that. There seems to be no way to
// report an error for the whole connection statement.
warning(String.format("Left width %s does not match right width %s", leftWidth, rightWidth), Literals.CONNECTION__LEFT_PORTS);
}
}
Reactor reactor = ASTUtils.getEnclosingReactor(connection);
// Make sure the right port is not already an effect of a reaction.
for (Reaction reaction : ASTUtils.allReactions(reactor)) {
for (VarRef effect : reaction.getEffects()) {
for (VarRef rightPort : connection.getRightPorts()) {
if (// Refers to the same variable
rightPort.getVariable().equals(effect.getVariable()) && // Refers to the same instance
rightPort.getContainer() == effect.getContainer() && (// Either is not part of a mode
reaction.eContainer() instanceof Reactor || connection.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == reaction.eContainer())) {
error("Cannot connect: Port named '" + effect.getVariable().getName() + "' is already effect of a reaction.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
// upstream connection.
for (Connection c : reactor.getConnections()) {
if (c != connection) {
for (VarRef thisRightPort : connection.getRightPorts()) {
for (VarRef thatRightPort : c.getRightPorts()) {
if (// Refers to the same variable
thisRightPort.getVariable().equals(thatRightPort.getVariable()) && // Refers to the same instance
thisRightPort.getContainer() == thatRightPort.getContainer() && (// Or either of the connections in not part of a mode
connection.eContainer() instanceof Reactor || c.eContainer() instanceof Reactor || // Or they are in the same mode
connection.eContainer() == c.eContainer())) {
error("Cannot connect: Port named '" + thisRightPort.getVariable().getName() + "' may only appear once on the right side of a connection.", Literals.CONNECTION__RIGHT_PORTS);
}
}
}
}
}
// Check the after delay
if (connection.getDelay() != null) {
final var delay = connection.getDelay();
if (delay instanceof ParameterReference || delay instanceof Time || delay instanceof Literal) {
checkExpressionAsTime(delay, Literals.CONNECTION__DELAY);
} else {
error("After delays can only be given by time literals or parameters.", Literals.CONNECTION__DELAY);
}
}
}
use of org.lflang.lf.Connection 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, Expression 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.Connection in project lingua-franca by lf-lang.
the class GeneratorBase method transformConflictingConnectionsInModalReactors.
/**
* Finds and transforms connections into forwarding reactions iff the connections have the same destination as other
* connections or reaction in mutually exclusive modes.
*/
private void transformConflictingConnectionsInModalReactors() {
for (LFResource r : resources) {
var transform = ASTUtils.findConflictingConnectionsInModalReactors(r.eResource);
if (!transform.isEmpty()) {
var factory = LfFactory.eINSTANCE;
for (Connection connection : transform) {
// Currently only simple transformations are supported
if (connection.isPhysical() || connection.getDelay() != null || connection.isIterated() || connection.getLeftPorts().size() > 1 || connection.getRightPorts().size() > 1) {
errorReporter.reportError(connection, "Cannot transform connection in modal reactor. Connection uses currently not supported features.");
} else {
var reaction = factory.createReaction();
((Mode) connection.eContainer()).getReactions().add(reaction);
var sourceRef = connection.getLeftPorts().get(0);
var destRef = connection.getRightPorts().get(0);
reaction.getTriggers().add(sourceRef);
reaction.getEffects().add(destRef);
var code = factory.createCode();
var source = (sourceRef.getContainer() != null ? sourceRef.getContainer().getName() + "." : "") + sourceRef.getVariable().getName();
var dest = (destRef.getContainer() != null ? destRef.getContainer().getName() + "." : "") + destRef.getVariable().getName();
code.setBody(getConflictingConnectionsInModalReactorsBody(source, dest));
reaction.setCode(code);
EcoreUtil.remove(connection);
}
}
}
}
}
use of org.lflang.lf.Connection in project lingua-franca by lf-lang.
the class GeneratorBase method replaceConnectionFromFederate.
/**
* Replace the connections from the specified output port for the specified federate reactor.
* @param output The output port instance.
* @param srcFederate The federate for which this port is an output.
* @param federateReactor The reactor instance for that federate.
* @param mainInstance The main reactor instance.
*/
private void replaceConnectionFromFederate(PortInstance output, ReactorInstance federateReactor, ReactorInstance mainInstance) {
for (SendRange srcRange : output.dependentPorts) {
for (RuntimeRange<PortInstance> dstRange : srcRange.destinations) {
MixedRadixInt srcID = srcRange.startMR();
MixedRadixInt dstID = dstRange.startMR();
int dstCount = 0;
int srcCount = 0;
while (dstCount++ < dstRange.width) {
int srcChannel = srcID.getDigits().get(0);
int srcBank = srcID.get(1);
int dstChannel = dstID.getDigits().get(0);
int dstBank = dstID.get(1);
FederateInstance srcFederate = federatesByInstantiation.get(srcRange.instance.parent.definition).get(srcBank);
FederateInstance dstFederate = federatesByInstantiation.get(dstRange.instance.parent.definition).get(dstBank);
Connection connection = srcRange.connection;
if (connection == null) {
// This should not happen.
errorReporter.reportError(output.definition, "Unexpected error. Cannot find output connection for port");
} else {
if (!connection.isPhysical() && targetConfig.coordination != CoordinationType.DECENTRALIZED) {
// Map the delays on connections between federates.
// First see if the cache has been created.
Set<Expression> dependsOnDelays = dstFederate.dependsOn.get(srcFederate);
if (dependsOnDelays == null) {
// If not, create it.
dependsOnDelays = new LinkedHashSet<Expression>();
dstFederate.dependsOn.put(srcFederate, dependsOnDelays);
}
// Put the delay on the cache.
if (connection.getDelay() != null) {
dependsOnDelays.add(connection.getDelay());
} else {
// To indicate that at least one connection has no delay, add a null entry.
dependsOnDelays.add(null);
}
// Map the connections between federates.
Set<Expression> sendsToDelays = srcFederate.sendsTo.get(dstFederate);
if (sendsToDelays == null) {
sendsToDelays = new LinkedHashSet<Expression>();
srcFederate.sendsTo.put(dstFederate, sendsToDelays);
}
if (connection.getDelay() != null) {
sendsToDelays.add(connection.getDelay());
} else {
// To indicate that at least one connection has no delay, add a null entry.
sendsToDelays.add(null);
}
}
FedASTUtils.makeCommunication(srcRange.instance, dstRange.instance, connection, srcFederate, srcBank, srcChannel, dstFederate, dstBank, dstChannel, this, targetConfig.coordination);
}
dstID.increment();
srcID.increment();
srcCount++;
if (srcCount == srcRange.width) {
// Multicast. Start over.
srcID = srcRange.startMR();
}
}
}
}
}
use of org.lflang.lf.Connection in project lingua-franca by lf-lang.
the class ReactorInstance method establishPortConnections.
/**
* Populate connectivity information in the port instances.
* Note that this can only happen _after_ the children and port instances have been created.
* Unfortunately, we have to do some complicated things here
* to support multiport-to-multiport, multiport-to-bank,
* and bank-to-multiport communication. The principle being followed is:
* in each connection statement, for each port instance on the left,
* connect to the next available port on the right.
*/
private void establishPortConnections() {
for (Connection connection : ASTUtils.allConnections(reactorDefinition)) {
List<RuntimeRange<PortInstance>> leftPorts = listPortInstances(connection.getLeftPorts(), connection);
Iterator<RuntimeRange<PortInstance>> srcRanges = leftPorts.iterator();
List<RuntimeRange<PortInstance>> rightPorts = listPortInstances(connection.getRightPorts(), connection);
Iterator<RuntimeRange<PortInstance>> dstRanges = rightPorts.iterator();
// Check for empty lists.
if (!srcRanges.hasNext()) {
if (dstRanges.hasNext()) {
reporter.reportWarning(connection, "No sources to provide inputs.");
}
return;
} else if (!dstRanges.hasNext()) {
reporter.reportWarning(connection, "No destination. Outputs will be lost.");
return;
}
RuntimeRange<PortInstance> src = srcRanges.next();
RuntimeRange<PortInstance> dst = dstRanges.next();
while (true) {
if (dst.width == src.width) {
connectPortInstances(src, dst, connection);
if (!dstRanges.hasNext()) {
if (srcRanges.hasNext()) {
// Should not happen (checked by the validator).
reporter.reportWarning(connection, "Source is wider than the destination. Outputs will be lost.");
}
break;
}
if (!srcRanges.hasNext()) {
if (connection.isIterated()) {
srcRanges = leftPorts.iterator();
} else {
if (dstRanges.hasNext()) {
// Should not happen (checked by the validator).
reporter.reportWarning(connection, "Destination is wider than the source. Inputs will be missing.");
}
break;
}
}
dst = dstRanges.next();
src = srcRanges.next();
} else if (dst.width < src.width) {
// Split the left (src) range in two.
connectPortInstances(src.head(dst.width), dst, connection);
src = src.tail(dst.width);
if (!dstRanges.hasNext()) {
// Should not happen (checked by the validator).
reporter.reportWarning(connection, "Source is wider than the destination. Outputs will be lost.");
break;
}
dst = dstRanges.next();
} else if (src.width < dst.width) {
// Split the right (dst) range in two.
connectPortInstances(src, dst.head(src.width), connection);
dst = dst.tail(src.width);
if (!srcRanges.hasNext()) {
if (connection.isIterated()) {
srcRanges = leftPorts.iterator();
} else {
reporter.reportWarning(connection, "Destination is wider than the source. Inputs will be missing.");
break;
}
}
src = srcRanges.next();
}
}
}
}
Aggregations