Search in sources :

Example 1 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, 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);
}
Also used : VarRef(org.lflang.lf.VarRef) Variable(org.lflang.lf.Variable) SupportedSerializers(org.lflang.federated.serialization.SupportedSerializers) Delay(org.lflang.lf.Delay) LfFactory(org.lflang.lf.LfFactory) Action(org.lflang.lf.Action) ArrayList(java.util.ArrayList) InferredType(org.lflang.InferredType) 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) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) Parameter(org.lflang.lf.Parameter) List(java.util.List) Value(org.lflang.lf.Value) 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 2 with Connection

use of org.lflang.lf.Connection 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);
}
Also used : Variable(org.lflang.lf.Variable) SupportedSerializers(org.lflang.federated.serialization.SupportedSerializers) Delay(org.lflang.lf.Delay) LfFactory(org.lflang.lf.LfFactory) Action(org.lflang.lf.Action) ArrayList(java.util.ArrayList) InferredType(org.lflang.InferredType) 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) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) Parameter(org.lflang.lf.Parameter) List(java.util.List) Value(org.lflang.lf.Value) 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) VarRef(org.lflang.lf.VarRef) ASTUtils(org.lflang.ASTUtils) Connection(org.lflang.lf.Connection) ArrayList(java.util.ArrayList) Reaction(org.lflang.lf.Reaction) LinkedList(java.util.LinkedList) TimeValue(org.lflang.TimeValue) Value(org.lflang.lf.Value) Parameter(org.lflang.lf.Parameter) Instantiation(org.lflang.lf.Instantiation) Reactor(org.lflang.lf.Reactor) TimeValue(org.lflang.TimeValue)

Example 3 with Connection

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

the class ASTUtils method rerouteViaDelay.

/**
 * Take a connection and reroute it via an instance of a generated delay
 * reactor. This method returns a list to new connections to substitute
 * the original one.
 * @param connection The connection to reroute.
 * @param delayInstance The delay instance to route the connection through.
 */
private static List<Connection> rerouteViaDelay(Connection connection, Instantiation delayInstance) {
    List<Connection> connections = new ArrayList<>();
    Connection upstream = factory.createConnection();
    Connection downstream = factory.createConnection();
    VarRef input = factory.createVarRef();
    VarRef output = factory.createVarRef();
    Reactor delayClass = toDefinition(delayInstance.getReactorClass());
    // Establish references to the involved ports.
    input.setContainer(delayInstance);
    input.setVariable(delayClass.getInputs().get(0));
    output.setContainer(delayInstance);
    output.setVariable(delayClass.getOutputs().get(0));
    upstream.getLeftPorts().addAll(connection.getLeftPorts());
    upstream.getRightPorts().add(input);
    downstream.getLeftPorts().add(output);
    downstream.getRightPorts().addAll(connection.getRightPorts());
    downstream.setIterated(connection.isIterated());
    connections.add(upstream);
    connections.add(downstream);
    return connections;
}
Also used : VarRef(org.lflang.lf.VarRef) Connection(org.lflang.lf.Connection) ArrayList(java.util.ArrayList) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor)

Example 4 with Connection

