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);
}
}
}
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);
}
}
}
}
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();
}
}
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);
}
}
}
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());
}
Aggregations