Search in sources :

Example 1 with Reactor

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

the class GeneratorBase method createMainInstantiation.

/**
 * If there is a main or federated reactor, then create a synthetic Instantiation
 * for that top-level reactor and set the field mainDef to refer to it.
 */
private void createMainInstantiation() {
    // Find the main reactor and create an AST node for its instantiation.
    Iterable<EObject> nodes = IteratorExtensions.toIterable(fileConfig.resource.getAllContents());
    for (Reactor reactor : Iterables.filter(nodes, Reactor.class)) {
        if (reactor.isMain() || reactor.isFederated()) {
            // Creating an definition for the main reactor because there isn't one.
            this.mainDef = LfFactory.eINSTANCE.createInstantiation();
            this.mainDef.setName(reactor.getName());
            this.mainDef.setReactorClass(reactor);
        }
    }
}
Also used : EObject(org.eclipse.emf.ecore.EObject) Reactor(org.lflang.lf.Reactor)

Example 2 with Reactor

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

the class GeneratorBase method setReactorsAndInstantiationGraph.

/**
 * Create a new instantiation graph. This is a graph where each node is a Reactor (not a ReactorInstance)
 * and an arc from Reactor A to Reactor B means that B contains an instance of A, constructed with a statement
 * like `a = new A();`  After creating the graph,
 * sort the reactors in topological order and assign them to the reactors class variable.
 * Hence, after this method returns, `this.reactors` will be a list of Reactors such that any
 * reactor is preceded in the list by reactors that it instantiates.
 */
protected void setReactorsAndInstantiationGraph(LFGeneratorContext.Mode mode) {
    // Build the instantiation graph .
    instantiationGraph = new InstantiationGraph(fileConfig.resource, false);
    // Topologically sort the reactors such that all of a reactor's instantiation dependencies occur earlier in
    // the sorted list of reactors. This helps the code generator output code in the correct order.
    // For example if `reactor Foo {bar = new Bar()}` then the definition of `Bar` has to be generated before
    // the definition of `Foo`.
    reactors = instantiationGraph.nodesInTopologicalOrder();
    // list includes even reactors that are not instantiated anywhere.
    if (mainDef == null || Objects.equal(mode, LFGeneratorContext.Mode.LSP_MEDIUM)) {
        Iterable<EObject> nodes = IteratorExtensions.toIterable(fileConfig.resource.getAllContents());
        for (Reactor r : IterableExtensions.filter(nodes, Reactor.class)) {
            if (!reactors.contains(r)) {
                reactors.add(r);
            }
        }
    }
}
Also used : InstantiationGraph(org.lflang.graph.InstantiationGraph) EObject(org.eclipse.emf.ecore.EObject) Reactor(org.lflang.lf.Reactor)

Example 3 with Reactor

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

the class GeneratorBase method analyzeFederates.

/**
 * Analyze the AST to determine whether code is being mapped to
 * single or to multiple target machines. If it is being mapped
 * to multiple machines, then set the {@link #isFederated} field to true,
 * create a FederateInstance for each federate, and record various
 * properties of the federation
 *
 * In addition, for each top-level connection, add top-level reactions to the AST
 * that send and receive messages over the network.
 *
 * This class is target independent, so the target code
 * generator still has quite a bit of work to do.
 * It needs to provide the body of the sending and
 * receiving reactions. It also needs to provide the
 * runtime infrastructure that uses the dependency
 * information between federates. See the C target
 * for a reference implementation.
 */