use of org.lflang.lf.Connection 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;
}
Also used : Code(org.lflang.lf.Code) LfPackage(org.lflang.lf.LfPackage) Delay(org.lflang.lf.Delay) WidthSpec(org.lflang.lf.WidthSpec) EStructuralFeature(org.eclipse.emf.ecore.EStructuralFeature) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) ILeafNode(org.eclipse.xtext.nodemodel.ILeafNode) StateVar(org.lflang.lf.StateVar) Matcher(java.util.regex.Matcher) HashMultimap(com.google.common.collect.HashMultimap) Port(org.lflang.lf.Port) Map(java.util.Map) Instantiation(org.lflang.lf.Instantiation) INode(org.eclipse.xtext.nodemodel.INode) Connection(org.lflang.lf.Connection) GeneratorBase(org.lflang.generator.GeneratorBase) Element(org.lflang.lf.Element) TypeParm(org.lflang.lf.TypeParm) Collection(java.util.Collection) CompositeNode(org.eclipse.xtext.nodemodel.impl.CompositeNode) Set(java.util.Set) InvalidSourceException(org.lflang.generator.InvalidSourceException) EObject(org.eclipse.emf.ecore.EObject) ICompositeNode(org.eclipse.xtext.nodemodel.ICompositeNode) ArraySpec(org.lflang.lf.ArraySpec) Mode(org.lflang.lf.Mode) Parameter(org.lflang.lf.Parameter) List(java.util.List) Value(org.lflang.lf.Value) CodeMap(org.lflang.generator.CodeMap) WidthTerm(org.lflang.lf.WidthTerm) Assignment(org.lflang.lf.Assignment) Resource(org.eclipse.emf.ecore.resource.Resource) ActionOrigin(org.lflang.lf.ActionOrigin) Pattern(java.util.regex.Pattern) StringExtensions(org.eclipse.xtext.xbase.lib.StringExtensions) Output(org.lflang.lf.Output) Variable(org.lflang.lf.Variable) Iterables(com.google.common.collect.Iterables) LfFactory(org.lflang.lf.LfFactory) NodeModelUtils(org.eclipse.xtext.nodemodel.util.NodeModelUtils) ImportedReactor(org.lflang.lf.ImportedReactor) Iterators(com.google.common.collect.Iterators) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Pair(org.eclipse.xtext.util.Pair) TargetDecl(org.lflang.lf.TargetDecl) StringUtil(org.lflang.util.StringUtil) Reaction(org.lflang.lf.Reaction) KeyValuePair(org.lflang.lf.KeyValuePair) Type(org.lflang.lf.Type) LinkedHashSet(java.util.LinkedHashSet) Tuples(org.eclipse.xtext.util.Tuples) XtextResource(org.eclipse.xtext.resource.XtextResource) HiddenLeafNode(org.eclipse.xtext.nodemodel.impl.HiddenLeafNode) Model(org.lflang.lf.Model) ReactorDecl(org.lflang.lf.ReactorDecl) EcoreUtil(org.eclipse.emf.ecore.util.EcoreUtil) Time(org.lflang.lf.Time) EList(org.eclipse.emf.common.util.EList) IteratorExtensions(org.eclipse.xtext.xbase.lib.IteratorExtensions) IterableExtensions(org.eclipse.xtext.xbase.lib.IterableExtensions) TerminalRule(org.eclipse.xtext.TerminalRule) Reactor(org.lflang.lf.Reactor) VarRef(org.lflang.lf.VarRef) Timer(org.lflang.lf.Timer) EObject(org.eclipse.emf.ecore.EObject) Port(org.lflang.lf.Port) Mode(org.lflang.lf.Mode) Connection(org.lflang.lf.Connection) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) Reaction(org.lflang.lf.Reaction) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Pair(org.eclipse.xtext.util.Pair) KeyValuePair(org.lflang.lf.KeyValuePair)

Example 5 with Connection

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

the class LinguaFrancaSynthesis method transformReactorNetwork.

