Search in sources :

Example 6 with Connection

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);
        }
    }
}
Also used : VarRef(org.lflang.lf.VarRef) ParameterReference(org.lflang.lf.ParameterReference) Port(org.lflang.lf.Port) Connection(org.lflang.lf.Connection) Time(org.lflang.lf.Time) Reaction(org.lflang.lf.Reaction) NamedInstance(org.lflang.generator.NamedInstance) ASTUtils.isOfTimeType(org.lflang.ASTUtils.isOfTimeType) CheckType(org.eclipse.xtext.validation.CheckType) ModeTransitionType(org.lflang.generator.ModeInstance.ModeTransitionType) Type(org.lflang.lf.Type) Literal(org.lflang.lf.Literal) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) Check(org.eclipse.xtext.validation.Check)

Example 7 with Connection

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);
}
Also used : VarRef(org.lflang.lf.VarRef) Variable(org.lflang.lf.Variable) SupportedSerializers(org.lflang.federated.serialization.SupportedSerializers) LfFactory(org.lflang.lf.LfFactory) Action(org.lflang.lf.Action) ArrayList(java.util.ArrayList) InferredType(org.lflang.InferredType) Expression(org.lflang.lf.Expression) Instantiation(org.lflang.lf.Instantiation) Reaction(org.lflang.lf.Reaction) LinkedList(java.util.LinkedList) TimeValue(org.lflang.TimeValue) Connection(org.lflang.lf.Connection) Type(org.lflang.lf.Type) GeneratorBase(org.lflang.generator.GeneratorBase) CoordinationType(org.lflang.TargetProperty.CoordinationType) EcoreUtil(org.eclipse.emf.ecore.util.EcoreUtil) PortInstance(org.lflang.generator.PortInstance) ParameterReference(org.lflang.lf.ParameterReference) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) List(java.util.List) Reactor(org.lflang.lf.Reactor) ActionOrigin(org.lflang.lf.ActionOrigin) Optional(java.util.Optional) VarRef(org.lflang.lf.VarRef) Collections(java.util.Collections) ASTUtils(org.lflang.ASTUtils) Action(org.lflang.lf.Action) LfFactory(org.lflang.lf.LfFactory) Reaction(org.lflang.lf.Reaction) Reactor(org.lflang.lf.Reactor)

Example 8 with Connection

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);
                }
            }
        }
    }
}
Also used : Connection(org.lflang.lf.Connection)

Example 9 with 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();
                }
            }
        }
    }
}
Also used : Expression(org.lflang.lf.Expression) FederateInstance(org.lflang.federated.FederateInstance) Connection(org.lflang.lf.Connection)

Example 10 with Connection

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();
            }
        }
    }
}
Also used : Connection(org.lflang.lf.Connection)

Aggregations

Connection (org.lflang.lf.Connection)12 Reactor (org.lflang.lf.Reactor)8 VarRef (org.lflang.lf.VarRef)8 ArrayList (java.util.ArrayList)7 Instantiation (org.lflang.lf.Instantiation)6 Reaction (org.lflang.lf.Reaction)6 Type (org.lflang.lf.Type)6 List (java.util.List)5 EcoreUtil (org.eclipse.emf.ecore.util.EcoreUtil)5 GeneratorBase (org.lflang.generator.GeneratorBase)5 Action (org.lflang.lf.Action)5 ActionOrigin (org.lflang.lf.ActionOrigin)5 ImportedReactor (org.lflang.lf.ImportedReactor)5 LfFactory (org.lflang.lf.LfFactory)5 Variable (org.lflang.lf.Variable)5 LinkedList (java.util.LinkedList)4 Collectors (java.util.stream.Collectors)4 Collections (java.util.Collections)3 HashSet (java.util.HashSet)3 Objects (java.util.Objects)3