Search in sources :

Example 1 with VarRef

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

the class FedASTUtils method addNetworkSenderReaction.

/**
 * Add a network sender reaction for a given input port 'source' to
 * source's parent reactor. This reaction will react to the 'source'
 * and then send a message on the network destined for the destinationFederate.
 *
 * @note Used in federated execution
 *
 * @param source The source port instance.
 * @param destination The destination port instance.
 * @param connection The network connection.
 * @param sourceFederate The source federate.
 * @param leftBankIndex The left bank index or -1 if the left reactor is not in a bank.
 * @param leftChannelIndex The left channel index or -1 if the left port is not a multiport.
 * @param destinationFederate The destination federate.
 * @param generator The GeneratorBase instance used to perform some target-specific actions
 * @param coordination One of CoordinationType.DECENTRALIZED or CoordinationType.CENTRALIZED.
 * @param serializer The serializer used on the connection
 */
private static void addNetworkSenderReaction(PortInstance source, PortInstance destination, Connection connection, FederateInstance sourceFederate, int leftBankIndex, int leftChannelIndex, FederateInstance destinationFederate, GeneratorBase generator, CoordinationType coordination, SupportedSerializers serializer) {
    LfFactory factory = LfFactory.eINSTANCE;
    // Assume all the types are the same, so just use the first on the right.
    Type type = EcoreUtil.copy(source.getDefinition().getType());
    VarRef sourceRef = factory.createVarRef();
    VarRef destRef = factory.createVarRef();
    Reactor parent = (Reactor) connection.eContainer();
    Reaction networkSenderReaction = factory.createReaction();
    // These reactions do not require any dependency relationship
    // to other reactions in the container.
    generator.makeUnordered(networkSenderReaction);
    // 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(networkSenderReaction, leftBankIndex);
    // The connection is 'physical' if it uses the ~> notation.
    if (connection.isPhysical()) {
        sourceFederate.outboundP2PConnections.add(destinationFederate);
    } else {
        // to make P2P connections
        if (coordination == CoordinationType.DECENTRALIZED) {
            sourceFederate.outboundP2PConnections.add(destinationFederate);
        }
    }
    // Record this action in the right federate.
    // The ID of the receiving port (rightPort) is the position
    // of the action in this list.
    int receivingPortID = destinationFederate.networkMessageActions.size();
    // Establish references to the involved ports.
    sourceRef.setContainer(source.getParent().getDefinition());
    sourceRef.setVariable(source.getDefinition());
    destRef.setContainer(destination.getParent().getDefinition());
    destRef.setVariable(destination.getDefinition());
    // Configure the sending reaction.
    networkSenderReaction.getTriggers().add(sourceRef);
    networkSenderReaction.setCode(factory.createCode());
    networkSenderReaction.getCode().setBody(generator.generateNetworkSenderBody(sourceRef, destRef, receivingPortID, sourceFederate, leftBankIndex, leftChannelIndex, destinationFederate, InferredType.fromAST(type), connection.isPhysical(), connection.getDelay(), serializer));
    // Add the sending reaction to the parent.
    parent.getReactions().add(networkSenderReaction);
    // Add the network sender reaction to the federate instance's list
    // of network reactions
    sourceFederate.networkReactions.add(networkSenderReaction);
}
Also used : VarRef(org.lflang.lf.VarRef) InferredType(org.lflang.InferredType) Type(org.lflang.lf.Type) CoordinationType(org.lflang.TargetProperty.CoordinationType) LfFactory(org.lflang.lf.LfFactory) Reactor(org.lflang.lf.Reactor) Reaction(org.lflang.lf.Reaction)

Example 2 with VarRef

use of org.lflang.lf.VarRef 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 3 with VarRef

use of org.lflang.lf.VarRef 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 4 with VarRef

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

the class ASTUtils method inferPortWidth.

/**
 * Infer the width of a port reference in a connection.
 * The port reference one or two parts, a port and an (optional) container
 * which is an Instantiation that may refer to a bank of reactors.
 * The width will be the product of the bank width and the port width.
 * The returned value will be 1 if the port is not in a bank and is not a multiport.
 *
 * If the width cannot be determined, this will return -1.
 * The width cannot be determined if the list of instantiations is
 * missing or incomplete.
 *
 * The instantiations list is as in
 * {@link initialValue(Parameter, List<Instantiation>}.
 * The first element on this list should be the instantiation
 * that contains the specified connection.
 *
 * @param reference A port reference.
 * @param connection A connection, or null if not in the context of a connection.
 * @param instantiations The (optional) list of instantiations.
 *
 * @return The width or -1 if it could not be determined.
 *
 * @throws IllegalArgumentException If an instantiation provided is not as
 *  given above or if the chain of instantiations is not nested.
 */
