Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support modal models in Python #1009

Merged
merged 41 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4f8b932
First step at supporting modal models in Python
edwardalee Mar 8, 2022
e3f84ef
Updated reactor-c
edwardalee Mar 8, 2022
093450c
Updated comments
edwardalee Mar 8, 2022
e5720f7
Moved code related to modal models into separate files
Soroosh129 Mar 8, 2022
1f5d82d
Updated pointer to reactor-c-py
Soroosh129 Mar 8, 2022
0d7a758
Updated pointer to reactor-c-py
Soroosh129 Mar 8, 2022
efc38f8
Updated reactor-c
edwardalee Mar 9, 2022
89aa41f
Bug fix
Soroosh129 Mar 9, 2022
9e9bcba
More bug fixes
Soroosh129 Mar 9, 2022
b7d2d2b
Updated pointer to reactor-c
Soroosh129 Mar 9, 2022
d2e7506
Updated pointer to reactor-c-py
Soroosh129 Mar 9, 2022
a294ab4
Moved MultipleContained test to the right directory matching the C te…
edwardalee Mar 9, 2022
13b748a
Updated tests to verify that both inputs arrive
edwardalee Mar 9, 2022
e7741d3
Converted ConvertCaseTest to Python.
edwardalee Mar 9, 2022
47b7e2a
Merge remote-tracking branch 'origin/master' into python-modal-models
edwardalee Mar 9, 2022
d40d428
Fixed test to remove reaction self-loop cycle that was masked.
edwardalee Mar 10, 2022
b1c6390
Merge remote-tracking branch 'origin/master' into python-modal-models
edwardalee Mar 10, 2022
f1ea738
Update reactor-c to point to branch
edwardalee Mar 10, 2022
e64abe2
Added another modal model test to Python
edwardalee Mar 10, 2022
5aa8ac6
Added one more passing test and several unported tests that will fail
edwardalee Mar 10, 2022
6978981
Finished porting tests. Three tests fail because of missing features
Soroosh129 Mar 10, 2022
9d4d243
resolve conflicts
housengw Mar 11, 2022
e68a4c7
bug fix
housengw Mar 11, 2022
08486be
bug fix
housengw Mar 11, 2022
7639be7
Align reactor-c
edwardalee Mar 11, 2022
5728000
Retrained test to get the right data types for training data
edwardalee Mar 12, 2022
a02a432
Print tags
edwardalee Mar 12, 2022
3cf7da7
Merge remote-tracking branch 'origin/master' into python-modal-models
lhstrh Mar 12, 2022
2d278c4
Added AST transformation for startup triggers in modes (not behaving …
Soroosh129 Mar 12, 2022
a0fac18
Cleaned up the code a bit
Soroosh129 Mar 12, 2022
924e9e8
Pass the appropriate arguments to _lf_handle_mode_changes. Cleaned up…
Soroosh129 Mar 12, 2022
a38bab6
Updated test to test startup triggers
Soroosh129 Mar 12, 2022
3bf7a1d
Ported the startup test to Python
Soroosh129 Mar 12, 2022
7a890b0
Revert seemingly faulty strategy of replacing startup triggers with t…
Soroosh129 Mar 13, 2022
b2fc05c
Added AST transformation for Python that adds startup reactions to al…
Soroosh129 Mar 13, 2022
76db8db
Only generate startup reactions if there are state vars
Soroosh129 Mar 13, 2022
aad8f5f
First working prototype for startup reactions (only for reset transit…
Soroosh129 Mar 14, 2022
348692f
Updated test and reactor-c
Soroosh129 Mar 14, 2022
2e46b40
Updated test and reactor-c
Soroosh129 Mar 14, 2022
db6bb67
Also handle startup reactions for history transitions
Soroosh129 Mar 14, 2022
b270a65
resolve conflicts
housengw Mar 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion org.lflang/src/lib/py/reactor-c-py
60 changes: 51 additions & 9 deletions org.lflang/src/org/lflang/generator/GeneratorBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
Expand All @@ -40,12 +39,10 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;

import org.eclipse.core.resources.IMarker;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
Expand All @@ -70,6 +67,7 @@
import org.lflang.lf.Delay;
import org.lflang.lf.Instantiation;
import org.lflang.lf.LfFactory;
import org.lflang.lf.Mode;
import org.lflang.lf.Model;
import org.lflang.lf.Parameter;
import org.lflang.lf.Reaction;
Expand All @@ -79,6 +77,9 @@
import org.lflang.lf.VarRef;
import org.lflang.validation.AbstractLFValidator;

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;

/**
* Generator base class for specifying core functionality
* that all code generators should have.
Expand Down Expand Up @@ -380,6 +381,7 @@ public void doGenerate(Resource resource, LFGeneratorContext context) {
// Check for existence and support of modes
hasModalReactors = IterableExtensions.exists(reactors, it -> !it.getModes().isEmpty());
checkModalReactorSupport(false);
generateStartupReactionsInModesIfNeeded();

enableSupportForSerializationIfApplicable(context.getCancelIndicator());
}
Expand Down Expand Up @@ -599,20 +601,60 @@ private void transformConflictingConnectionsInModalReactors() {
for (LFResource r : resources) {
var transform = ASTUtils.findConflictingConnectionsInModalReactors(r.eResource);
if (!transform.isEmpty()) {
transformConflictingConnectionsInModalReactors(transform);
var factory = LfFactory.eINSTANCE;
for (Connection connection : transform) {
// Currently only simple transformations are supported
if (connection.isPhysical() || connection.getDelay() != null || connection.isIterated() ||
connection.getLeftPorts().size() > 1 || connection.getRightPorts().size() > 1
) {
errorReporter.reportError(connection, "Cannot transform connection in modal reactor. Connection uses currently not supported features.");
} else {
var reaction = factory.createReaction();
((Mode)connection.eContainer()).getReactions().add(reaction);

var sourceRef = connection.getLeftPorts().get(0);
var destRef = connection.getRightPorts().get(0);
reaction.getTriggers().add(sourceRef);
reaction.getEffects().add(destRef);

var code = factory.createCode();
var source = (sourceRef.getContainer() != null ?
sourceRef.getContainer().getName() + "." : "") + sourceRef.getVariable().getName();
var dest = (destRef.getContainer() != null ?
destRef.getContainer().getName() + "." : "") + destRef.getVariable().getName();
code.setBody(getConflictingConnectionsInModalReactorsBody(source, dest));
reaction.setCode(code);

EcoreUtil.remove(connection);
}
}
}
}
}
/**
* Transforms connections into forwarding reactions iff the connections have the same destination as other
* connections or reaction in mutually exclusive modes.
* Return target code for forwarding reactions iff the connections have the
* same destination as other connections or reaction in mutually exclusive modes.
*
* This methods needs to be overridden in target specific code generators that support modal reactors.
* This methods needs to be overridden in target specific code generators that
* support modal reactors.
*/
protected void transformConflictingConnectionsInModalReactors(Collection<Connection> transform) {
protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) {
errorReporter.reportError("The currently selected code generation " +
"is missing an implementation for conflicting " +
"transforming connections in modal reactors.");
return "MODAL MODELS NOT SUPPORTED";
}

/**
* Generate startup reactions in modes.
*
* Startup reactions (reactions that have startup in their list of triggers)
* will be triggered when the mode is entered for the first time and on each subsequent
* reset transition to that mode. These reactions could be useful for targets
* to perform cleanups, for example, to reset state variables.
*/
protected void generateStartupReactionsInModesIfNeeded() {
// Do nothing
}

/**
Expand Down
114 changes: 49 additions & 65 deletions org.lflang/src/org/lflang/generator/c/CGenerator.xtend
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ package org.lflang.generator.c;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
Expand All @@ -38,7 +37,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.util.CancelIndicator;
import org.lflang.ErrorReporter;
import org.lflang.FileConfig;
Expand All @@ -49,7 +47,6 @@ import org.lflang.TargetConfig;
import org.lflang.TargetProperty;
import org.lflang.TargetProperty.ClockSyncMode;
import org.lflang.TargetProperty.CoordinationType;
import org.lflang.TargetProperty.LogLevel;
import org.lflang.TimeValue;
import org.lflang.federated.CGeneratorExtension;
import org.lflang.federated.FedFileConfig;
Expand All @@ -67,7 +64,6 @@ import org.lflang.generator.LFGeneratorContext;
import org.lflang.generator.PortInstance;
import org.lflang.generator.ReactionInstance;
import org.lflang.generator.ReactorInstance;
import org.lflang.generator.SendRange;
import org.lflang.generator.SubContext;
import org.lflang.generator.TriggerInstance;
import org.lflang.generator.c.CActionGenerator;
Expand All @@ -83,11 +79,9 @@ import org.lflang.generator.c.CTriggerObjectsGenerator;
import org.lflang.generator.c.InteractingContainedReactors;
import org.lflang.lf.Action;
import org.lflang.lf.ActionOrigin;
import org.lflang.lf.Connection;
import org.lflang.lf.Delay;
import org.lflang.lf.Input;
import org.lflang.lf.Instantiation;
import org.lflang.lf.LfFactory;
import org.lflang.lf.Mode;
import org.lflang.lf.Model;
import org.lflang.lf.Output;
Expand All @@ -98,10 +92,8 @@ import org.lflang.lf.ReactorDecl;
import org.lflang.lf.VarRef;
import org.lflang.lf.Variable;
import org.lflang.util.FileUtil;

import static extension org.lflang.ASTUtils.*;
import static extension org.lflang.ASTUtils.*;
import static org.lflang.generator.c.CMixedRadixGenerator.*;

/**
* Generator for C target. This class generates C code defining each reactor
Expand Down Expand Up @@ -542,7 +534,8 @@ class CGenerator extends GeneratorBase {
// Copy the header files
copyTargetHeaderFile()

generatePreamble()
generateDirectives()
generateTopLevelPreambles();
code.pr(CMainGenerator.generateCode());
// Generate code for each reactor.
generateReactorDefinitions();
Expand Down Expand Up @@ -610,22 +603,7 @@ class CGenerator extends GeneratorBase {
int _lf_timer_triggers_size = 0;
''')
}

// If there are startup reactions, store them in an array.
if (startupReactionCount > 0) {
code.pr('''
// Array of pointers to reactions to be scheduled in _lf_trigger_startup_reactions().
reaction_t* _lf_startup_reactions[«startupReactionCount»];
int _lf_startup_reactions_size = «startupReactionCount»;
''')
} else {
code.pr('''
// Array of pointers to reactions to be scheduled in _lf_trigger_startup_reactions().
reaction_t** _lf_startup_reactions = NULL;
int _lf_startup_reactions_size = 0;
''')
}


// If there are shutdown reactions, create a table of triggers.
if (shutdownReactionCount > 0) {
code.pr('''
Expand Down Expand Up @@ -762,7 +740,14 @@ class CGenerator extends GeneratorBase {
// Generate mode change detection
code.pr('''
void _lf_handle_mode_changes() {
_lf_process_mode_changes(_lf_modal_reactor_states, _lf_modal_reactor_states_size, «modalStateResetCount > 0 ? "_lf_modal_state_reset" : "NULL"», «modalStateResetCount > 0 ? "_lf_modal_state_reset_size" : 0»);
_lf_process_mode_changes(
_lf_modal_reactor_states,
_lf_modal_reactor_states_size,
«modalStateResetCount > 0 ? "_lf_modal_state_reset" : "NULL"»,
«modalStateResetCount > 0 ? "_lf_modal_state_reset_size" : 0»,
_lf_timer_triggers,
_lf_timer_triggers_size
);
}
''')
}
Expand Down Expand Up @@ -908,35 +893,12 @@ class CGenerator extends GeneratorBase {
super.checkModalReactorSupport(!isFederated);
}

override transformConflictingConnectionsInModalReactors(Collection<Connection> transform) {
val factory = LfFactory.eINSTANCE
for (connection : transform) {
// Currently only simple transformations are supported
if (connection.physical || connection.delay !== null || connection.iterated ||
connection.leftPorts.size > 1 || connection.rightPorts.size > 1
) {
errorReporter.reportError(connection, "Cannot transform connection in modal reactor. Connection uses currently not supported features.");
} else {
var reaction = factory.createReaction();
(connection.eContainer() as Mode).getReactions().add(reaction);

var sourceRef = connection.getLeftPorts().head
var destRef = connection.getRightPorts().head
reaction.getTriggers().add(sourceRef);
reaction.getEffects().add(destRef);

var code = factory.createCode();
var source = (sourceRef.container !== null ? sourceRef.container.name + "." : "") + sourceRef.variable.name
var dest = (destRef.container !== null ? destRef.container.name + "." : "") + destRef.variable.name
code.setBody('''
// Generated forwarding reaction for connections with the same destination but located in mutually exclusive modes.
SET(«dest», «source»->value);
''');
reaction.setCode(code);

EcoreUtil.remove(connection);
}
}
override protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) {
return String.join("\n",
"// Generated forwarding reaction for connections with the same destination",
"// but located in mutually exclusive modes.",
"SET("+dest+", "+source+"->value);"
);
}

/**
Expand Down Expand Up @@ -979,6 +941,14 @@ class CGenerator extends GeneratorBase {
«IF startupReactionCount > 0»
for (int i = 0; i < _lf_startup_reactions_size; i++) {
if (_lf_startup_reactions[i] != NULL) {
#ifdef MODAL_REACTORS
if (!_lf_mode_is_active(_lf_startup_reactions[i]->mode)) {
// Mode is not active. Remember to trigger startup when the mode
// becomes active.
_lf_startup_reactions[i]->mode->should_trigger_startup = true;
continue;
}
#endif
_lf_trigger_reaction(_lf_startup_reactions[i], -1);
}
}
Expand Down Expand Up @@ -1040,6 +1010,11 @@ class CGenerator extends GeneratorBase {
_lf_is_present_fields_abbreviated_size = 0;
''')
}

code.pr('''
_lf_startup_reactions = (reaction_t**)calloc(«startupReactionCount», sizeof(reaction_t*));
_lf_startup_reactions_size = «startupReactionCount»;
''')

// Allocate the memory for triggers used in federated execution
code.pr(CGeneratorExtension.allocateTriggersForFederate(federate, this, startTimeStepIsPresentCount));
Expand Down Expand Up @@ -2714,7 +2689,7 @@ class CGenerator extends GeneratorBase {
}
} else { // Otherwise, only reactions outside modes must be linked and the mode state itself gets a parent relation
initializeTriggerObjects.pr('''
«nameOfSelfStruct»->_lf__mode_state.parent_mode = «parentModeRef»;
((self_base_t*)«nameOfSelfStruct»)->_lf__mode_state.parent_mode = «parentModeRef»;
''')
for (reaction : instance.reactions.filter[it.getMode(true) === null]) {
initializeTriggerObjects.pr('''
Expand All @@ -2727,7 +2702,7 @@ class CGenerator extends GeneratorBase {
if (!instance.modes.empty) {
initializeTriggerObjects.pr('''
// Register for transition handling
_lf_modal_reactor_states[«modalReactorCount++»] = &«nameOfSelfStruct»->_lf__mode_state;
_lf_modal_reactor_states[«modalReactorCount++»] = &((self_base_t*)«nameOfSelfStruct»)->_lf__mode_state;
''')
}
}
Expand Down Expand Up @@ -2882,17 +2857,14 @@ class CGenerator extends GeneratorBase {
// // Protected methods.

// Perform set up that does not generate code
protected def setUpParameters(LFGeneratorContext context) {
protected def void setUpParameters(LFGeneratorContext context) {
accommodatePhysicalActionsIfPresent()
targetConfig.compileDefinitions.put("LOG_LEVEL", targetConfig.logLevel.ordinal.toString);
targetConfig.compileAdditionalSources.add("ctarget.c");
targetConfig.compileAdditionalSources.add("core" + File.separator + "mixed_radix.c");
setCSpecificDefaults(context)
parseTargetParameters()

// Create the main reactor instance if there is a main reactor.
createMainReactorInstance();

// If there are federates, copy the required files for that.
// Also, create the RTI C file and the launcher script.
if (isFederated) {
Expand All @@ -2906,10 +2878,15 @@ class CGenerator extends GeneratorBase {
CUtil.minThreadsToHandleInputPorts(federates).toString
);
}
if (hasModalReactors) {
// So that each separate compile knows about modal reactors, do this:
targetConfig.compileDefinitions.put("MODAL_REACTORS", "");
}
if (targetConfig.threading) {
pickScheduler();
}
pickCompilePlatform();
parseTargetParameters();
}

/**
Expand Down Expand Up @@ -3160,7 +3137,9 @@ class CGenerator extends GeneratorBase {
* Generate code that needs to appear at the top of the generated
* C file, such as #define and #include statements.
*/
def void generatePreamble() {
def void generateDirectives() {
code.prComment("Code generated by the Lingua Franca compiler from:")
code.prComment("file:/" + FileUtil.toUnixString(fileConfig.srcFile))
code.pr(CPreambleGenerator.generateDefineDirectives(
targetConfig,
federates.size,
Expand All @@ -3176,9 +3155,14 @@ class CGenerator extends GeneratorBase {
))

// Do this after the above includes so that the preamble can
// call built-in functions.
code.prComment("Code generated by the Lingua Franca compiler from:")
code.prComment("file:/" + FileUtil.toUnixString(fileConfig.srcFile))
// call built-in functions

}

/**
* Generate top-level preamble code.
*/
protected def void generateTopLevelPreambles() {
if (this.mainDef !== null) {
val mainModel = this.mainDef.reactorClass.toDefinition.eContainer as Model
for (p : mainModel.preambles) {
Expand Down
Loading