Search in sources :

Example 1 with Action

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

the class FedASTUtils method makeCommunication.

/**
 * Replace the specified connection with communication between federates.
 * @param source The source port instance.
 * @param destination The destination port instance.
 * @param connection The 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 rightBankIndex The right bank index or -1 if the right reactor is not in a bank.
 * @param rightChannelIndex The right channel index or -1 if the right port is not a multiport.
 * @param generator The GeneratorBase instance used to perform some target-specific actions
 * @param coordination One of CoordinationType.DECENTRALIZED or CoordinationType.CENTRALIZED.
 */
public static void makeCommunication(PortInstance source, PortInstance destination, Connection connection, FederateInstance sourceFederate, int leftBankIndex, int leftChannelIndex, FederateInstance destinationFederate, int rightBankIndex, int rightChannelIndex, GeneratorBase generator, CoordinationType coordination) {
    // Get the serializer
    var serializer = SupportedSerializers.NATIVE;
    if (connection.getSerializer() != null) {
        serializer = SupportedSerializers.valueOf(connection.getSerializer().getType().toUpperCase());
    }
    // Add it to the list of enabled serializers
    generator.enabledSerializers.add(serializer);
    // Add the sender reaction.
    addNetworkSenderReaction(source, destination, connection, sourceFederate, leftBankIndex, leftChannelIndex, destinationFederate, generator, coordination, serializer);
    if (!connection.isPhysical()) {
        // The ID of the receiving port (rightPort) is the position
        // of the networkAction (see below) in this list.
        int receivingPortID = destinationFederate.networkMessageActions.size();
        // Add the network output control reaction to the parent
        FedASTUtils.addNetworkOutputControlReaction(source, sourceFederate, receivingPortID, leftBankIndex, leftChannelIndex, destinationFederate.id, generator, connection.getDelay());
        // Add the network input control reaction to the parent
        FedASTUtils.addNetworkInputControlReaction(source, destination, receivingPortID, rightBankIndex, destinationFederate, generator);
    }
    // Create the network action (@see createNetworkAction)
    Action networkAction = createNetworkAction(connection, serializer, EcoreUtil.copy(source.getDefinition().getType()), generator.getNetworkBufferType());
    // Keep track of this action in the destination federate.
    destinationFederate.networkMessageActions.add(networkAction);
    // Add the action definition to the parent reactor.
    ((Reactor) connection.eContainer()).getActions().add(networkAction);
    // Add the network receiver reaction in the destinationFederate
    addNetworkReceiverReaction(networkAction, source, destination, connection, sourceFederate, destinationFederate, rightBankIndex, rightChannelIndex, generator, coordination, serializer);
}
Also used : Action(org.lflang.lf.Action)

Example 2 with Action

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

the class FedASTUtils method createNetworkAction.

/**
 * Create a "network action" in the reactor that contains the given
 * connection and return it.
 *
 * The purpose of this action is to serve as a trigger for a "network
 * input reaction" that is responsible for relaying messages to the
 * port that is on the receiving side of the given connection. The
 * connection is assumed to be between two reactors that reside in
 * distinct federates. Hence, the container of the connection is
 * assumed to be top-level.
 *
 * @param connection A connection between to federates.
 * @param serializer The serializer used on the connection.
 * @param type The type of the source port (indicating the type of
 *  data to be received).
 * @param networkBufferType The type of the buffer used for network
 *  communication in the target (e.g., uint8_t* in C).
 * @return The newly created action.
 */
private static Action createNetworkAction(Connection connection, SupportedSerializers serializer, Type type, String networkBufferType) {
    Reactor top = (Reactor) connection.eContainer();
    LfFactory factory = LfFactory.eINSTANCE;
    Action action = factory.createAction();
    // Name the newly created action; set its delay and type.
    action.setName(ASTUtils.getUniqueIdentifier(top, "networkMessage"));
    if (serializer == SupportedSerializers.NATIVE) {
        action.setType(type);
    } else {
        Type action_type = factory.createType();
        action_type.setId(networkBufferType);
        action.setType(action_type);
    }
    // The connection is 'physical' if it uses the ~> notation.
    if (connection.isPhysical()) {
        action.setOrigin(ActionOrigin.PHYSICAL);
        // the minDelay.
        if (connection.getDelay() != null) {
            action.setMinDelay(factory.createValue());
            action.getMinDelay().setTime(connection.getDelay().getTime());
        }
    } else {
        action.setOrigin(ActionOrigin.LOGICAL);
    }
    return action;
}
Also used : Action(org.lflang.lf.Action) InferredType(org.lflang.InferredType) Type(org.lflang.lf.Type) CoordinationType(org.lflang.TargetProperty.CoordinationType) Reactor(org.lflang.lf.Reactor) LfFactory(org.lflang.lf.LfFactory)