private void analyzeFederates(LFGeneratorContext context) {
    // Next, if there actually are federates, analyze the topology
    // interconnecting them and replace the connections between them
    // with an action and two reactions.
    Reactor mainReactor = mainDef != null ? ASTUtils.toDefinition(mainDef.getReactorClass()) : null;
    if (mainDef == null || !mainReactor.isFederated()) {
        // The program is not federated.
        // Ensure federates is never empty.
        FederateInstance federateInstance = new FederateInstance(null, 0, 0, this, errorReporter);
        federates.add(federateInstance);
        federateByID.put(0, federateInstance);
    } else {
        // The Lingua Franca program is federated
        isFederated = true;
        // If the "--rti" flag is given to the compiler, use the argument from the flag.
        if (context.getArgs().containsKey("rti")) {
            setFederationRTIProperties(context);
        } else if (mainReactor.getHost() != null) {
            // If not specified, this defaults to 'localhost'
            if (mainReactor.getHost().getAddr() != null) {
                federationRTIProperties.put("host", mainReactor.getHost().getAddr());
            }
            // If not specified, this defaults to 14045
            if (mainReactor.getHost().getPort() != 0) {
                federationRTIProperties.put("port", mainReactor.getHost().getPort());
            }
            // Get the user information, if specified.
            if (mainReactor.getHost().getUser() != null) {
                federationRTIProperties.put("user", mainReactor.getHost().getUser());
            }
        }
        // Since federates are always within the main (federated) reactor,
        // create a list containing just that one containing instantiation.
        // This will be used to look up parameter values.
        List<Instantiation> mainReactorContext = new ArrayList<>();
        mainReactorContext.add(mainDef);
        // Create a FederateInstance for each top-level reactor.
        for (Instantiation instantiation : ASTUtils.allInstantiations(mainReactor)) {
            int bankWidth = ASTUtils.width(instantiation.getWidthSpec(), mainReactorContext);
            if (bankWidth < 0) {
                errorReporter.reportError(instantiation, "Cannot determine bank width! Assuming width of 1.");
                // Continue with a bank width of 1.
                bankWidth = 1;
            }
            // Create one federate instance for each instance in a bank of reactors.
            List<FederateInstance> federateInstances = new ArrayList<>(bankWidth);
            for (int i = 0; i < bankWidth; i++) {
                // Assign an integer ID to the federate.
                int federateID = federates.size();
                FederateInstance federateInstance = new FederateInstance(instantiation, federateID, i, this, errorReporter);
                federateInstance.bankIndex = i;
                federates.add(federateInstance);
                federateInstances.add(federateInstance);
                federateByID.put(federateID, federateInstance);
                if (instantiation.getHost() != null) {
                    federateInstance.host = instantiation.getHost().getAddr();
                    // The following could be 0.
                    federateInstance.port = instantiation.getHost().getPort();
                    // The following could be null.
                    federateInstance.user = instantiation.getHost().getUser();
                    /* FIXME: The at keyword should support a directory component.
                         * federateInstance.dir = instantiation.getHost().dir
                         */
                    if (federateInstance.host != null && federateInstance.host != "localhost" && federateInstance.host != "0.0.0.0") {
                        federateInstance.isRemote = true;
                    }
                }
            }
            if (federatesByInstantiation == null) {
                federatesByInstantiation = new LinkedHashMap<Instantiation, List<FederateInstance>>();
            }
            federatesByInstantiation.put(instantiation, federateInstances);
        }
        // In a federated execution, we need keepalive to be true,
        // otherwise a federate could exit simply because it hasn't received
        // any messages.
        targetConfig.keepalive = true;
        // Analyze the connection topology of federates.
        // First, find all the connections between federates.
        // For each connection between federates, replace it in the
        // AST with an action (which inherits the delay) and two reactions.
        // The action will be physical for physical connections and logical
        // for logical connections.
        replaceFederateConnectionsWithActions();
        // Remove the connections at the top level
        mainReactor.getConnections().clear();
    }
}
Also used : FederateInstance(org.lflang.federated.FederateInstance) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) Reactor(org.lflang.lf.Reactor) Instantiation(org.lflang.lf.Instantiation)

Example 4 with Reactor

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

the class GeneratorBase method replaceFederateConnectionsWithActions.

/**
 * Replace connections between federates in the AST with actions that
 * handle sending and receiving data.
 */
