Search in sources :

Example 1 with NamedInstance

use of org.lflang.generator.NamedInstance in project lingua-franca by lf-lang.

the class CycleVisualization method detectAndHighlightCycles.

/**
 * Performs cycle detection based on the diagram's graph structure and applies given highlighting to the included elements
 */
public boolean detectAndHighlightCycles(ReactorInstance rootReactorInstance, Map<ReactorInstance, KNode> allReactorNodes, Consumer<KGraphElement> highlighter) {
    if (rootReactorInstance.hasCycles() && highlighter != null) {
        // Highlight cycles
        // A cycle consists of reactions and ports.
        HashMultimap<ReactorInstance, NamedInstance<?>> cycleElementsByReactor = HashMultimap.create();
        Set<NamedInstance<?>> cycles = rootReactorInstance.getCycles();
        for (NamedInstance<?> element : cycles) {
            // First find the involved reactor instances
            if (element instanceof ReactorInstance) {
                cycleElementsByReactor.put((ReactorInstance) element, element);
            } else {
                cycleElementsByReactor.put(element.getParent(), element);
            }
        }
        for (ReactorInstance reactor : cycleElementsByReactor.keySet()) {
            KNode node = allReactorNodes.get(reactor);
            if (node != null) {
                node.setProperty(DEPENDENCY_CYCLE, true);
                highlighter.accept(node);
                Set<NamedInstance<?>> reactorContentInCycle = cycleElementsByReactor.get(reactor);
                // Reactor edges
                for (KEdge edge : node.getOutgoingEdges()) {
                    if (connectsCycleElements(edge, cycles)) {
                        edge.setProperty(DEPENDENCY_CYCLE, true);
                        highlighter.accept(edge);
                    }
                }
                // Reactor ports
                for (KPort port : node.getPorts()) {
                    if (reactorContentInCycle.contains(NamedInstanceUtil.getLinkedInstance(port))) {
                        port.setProperty(DEPENDENCY_CYCLE, true);
                        highlighter.accept(port);
                    }
                }
                // Child Nodes
                for (KNode childNode : node.getChildren()) {
                    if (reactorContentInCycle.contains(NamedInstanceUtil.getLinkedInstance(childNode)) && !_utilityExtensions.sourceIsReactor(childNode)) {
                        childNode.setProperty(DEPENDENCY_CYCLE, true);
                        highlighter.accept(childNode);
                        for (KEdge edge : childNode.getOutgoingEdges()) {
                            if (connectsCycleElements(edge, cycles)) {
                                edge.setProperty(DEPENDENCY_CYCLE, true);
                                highlighter.accept(edge);
                            }
                        }
                    }
                }
            }
        }
        return true;
    }
    return false;
}
Also used : ReactorInstance(org.lflang.generator.ReactorInstance) KEdge(de.cau.cs.kieler.klighd.kgraph.KEdge) KPort(de.cau.cs.kieler.klighd.kgraph.KPort) KNode(de.cau.cs.kieler.klighd.kgraph.KNode) NamedInstance(org.lflang.generator.NamedInstance)

Example 2 with NamedInstance

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

Example 3 with NamedInstance

use of org.lflang.generator.NamedInstance 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.
}
Also used : VarRef(org.lflang.lf.VarRef) TypedVariable(org.lflang.lf.TypedVariable) Variable(org.lflang.lf.Variable) ArrayList(java.util.ArrayList) TriggerRef(org.lflang.lf.TriggerRef) NamedInstance(org.lflang.generator.NamedInstance) Input(org.lflang.lf.Input) Output(org.lflang.lf.Output) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Check(org.eclipse.xtext.validation.Check)

Aggregations

NamedInstance (org.lflang.generator.NamedInstance)3 Check (org.eclipse.xtext.validation.Check)2 ImportedReactor (org.lflang.lf.ImportedReactor)2 Reactor (org.lflang.lf.Reactor)2 VarRef (org.lflang.lf.VarRef)2 KEdge (de.cau.cs.kieler.klighd.kgraph.KEdge)1 KNode (de.cau.cs.kieler.klighd.kgraph.KNode)1 KPort (de.cau.cs.kieler.klighd.kgraph.KPort)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 LinkedHashSet (java.util.LinkedHashSet)1 CheckType (org.eclipse.xtext.validation.CheckType)1 ASTUtils.isOfTimeType (org.lflang.ASTUtils.isOfTimeType)1 ModeTransitionType (org.lflang.generator.ModeInstance.ModeTransitionType)1 ReactorInstance (org.lflang.generator.ReactorInstance)1 Connection (org.lflang.lf.Connection)1 Input (org.lflang.lf.Input)1 Output (org.lflang.lf.Output)1 Port (org.lflang.lf.Port)1 Reaction (org.lflang.lf.Reaction)1