private Collection<KNode> transformReactorNetwork(ReactorInstance reactorInstance, Map<PortInstance, KPort> parentInputPorts, Map<PortInstance, KPort> parentOutputPorts, Map<ReactorInstance, KNode> allReactorNodes) {
    List<KNode> nodes = new ArrayList<>();
    Table<ReactorInstance, PortInstance, KPort> inputPorts = HashBasedTable.create();
    Table<ReactorInstance, PortInstance, KPort> outputPorts = HashBasedTable.create();
    Map<ReactionInstance, KNode> reactionNodes = new HashMap<>();
    Map<KPort, KNode> directConnectionDummyNodes = new HashMap<>();
    Multimap<ActionInstance, KPort> actionDestinations = HashMultimap.create();
    Multimap<ActionInstance, KPort> actionSources = HashMultimap.create();
    Map<TimerInstance, KNode> timerNodes = new HashMap<>();
    KNode startupNode = _kNodeExtensions.createNode();
    boolean startupUsed = false;
    KNode shutdownNode = _kNodeExtensions.createNode();
    boolean shutdownUsed = false;
    // Transform instances
    int index = 0;
    for (ReactorInstance child : ListExtensions.reverseView(reactorInstance.children)) {
        Boolean expansionState = MemorizingExpandCollapseAction.getExpansionState(child);
        Collection<KNode> rNodes = createReactorNode(child, expansionState != null ? expansionState : false, inputPorts, outputPorts, allReactorNodes);
        setLayoutOption(IterableExtensions.<KNode>head(rNodes), CoreOptions.PRIORITY, index);
        nodes.addAll(rNodes);
        index++;
    }
    // Create timers
    for (TimerInstance timer : reactorInstance.timers) {
        KNode node = associateWith(_kNodeExtensions.createNode(), timer.getDefinition());
        NamedInstanceUtil.linkInstance(node, timer);
        _utilityExtensions.setID(node, timer.uniqueID());
        nodes.add(node);
        Iterables.addAll(nodes, createUserComments(timer.getDefinition(), node));
        timerNodes.put(timer, node);
        _linguaFrancaShapeExtensions.addTimerFigure(node, timer);
    }
    // Create reactions
    for (ReactionInstance reaction : ListExtensions.reverseView(reactorInstance.reactions)) {
        int idx = reactorInstance.reactions.indexOf(reaction);
        KNode node = associateWith(_kNodeExtensions.createNode(), reaction.getDefinition());
        NamedInstanceUtil.linkInstance(node, reaction);
        _utilityExtensions.setID(node, reaction.uniqueID());
        nodes.add(node);
        Iterables.addAll(nodes, createUserComments(reaction.getDefinition(), node));
        reactionNodes.put(reaction, node);
        setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
        // always place with higher priority than reactor nodes
        setLayoutOption(node, CoreOptions.PRIORITY, (reactorInstance.reactions.size() - idx) * 10);
        // try order reactions vertically if in one layer
        setLayoutOption(node, LayeredOptions.POSITION, new KVector(0, idx));
        _linguaFrancaShapeExtensions.addReactionFigure(node, reaction);
        // connect input
        KPort port = null;
        for (TriggerInstance<?> trigger : reaction.triggers) {
            port = addInvisiblePort(node);
            setLayoutOption(port, CoreOptions.PORT_SIDE, PortSide.WEST);
            int triggersSize = reaction.triggers != null ? reaction.triggers.size() : 0;
            int sourcesSize = reaction.sources != null ? reaction.sources.size() : 0;
            if (getBooleanValue(REACTIONS_USE_HYPEREDGES) || triggersSize + sourcesSize == 1) {
                // manual adjustment disabling automatic one
                setLayoutOption(port, CoreOptions.PORT_BORDER_OFFSET, (double) -LinguaFrancaShapeExtensions.REACTION_POINTINESS);
            }
            if (trigger.isStartup()) {
                connect(createDependencyEdge(((TriggerInstance.BuiltinTriggerVariable) trigger.getDefinition()).definition), startupNode, port);
                startupUsed = true;
            } else if (trigger.isShutdown()) {
                connect(createDelayEdge(((TriggerInstance.BuiltinTriggerVariable) trigger.getDefinition()).definition), shutdownNode, port);
                shutdownUsed = true;
            } else if (trigger instanceof ActionInstance) {
                actionDestinations.put(((ActionInstance) trigger), port);
            } else if (trigger instanceof PortInstance) {
                KPort src = null;
                PortInstance triggerAsPort = (PortInstance) trigger;
                if (triggerAsPort.getParent() == reactorInstance) {
                    src = parentInputPorts.get(trigger);
                } else {
                    src = outputPorts.get(triggerAsPort.getParent(), trigger);
                }
                if (src != null) {
                    connect(createDependencyEdge(triggerAsPort.getDefinition()), src, port);
                }
            } else if (trigger instanceof TimerInstance) {
                KNode src = timerNodes.get(trigger);
                if (src != null) {
                    connect(createDependencyEdge(trigger.getDefinition()), src, port);
                }
            }
        }
        // port = null // create new ports
        for (TriggerInstance<?> dep : reaction.sources) {
            if (reaction.triggers.contains(dep))
                continue;
            if (!(getBooleanValue(REACTIONS_USE_HYPEREDGES) && port != null)) {
                port = addInvisiblePort(node);
                setLayoutOption(port, CoreOptions.PORT_SIDE, PortSide.WEST);
                int triggersSize = reaction.triggers != null ? reaction.triggers.size() : 0;
                int sourcesSize = reaction.sources != null ? reaction.sources.size() : 0;
                if (getBooleanValue(REACTIONS_USE_HYPEREDGES) || triggersSize + sourcesSize == 1) {
                    // manual adjustment disabling automatic one
                    setLayoutOption(port, CoreOptions.PORT_BORDER_OFFSET, (double) -LinguaFrancaShapeExtensions.REACTION_POINTINESS);
                }
            }
            if (dep instanceof PortInstance) {
                KPort src = null;
                PortInstance depAsPort = (PortInstance) dep;
                if (dep.getParent() == reactorInstance) {
                    src = parentInputPorts.get(dep);
                } else {
                    src = outputPorts.get(depAsPort.getParent(), dep);
                }
                if (src != null) {
                    connect(createDependencyEdge(dep.getDefinition()), src, port);
                }
            }
        }
        // connect outputs
        // create new ports
        port = null;
        Set<TriggerInstance<?>> iterSet = reaction.effects != null ? reaction.effects : new HashSet<>();
        for (TriggerInstance<?> effect : iterSet) {
            port = addInvisiblePort(node);
            setLayoutOption(port, CoreOptions.PORT_SIDE, PortSide.EAST);
            if (effect instanceof ActionInstance) {
                actionSources.put((ActionInstance) effect, port);
            } else if (effect instanceof PortInstance) {
                KPort dst = null;
                PortInstance effectAsPort = (PortInstance) effect;
                if (effectAsPort.isOutput()) {
                    dst = parentOutputPorts.get(effect);
                } else {
                    dst = inputPorts.get(effectAsPort.getParent(), effect);
                }
                if (dst != null) {
                    connect(createDependencyEdge(effect), port, dst);
                }
            }
        }
    }
    // Connect actions
    Set<ActionInstance> actions = new HashSet<>();
    actions.addAll(actionSources.keySet());
    actions.addAll(actionDestinations.keySet());
    for (ActionInstance action : actions) {
        KNode node = associateWith(_kNodeExtensions.createNode(), action.getDefinition());
        NamedInstanceUtil.linkInstance(node, action);
        _utilityExtensions.setID(node, action.uniqueID());
        nodes.add(node);
        Iterables.addAll(nodes, createUserComments(action.getDefinition(), node));
        setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
        Pair<KPort, KPort> ports = _linguaFrancaShapeExtensions.addActionFigureAndPorts(node, action.isPhysical() ? "P" : "L");
        // TODO handle variables?
        if (action.getMinDelay() != null && action.getMinDelay() != ActionInstance.DEFAULT_MIN_DELAY) {
            _kLabelExtensions.addOutsideBottomCenteredNodeLabel(node, String.format("min delay: %s", action.getMinDelay().toString()), 7);
        }
        // TODO default value?
        if (action.getDefinition().getMinSpacing() != null) {
            _kLabelExtensions.addOutsideBottomCenteredNodeLabel(node, String.format("min spacing: %s", action.getMinSpacing().toString()), 7);
        }
        if (!StringExtensions.isNullOrEmpty(action.getDefinition().getPolicy())) {
            _kLabelExtensions.addOutsideBottomCenteredNodeLabel(node, String.format("policy: %s", action.getPolicy().toString()), 7);
        }
        // connect source
        for (KPort source : actionSources.get(action)) {
            connect(createDelayEdge(action), source, ports.getKey());
        }
        // connect targets
        for (KPort target : actionDestinations.get(action)) {
            connect(createDelayEdge(action), ports.getValue(), target);
        }
    }
    // Transform connections.
    // First, collect all the source ports.
    List<PortInstance> sourcePorts = new LinkedList<>(reactorInstance.inputs);
    for (ReactorInstance child : reactorInstance.children) {
        sourcePorts.addAll(child.outputs);
    }
    for (PortInstance leftPort : sourcePorts) {
        KPort source = leftPort.getParent() == reactorInstance ? parentInputPorts.get(leftPort) : outputPorts.get(leftPort.getParent(), leftPort);
        for (SendRange sendRange : leftPort.getDependentPorts()) {
            for (RuntimeRange<PortInstance> rightRange : sendRange.destinations) {
                PortInstance rightPort = rightRange.instance;
                KPort target = rightPort.getParent() == reactorInstance ? parentOutputPorts.get(rightPort) : inputPorts.get(rightPort.getParent(), rightPort);
                // There should be a connection, but skip if not.
                Connection connection = sendRange.connection;
                if (connection != null) {
                    KEdge edge = createIODependencyEdge(connection, (leftPort.isMultiport() || rightPort.isMultiport()));
                    if (connection.getDelay() != null) {
                        KLabel delayLabel = _kLabelExtensions.addCenterEdgeLabel(edge, ASTUtils.toText(connection.getDelay()));
                        associateWith(delayLabel, connection.getDelay());
                        if (connection.isPhysical()) {
                            _linguaFrancaStyleExtensions.applyOnEdgePysicalDelayStyle(delayLabel, reactorInstance.isMainOrFederated() ? Colors.WHITE : Colors.GRAY_95);
                        } else {
                            _linguaFrancaStyleExtensions.applyOnEdgeDelayStyle(delayLabel);
                        }
                    } else if (connection.isPhysical()) {
                        KLabel physicalConnectionLabel = _kLabelExtensions.addCenterEdgeLabel(edge, "---");
                        _linguaFrancaStyleExtensions.applyOnEdgePysicalStyle(physicalConnectionLabel, reactorInstance.isMainOrFederated() ? Colors.WHITE : Colors.GRAY_95);
                    }
                    if (source != null && target != null) {
                        // check for inside loop (direct in -> out connection with delay)
                        if (parentInputPorts.values().contains(source) && parentOutputPorts.values().contains(target)) {
                            // edge.setLayoutOption(CoreOptions.INSIDE_SELF_LOOPS_YO, true) // Does not work as expected
                            // Introduce dummy node to enable direct connection (that is also hidden when collapsed)
                            KNode dummy = _kNodeExtensions.createNode();
                            if (directConnectionDummyNodes.containsKey(target)) {
                                dummy = directConnectionDummyNodes.get(target);
                            } else {
                                nodes.add(dummy);
                                directConnectionDummyNodes.put(target, dummy);
                                _kRenderingExtensions.addInvisibleContainerRendering(dummy);
                                _kNodeExtensions.setNodeSize(dummy, 0, 0);
                                KEdge extraEdge = createIODependencyEdge(null, (leftPort.isMultiport() || rightPort.isMultiport()));
                                connect(extraEdge, dummy, target);
                            }
                            connect(edge, source, dummy);
                        } else {
                            connect(edge, source, target);
                        }
                    }
                }
            }
        }
    }
    // Add startup/shutdown
    if (startupUsed) {
        _linguaFrancaShapeExtensions.addStartupFigure(startupNode);
        nodes.add(0, startupNode);
        setLayoutOption(startupNode, LayeredOptions.LAYERING_LAYER_CONSTRAINT, LayerConstraint.FIRST);
        if (getBooleanValue(REACTIONS_USE_HYPEREDGES)) {
            KPort port = addInvisiblePort(startupNode);
            startupNode.getOutgoingEdges().forEach(it -> {
                it.setSourcePort(port);
            });
        }
    }
    if (shutdownUsed) {
        _linguaFrancaShapeExtensions.addShutdownFigure(shutdownNode);
        nodes.add(0, shutdownNode);
        if (getBooleanValue(REACTIONS_USE_HYPEREDGES)) {
            // connect all edges to one port
            KPort port = addInvisiblePort(shutdownNode);
            shutdownNode.getOutgoingEdges().forEach(it -> {
                it.setSourcePort(port);
            });
        }
    }
    // Postprocess timer nodes
    if (getBooleanValue(REACTIONS_USE_HYPEREDGES)) {
        // connect all edges to one port
        for (KNode timerNode : timerNodes.values()) {
            KPort port = addInvisiblePort(timerNode);
            timerNode.getOutgoingEdges().forEach(it -> {
                it.setSourcePort(port);
            });
        }
    }
    // Add reaction order edges (add last to have them on top of other edges)
    if (reactorInstance.reactions.size() > 1) {
        KNode prevNode = reactionNodes.get(IterableExtensions.head(reactorInstance.reactions));
        Iterable<KNode> iterList = IterableExtensions.map(IterableExtensions.drop(reactorInstance.reactions, 1), reactionNodes::get);
        for (KNode node : iterList) {
            KEdge edge = createOrderEdge();
            edge.setSource(prevNode);
            edge.setTarget(node);
            edge.setProperty(CoreOptions.NO_LAYOUT, true);
            // Do not remove them, as they are needed for cycle detection
            KRendering edgeRendering = _kRenderingExtensions.getKRendering(edge);
            _kRenderingExtensions.setInvisible(edgeRendering, !getBooleanValue(SHOW_REACTION_ORDER_EDGES));
            _kRenderingExtensions.getInvisible(edgeRendering).setPropagateToChildren(true);
            // TODO this does not work work with incremental update (https://github.com/kieler/KLighD/issues/37)
            // if (!getBooleanValue(SHOW_REACTION_ORDER_EDGES)) edge.initiallyHide()
            prevNode = node;
        }
    }
    _modeDiagrams.handleModes(nodes, reactorInstance);
    return nodes;
}
Also used : PortInstance(org.lflang.generator.PortInstance) ReactionInstance(org.lflang.generator.ReactionInstance) HashMap(java.util.HashMap) TimerInstance(org.lflang.generator.TimerInstance) ArrayList(java.util.ArrayList) KVector(org.eclipse.elk.core.math.KVector) KRendering(de.cau.cs.kieler.klighd.krendering.KRendering) HashSet(java.util.HashSet) Connection(org.lflang.lf.Connection) KEdge(de.cau.cs.kieler.klighd.kgraph.KEdge) KPort(de.cau.cs.kieler.klighd.kgraph.KPort) KNode(de.cau.cs.kieler.klighd.kgraph.KNode) TriggerInstance(org.lflang.generator.TriggerInstance) SizeConstraint(org.eclipse.elk.core.options.SizeConstraint) LayerConstraint(org.eclipse.elk.alg.layered.options.LayerConstraint) LinkedList(java.util.LinkedList) KLabel(de.cau.cs.kieler.klighd.kgraph.KLabel) ReactorInstance(org.lflang.generator.ReactorInstance) ActionInstance(org.lflang.generator.ActionInstance) SendRange(org.lflang.generator.SendRange)

Aggregations

Connection (org.lflang.lf.Connection)10 Reactor (org.lflang.lf.Reactor)7 VarRef (org.lflang.lf.VarRef)7 ArrayList (java.util.ArrayList)6 Delay (org.lflang.lf.Delay)5 ImportedReactor (org.lflang.lf.ImportedReactor)5 Instantiation (org.lflang.lf.Instantiation)5 Reaction (org.lflang.lf.Reaction)5 Type (org.lflang.lf.Type)5 List (java.util.List)4 EcoreUtil (org.eclipse.emf.ecore.util.EcoreUtil)4 HashSet (java.util.HashSet)3 LinkedList (java.util.LinkedList)3 GeneratorBase (org.lflang.generator.GeneratorBase)3 PortInstance (org.lflang.generator.PortInstance)3 Action (org.lflang.lf.Action)3 ActionOrigin (org.lflang.lf.ActionOrigin)3 LfFactory (org.lflang.lf.LfFactory)3 Parameter (org.lflang.lf.Parameter)3 Value (org.lflang.lf.Value)3