private void replaceFederateConnectionsWithActions() {
    Reactor mainReactor = mainDef != null ? ASTUtils.toDefinition(mainDef.getReactorClass()) : null;
    // Each connection in the AST may represent more than one connection between
    // federate instances because of banks and multiports. We need to generate communication
    // for each of these. To do this, we create a ReactorInstance so that we don't have
    // to duplicate the rather complicated logic in that class. We specify a depth of 1,
    // so it only creates the reactors immediately within the top level, not reactors
    // that those contain.
    ReactorInstance mainInstance = new ReactorInstance(mainReactor, errorReporter, 1);
    for (ReactorInstance child : mainInstance.children) {
        for (PortInstance output : child.outputs) {
            replaceConnectionFromFederate(output, child, mainInstance);
        }
    }
}
Also used : Reactor(org.lflang.lf.Reactor)

Example 5 with Reactor

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

the class GeneratorBase method doGenerate.

/**
 * Generate code from the Lingua Franca model contained by the specified resource.
 *
 * This is the main entry point for code generation. This base class finds all
 * reactor class definitions, including any reactors defined in imported .lf files
 * (except any main reactors in those imported files), and adds them to the
 * {@link #GeneratorBase.reactors reactors} list. If errors occur during
 * generation, then a subsequent call to errorsOccurred() will return true.
 * @param resource The resource containing the source code.
 * @param context Context relating to invocation of the code generator.
 * In stand alone mode, this object is also used to relay CLI arguments.
 */