public static int inferPortWidth(VarRef reference, Connection connection, List<Instantiation> instantiations) {
    if (reference.getVariable() instanceof Port) {
        // If the port is given as a.b, then we want to prepend a to
        // the list of instantiations to determine the width of this port.
        List<Instantiation> extended = instantiations;
        if (reference.getContainer() != null) {
            extended = new ArrayList<>();
            extended.add(reference.getContainer());
            if (instantiations != null) {
                extended.addAll(instantiations);
            }
        }
        int portWidth = width(((Port) reference.getVariable()).getWidthSpec(), extended);
        if (portWidth < 0) {
            // Could not determine port width.
            return -1;
        }
        // Next determine the bank width. This may be unspecified, in which
        // case it has to be inferred using the connection.
        int bankWidth = 1;
        if (reference.getContainer() != null) {
            bankWidth = width(reference.getContainer().getWidthSpec(), instantiations);
            if (bankWidth < 0 && connection != null) {
                // Try to infer the bank width from the connection.
                if (reference.getContainer().getWidthSpec().isOfVariableLength()) {
                    // This occurs for a bank of delays.
                    int leftWidth = 0;
                    int rightWidth = 0;
                    int leftOrRight = 0;
                    for (VarRef leftPort : connection.getLeftPorts()) {
                        if (leftPort == reference) {
                            if (leftOrRight != 0) {
                                throw new InvalidSourceException("Multiple ports with variable width on a connection.");
                            }
                            // Indicate that this port is on the left.
                            leftOrRight = -1;
                        } else {
                            // The left port is not the same as this reference.
                            int otherWidth = inferPortWidth(leftPort, connection, instantiations);
                            if (otherWidth < 0) {
                                // Cannot determine width.
                                return -1;
                            }
                            leftWidth += otherWidth;
                        }
                    }
                    for (VarRef rightPort : connection.getRightPorts()) {
                        if (rightPort == reference) {
                            if (leftOrRight != 0) {
                                throw new InvalidSourceException("Multiple ports with variable width on a connection.");
                            }
                            // Indicate that this port is on the right.
                            leftOrRight = 1;
                        } else {
                            int otherWidth = inferPortWidth(rightPort, connection, instantiations);
                            if (otherWidth < 0) {
                                // Cannot determine width.
                                return -1;
                            }
                            rightWidth += otherWidth;
                        }
                    }
                    int discrepancy = 0;
                    if (leftOrRight < 0) {
                        // This port is on the left.
                        discrepancy = rightWidth - leftWidth;
                    } else if (leftOrRight > 0) {
                        // This port is on the right.
                        discrepancy = leftWidth - rightWidth;
                    }
                    // Check that portWidth divides the discrepancy.
                    if (discrepancy % portWidth != 0) {
                        // This is an error.
                        return -1;
                    }
                    bankWidth = discrepancy / portWidth;
                } else {
                    // Could not determine the bank width.
                    return -1;
                }
            }
        }
        return portWidth * bankWidth;
    }
    // Argument is not a port.
    return -1;
}
Also used : VarRef(org.lflang.lf.VarRef) InvalidSourceException(org.lflang.generator.InvalidSourceException) Port(org.lflang.lf.Port) Instantiation(org.lflang.lf.Instantiation)

Example 5 with VarRef

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

the class ASTUtils method getDelayClass.

/**
 * Return a synthesized AST node that represents the definition of a delay
 * reactor. Depending on whether the target supports generics, either this
 * method will synthesize a generic definition and keep returning it upon
 * subsequent calls, or otherwise, it will synthesize a new definition for
 * each new type it hasn't yet created a compatible delay reactor for.
 * @param type The type the delay class must be compatible with.
 * @param generator A code generator.
 */