Example 3 with Action

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

use of org.lflang.lf.Action 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)

Example 5 with Action

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

the class PythonReactionGenerator method generateCPythonInitializers.

/**
 * Generate necessary Python-specific initialization code for <code>reaction<code> that belongs to reactor
 * <code>decl<code>.
 *
 * @param reaction The reaction to generate Python-specific initialization for.
 * @param decl The reactor to which <code>reaction<code> belongs to.
 * @param pyObjectDescriptor For each port object created, a Python-specific descriptor will be added to this that
 *  then can be used as an argument to <code>Py_BuildValue<code>
 *  (@see <a href="https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue">docs.python.org/3/c-api</a>).
 * @param pyObjects A "," delimited list of expressions that would be (or result in a creation of) a PyObject.
 */
private static String generateCPythonInitializers(Reaction reaction, ReactorDecl decl, List<String> pyObjects, ErrorReporter errorReporter) {
    Set<Action> actionsAsTriggers = new LinkedHashSet<>();
    Reactor reactor = ASTUtils.toDefinition(decl);
    CodeBuilder code = new CodeBuilder();
    // TODO: handle triggers
    for (TriggerRef trigger : ASTUtils.convertToEmptyListIfNull(reaction.getTriggers())) {
        if (trigger instanceof VarRef) {
            VarRef triggerAsVarRef = (VarRef) trigger;
            code.pr(generateVariableToSendPythonReaction(triggerAsVarRef, actionsAsTriggers, decl, pyObjects));
        }
    }
    if (reaction.getTriggers() == null || reaction.getTriggers().size() == 0) {
        // NOTE: this does not include contained outputs.
        for (Input input : reactor.getInputs()) {
            PythonPortGenerator.generateInputVariablesToSendToPythonReaction(pyObjects, input, decl);
        }
    }
    // Next add non-triggering inputs.
    for (VarRef src : ASTUtils.convertToEmptyListIfNull(reaction.getSources())) {
        code.pr(generateVariableToSendPythonReaction(src, actionsAsTriggers, decl, pyObjects));
    }
    // Next, handle effects
    if (reaction.getEffects() != null) {
        for (VarRef effect : reaction.getEffects()) {
            if (effect.getVariable() instanceof Action) {
                // If it has already appeared as trigger, do not redefine it.
                if (!actionsAsTriggers.contains(effect.getVariable())) {
                    PythonPortGenerator.generateActionVariableToSendToPythonReaction(pyObjects, (Action) effect.getVariable(), decl);
                }
            } else {
                if (effect.getVariable() instanceof Output) {
                    PythonPortGenerator.generateOutputVariablesToSendToPythonReaction(pyObjects, (Output) effect.getVariable());
                } else if (effect.getVariable() instanceof Input) {
                    // It is the input of a contained reactor.
                    code.pr(PythonPortGenerator.generateVariablesForSendingToContainedReactors(pyObjects, effect.getContainer(), (Input) effect.getVariable()));
                } else {
                    errorReporter.reportError(reaction, "In generateReaction(): " + effect.getVariable().getName() + " is neither an input nor an output.");
                }
            }
        }
    }
    return code.toString();
}
Also used : LinkedHashSet(java.util.LinkedHashSet) VarRef(org.lflang.lf.VarRef) Action(org.lflang.lf.Action) Input(org.lflang.lf.Input) Output(org.lflang.lf.Output) Reactor(org.lflang.lf.Reactor) TriggerRef(org.lflang.lf.TriggerRef) CodeBuilder(org.lflang.generator.CodeBuilder)

Aggregations

Action (org.lflang.lf.Action)14 Reactor (org.lflang.lf.Reactor)10 VarRef (org.lflang.lf.VarRef)8 Output (org.lflang.lf.Output)6 Input (org.lflang.lf.Input)5 TriggerRef (org.lflang.lf.TriggerRef)4 LinkedHashSet (java.util.LinkedHashSet)3 TimeValue (org.lflang.TimeValue)3 CodeBuilder (org.lflang.generator.CodeBuilder)3 Instantiation (org.lflang.lf.Instantiation)3 LfFactory (org.lflang.lf.LfFactory)3 Port (org.lflang.lf.Port)3 Reaction (org.lflang.lf.Reaction)3 LinkedHashMap (java.util.LinkedHashMap)2 InferredType (org.lflang.InferredType)2 CoordinationType (org.lflang.TargetProperty.CoordinationType)2 PortInstance (org.lflang.generator.PortInstance)2 CUtil.generateWidthVariable (org.lflang.generator.c.CUtil.generateWidthVariable)2 ImportedReactor (org.lflang.lf.ImportedReactor)2 Parameter (org.lflang.lf.Parameter)2