public void doGenerate(Resource resource, LFGeneratorContext context) {
    GeneratorUtils.setTargetConfig(context, GeneratorUtils.findTarget(fileConfig.resource), targetConfig, errorReporter);
    cleanIfNeeded(context);
    printInfo(context.getMode());
    // Markers mark problems in the Eclipse IDE when running in integrated mode.
    if (errorReporter instanceof EclipseErrorReporter) {
        ((EclipseErrorReporter) errorReporter).clearMarkers();
    }
    ASTUtils.setMainName(fileConfig.resource, fileConfig.name);
    createMainInstantiation();
    // Check if there are any conflicting main reactors elsewhere in the package.
    if (Objects.equal(context.getMode(), LFGeneratorContext.Mode.STANDALONE) && mainDef != null) {
        for (String conflict : new MainConflictChecker(fileConfig).conflicts) {
            errorReporter.reportError(this.mainDef.getReactorClass(), "Conflicting main reactor in " + conflict);
        }
    }
    // Configure the command factory
    commandFactory.setVerbose();
    if (Objects.equal(context.getMode(), LFGeneratorContext.Mode.STANDALONE) && context.getArgs().containsKey("quiet")) {
        commandFactory.setQuiet();
    }
    // This must be done before desugaring delays below.
    analyzeFederates(context);
    // Process target files. Copy each of them into the src-gen dir.
    // FIXME: Should we do this here? This doesn't make sense for federates the way it is
    // done here.
    copyUserFiles(this.targetConfig, this.fileConfig);
    // Collect reactors and create an instantiation graph.
    // These are needed to figure out which resources we need
    // to validate, which happens in setResources().
    setReactorsAndInstantiationGraph(context.getMode());
    GeneratorUtils.validate(context, fileConfig, instantiationGraph, errorReporter);
    List<Resource> allResources = GeneratorUtils.getResources(reactors);
    resources.addAll(// FIXME: This filter reproduces the behavior of the method it replaces. But why must it be so complicated? Why are we worried about weird corner cases like this?
    allResources.stream().filter(it -> !Objects.equal(it, fileConfig.resource) || mainDef != null && it == mainDef.getReactorClass().eResource()).map(it -> GeneratorUtils.getLFResource(it, fileConfig.getSrcGenBasePath(), context, errorReporter)).collect(Collectors.toList()));
    GeneratorUtils.accommodatePhysicalActionsIfPresent(allResources, getTarget().setsKeepAliveOptionAutomatically(), targetConfig, errorReporter);
    // FIXME: Should the GeneratorBase pull in `files` from imported
    // resources?
    // Reroute connections that have delays associated with them via
    // generated delay reactors.
    transformDelays();
    // Transform connections that reside in mutually exclusive modes and are otherwise conflicting
    // This should be done before creating the instantiation graph
    transformConflictingConnectionsInModalReactors();
    // Invoke these functions a second time because transformations
    // may have introduced new reactors!
    setReactorsAndInstantiationGraph(context.getMode());
    // Check for existence and support of modes
    hasModalReactors = IterableExtensions.exists(reactors, it -> !it.getModes().isEmpty());
    checkModalReactorSupport(false);
    enableSupportForSerializationIfApplicable(context.getCancelIndicator());
}
Also used : SupportedSerializers(org.lflang.federated.serialization.SupportedSerializers) Delay(org.lflang.lf.Delay) FedASTUtils(org.lflang.federated.FedASTUtils) Action(org.lflang.lf.Action) InferredType(org.lflang.InferredType) Matcher(java.util.regex.Matcher) Map(java.util.Map) Instantiation(org.lflang.lf.Instantiation) Objects(com.google.common.base.Objects) Path(java.nio.file.Path) Connection(org.lflang.lf.Connection) MainConflictChecker(org.lflang.MainConflictChecker) TargetConfig(org.lflang.TargetConfig) Collection(java.util.Collection) Set(java.util.Set) EObject(org.eclipse.emf.ecore.EObject) Collectors(java.util.stream.Collectors) Parameter(org.lflang.lf.Parameter) List(java.util.List) Value(org.lflang.lf.Value) CollectionLiterals(org.eclipse.xtext.xbase.lib.CollectionLiterals) Target(org.lflang.Target) Resource(org.eclipse.emf.ecore.resource.Resource) Pattern(java.util.regex.Pattern) Pair(org.eclipse.xtext.xbase.lib.Pair) FileConfig(org.lflang.FileConfig) ASTUtils(org.lflang.ASTUtils) FederateInstance(org.lflang.federated.FederateInstance) Iterables(com.google.common.collect.Iterables) LfFactory(org.lflang.lf.LfFactory) ErrorReporter(org.lflang.ErrorReporter) AbstractLFValidator(org.lflang.validation.AbstractLFValidator) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) CancelIndicator(org.eclipse.xtext.util.CancelIndicator) Reaction(org.lflang.lf.Reaction) TimeUnit(org.lflang.TimeUnit) TimeValue(org.lflang.TimeValue) LinkedHashSet(java.util.LinkedHashSet) IMarker(org.eclipse.core.resources.IMarker) InstantiationGraph(org.lflang.graph.InstantiationGraph) CoordinationType(org.lflang.TargetProperty.CoordinationType) Model(org.lflang.lf.Model) IOException(java.io.IOException) Time(org.lflang.lf.Time) File(java.io.File) IteratorExtensions(org.eclipse.xtext.xbase.lib.IteratorExtensions) IterableExtensions(org.eclipse.xtext.xbase.lib.IterableExtensions) Paths(java.nio.file.Paths) Reactor(org.lflang.lf.Reactor) VarRef(org.lflang.lf.VarRef) MainConflictChecker(org.lflang.MainConflictChecker) Resource(org.eclipse.emf.ecore.resource.Resource)

Aggregations

Reactor (org.lflang.lf.Reactor)54 ImportedReactor (org.lflang.lf.ImportedReactor)19 VarRef (org.lflang.lf.VarRef)19 ArrayList (java.util.ArrayList)16 Action (org.lflang.lf.Action)15 Instantiation (org.lflang.lf.Instantiation)14 Reaction (org.lflang.lf.Reaction)13 EObject (org.eclipse.emf.ecore.EObject)12 LinkedHashSet (java.util.LinkedHashSet)11 ReactorInstance (org.lflang.generator.ReactorInstance)9 Connection (org.lflang.lf.Connection)9 Input (org.lflang.lf.Input)9 LfFactory (org.lflang.lf.LfFactory)9 Output (org.lflang.lf.Output)9 Variable (org.lflang.lf.Variable)9 HashSet (java.util.HashSet)8 List (java.util.List)8 PortInstance (org.lflang.generator.PortInstance)8 Model (org.lflang.lf.Model)7 Port (org.lflang.lf.Port)7