private static Reactor getDelayClass(Type type, GeneratorBase generator) {
    String className;
    if (generator.getTargetTypes().supportsGenerics()) {
        className = GeneratorBase.GEN_DELAY_CLASS_NAME;
    } else {
        String id = Integer.toHexString(InferredType.fromAST(type).toText().hashCode());
        className = String.format("%s_%s", GeneratorBase.GEN_DELAY_CLASS_NAME, id);
    }
    // Only add class definition if it is not already there.
    Reactor classDef = generator.findDelayClass(className);
    if ((classDef != null)) {
        return classDef;
    }
    Reactor delayClass = factory.createReactor();
    Parameter delayParameter = factory.createParameter();
    Action action = factory.createAction();
    VarRef triggerRef = factory.createVarRef();
    VarRef effectRef = factory.createVarRef();
    Input input = factory.createInput();
    Output output = factory.createOutput();
    VarRef inRef = factory.createVarRef();
    VarRef outRef = factory.createVarRef();
    Reaction r1 = factory.createReaction();
    Reaction r2 = factory.createReaction();
    delayParameter.setName("delay");
    delayParameter.setType(factory.createType());
    delayParameter.getType().setId("time");
    delayParameter.getType().setTime(true);
    Time defaultTime = factory.createTime();
    defaultTime.setUnit(null);
    defaultTime.setInterval(0);
    Value defaultValue = factory.createValue();
    defaultValue.setTime(defaultTime);
    delayParameter.getInit().add(defaultValue);
    // Name the newly created action; set its delay and type.
    action.setName("act");
    action.setMinDelay(factory.createValue());
    action.getMinDelay().setParameter(delayParameter);
    action.setOrigin(ActionOrigin.LOGICAL);
    if (generator.getTargetTypes().supportsGenerics()) {
        action.setType(factory.createType());
        action.getType().setId("T");
    } else {
        action.setType(EcoreUtil.copy(type));
    }
    input.setName("inp");
    input.setType(EcoreUtil.copy(action.getType()));
    output.setName("out");
    output.setType(EcoreUtil.copy(action.getType()));
    // Establish references to the involved ports.
    inRef.setVariable(input);
    outRef.setVariable(output);
    // Establish references to the action.
    triggerRef.setVariable(action);
    effectRef.setVariable(action);
    // Add the action to the reactor.
    delayClass.setName(className);
    delayClass.getActions().add(action);
    // Configure the second reaction, which reads the input.
    r1.getTriggers().add(inRef);
    r1.getEffects().add(effectRef);
    r1.setCode(factory.createCode());
    r1.getCode().setBody(generator.generateDelayBody(action, inRef));
    // Configure the first reaction, which produces the output.
    r2.getTriggers().add(triggerRef);
    r2.getEffects().add(outRef);
    r2.setCode(factory.createCode());
    r2.getCode().setBody(generator.generateForwardBody(action, outRef));
    // Add the reactions to the newly created reactor class.
    // These need to go in the opposite order in case
    // a new input arrives at the same time the delayed
    // output is delivered!
    delayClass.getReactions().add(r2);
    delayClass.getReactions().add(r1);
    // Add a type parameter if the target supports it.
    if (generator.getTargetTypes().supportsGenerics()) {
        TypeParm parm = factory.createTypeParm();
        parm.setLiteral(generator.generateDelayGeneric());
        delayClass.getTypeParms().add(parm);
    }
    delayClass.getInputs().add(input);
    delayClass.getOutputs().add(output);
    delayClass.getParameters().add(delayParameter);
    generator.addDelayClass(delayClass);
    return delayClass;
}
Also used : VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) Output(org.lflang.lf.Output) Value(org.lflang.lf.Value) Parameter(org.lflang.lf.Parameter) Time(org.lflang.lf.Time) ImportedReactor(org.lflang.lf.ImportedReactor) Reactor(org.lflang.lf.Reactor) Reaction(org.lflang.lf.Reaction) TypeParm(org.lflang.lf.TypeParm)

Aggregations

VarRef (org.lflang.lf.VarRef)21 Reactor (org.lflang.lf.Reactor)16 Action (org.lflang.lf.Action)10 Instantiation (org.lflang.lf.Instantiation)9 Reaction (org.lflang.lf.Reaction)8 Input (org.lflang.lf.Input)7 Output (org.lflang.lf.Output)7 Port (org.lflang.lf.Port)7 ArrayList (java.util.ArrayList)6 LinkedHashSet (java.util.LinkedHashSet)6 TriggerRef (org.lflang.lf.TriggerRef)6 Variable (org.lflang.lf.Variable)6 Connection (org.lflang.lf.Connection)5 ImportedReactor (org.lflang.lf.ImportedReactor)5 LfFactory (org.lflang.lf.LfFactory)5 TimeValue (org.lflang.TimeValue)4 Delay (org.lflang.lf.Delay)4 LinkedList (java.util.LinkedList)3 List (java.util.List)3 Collectors (java.util.stream.Collectors)3