From 4f8b93243879d4f05185be759f57ec0fcee94e07 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 10:48:33 -0800 Subject: [PATCH 01/36] First step at supporting modal models in Python --- org.lflang/src/lib/c/reactor-c | 2 +- org.lflang/src/lib/py/reactor-c-py | 2 +- .../org/lflang/generator/GeneratorBase.java | 47 ++++-- .../org/lflang/generator/c/CGenerator.xtend | 91 +++++------ .../generator/c/CReactionGenerator.java | 6 +- .../generator/python/PythonGenerator.java | 36 ++--- .../python/PythonReactionGenerator.java | 39 ++--- .../src/modal_models/ConvertCaseTest.lf | 144 ++++++++++++++++++ test/Python/src/modal_models/Count3Modes.lf | 61 ++++++++ .../src/modal_models/util/TraceTesting.lf | 83 ++++++++++ 10 files changed, 403 insertions(+), 108 deletions(-) create mode 100644 test/Python/src/modal_models/ConvertCaseTest.lf create mode 100644 test/Python/src/modal_models/Count3Modes.lf create mode 100644 test/Python/src/modal_models/util/TraceTesting.lf diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 3d7c4f10f4..c649f166b5 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 3d7c4f10f4a93379b5a550639ab3ea836a38835f +Subproject commit c649f166b52ecc325d7915f2456f4bde77084cc2 diff --git a/org.lflang/src/lib/py/reactor-c-py b/org.lflang/src/lib/py/reactor-c-py index 9e78ad3596..58558873f5 160000 --- a/org.lflang/src/lib/py/reactor-c-py +++ b/org.lflang/src/lib/py/reactor-c-py @@ -1 +1 @@ -Subproject commit 9e78ad35961e559d88abfaf950f76a5f2d4323e7 +Subproject commit 58558873f592216b9904bdca03c4a977b0340ed1 diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 8f82df6746..5261445251 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -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; @@ -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; @@ -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; @@ -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. @@ -594,20 +595,48 @@ 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 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"; } /** diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 56c7ba5fc2..f0b5f115ed 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -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 @@ -39,12 +38,11 @@ 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.ASTUtils import org.lflang.ErrorReporter import org.lflang.FileConfig import org.lflang.InferredType -import org.lflang.ASTUtils import org.lflang.Target import org.lflang.TargetConfig import org.lflang.TargetProperty @@ -61,8 +59,8 @@ import org.lflang.federated.serialization.SupportedSerializers import org.lflang.generator.CodeBuilder import org.lflang.generator.GeneratorBase import org.lflang.generator.GeneratorResult -import org.lflang.generator.IntegratedBuilder import org.lflang.generator.GeneratorUtils +import org.lflang.generator.IntegratedBuilder import org.lflang.generator.LFGeneratorContext import org.lflang.generator.ModeInstance import org.lflang.generator.PortInstance @@ -74,11 +72,9 @@ import org.lflang.generator.SubContext import org.lflang.generator.TriggerInstance 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 @@ -93,7 +89,6 @@ import org.lflang.lf.Variable import org.lflang.util.FileUtil import org.lflang.util.XtendUtil -import static extension org.lflang.ASTUtils.* import static extension org.lflang.ASTUtils.* /** @@ -949,35 +944,12 @@ class CGenerator extends GeneratorBase { super.checkModalReactorSupport(!isFederated); } - override transformConflictingConnectionsInModalReactors(Collection 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);" + ); } /** @@ -2098,17 +2070,17 @@ class CGenerator extends GeneratorBase { // Reactor's mode instances and its state. body.pr(''' reactor_mode_t _lf__modes[«reactor.modes.size»]; - reactor_mode_state_t _lf__mode_state; ''') // Initialize the mode instances constructorCode.pr(''' // Initialize modes + self_base_t* _lf_self_base = (self_base_t*)self; ''') for (modeAndIdx : reactor.allModes.indexed) { val mode = modeAndIdx.value constructorCode.pr(mode, ''' - self->_lf__modes[«modeAndIdx.key»].state = &self->_lf__mode_state; + self->_lf__modes[«modeAndIdx.key»].state = &_lf_self_base->_lf__mode_state; self->_lf__modes[«modeAndIdx.key»].name = "«mode.name»"; self->_lf__modes[«modeAndIdx.key»].deactivation_time = 0; ''') @@ -2117,11 +2089,11 @@ class CGenerator extends GeneratorBase { // Initialize mode state with initial mode active upon start constructorCode.pr(''' // Initialize mode state - self->_lf__mode_state.parent_mode = NULL; - self->_lf__mode_state.initial_mode = &self->_lf__modes[«reactor.modes.indexed.findFirst[it.value.initial].key»]; - self->_lf__mode_state.active_mode = self->_lf__mode_state.initial_mode; - self->_lf__mode_state.next_mode = NULL; - self->_lf__mode_state.mode_change = 0; + _lf_self_base->_lf__mode_state.parent_mode = NULL; + _lf_self_base->_lf__mode_state.initial_mode = &self->_lf__modes[«reactor.modes.indexed.findFirst[it.value.initial].key»]; + _lf_self_base->_lf__mode_state.active_mode = _lf_self_base->_lf__mode_state.initial_mode; + _lf_self_base->_lf__mode_state.next_mode = NULL; + _lf_self_base->_lf__mode_state.mode_change = no_transition; ''') } @@ -3443,7 +3415,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(''' @@ -3456,7 +3428,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; ''') } } @@ -4144,7 +4116,10 @@ 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 generatePreamble() { + def void generatePreamble() { + code.prComment("Code generated by the Lingua Franca compiler from:") + code.prComment("file:/" + FileUtil.toUnixString(fileConfig.srcFile)) + code.pr(this.defineLogLevel) if (isFederated) { @@ -4155,6 +4130,9 @@ class CGenerator extends GeneratorBase { } if (hasModalReactors) { + // So that each separate compile knows about modal reactors, do this: + targetConfig.compileDefinitions.put("MODAL_REACTORS", ""); + // For readability, put this in the generated code. code.pr(''' #define MODAL_REACTORS ''') @@ -4174,14 +4152,7 @@ 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)) - if (this.mainDef !== null) { - val mainModel = this.mainDef.reactorClass.toDefinition.eContainer as Model - for (p : mainModel.preambles) { - code.pr(p.code.toText) - } - } + generateTopLevelPreambles(); parseTargetParameters() @@ -4189,6 +4160,18 @@ class CGenerator extends GeneratorBase { fileConfig.getSrcGenPath.toFile.mkdirs } + /** + * 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) { + code.pr(p.code.toText) + } + } + } + /** * Print the main function. */ diff --git a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java index 135a855c37..de263bb097 100644 --- a/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CReactionGenerator.java @@ -11,7 +11,6 @@ import org.lflang.ASTUtils; import org.lflang.ErrorReporter; import org.lflang.InferredType; -import org.lflang.ASTUtils; import org.lflang.generator.CodeBuilder; import org.lflang.generator.ModeInstance.ModeTransitionType; import org.lflang.lf.Action; @@ -162,8 +161,9 @@ public static String generateInitializationForReaction(String body, if (idx >= 0) { reactionInitialization.pr( "reactor_mode_t* " + name + " = &self->_lf__modes[" + idx + "];\n" - + "char _lf_" + name + "_change_type = " - + (ModeTransitionType.getModeTransitionType(effect) == ModeTransitionType.HISTORY ? 2 : 1) + + "lf_mode_change_type_t _lf_" + name + "_change_type = " + + (ModeTransitionType.getModeTransitionType(effect) == ModeTransitionType.HISTORY ? + "history_transition" : "reset_transition") + ";" ); } else { diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index 58c4443d96..a1ac4daaff 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -48,7 +48,6 @@ import org.lflang.ErrorReporter; import org.lflang.FileConfig; import org.lflang.InferredType; -import org.lflang.ASTUtils; import org.lflang.Target; import org.lflang.federated.FedFileConfig; import org.lflang.federated.FederateInstance; @@ -65,7 +64,6 @@ import org.lflang.generator.SubContext; import org.lflang.generator.TargetTypes; import org.lflang.generator.c.CGenerator; -import org.lflang.generator.c.CPreambleGenerator; import org.lflang.generator.c.CUtil; import org.lflang.lf.Action; import org.lflang.lf.Delay; @@ -342,15 +340,11 @@ public void pythonCompileCode(LFGeneratorContext context) { } /** - * Generate top-level preambles and #include of pqueue.c and either reactor.c or reactor_threaded.c - * depending on whether threads are specified in target directive. - * As a side effect, this populates the runCommand and compileCommand - * private variables if such commands are specified in the target directive. - * - * TODO: This function returns a boolean because xtend-generated parent function in CGenerator returns boolean + * Override generate top-level preambles, but put the preambles in the + * .py file rather than the C file. */ @Override - public boolean generatePreamble() { + protected void generateTopLevelPreambles() { Set models = new LinkedHashSet<>(); for (Reactor r : ASTUtils.convertToEmptyListIfNull(reactors)) { // The following assumes all reactors have a container. @@ -365,19 +359,6 @@ public boolean generatePreamble() { for (Model m : models) { pythonPreamble.pr(PythonPreambleGenerator.generatePythonPreambles(m.getPreambles())); } - code.pr(CGenerator.defineLogLevel(this)); - if (isFederated) { - code.pr(CPreambleGenerator.generateFederatedDirective(targetConfig.coordination)); - // Handle target parameters. - // First, if there are federates, then ensure that threading is enabled. - targetConfig.threads = CUtil.minThreadsToHandleInputPorts(federates); - } - includeTargetLanguageHeaders(); - code.pr(CPreambleGenerator.generateNumFederatesDirective(federates.size())); - code.pr(CPreambleGenerator.generateMixedRadixIncludeHeader()); - super.includeTargetLanguageSourceFiles(); - super.parseTargetParameters(); - return false; // placeholder return value. See comment above } /** @@ -919,6 +900,17 @@ public void writeDockerFile(File dockerComposeDir, String dockerFileName, String } System.out.println(getDockerBuildCommand(dockerFile, dockerComposeDir, federateName)); } + + @Override + protected String getConflictingConnectionsInModalReactorsBody(String source, String dest) { + // NOTE: Strangely, a newline is needed at the beginning or indentation + // gets swallowed. + return String.join("\n", + "\n# Generated forwarding reaction for connections with the same destination", + "# but located in mutually exclusive modes.", + dest+".set("+source+".value)\n" + ); + } private static String addDoubleQuotes(String str) { return "\""+str+"\""; diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java index 8aa4494a29..11b6066848 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java @@ -1,31 +1,31 @@ package org.lflang.generator.python; -import java.util.Set; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; -import org.lflang.lf.ReactorDecl; -import org.lflang.lf.Reaction; -import org.lflang.lf.Reactor; -import org.lflang.lf.Action; -import org.lflang.lf.TriggerRef; -import org.lflang.lf.VarRef; -import org.lflang.lf.Instantiation; -import org.lflang.lf.Port; -import org.lflang.lf.Input; -import org.lflang.lf.Output; -import org.lflang.generator.c.CReactionGenerator; -import org.lflang.generator.c.CTypes; -import org.lflang.generator.c.CUtil; +import org.lflang.ASTUtils; +import org.lflang.ErrorReporter; +import org.lflang.Target; import org.lflang.generator.CodeBuilder; import org.lflang.generator.GeneratorBase; import org.lflang.generator.ReactionInstance; import org.lflang.generator.ReactorInstance; -import org.lflang.ErrorReporter; -import org.lflang.ASTUtils; -import org.lflang.Target; -import org.lflang.ASTUtils; +import org.lflang.generator.c.CReactionGenerator; +import org.lflang.generator.c.CTypes; +import org.lflang.generator.c.CUtil; +import org.lflang.lf.Action; +import org.lflang.lf.Input; +import org.lflang.lf.Instantiation; +import org.lflang.lf.Mode; +import org.lflang.lf.Output; +import org.lflang.lf.Port; +import org.lflang.lf.Reaction; +import org.lflang.lf.Reactor; +import org.lflang.lf.ReactorDecl; +import org.lflang.lf.TriggerRef; +import org.lflang.lf.VarRef; public class PythonReactionGenerator { /** @@ -198,6 +198,9 @@ private static String generateCPythonInitializers(Reaction reaction, PythonPortGenerator.generateActionVariableToSendToPythonReaction(pyObjects, (Action) effect.getVariable(), decl); } + } else if (effect.getVariable() instanceof Mode) { + String name = effect.getVariable().getName(); + pyObjects.add("convert_C_mode_to_py("+name+",(self_base_t*)self, _lf_"+name+"_change_type)"); } else { if (effect.getVariable() instanceof Output) { PythonPortGenerator.generateOutputVariablesToSendToPythonReaction(pyObjects, (Output) effect.getVariable()); diff --git a/test/Python/src/modal_models/ConvertCaseTest.lf b/test/Python/src/modal_models/ConvertCaseTest.lf new file mode 100644 index 0000000000..6855e3797d --- /dev/null +++ b/test/Python/src/modal_models/ConvertCaseTest.lf @@ -0,0 +1,144 @@ +/* + * Modal Reactor Test. + * Tests nested reactors with modes. + */ +target Python { + fast: false, + timeout: 4 sec, +// logging: debug +} + +// import TraceTesting from "util/TraceTesting.lf" + +reactor ResetProcessor { + input discard + input character + output converted + + initial mode Converting { + converter = new Converter() + character -> converter.raw + converter.converted -> converted + + reaction(discard) -> Discarding {= + Discarding.set() + =} + } + + mode Discarding { + reaction(character) -> converted {= + converted.set('_') + =} + + reaction(character) -> Converting {= + Converting.set() + =} + } +} + +reactor HistoryProcessor { + input discard + input character + output converted + + initial mode Converting { + converter = new Converter() + character -> converter.raw + converter.converted -> converted + + reaction(discard) -> Discarding {= + Discarding.set() + =} + } + + mode Discarding { + reaction(character) -> converted {= + converted.set('_') + =} + + reaction(character) -> continue(Converting) {= + Converting.set() + =} + } +} + +reactor Converter { + input raw + output converted + + initial mode Upper { + reaction(raw) -> converted, Lower {= + character = raw.value.upper() + converted.set(character) + if character == ' ': + Lower.set() + =} + } + mode Lower { + reaction(raw) -> converted, Upper {= + character = raw.value.lower() + converted.set(character) + if character == ' ': + Upper.set() + =} + } +} + +reactor InputFeeder(message("")) { + output character + state idx(0) + + timer t(0, 250 msec) + + reaction(t) -> character {= + if self.idx < len(self.message): + character.set(self.message[self.idx]) + self.idx += 1 + =} +} + +main reactor { + timer stepper(500msec, 1sec) + + feeder = new InputFeeder(message="Hello World!") + reset_processor = new ResetProcessor() + history_processor = new HistoryProcessor() + + feeder.character -> reset_processor.character + feeder.character -> history_processor.character + + /* + test = new TraceTesting( + events_size = 2, + trace_size = 60, + trace = ( + 0,1,72,1,72, + 250000000,1,69,1,69, + 250000000,1,76,1,76, + 250000000,1,95,1,95, + 250000000,1,79,1,79, + 250000000,1,32,1,32, + 250000000,1,119,1,119, + 250000000,1,95,1,95, + 250000000,1,82,1,114, + 250000000,1,76,1,108, + 250000000,1,68,1,100, + 250000000,1,95,1,95 + ), training = false) + */ + // Trigger mode change + reaction(stepper) -> reset_processor.discard, history_processor.discard {= + reset_processor.discard.set(True) + history_processor.discard.set(True) + =} + + reaction(reset_processor.converted) {= + print(f"Reset: {reset_processor.converted.value}") + =} + + reaction(history_processor.converted) {= + print(f"History: {history_processor.converted.value}") + =} + + // reset_processor.converted, history_processor.converted -> test.events +} \ No newline at end of file diff --git a/test/Python/src/modal_models/Count3Modes.lf b/test/Python/src/modal_models/Count3Modes.lf new file mode 100644 index 0000000000..ac3661505e --- /dev/null +++ b/test/Python/src/modal_models/Count3Modes.lf @@ -0,0 +1,61 @@ +/* + * Modal Reactor Test. + * Tests cycling through modes. + */ +target Python { + fast: false, + timeout: 2 sec +}; + +reactor CounterCycle { + input next; + output count; + + initial mode One { + reaction(next) -> count, Two {= + count.set(1) + Two.set() + =} + } + mode Two { + reaction(next) -> count, Three {= + count.set(2) + Three.set() + =} + } + mode Three { + reaction(next) -> count, One {= + count.set(3) + One.set() + =} + } +} + +main reactor { + timer stepper(0, 250msec); + counter = new CounterCycle(); + + state expected_value(1); + + // Trigger + reaction(stepper) -> counter.next {= + counter.next.set(True) + =} + + // Check + reaction(stepper) counter.count {= + print(f"{counter.count.value}") + + if counter.count.is_present is not True: + sys.stderr.write("ERROR: Missing mode change.\n") + exit(1) + elif counter.count.value != self.expected_value: + sys.stderr.write("ERROR: Wrong mode.\n") + exit(2) + + if self.expected_value == 3: + self.expected_value = 1 + else: + self.expected_value += 1 + =} +} \ No newline at end of file diff --git a/test/Python/src/modal_models/util/TraceTesting.lf b/test/Python/src/modal_models/util/TraceTesting.lf new file mode 100644 index 0000000000..db0359c04c --- /dev/null +++ b/test/Python/src/modal_models/util/TraceTesting.lf @@ -0,0 +1,83 @@ +/* + * Utility reactor to record and test execution traces. + */ +target C; + +preamble {= + #include +=} + +reactor TraceTesting(events_size:int(0), trace_size:int(0), trace:int[](0), training:bool(false)) { + input [events_size]events:int + + state last_reaction_time:int(0) + state trace_idx:int(0) + state recorded_events:int*(0) + state recorded_events_next:int(0) + + reaction(startup) {= + self->last_reaction_time = get_logical_time(); + =} + + reaction(events) {= + // Time passed since last reaction + int curr_reaction_delay = get_logical_time() - self->last_reaction_time; + + if (self->training) { + // Save time + self->recorded_events = (int*) realloc(self->recorded_events, sizeof(int) * (self->recorded_events_next + 1 + 2 * self->events_size)); + self->recorded_events[self->recorded_events_next++] = curr_reaction_delay; + } else { + if (self->trace_idx >= self->trace_size) { + printf("ERROR: Trace Error: Current execution exceeds given trace.\n"); + exit(1); + } + + int trace_reaction_delay = self->trace[self->trace_idx++]; + + if (curr_reaction_delay != trace_reaction_delay) { + printf("ERROR: Trace Mismatch: Unexpected reaction timing. (delay: %d, expected: %d)\n", curr_reaction_delay, trace_reaction_delay); + exit(2); + } + } + + for (int i = 0; i < self->events_size; i++) { + int curr_present = events[i]->is_present; + int curr_value = events[i]->value; + + if (self->training) { + // Save event + self->recorded_events[self->recorded_events_next++] = curr_present; + self->recorded_events[self->recorded_events_next++] = curr_value; + } else { + int trace_present = self->trace[self->trace_idx++]; + int trace_value = self->trace[self->trace_idx++]; + + if (trace_present != curr_present) { + printf("ERROR: Trace Mismatch: Unexpected event presence. (event: %d, presence: %d, expected: %d)\n", i, curr_present, trace_present); + exit(3); + } else if (curr_present && trace_value != curr_value) { + printf("ERROR: Trace Mismatch: Unexpected event value. (event: %d, presence: %d, expected: %d)\n", i, curr_value, trace_value); + exit(4); + } + } + } + + self->last_reaction_time = get_logical_time(); + =} + + reaction(shutdown) {= + if (self->training) { + printf("Recorded event trace (%d): (", self->recorded_events_next); + for (int i = 0; i < self->recorded_events_next; i++) { + printf("%d", self->recorded_events[i]); + if (i < self->recorded_events_next - 1) { + printf(","); + } + } + printf(")\n"); + + free(self->recorded_events); + } + =} +} \ No newline at end of file From e3f84ef77db0b1b7bfd82f2c7345886c05b55fb4 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 13:24:40 -0800 Subject: [PATCH 02/36] Updated reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index c649f166b5..3c96abe46f 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit c649f166b52ecc325d7915f2456f4bde77084cc2 +Subproject commit 3c96abe46fdb8b0dd8468e195499ff90741e787c From 093450cf3942775b2873afb849ddfd5c7e10823e Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 15:30:06 -0800 Subject: [PATCH 03/36] Updated comments --- .../org/lflang/generator/python/PythonReactionGenerator.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java index 11b6066848..2409843eca 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonReactionGenerator.java @@ -154,10 +154,9 @@ public static String generateCReaction(Reaction reaction, * * @param reaction The reaction to generate Python-specific initialization for. * @param decl The reactor to which reaction 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 Py_BuildValue + * @param pyObjects A list of expressions that can be used as additional arguments to Py_BuildValue * (@see docs.python.org/3/c-api). - * @param pyObjects A "," delimited list of expressions that would be (or result in a creation of) a PyObject. + * We will use as a format string, "(O...O)" where the number of O's is equal to the length of the list. */ private static String generateCPythonInitializers(Reaction reaction, ReactorDecl decl, From e5720f71bed01276e4ea865acdeac73cc0d8440b Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 17:40:41 -0600 Subject: [PATCH 04/36] Moved code related to modal models into separate files --- org.lflang/src/lib/py/reactor-c-py | 2 +- .../org/lflang/generator/python/PyUtil.java | 18 ++++++++---------- .../generator/python/PythonGenerator.java | 5 +++++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/org.lflang/src/lib/py/reactor-c-py b/org.lflang/src/lib/py/reactor-c-py index 58558873f5..6aaf075ce8 160000 --- a/org.lflang/src/lib/py/reactor-c-py +++ b/org.lflang/src/lib/py/reactor-c-py @@ -1 +1 @@ -Subproject commit 58558873f592216b9904bdca03c4a977b0340ed1 +Subproject commit 6aaf075ce82f4104875f0aa809a1300445442720 diff --git a/org.lflang/src/org/lflang/generator/python/PyUtil.java b/org.lflang/src/org/lflang/generator/python/PyUtil.java index b75cabf960..58b5996421 100644 --- a/org.lflang/src/org/lflang/generator/python/PyUtil.java +++ b/org.lflang/src/org/lflang/generator/python/PyUtil.java @@ -178,17 +178,15 @@ public static void copyTargetFiles(FileConfig fileConfig) throws IOException { // Copy the required target language files into the target file system. // This will also overwrite previous versions. final Path srcGen = fileConfig.getSrcGenPath(); - FileUtil.copyFileFromClassPath( - "/lib/py/reactor-c-py/include/pythontarget.h", - srcGen.resolve("pythontarget.h") + FileUtil.copyDirectoryFromClassPath( + "/lib/py/reactor-c-py/include", + srcGen, + true ); - FileUtil.copyFileFromClassPath( - "/lib/py/reactor-c-py/lib/pythontarget.c", - srcGen.resolve("pythontarget.c") - ); - FileUtil.copyFileFromClassPath( - "/lib/c/reactor-c/include/ctarget.h", - srcGen.resolve("ctarget.h") + FileUtil.copyDirectoryFromClassPath( + "/lib/py/reactor-c-py/lib", + srcGen, + true ); } } \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index a1ac4daaff..82a910710f 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -606,6 +606,11 @@ public void includeTargetLanguageHeaders() { if (targetConfig.tracing != null) { code.pr("#include \"core/trace.c\""); } + + if (hasModalReactors) { + code.pr("#include \"modal_models/definitions.h\""); + targetConfig.compileAdditionalSources.add("modal_models/impl.c"); + } } /** From 1f5d82dd2f4a0c77ac3b707b3eb142be8ed8308c Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 17:51:07 -0600 Subject: [PATCH 05/36] Updated pointer to reactor-c-py --- org.lflang/src/lib/py/reactor-c-py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/py/reactor-c-py b/org.lflang/src/lib/py/reactor-c-py index 6aaf075ce8..0dfd9dc2fe 160000 --- a/org.lflang/src/lib/py/reactor-c-py +++ b/org.lflang/src/lib/py/reactor-c-py @@ -1 +1 @@ -Subproject commit 6aaf075ce82f4104875f0aa809a1300445442720 +Subproject commit 0dfd9dc2feae0303bd987f4bad7eaf8d42a246f1 From 0d7a758fdef843274e3d9c7e9ca21fa7c3182a44 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 17:57:51 -0600 Subject: [PATCH 06/36] Updated pointer to reactor-c-py --- org.lflang/src/lib/py/reactor-c-py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/py/reactor-c-py b/org.lflang/src/lib/py/reactor-c-py index 0dfd9dc2fe..c4e40d11be 160000 --- a/org.lflang/src/lib/py/reactor-c-py +++ b/org.lflang/src/lib/py/reactor-c-py @@ -1 +1 @@ -Subproject commit 0dfd9dc2feae0303bd987f4bad7eaf8d42a246f1 +Subproject commit c4e40d11be543705917305b3069d713f0351c107 From efc38f8c267fbbd71b33073e46bcd8bef58c2089 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 16:54:12 -0800 Subject: [PATCH 07/36] Updated reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 3c96abe46f..bd27e76c12 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 3c96abe46fdb8b0dd8468e195499ff90741e787c +Subproject commit bd27e76c12d5f9cb8b3eab1f9e6aec52b790358c From 89aa41fa4235c485d56307075e9e070b1c386516 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 19:01:34 -0600 Subject: [PATCH 08/36] Bug fix --- .../src/org/lflang/generator/python/PythonPortGenerator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java index efbb111e1d..5b5c2b122a 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -248,7 +248,8 @@ public static String generatePythonPortVariableInReaction(VarRef port) { ); } else { return String.join("\n", - containerName+" = Make", + "try: "+containerName+"", + "except NameError: "+containerName+" = Make()", containerName+"."+variableName+" = "+containerName+"_"+variableName ); } From 9e9bcba525a79e05b582eb89b4e295ae6b4155de Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 19:13:31 -0600 Subject: [PATCH 09/36] More bug fixes --- org.lflang/src/lib/c/reactor-c | 2 +- org.lflang/src/org/lflang/generator/python/PyUtil.java | 4 ++-- .../org/lflang/generator/python/PythonPortGenerator.java | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index bd27e76c12..3c96abe46f 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit bd27e76c12d5f9cb8b3eab1f9e6aec52b790358c +Subproject commit 3c96abe46fdb8b0dd8468e195499ff90741e787c diff --git a/org.lflang/src/org/lflang/generator/python/PyUtil.java b/org.lflang/src/org/lflang/generator/python/PyUtil.java index 58b5996421..8623a9bedb 100644 --- a/org.lflang/src/org/lflang/generator/python/PyUtil.java +++ b/org.lflang/src/org/lflang/generator/python/PyUtil.java @@ -181,12 +181,12 @@ public static void copyTargetFiles(FileConfig fileConfig) throws IOException { FileUtil.copyDirectoryFromClassPath( "/lib/py/reactor-c-py/include", srcGen, - true + false ); FileUtil.copyDirectoryFromClassPath( "/lib/py/reactor-c-py/lib", srcGen, - true + false ); } } \ No newline at end of file diff --git a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java index 5b5c2b122a..9c0f1a0e89 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -240,15 +240,16 @@ public static String generatePythonPortVariableInReaction(VarRef port) { String variableName = port.getVariable().getName(); if (port.getContainer().getWidthSpec() != null) { // It's a bank - return String.join("\n", - containerName+" = [None] * len("+containerName+"_"+variableName+")", + return String.join("\n", + "try: "+containerName, + "except NameError: "+containerName+" = [None] * len("+containerName+"_"+variableName+")", "for i in range(len("+containerName+"_"+variableName+")):", - " "+containerName+"[i] = Make()", + " if "+containerName+"[i] is None: "+containerName+"[i] = Make()", " "+containerName+"[i]."+variableName+" = "+containerName+"_"+variableName+"[i]" ); } else { return String.join("\n", - "try: "+containerName+"", + "try: "+containerName, "except NameError: "+containerName+" = Make()", containerName+"."+variableName+" = "+containerName+"_"+variableName ); From b7d2d2b3c278c199c1ef8a1a3c3a0e7b50937b45 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 19:14:15 -0600 Subject: [PATCH 10/36] Updated pointer to reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 3c96abe46f..bd27e76c12 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 3c96abe46fdb8b0dd8468e195499ff90741e787c +Subproject commit bd27e76c12d5f9cb8b3eab1f9e6aec52b790358c From d2e75061044096de54df447873eec7e17c6f1602 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Tue, 8 Mar 2022 19:28:40 -0600 Subject: [PATCH 11/36] Updated pointer to reactor-c-py --- org.lflang/src/lib/py/reactor-c-py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/py/reactor-c-py b/org.lflang/src/lib/py/reactor-c-py index c4e40d11be..78dc0b241d 160000 --- a/org.lflang/src/lib/py/reactor-c-py +++ b/org.lflang/src/lib/py/reactor-c-py @@ -1 +1 @@ -Subproject commit c4e40d11be543705917305b3069d713f0351c107 +Subproject commit 78dc0b241d2797423d824080df7e7f685fd45b30 From a294ab48d8d086406ce52f5b7da86068c3d638e9 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 17:43:34 -0800 Subject: [PATCH 12/36] Moved MultipleContained test to the right directory matching the C tests. --- test/Python/src/{multiport => }/MultipleContained.lf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/Python/src/{multiport => }/MultipleContained.lf (100%) diff --git a/test/Python/src/multiport/MultipleContained.lf b/test/Python/src/MultipleContained.lf similarity index 100% rename from test/Python/src/multiport/MultipleContained.lf rename to test/Python/src/MultipleContained.lf From 13b748a95567f3a2f771b6cce105c494be97b318 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 17:49:02 -0800 Subject: [PATCH 13/36] Updated tests to verify that both inputs arrive --- test/C/src/MultipleContained.lf | 8 ++++++++ test/Python/src/MultipleContained.lf | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/test/C/src/MultipleContained.lf b/test/C/src/MultipleContained.lf index 6d09798527..7c05f1fbe2 100644 --- a/test/C/src/MultipleContained.lf +++ b/test/C/src/MultipleContained.lf @@ -5,6 +5,7 @@ reactor Contained { output trigger:int; input in1:int; input in2:int; + state count:int(0); reaction(startup) -> trigger {= SET(trigger, 42); =} @@ -14,6 +15,7 @@ reactor Contained { fprintf(stderr, "FAILED: Expected 42.\n"); exit(1); } + self->count++; =} reaction(in2) {= printf("in2 received %d.\n", in2->value); @@ -21,6 +23,12 @@ reactor Contained { fprintf(stderr, "FAILED: Expected 42.\n"); exit(1); } + self->count++; + =} + reaction(shutdown) {= + if (self->count != 2) { + error_print_and_exit("FAILED: Expected two inputs!"); + } =} } main reactor MultipleContained { diff --git a/test/Python/src/MultipleContained.lf b/test/Python/src/MultipleContained.lf index 3c9c29ddbd..cc549b5be2 100644 --- a/test/Python/src/MultipleContained.lf +++ b/test/Python/src/MultipleContained.lf @@ -3,6 +3,7 @@ target Python; reactor Contained { output trigger; + state count(0); input in1; input in2; reaction(startup) -> trigger {= @@ -13,12 +14,19 @@ reactor Contained { if in1.value != 42: sys.stderr.write("FAILED: Expected 42.\n") exit(1) + self.count += 1 =} reaction(in2) {= print("in2 received ", in2.value) if in2.value != 42: sys.stderr.write("FAILED: Expected 42.\n") exit(1) + self.count += 1 + =} + reaction(shutdown) {= + if self.count != 2: + sys.stderr.write("FAILED: Should have received two inputs.\n") + exit(1) =} } main reactor MultipleContained { From e7741d3c1b8adb11ba20967fc56677c47995d0c1 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 8 Mar 2022 18:33:29 -0800 Subject: [PATCH 14/36] Converted ConvertCaseTest to Python. --- .../src/modal_models/ConvertCaseTest.lf | 13 +-- .../src/modal_models/util/TraceTesting.lf | 105 ++++++++---------- 2 files changed, 49 insertions(+), 69 deletions(-) diff --git a/test/Python/src/modal_models/ConvertCaseTest.lf b/test/Python/src/modal_models/ConvertCaseTest.lf index 6855e3797d..e80fd5429b 100644 --- a/test/Python/src/modal_models/ConvertCaseTest.lf +++ b/test/Python/src/modal_models/ConvertCaseTest.lf @@ -5,10 +5,9 @@ target Python { fast: false, timeout: 4 sec, -// logging: debug } -// import TraceTesting from "util/TraceTesting.lf" +import TraceTesting from "util/TraceTesting.lf" reactor ResetProcessor { input discard @@ -107,10 +106,8 @@ main reactor { feeder.character -> reset_processor.character feeder.character -> history_processor.character - /* test = new TraceTesting( events_size = 2, - trace_size = 60, trace = ( 0,1,72,1,72, 250000000,1,69,1,69, @@ -124,9 +121,9 @@ main reactor { 250000000,1,76,1,108, 250000000,1,68,1,100, 250000000,1,95,1,95 - ), training = false) - */ - // Trigger mode change + ), training = False) + + # Trigger mode change reaction(stepper) -> reset_processor.discard, history_processor.discard {= reset_processor.discard.set(True) history_processor.discard.set(True) @@ -140,5 +137,5 @@ main reactor { print(f"History: {history_processor.converted.value}") =} - // reset_processor.converted, history_processor.converted -> test.events + reset_processor.converted, history_processor.converted -> test.events } \ No newline at end of file diff --git a/test/Python/src/modal_models/util/TraceTesting.lf b/test/Python/src/modal_models/util/TraceTesting.lf index db0359c04c..82b2f6a4b1 100644 --- a/test/Python/src/modal_models/util/TraceTesting.lf +++ b/test/Python/src/modal_models/util/TraceTesting.lf @@ -1,83 +1,66 @@ /* * Utility reactor to record and test execution traces. */ -target C; +target Python -preamble {= - #include -=} +reactor TraceTesting(events_size(0), trace({=[]=}), training(False)) { + input [events_size]events -reactor TraceTesting(events_size:int(0), trace_size:int(0), trace:int[](0), training:bool(false)) { - input [events_size]events:int - - state last_reaction_time:int(0) - state trace_idx:int(0) - state recorded_events:int*(0) - state recorded_events_next:int(0) + state last_reaction_time(0) + state trace_idx(0) + state recorded_events({=[]=}) + state recorded_events_next(0) reaction(startup) {= - self->last_reaction_time = get_logical_time(); + self.last_reaction_time = get_logical_time() =} reaction(events) {= - // Time passed since last reaction - int curr_reaction_delay = get_logical_time() - self->last_reaction_time; + # Time passed since last reaction + curr_reaction_delay = get_logical_time() - self.last_reaction_time - if (self->training) { - // Save time - self->recorded_events = (int*) realloc(self->recorded_events, sizeof(int) * (self->recorded_events_next + 1 + 2 * self->events_size)); - self->recorded_events[self->recorded_events_next++] = curr_reaction_delay; - } else { - if (self->trace_idx >= self->trace_size) { - printf("ERROR: Trace Error: Current execution exceeds given trace.\n"); - exit(1); - } + if self.training: + # Save the time + self.recorded_events.append(curr_reaction_delay) + else: + if self.trace_idx >= len(self.trace): + sys.stderr.write("ERROR: Trace Error: Current execution exceeds given trace.\n") + exit(1) - int trace_reaction_delay = self->trace[self->trace_idx++]; + trace_reaction_delay = self.trace[self.trace_idx] + self.trace_idx += 1 - if (curr_reaction_delay != trace_reaction_delay) { - printf("ERROR: Trace Mismatch: Unexpected reaction timing. (delay: %d, expected: %d)\n", curr_reaction_delay, trace_reaction_delay); - exit(2); - } - } + if curr_reaction_delay != trace_reaction_delay: + sys.stderr.write(f"ERROR: Trace Mismatch: Unexpected reaction timing. (delay: {curr_reaction_delay}, expected: {trace_reaction_delay})\n") + exit(2) - for (int i = 0; i < self->events_size; i++) { - int curr_present = events[i]->is_present; - int curr_value = events[i]->value; + for i in range(self.events_size): + curr_present = events[i].is_present + curr_value = events[i].value - if (self->training) { - // Save event - self->recorded_events[self->recorded_events_next++] = curr_present; - self->recorded_events[self->recorded_events_next++] = curr_value; - } else { - int trace_present = self->trace[self->trace_idx++]; - int trace_value = self->trace[self->trace_idx++]; + if self.training: + # Save the event + self.recorded_events.append(curr_present) + self.recorded_events.append(curr_value) + else: + trace_present = self.trace[self.trace_idx] + self.trace_idx += 1 + trace_value = self.trace[self.trace_idx] + self.trace_idx += 1 - if (trace_present != curr_present) { - printf("ERROR: Trace Mismatch: Unexpected event presence. (event: %d, presence: %d, expected: %d)\n", i, curr_present, trace_present); - exit(3); - } else if (curr_present && trace_value != curr_value) { - printf("ERROR: Trace Mismatch: Unexpected event value. (event: %d, presence: %d, expected: %d)\n", i, curr_value, trace_value); - exit(4); - } - } - } + if trace_present != curr_present: + sys.stderr.write(f"ERROR: Trace Mismatch: Unexpected event presence. (event: {i}, presence: {curr_present}, expected: {trace_present})\n") + exit(3) + elif curr_present and trace_value != curr_value: + sys.stderr.write(f"ERROR: Trace Mismatch: Unexpected event presence. (event: {i}, value: {curr_value}, expected: {trace_value})\n") + exit(4) - self->last_reaction_time = get_logical_time(); + self.last_reaction_time = get_logical_time() =} reaction(shutdown) {= - if (self->training) { - printf("Recorded event trace (%d): (", self->recorded_events_next); - for (int i = 0; i < self->recorded_events_next; i++) { - printf("%d", self->recorded_events[i]); - if (i < self->recorded_events_next - 1) { - printf(","); - } - } - printf(")\n"); - - free(self->recorded_events); - } + if self.training: + print(f"Recorded event trace ({self.recorded_events_next}):") + print(self.recorded_events) =} } \ No newline at end of file From d40d42835dcaa6a99863ac623d2245391a750e6a Mon Sep 17 00:00:00 2001 From: eal Date: Thu, 10 Mar 2022 09:55:46 -0800 Subject: [PATCH 15/36] Fixed test to remove reaction self-loop cycle that was masked. --- .../generator/ReactionInstanceGraph.java | 18 ++++++++---------- test/C/src/modal_models/ModalCycleBreaker.lf | 4 ++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index ccd5090a3e..5f99791a04 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -135,15 +135,13 @@ protected void addDownstreamReactions(PortInstance port, ReactionInstance reacti List dstRuntimes = dstReaction.getRuntimeInstances(); Runtime srcRuntime = srcRuntimes.get(srcIndex); Runtime dstRuntime = dstRuntimes.get(dstIndex); - if (dstRuntime != srcRuntime) { - // Only add this dependency if the reactions are not in modes at all or in the same mode or in modes of separate reactors - // This allows modes to break cycles since modes are always mutually exclusive. - if (srcRuntime.getReaction().getMode(true) == null || - dstRuntime.getReaction().getMode(true) == null || - srcRuntime.getReaction().getMode(true) == dstRuntime.getReaction().getMode(true) || - srcRuntime.getReaction().getParent() != dstRuntime.getReaction().getParent()) { - addEdge(dstRuntime, srcRuntime); - } + // Only add this dependency if the reactions are not in modes at all or in the same mode or in modes of separate reactors + // This allows modes to break cycles since modes are always mutually exclusive. + if (srcRuntime.getReaction().getMode(true) == null || + dstRuntime.getReaction().getMode(true) == null || + srcRuntime.getReaction().getMode(true) == dstRuntime.getReaction().getMode(true) || + srcRuntime.getReaction().getParent() != dstRuntime.getReaction().getParent()) { + addEdge(dstRuntime, srcRuntime); } // Propagate the deadlines, if any. @@ -263,7 +261,7 @@ private void assignLevels() { Set toRemove = new LinkedHashSet(); Set downstreamAdjacentNodes = getDownstreamAdjacentNodes(origin); // All downstream adjacent nodes start with a level 0. Adjust the - // maxNumOfReactionPerLevel field accordingly (to be + // numReactionsPerLevel field accordingly (to be // updated in the for loop below). adjustNumReactionsPerLevel(0, downstreamAdjacentNodes.size()); // Visit effect nodes. diff --git a/test/C/src/modal_models/ModalCycleBreaker.lf b/test/C/src/modal_models/ModalCycleBreaker.lf index b4e1b8af8f..ce8298fd03 100644 --- a/test/C/src/modal_models/ModalCycleBreaker.lf +++ b/test/C/src/modal_models/ModalCycleBreaker.lf @@ -18,8 +18,8 @@ reactor Modal { mode Two { // Defining reaction to in2 before in1 would cause cycle if no mode were present timer wait(150msec, 1sec) - reaction(in2) -> out {= - SET(out, in2->value); + reaction(in2) {= + // SET(out, in2->value); =} reaction(wait) -> One {= From f1ea738304c90089f46078b9df6ee98132f24b0a Mon Sep 17 00:00:00 2001 From: eal Date: Thu, 10 Mar 2022 11:51:17 -0800 Subject: [PATCH 16/36] Update reactor-c to point to branch --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index ff11e7bfde..01b0defbf9 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit ff11e7bfdeb5aeac962ee18ac7a031b5b9f3b864 +Subproject commit 01b0defbf91c8938cc742e75a02d9d8fb88b86db From e64abe240c621b93e80425db3beead96ba37c6a2 Mon Sep 17 00:00:00 2001 From: eal Date: Thu, 10 Mar 2022 11:52:49 -0800 Subject: [PATCH 17/36] Added another modal model test to Python --- .../src/modal_models/ModalCycleBreaker.lf | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test/Python/src/modal_models/ModalCycleBreaker.lf diff --git a/test/Python/src/modal_models/ModalCycleBreaker.lf b/test/Python/src/modal_models/ModalCycleBreaker.lf new file mode 100644 index 0000000000..30a373618e --- /dev/null +++ b/test/Python/src/modal_models/ModalCycleBreaker.lf @@ -0,0 +1,81 @@ +/* + * Modal Reactor Test. + * + * Tests if connections in the same reactor that have the same destination work if they are located in separate modes. + */ +target Python { + fast: false, + timeout: 1 sec +} + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input in1 + input in2 + output out + + mode Two { # Defining reaction to in2 before in1 would cause cycle if no mode were present + timer wait(150msec, 1sec) + + reaction(in2) {= + =} + + reaction(wait) -> One {= + One.set() + print("Switching to mode One") + =} + } + initial mode One { + reaction(in1) -> out {= + out.set(in1.value) + =} + + reaction(in1) -> Two {= + if in1.value % 5 == 4: + Two.set() + print("Switching to mode Two") + =} + } +} + +reactor Counter(period(1 sec)) { + output value + + timer t(0, period) + state curval(0) + + reaction(t) -> value {= + value.set(self.curval) + self.curval += 1 + =} +} + +main reactor { + counter = new Counter(period=100msec) + modal = new Modal() + test = new TraceTesting( + events_size = 1, + trace = ( + 0,1,0, + 100000000,1,1, + 100000000,1,2, + 100000000,1,3, + 100000000,1,4, + 200000000,1,6, + 100000000,1,7, + 100000000,1,8, + 100000000,1,9 + ), training = False) + + counter.value -> modal.in1 + modal.out -> modal.in2 + + modal.out + -> test.events + + // Print + reaction(modal.out) {= + print(modal.out.value) + =} +} \ No newline at end of file From 5aa8ac60cc822e93d8ff00b5b923b86d2792fe54 Mon Sep 17 00:00:00 2001 From: eal Date: Thu, 10 Mar 2022 12:06:38 -0800 Subject: [PATCH 18/36] Added one more passing test and several unported tests that will fail --- test/Python/src/modal_models/ModalActions.lf | 98 ++++++++++++++++ test/Python/src/modal_models/ModalAfter.lf | 106 ++++++++++++++++++ .../src/modal_models/ModalNestedReactions.lf | 78 +++++++++++++ .../src/modal_models/ModalStateReset.lf | 98 ++++++++++++++++ test/Python/src/modal_models/ModalTimers.lf | 79 +++++++++++++ .../MultipleOutputFeeder_2Connections.lf | 85 ++++++++++++++ ...ultipleOutputFeeder_ReactionConnections.lf | 88 +++++++++++++++ 7 files changed, 632 insertions(+) create mode 100644 test/Python/src/modal_models/ModalActions.lf create mode 100644 test/Python/src/modal_models/ModalAfter.lf create mode 100644 test/Python/src/modal_models/ModalNestedReactions.lf create mode 100644 test/Python/src/modal_models/ModalStateReset.lf create mode 100644 test/Python/src/modal_models/ModalTimers.lf create mode 100644 test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf create mode 100644 test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf diff --git a/test/Python/src/modal_models/ModalActions.lf b/test/Python/src/modal_models/ModalActions.lf new file mode 100644 index 0000000000..da16feea30 --- /dev/null +++ b/test/Python/src/modal_models/ModalActions.lf @@ -0,0 +1,98 @@ +/* + * Modal Reactor Test. + * Tests actions, their suspension during mode inactivity and continuation of delays with history transitions. + */ +target Python { + fast: false, + timeout: 4 sec +} + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next + + output mode_switch + output action1_sched + output action1_exec + output action2_sched + output action2_exec + + initial mode One { + timer T1(0, 750msec) + logical action delay1(500msec) + + reaction(T1) -> delay1, action1_sched {= + print("Scheduled Action") + delay1.schedule(0) + action1_sched.set(1) + =} + reaction(delay1) -> action1_exec {= + print("Executed Action") + action1_exec.set(1) + =} + + reaction(next) -> reset(Two), mode_switch {= + print("Transitioning to mode Two (reset)") + mode_switch.set(1) + Two.set() + =} + } + mode Two { + timer T2(0, 750 msec) + logical action delay2(500 msec) + + reaction(T2) -> delay2, action2_sched {= + print("Scheduled Action2") + delay2.schedule(0) + action2_sched.set(1) + =} + reaction(delay2) -> action2_exec {= + print("Executed Action2") + action2_exec.set(1) + =} + + reaction(next) -> continue(One), mode_switch {= + print("Transitioning to mode One (continue)") + mode_switch.set(1) + One.set() + =} + } +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 5, + trace = ( + 0,0,0,1,1,0,0,0,0,0,0, + 500000000,0,0,0,1,1,1,0,0,0,0, + 250000000,0,0,1,1,0,1,0,0,0,0, + 250000000,1,1,0,1,0,1,0,0,0,0, + 0,0,1,0,1,0,1,1,1,0,0, + 500000000,0,1,0,1,0,1,0,1,1,1, + 250000000,0,1,0,1,0,1,1,1,0,1, + 250000000,1,1,0,1,0,1,0,1,0,1, + 250000000,0,1,0,1,1,1,0,1,0,1, + 250000000,0,1,1,1,0,1,0,1,0,1, + 500000000,1,1,0,1,1,1,0,1,0,1, + 0,0,1,0,1,0,1,1,1,0,1, + 500000000,0,1,0,1,0,1,0,1,1,1, + 250000000,0,1,0,1,0,1,1,1,0,1, + 250000000,1,1,0,1,0,1,0,1,0,1 + ), training = False) + + // Trigger mode change + reaction(stepper) -> modal.next {= + modal.next.set(True) + =} + + modal.mode_switch, + modal.action1_sched, + modal.action1_exec, + modal.action2_sched, + modal.action2_exec + -> test.events +} \ No newline at end of file diff --git a/test/Python/src/modal_models/ModalAfter.lf b/test/Python/src/modal_models/ModalAfter.lf new file mode 100644 index 0000000000..c162d347c9 --- /dev/null +++ b/test/Python/src/modal_models/ModalAfter.lf @@ -0,0 +1,106 @@ +/* + * Modal Reactor Test. + * Tests after delays, its suspension during mode inactivity and continuation with history transitions. + */ +target Python { + fast: false, + timeout: 4 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool + + output mode_switch:int + output produced1:int + output consumed1:int + output produced2:int + output consumed2:int + + initial mode One { + producer1 = new Producer(mode_id=1) + consumer1 = new Consumer(mode_id=1) + producer1.product -> produced1 + producer1.product -> consumer1.product after 500msec; + consumer1.report -> consumed1; + + reaction(next) -> reset(Two), mode_switch {= + printf("Transitioning to mode Two (reset)\n"); + SET(mode_switch, 1); + SET_MODE(Two); + =} + } + mode Two { + producer2 = new Producer(mode_id=2) + consumer2 = new Consumer(mode_id=2) + producer2.product -> produced2 + producer2.product -> consumer2.product after 500msec; + consumer2.report -> consumed2; + + reaction(next) -> continue(One), mode_switch {= + printf("Transitioning to mode One (continue)\n"); + SET(mode_switch, 1); + SET_MODE(One); + =} + } +} + +reactor Producer(mode_id:int(0)) { + output product:int + + timer t(0, 750msec) + + reaction(t) -> product {= + printf("Produced in %d\n", self->mode_id); + SET(product, 1); + =} +} + +reactor Consumer(mode_id:int(0)) { + input product:int + output report:int + + reaction(product) -> report {= + printf("Consumed in %d\n", self->mode_id); + SET(report, 1); + =} +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 5, + trace_size = 165, + trace = ( + 0,0,0,1,1,0,0,0,0,0,0, + 500000000,0,0,0,1,1,1,0,0,0,0, + 250000000,0,0,1,1,0,1,0,0,0,0, + 250000000,1,1,0,1,0,1,0,0,0,0, + 0,0,1,0,1,0,1,1,1,0,0, + 500000000,0,1,0,1,0,1,0,1,1,1, + 250000000,0,1,0,1,0,1,1,1,0,1, + 250000000,1,1,0,1,0,1,0,1,0,1, + 250000000,0,1,0,1,1,1,0,1,0,1, + 250000000,0,1,1,1,0,1,0,1,0,1, + 500000000,1,1,0,1,1,1,0,1,0,1, + 0,0,1,0,1,0,1,1,1,0,1, + 500000000,0,1,0,1,0,1,0,1,1,1, + 250000000,0,1,0,1,0,1,1,1,0,1, + 250000000,1,1,0,1,0,1,0,1,0,1 + ), training = false) + + // Trigger mode change + reaction(stepper) -> modal.next {= + SET(modal.next, true); + =} + + modal.mode_switch, + modal.produced1, + modal.consumed1, + modal.produced2, + modal.consumed2 + -> test.events +} \ No newline at end of file diff --git a/test/Python/src/modal_models/ModalNestedReactions.lf b/test/Python/src/modal_models/ModalNestedReactions.lf new file mode 100644 index 0000000000..cbb5db417f --- /dev/null +++ b/test/Python/src/modal_models/ModalNestedReactions.lf @@ -0,0 +1,78 @@ +/** + * Modal Reactor Test. + * Checks disabling of reactions indirectly nested in an inactive mode + */ +target Python { + fast: false, + timeout: 2 sec +}; + +reactor CounterCycle { + input next:bool; + + output count:int; + output only_in_two:bool; + output never:int; + + initial mode One { + reaction(next) -> count, Two {= + SET(count, 1); + SET_MODE(Two); + =} + } + mode Two { + fwd = new Forward() + next -> fwd.in + fwd.out -> only_in_two + + reaction(next) -> count, One {= + SET(count, 2); + SET_MODE(One); + =} + } + mode Three { + reaction(next) -> never {= + SET(never, true); + =} + } +} + +reactor Forward { + input in:bool; + output out:bool; + + reaction(in) -> out {= + SET(out, in->value); + =} +} + +main reactor { + timer stepper(0, 250msec) + counter = new CounterCycle() + + // Trigger + reaction(stepper) -> counter.next {= + SET(counter.next, true); + =} + + // Check + reaction(stepper) counter.count, counter.only_in_two {= + printf("%d\n", counter.count->value); + + if (!counter.count->is_present) { + printf("ERROR: Missing mode change.\n"); + exit(1); + } else if (counter.only_in_two->is_present && counter.count->value != 2) { + printf("ERROR: Indirectly nested reaction was not properly deactivated.\n"); + exit(2); + } else if (!counter.only_in_two->is_present && counter.count->value == 2) { + printf("ERROR: Missing output from indirectly nested reaction.\n"); + exit(3); + } + =} + + reaction(counter.never) {= + printf("ERROR: Detected output from unreachable mode.\n"); + exit(4); + =} +} diff --git a/test/Python/src/modal_models/ModalStateReset.lf b/test/Python/src/modal_models/ModalStateReset.lf new file mode 100644 index 0000000000..2239a7fa80 --- /dev/null +++ b/test/Python/src/modal_models/ModalStateReset.lf @@ -0,0 +1,98 @@ +/* + * Modal Reactor Test. + * Tests reset of state variables in modes. + */ +target Python { + fast: false, + timeout: 4 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool; + + output mode_switch:int + output count0:int; + output count1:int; + output count2:int; + + state counter0:int(0); + + reaction(next) -> count0 {= + printf("Counter0: %d\n", self->counter0); + SET(count0, self->counter0++); + =} + + initial mode One { + state counter1:int(0); + timer T1(0msec, 250msec); + + reaction(T1) -> count1 {= + printf("Counter1: %d\n", self->counter1); + SET(count1, self->counter1++); + =} + + reaction(next) -> reset(Two), mode_switch {= + printf("Transitioning to mode Two (reset)\n"); + SET(mode_switch, 1); + SET_MODE(Two); + =} + } + mode Two { + state counter2:int(-2); + timer T2(0msec, 250msec); + + reaction(T2) -> count2 {= + printf("Counter2: %d\n", self->counter2); + SET(count2, self->counter2++); + =} + + reaction(next) -> continue(One), mode_switch {= + printf("Transitioning to mode One (continue)\n"); + SET(mode_switch, 1); + SET_MODE(One); + =} + } +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 4, + trace_size = 171, + trace = ( + 0,0,0,0,0,1,0,0,0, + 250000000,0,0,0,0,1,1,0,0, + 250000000,0,0,0,0,1,2,0,0, + 250000000,0,0,0,0,1,3,0,0, + 250000000,1,1,1,0,1,4,0,0, + 0,0,1,0,0,0,4,1,-2, + 250000000,0,1,0,0,0,4,1,-1, + 250000000,0,1,0,0,0,4,1,0, + 250000000,0,1,0,0,0,4,1,1, + 250000000,1,1,1,1,0,4,1,2, + 250000000,0,1,0,1,1,5,0,2, + 250000000,0,1,0,1,1,6,0,2, + 250000000,0,1,0,1,1,7,0,2, + 250000000,1,1,1,2,1,8,0,2, + 0,0,1,0,2,0,8,1,-2, + 250000000,0,1,0,2,0,8,1,-1, + 250000000,0,1,0,2,0,8,1,0, + 250000000,0,1,0,2,0,8,1,1, + 250000000,1,1,1,3,0,8,1,2 + ), training = false) + + // Trigger mode change + reaction(stepper) -> modal.next {= + SET(modal.next, true); + =} + + modal.mode_switch, + modal.count0, + modal.count1, + modal.count2 + -> test.events +} \ No newline at end of file diff --git a/test/Python/src/modal_models/ModalTimers.lf b/test/Python/src/modal_models/ModalTimers.lf new file mode 100644 index 0000000000..02ce11ab3b --- /dev/null +++ b/test/Python/src/modal_models/ModalTimers.lf @@ -0,0 +1,79 @@ +/* + * Modal Reactor Test. + * Tests timers, their deactivation and reset in modes. + */ +target Python { + fast: false, + timeout: 4 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool + + output mode_switch:int + output timer1:int + output timer2:int + + initial mode One { + timer T1(0, 750msec) + + reaction(T1) -> timer1 {= + printf("T1\n"); + SET(timer1, 1); + =} + + reaction(next) -> reset(Two), mode_switch {= + printf("Transitioning to mode Two (reset)\n"); + SET(mode_switch, 1); + SET_MODE(Two); + =} + } + mode Two { + timer T2(0, 750msec) + + reaction(T2) -> timer2 {= + printf("T2\n"); + SET(timer2, 1); + =} + + reaction(next) -> continue(One), mode_switch {= + printf("Transitioning to mode One (continue)\n"); + SET(mode_switch, 1); + SET_MODE(One); + =} + } +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 3, + trace_size = 77, + trace = ( + 0,0,0,1,1,0,0, + 750000000,0,0,1,1,0,0, + 250000000,1,1,0,1,0,0, + 0,0,1,0,1,1,1, + 750000000,0,1,0,1,1,1, + 250000000,1,1,0,1,0,1, + 500000000,0,1,1,1,0,1, + 500000000,1,1,0,1,0,1, + 0,0,1,0,1,1,1, + 750000000,0,1,0,1,1,1, + 250000000,1,1,0,1,0,1 + ), training = false) + + // Trigger mode change + reaction(stepper) -> modal.next {= + SET(modal.next, true); + =} + + modal.mode_switch, + modal.timer1, + modal.timer2 + -> test.events +} \ No newline at end of file diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf new file mode 100644 index 0000000000..36e399ceb1 --- /dev/null +++ b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -0,0 +1,85 @@ +/* + * Modal Reactor Test. + * + * Tests if connections in the same reactor that have the same destination work if they are located in separate modes. + */ +target Python { + fast: false, + timeout: 2 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool; + output count:int; + + initial mode One { + counter1 = new Counter(period=250msec); + counter1.value -> count; + + reaction(next) -> Two {= + SET_MODE(Two); + =} + } + mode Two { + counter2 = new Counter(period=100msec); + counter2.value -> count; + + reaction(next) -> continue(One) {= + SET_MODE(One); + =} + } +} + +reactor Counter(period:time(1sec)) { + output value:int + + timer t(0, period) + state curval:int(0) + + reaction(t) -> value {= + SET(value, self->curval++); + =} +} + +main reactor { + timer stepper(500msec, 500msec) + + modal = new Modal() + test = new TraceTesting( + events_size = 1, + trace_size = 51, + trace = ( + 0,1,0, + 250000000,1,1, + 250000000,1,2, + 0,1,0, + 100000000,1,1, + 100000000,1,2, + 100000000,1,3, + 100000000,1,4, + 100000000,1,5, + 250000000,1,3, + 250000000,1,4, + 0,1,0, + 100000000,1,1, + 100000000,1,2, + 100000000,1,3, + 100000000,1,4, + 100000000,1,5 + ), training = false) + + // Trigger mode change + reaction(stepper) -> modal.next {= + SET(modal.next, true); + =} + + // Print + reaction(modal.count) {= + printf("%d\n", modal.count->value); + =} + + modal.count + -> test.events +} \ No newline at end of file diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf new file mode 100644 index 0000000000..52b9e834c6 --- /dev/null +++ b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -0,0 +1,88 @@ +/* + * Modal Reactor Test. + * + * Tests if a connection and a reaction in the same reactor can have the same destination if they are located in separate modes. + */ +target Python { + fast: false, + timeout: 2 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool; + output count:int; + + initial mode One { + counter1 = new Counter(period=250msec); + counter1.value -> count; + + reaction(next) -> Two {= + SET_MODE(Two); + =} + } + mode Two { + counter2 = new Counter(period=100msec); + + reaction(counter2.value) -> count {= + SET(count, counter2.value->value * 10); + =} + + reaction(next) -> continue(One) {= + SET_MODE(One); + =} + } +} + +reactor Counter(period:time(1sec)) { + output value:int + + timer t(0, period) + state curval:int(0) + + reaction(t) -> value {= + SET(value, self->curval++); + =} +} + +main reactor { + timer stepper(500msec, 500msec) + + modal = new Modal() + test = new TraceTesting( + events_size = 1, + trace_size = 51, + trace = ( + 0,1,0, + 250000000,1,1, + 250000000,1,2, + 0,1,0, + 100000000,1,10, + 100000000,1,20, + 100000000,1,30, + 100000000,1,40, + 100000000,1,50, + 250000000,1,3, + 250000000,1,4, + 0,1,0, + 100000000,1,10, + 100000000,1,20, + 100000000,1,30, + 100000000,1,40, + 100000000,1,50 + ), training = false) + + // Trigger mode change + reaction(stepper) -> modal.next {= + SET(modal.next, true); + =} + + // Print + reaction(modal.count) {= + printf("%d\n", modal.count->value); + =} + + modal.count + -> test.events +} \ No newline at end of file From 6978981afadc903c2aafabf0745d1a180423a046 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Thu, 10 Mar 2022 15:17:12 -0600 Subject: [PATCH 19/36] Finished porting tests. Three tests fail because of missing features --- test/Python/src/modal_models/ModalAfter.lf | 57 +++++++++---------- .../src/modal_models/ModalNestedReactions.lf | 57 +++++++++---------- .../src/modal_models/ModalStateReset.lf | 54 +++++++++--------- test/Python/src/modal_models/ModalTimers.lf | 35 ++++++------ .../MultipleOutputFeeder_2Connections.lf | 34 +++++------ ...ultipleOutputFeeder_ReactionConnections.lf | 34 +++++------ 6 files changed, 135 insertions(+), 136 deletions(-) diff --git a/test/Python/src/modal_models/ModalAfter.lf b/test/Python/src/modal_models/ModalAfter.lf index c162d347c9..4202311b4f 100644 --- a/test/Python/src/modal_models/ModalAfter.lf +++ b/test/Python/src/modal_models/ModalAfter.lf @@ -5,65 +5,65 @@ target Python { fast: false, timeout: 4 sec -}; +} import TraceTesting from "util/TraceTesting.lf" reactor Modal { - input next:bool + input next - output mode_switch:int - output produced1:int - output consumed1:int - output produced2:int - output consumed2:int + output mode_switch + output produced1 + output consumed1 + output produced2 + output consumed2 initial mode One { producer1 = new Producer(mode_id=1) consumer1 = new Consumer(mode_id=1) producer1.product -> produced1 - producer1.product -> consumer1.product after 500msec; - consumer1.report -> consumed1; + producer1.product -> consumer1.product after 500msec + consumer1.report -> consumed1 reaction(next) -> reset(Two), mode_switch {= - printf("Transitioning to mode Two (reset)\n"); - SET(mode_switch, 1); - SET_MODE(Two); + print("Transitioning to mode Two (reset)") + mode_switch.set(1) + Two.set() =} } mode Two { producer2 = new Producer(mode_id=2) consumer2 = new Consumer(mode_id=2) producer2.product -> produced2 - producer2.product -> consumer2.product after 500msec; - consumer2.report -> consumed2; + producer2.product -> consumer2.product after 500msec + consumer2.report -> consumed2 reaction(next) -> continue(One), mode_switch {= - printf("Transitioning to mode One (continue)\n"); - SET(mode_switch, 1); - SET_MODE(One); + print("Transitioning to mode One (continue)") + mode_switch.set(1) + One.set() =} } } -reactor Producer(mode_id:int(0)) { - output product:int +reactor Producer(mode_id(0)) { + output product timer t(0, 750msec) reaction(t) -> product {= - printf("Produced in %d\n", self->mode_id); - SET(product, 1); + print(f"Produced in {self.mode_id}") + product.set(1) =} } -reactor Consumer(mode_id:int(0)) { - input product:int - output report:int +reactor Consumer(mode_id(0)) { + input product + output report reaction(product) -> report {= - printf("Consumed in %d\n", self->mode_id); - SET(report, 1); + print("Consumed in {self.mode_id}") + report.set(1) =} } @@ -73,7 +73,6 @@ main reactor { modal = new Modal() test = new TraceTesting( events_size = 5, - trace_size = 165, trace = ( 0,0,0,1,1,0,0,0,0,0,0, 500000000,0,0,0,1,1,1,0,0,0,0, @@ -90,11 +89,11 @@ main reactor { 500000000,0,1,0,1,0,1,0,1,1,1, 250000000,0,1,0,1,0,1,1,1,0,1, 250000000,1,1,0,1,0,1,0,1,0,1 - ), training = false) + ), training = False) // Trigger mode change reaction(stepper) -> modal.next {= - SET(modal.next, true); + modal.next.set(True) =} modal.mode_switch, diff --git a/test/Python/src/modal_models/ModalNestedReactions.lf b/test/Python/src/modal_models/ModalNestedReactions.lf index cbb5db417f..5f134ea33f 100644 --- a/test/Python/src/modal_models/ModalNestedReactions.lf +++ b/test/Python/src/modal_models/ModalNestedReactions.lf @@ -5,44 +5,44 @@ target Python { fast: false, timeout: 2 sec -}; +} reactor CounterCycle { - input next:bool; + input next - output count:int; - output only_in_two:bool; - output never:int; + output count + output only_in_two + output never initial mode One { reaction(next) -> count, Two {= - SET(count, 1); - SET_MODE(Two); + count.set(1) + Two.set() =} } mode Two { fwd = new Forward() - next -> fwd.in + next -> fwd.inp fwd.out -> only_in_two reaction(next) -> count, One {= - SET(count, 2); - SET_MODE(One); + count.set(2) + One.set() =} } mode Three { reaction(next) -> never {= - SET(never, true); + never.set(True) =} } } reactor Forward { - input in:bool; - output out:bool; + input inp + output out - reaction(in) -> out {= - SET(out, in->value); + reaction(inp) -> out {= + out.set(inp.value) =} } @@ -52,27 +52,26 @@ main reactor { // Trigger reaction(stepper) -> counter.next {= - SET(counter.next, true); + counter.next.set(True) =} // Check reaction(stepper) counter.count, counter.only_in_two {= - printf("%d\n", counter.count->value); + print(counter.count.value) - if (!counter.count->is_present) { - printf("ERROR: Missing mode change.\n"); - exit(1); - } else if (counter.only_in_two->is_present && counter.count->value != 2) { - printf("ERROR: Indirectly nested reaction was not properly deactivated.\n"); - exit(2); - } else if (!counter.only_in_two->is_present && counter.count->value == 2) { - printf("ERROR: Missing output from indirectly nested reaction.\n"); - exit(3); - } + if counter.count.is_present is not True: + sys.stderr.write("ERROR: Missing mode change.\n") + exit(1) + elif counter.only_in_two.is_present and (counter.count.value != 2): + sys.stderr.write("ERROR: Indirectly nested reaction was not properly deactivated.\n") + exit(2) + elif counter.only_in_two.is_present is not True and (counter.count.value == 2): + sys.stderr.write("ERROR: Missing output from indirectly nested reaction.\n") + exit(3) =} reaction(counter.never) {= - printf("ERROR: Detected output from unreachable mode.\n"); - exit(4); + sys.stderr.write("ERROR: Detected output from unreachable mode.\n") + exit(4) =} } diff --git a/test/Python/src/modal_models/ModalStateReset.lf b/test/Python/src/modal_models/ModalStateReset.lf index 2239a7fa80..3ee796508e 100644 --- a/test/Python/src/modal_models/ModalStateReset.lf +++ b/test/Python/src/modal_models/ModalStateReset.lf @@ -5,53 +5,56 @@ target Python { fast: false, timeout: 4 sec -}; +} import TraceTesting from "util/TraceTesting.lf" reactor Modal { - input next:bool; + input next - output mode_switch:int - output count0:int; - output count1:int; - output count2:int; + output mode_switch + output count0 + output count1 + output count2 - state counter0:int(0); + state counter0(0) reaction(next) -> count0 {= - printf("Counter0: %d\n", self->counter0); - SET(count0, self->counter0++); + print(f"Counter0: {self.counter0}") + count0.set(self.counter0) + self.counter0 += 1 =} initial mode One { - state counter1:int(0); - timer T1(0msec, 250msec); + state counter1(0) + timer T1(0msec, 250msec) reaction(T1) -> count1 {= - printf("Counter1: %d\n", self->counter1); - SET(count1, self->counter1++); + print(f"Counter1: {self.counter1}") + count1.set(self.counter1) + self.counter1 += 1 =} reaction(next) -> reset(Two), mode_switch {= - printf("Transitioning to mode Two (reset)\n"); - SET(mode_switch, 1); - SET_MODE(Two); + print("Transitioning to mode Two (reset)") + mode_switch.set(1) + Two.set() =} } mode Two { - state counter2:int(-2); - timer T2(0msec, 250msec); + state counter2(-2) + timer T2(0msec, 250msec) reaction(T2) -> count2 {= - printf("Counter2: %d\n", self->counter2); - SET(count2, self->counter2++); + print(f"Counter2: {self.counter2}") + count2.set(self.counter2) + self.counter2 += 1 =} reaction(next) -> continue(One), mode_switch {= - printf("Transitioning to mode One (continue)\n"); - SET(mode_switch, 1); - SET_MODE(One); + print("Transitioning to mode One (continue)") + mode_switch.set(1) + One.set() =} } } @@ -62,7 +65,6 @@ main reactor { modal = new Modal() test = new TraceTesting( events_size = 4, - trace_size = 171, trace = ( 0,0,0,0,0,1,0,0,0, 250000000,0,0,0,0,1,1,0,0, @@ -83,11 +85,11 @@ main reactor { 250000000,0,1,0,2,0,8,1,0, 250000000,0,1,0,2,0,8,1,1, 250000000,1,1,1,3,0,8,1,2 - ), training = false) + ), training = False) // Trigger mode change reaction(stepper) -> modal.next {= - SET(modal.next, true); + modal.next.set(True) =} modal.mode_switch, diff --git a/test/Python/src/modal_models/ModalTimers.lf b/test/Python/src/modal_models/ModalTimers.lf index 02ce11ab3b..8883a4819d 100644 --- a/test/Python/src/modal_models/ModalTimers.lf +++ b/test/Python/src/modal_models/ModalTimers.lf @@ -5,43 +5,43 @@ target Python { fast: false, timeout: 4 sec -}; +} import TraceTesting from "util/TraceTesting.lf" reactor Modal { - input next:bool + input next - output mode_switch:int - output timer1:int - output timer2:int + output mode_switch + output timer1 + output timer2 initial mode One { timer T1(0, 750msec) reaction(T1) -> timer1 {= - printf("T1\n"); - SET(timer1, 1); + print("T1") + timer1.set(1) =} reaction(next) -> reset(Two), mode_switch {= - printf("Transitioning to mode Two (reset)\n"); - SET(mode_switch, 1); - SET_MODE(Two); + print("Transitioning to mode Two (reset)") + mode_switch.set(1) + Two.set() =} } mode Two { timer T2(0, 750msec) reaction(T2) -> timer2 {= - printf("T2\n"); - SET(timer2, 1); + print("T2") + timer2.set(1) =} reaction(next) -> continue(One), mode_switch {= - printf("Transitioning to mode One (continue)\n"); - SET(mode_switch, 1); - SET_MODE(One); + print("Transitioning to mode One (continue)") + mode_switch.set(1) + One.set() =} } } @@ -52,7 +52,6 @@ main reactor { modal = new Modal() test = new TraceTesting( events_size = 3, - trace_size = 77, trace = ( 0,0,0,1,1,0,0, 750000000,0,0,1,1,0,0, @@ -65,11 +64,11 @@ main reactor { 0,0,1,0,1,1,1, 750000000,0,1,0,1,1,1, 250000000,1,1,0,1,0,1 - ), training = false) + ), training = False) // Trigger mode change reaction(stepper) -> modal.next {= - SET(modal.next, true); + modal.next.set(True) =} modal.mode_switch, diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf index 36e399ceb1..6a6ef990d4 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -6,40 +6,41 @@ target Python { fast: false, timeout: 2 sec -}; +} import TraceTesting from "util/TraceTesting.lf" reactor Modal { - input next:bool; - output count:int; + input next + output count initial mode One { - counter1 = new Counter(period=250msec); - counter1.value -> count; + counter1 = new Counter(period=250msec) + counter1.value -> count reaction(next) -> Two {= - SET_MODE(Two); + Two.set() =} } mode Two { - counter2 = new Counter(period=100msec); - counter2.value -> count; + counter2 = new Counter(period=100msec) + counter2.value -> count reaction(next) -> continue(One) {= - SET_MODE(One); + One.set() =} } } -reactor Counter(period:time(1sec)) { - output value:int +reactor Counter(period(1sec)) { + output value timer t(0, period) - state curval:int(0) + state curval(0) reaction(t) -> value {= - SET(value, self->curval++); + value.set(self.curval) + self.curval += 1 =} } @@ -49,7 +50,6 @@ main reactor { modal = new Modal() test = new TraceTesting( events_size = 1, - trace_size = 51, trace = ( 0,1,0, 250000000,1,1, @@ -68,16 +68,16 @@ main reactor { 100000000,1,3, 100000000,1,4, 100000000,1,5 - ), training = false) + ), training = False) // Trigger mode change reaction(stepper) -> modal.next {= - SET(modal.next, true); + modal.next.set(True) =} // Print reaction(modal.count) {= - printf("%d\n", modal.count->value); + print(modal.count.value) =} modal.count diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf index 52b9e834c6..5bb5d18fdc 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_ReactionConnections.lf @@ -6,43 +6,44 @@ target Python { fast: false, timeout: 2 sec -}; +} import TraceTesting from "util/TraceTesting.lf" reactor Modal { - input next:bool; - output count:int; + input next + output count initial mode One { - counter1 = new Counter(period=250msec); - counter1.value -> count; + counter1 = new Counter(period=250msec) + counter1.value -> count reaction(next) -> Two {= - SET_MODE(Two); + Two.set() =} } mode Two { - counter2 = new Counter(period=100msec); + counter2 = new Counter(period=100msec) reaction(counter2.value) -> count {= - SET(count, counter2.value->value * 10); + count.set(counter2.value.value * 10) =} reaction(next) -> continue(One) {= - SET_MODE(One); + One.set() =} } } -reactor Counter(period:time(1sec)) { - output value:int +reactor Counter(period(1sec)) { + output value timer t(0, period) - state curval:int(0) + state curval(0) reaction(t) -> value {= - SET(value, self->curval++); + value.set(self.curval) + self.curval += 1 =} } @@ -52,7 +53,6 @@ main reactor { modal = new Modal() test = new TraceTesting( events_size = 1, - trace_size = 51, trace = ( 0,1,0, 250000000,1,1, @@ -71,16 +71,16 @@ main reactor { 100000000,1,30, 100000000,1,40, 100000000,1,50 - ), training = false) + ), training = False) // Trigger mode change reaction(stepper) -> modal.next {= - SET(modal.next, true); + modal.next.set(True) =} // Print reaction(modal.count) {= - printf("%d\n", modal.count->value); + print(modal.count.value) =} modal.count From e68a4c78fe40b3b5044e0914f2d06ff42b20850e Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 11 Mar 2022 11:36:46 -0800 Subject: [PATCH 20/36] bug fix --- org.lflang/src/org/lflang/generator/c/CModesGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java index 198a7a3fc2..bbb3c7a8ab 100644 --- a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java @@ -58,7 +58,7 @@ public static void generateDeclarations( "// Initialize mode state", "_lf_self_base->_lf__mode_state.parent_mode = NULL;", "_lf_self_base->_lf__mode_state.initial_mode = &self->_lf__modes["+initialMode+"];", - "_lf_self_base->_lf__mode_state.active_mode = self->_lf__mode_state.initial_mode;", + "_lf_self_base->_lf__mode_state.active_mode = _lf_self_base->_lf__mode_state.initial_mode;", "_lf_self_base->_lf__mode_state.next_mode = NULL;", "_lf_self_base->_lf__mode_state.mode_change = 0;" )); From 08486bef5bdf159d704139c22ab65b924504befc Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 11 Mar 2022 13:31:42 -0800 Subject: [PATCH 21/36] bug fix --- org.lflang/src/org/lflang/generator/c/CModesGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java index bbb3c7a8ab..c887198d4f 100644 --- a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java @@ -60,7 +60,7 @@ public static void generateDeclarations( "_lf_self_base->_lf__mode_state.initial_mode = &self->_lf__modes["+initialMode+"];", "_lf_self_base->_lf__mode_state.active_mode = _lf_self_base->_lf__mode_state.initial_mode;", "_lf_self_base->_lf__mode_state.next_mode = NULL;", - "_lf_self_base->_lf__mode_state.mode_change = 0;" + "_lf_self_base->_lf__mode_state.mode_change = no_transition;" )); } } From 7639be74c36d60a00a7bc2f43e8189cbd7ecda17 Mon Sep 17 00:00:00 2001 From: eal Date: Fri, 11 Mar 2022 11:50:33 -0800 Subject: [PATCH 22/36] Align reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 01b0defbf9..8d0972f7a4 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 01b0defbf91c8938cc742e75a02d9d8fb88b86db +Subproject commit 8d0972f7a44cee8efc7448e140e834f97f2fad73 From 5728000ba0440660a5fcd75ccb347792c1f69de0 Mon Sep 17 00:00:00 2001 From: eal Date: Fri, 11 Mar 2022 18:41:48 -0800 Subject: [PATCH 23/36] Retrained test to get the right data types for training data --- .../src/modal_models/ConvertCaseTest.lf | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/Python/src/modal_models/ConvertCaseTest.lf b/test/Python/src/modal_models/ConvertCaseTest.lf index e80fd5429b..2c5150da21 100644 --- a/test/Python/src/modal_models/ConvertCaseTest.lf +++ b/test/Python/src/modal_models/ConvertCaseTest.lf @@ -109,18 +109,18 @@ main reactor { test = new TraceTesting( events_size = 2, trace = ( - 0,1,72,1,72, - 250000000,1,69,1,69, - 250000000,1,76,1,76, - 250000000,1,95,1,95, - 250000000,1,79,1,79, - 250000000,1,32,1,32, - 250000000,1,119,1,119, - 250000000,1,95,1,95, - 250000000,1,82,1,114, - 250000000,1,76,1,108, - 250000000,1,68,1,100, - 250000000,1,95,1,95 + 0, True, 'H', True, 'H', + 250000000, True, 'E', True, 'E', + 250000000, True, 'L', True, 'L', + 250000000, True, '_', True, '_', + 250000000, True, 'O', True, 'O', + 250000000, True, ' ', True, ' ', + 250000000, True, 'w', True, 'w', + 250000000, True, '_', True, '_', + 250000000, True, 'R', True, 'r', + 250000000, True, 'L', True, 'l', + 250000000, True, 'D', True, 'd', + 250000000, True, '_', True, '_' ), training = False) # Trigger mode change From a02a4325ffcf54bf90c69c917838c4f836611452 Mon Sep 17 00:00:00 2001 From: eal Date: Fri, 11 Mar 2022 18:44:30 -0800 Subject: [PATCH 24/36] Print tags --- test/Python/src/modal_models/ModalTimers.lf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Python/src/modal_models/ModalTimers.lf b/test/Python/src/modal_models/ModalTimers.lf index 8883a4819d..cddd17aa6a 100644 --- a/test/Python/src/modal_models/ModalTimers.lf +++ b/test/Python/src/modal_models/ModalTimers.lf @@ -20,7 +20,7 @@ reactor Modal { timer T1(0, 750msec) reaction(T1) -> timer1 {= - print("T1") + print(f"T1: at tag ({get_elapsed_logical_time()}, {get_microstep()})") timer1.set(1) =} @@ -34,7 +34,7 @@ reactor Modal { timer T2(0, 750msec) reaction(T2) -> timer2 {= - print("T2") + print(f"T2: at tag ({get_elapsed_logical_time()}, {get_microstep()})") timer2.set(1) =} From 2d278c40b23cf17817a5cef6a8df5d0f40ae059f Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 09:34:00 -0600 Subject: [PATCH 25/36] Added AST transformation for startup triggers in modes (not behaving as expected) --- .../org/lflang/generator/GeneratorBase.java | 46 ++++++++++++ test/C/src/modal_models/ModalStartup.lf | 75 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 test/C/src/modal_models/ModalStartup.lf diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index f5a7895e1c..227df44e7e 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -381,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); + transformStartupTriggersInModalReactors(); enableSupportForSerializationIfApplicable(context.getCancelIndicator()); } @@ -643,6 +644,51 @@ protected String getConflictingConnectionsInModalReactorsBody(String source, Str "transforming connections in modal reactors."); return "MODAL MODELS NOT SUPPORTED"; } + + /** + * Transform the startup trigger in modes to a timer with an offset and a period of zero. + * + * This allows reactions in modes with startup in their trigger to be triggered when the mode + * is entered for the first time or via a reset transition. + */ + protected void transformStartupTriggersInModalReactors() { + for (Reactor reactor : reactors) { + var reactorModes = reactor.getModes(); + if (!reactorModes.isEmpty()) { + for (Mode mode : reactorModes) { + // Create the timer with an offset and period of zero + var zeroTime = LfFactory.eINSTANCE.createTime(); + zeroTime.setInterval(0); + zeroTime.setUnit("msec"); + var zeroValue = LfFactory.eINSTANCE.createValue(); + zeroValue.setTime(zeroTime); + var timer = LfFactory.eINSTANCE.createTimer(); + timer.setOffset(zeroValue); + timer.setPeriod(zeroValue); + timer.setName("_lf_startup_timer_for_mode_"+mode.getName()); + + // Replace startup triggers + boolean foundAtLeastOneStartupTriggerInMode = false; + for (Reaction reaction : mode.getReactions()) { + var hadStartupTrigger = reaction.getTriggers() + .removeIf(trigger -> trigger.isStartup()); + if (hadStartupTrigger) { + var timerRef = LfFactory.eINSTANCE.createVarRef(); + timerRef.setVariable(timer); + reaction.getTriggers().add(timerRef); + + foundAtLeastOneStartupTriggerInMode = true; + } + } + + if (foundAtLeastOneStartupTriggerInMode) { + // Add the timer to the mode + mode.getTimers().add(timer); + } + } + } + } + } /** * Generate code for the body of a reaction that handles the diff --git a/test/C/src/modal_models/ModalStartup.lf b/test/C/src/modal_models/ModalStartup.lf new file mode 100644 index 0000000000..5d772edb5b --- /dev/null +++ b/test/C/src/modal_models/ModalStartup.lf @@ -0,0 +1,75 @@ +/* + * Modal Reactor Test. + * Test startup reactions in modes. + */ +target C { + fast: false, + timeout: 4 sec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next:bool + + output mode_switch:int + output timer1:int + output timer2:int + + initial mode One { + reaction(startup) -> timer1 {= + printf("T1\n"); + SET(timer1, 1); + =} + + reaction(next) -> reset(Two), mode_switch {= + printf("Transitioning to mode Two (reset)\n"); + SET(mode_switch, 1); + SET_MODE(Two); + =} + } + mode Two { + reaction(startup) -> timer2 {= + printf("T2\n"); + SET(timer2, 1); + =} + + reaction(next) -> continue(One), mode_switch {= + printf("Transitioning to mode One (continue)\n"); + SET(mode_switch, 1); + SET_MODE(One); + =} + } +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 3, + trace_size = 77, + trace = ( + 0,0,0,1,1,0,0, + 750000000,0,0,1,1,0,0, + 250000000,1,1,0,1,0,0, + 0,0,1,0,1,1,1, + 750000000,0,1,0,1,1,1, + 250000000,1,1,0,1,0,1, + 500000000,0,1,1,1,0,1, + 500000000,1,1,0,1,0,1, + 0,0,1,0,1,1,1, + 750000000,0,1,0,1,1,1, + 250000000,1,1,0,1,0,1 + ), training = true) + + // Trigger mode change + reaction(stepper) -> modal.next {= + SET(modal.next, true); + =} + + modal.mode_switch, + modal.timer1, + modal.timer2 + -> test.events +} From a0fac1889e96cc526bc9819f634132a1ef155581 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 09:56:39 -0600 Subject: [PATCH 26/36] Cleaned up the code a bit --- .../org/lflang/generator/GeneratorBase.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 227df44e7e..0aa3d3b98d 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -652,19 +652,23 @@ protected String getConflictingConnectionsInModalReactorsBody(String source, Str * is entered for the first time or via a reset transition. */ protected void transformStartupTriggersInModalReactors() { + // Construct a timer with an offset and a period of zero + var zeroTime = LfFactory.eINSTANCE.createTime(); + zeroTime.setInterval(0); + zeroTime.setUnit("msec"); + var zeroValue = LfFactory.eINSTANCE.createValue(); + zeroValue.setTime(zeroTime); + var baseTimer = LfFactory.eINSTANCE.createTimer(); + baseTimer.setOffset(zeroValue); + baseTimer.setPeriod(zeroValue); + + // Look for reactors with modes for (Reactor reactor : reactors) { var reactorModes = reactor.getModes(); if (!reactorModes.isEmpty()) { for (Mode mode : reactorModes) { // Create the timer with an offset and period of zero - var zeroTime = LfFactory.eINSTANCE.createTime(); - zeroTime.setInterval(0); - zeroTime.setUnit("msec"); - var zeroValue = LfFactory.eINSTANCE.createValue(); - zeroValue.setTime(zeroTime); - var timer = LfFactory.eINSTANCE.createTimer(); - timer.setOffset(zeroValue); - timer.setPeriod(zeroValue); + var timer = EcoreUtil.copy(baseTimer); timer.setName("_lf_startup_timer_for_mode_"+mode.getName()); // Replace startup triggers From 924e9e86cd09c56c36867894edccdb50684f8894 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 16:50:20 -0600 Subject: [PATCH 27/36] Pass the appropriate arguments to _lf_handle_mode_changes. Cleaned up the imports. --- .../org/lflang/generator/c/CGenerator.xtend | 146 +++++++++--------- 1 file changed, 69 insertions(+), 77 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 22c4f04f9d..032829c753 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -26,82 +26,67 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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; -import java.util.LinkedList; -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; -import org.lflang.InferredType; -import org.lflang.ASTUtils; -import org.lflang.Target; -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; -import org.lflang.federated.FederateInstance; -import org.lflang.federated.launcher.FedCLauncher; -import org.lflang.federated.serialization.FedROS2CPPSerialization; -import org.lflang.federated.serialization.SupportedSerializers; -import org.lflang.generator.CodeBuilder; -import org.lflang.generator.GeneratorBase; -import org.lflang.generator.DockerComposeGenerator; -import org.lflang.generator.GeneratorResult; -import org.lflang.generator.IntegratedBuilder; -import org.lflang.generator.GeneratorUtils; -import org.lflang.generator.LFGeneratorContext; -import org.lflang.generator.PortInstance; -import org.lflang.generator.ReactionInstance; -import org.lflang.generator.ReactorInstance; -import org.lflang.generator.RuntimeRange; -import org.lflang.generator.SendRange; -import org.lflang.generator.SubContext; -import org.lflang.generator.TriggerInstance; -import org.lflang.generator.c.CActionGenerator; -import org.lflang.generator.c.CTimerGenerator; -import org.lflang.generator.c.CStateGenerator; -import org.lflang.generator.c.CTracingGenerator; -import org.lflang.generator.c.CPortGenerator; -import org.lflang.generator.c.CModesGenerator; -import org.lflang.generator.c.CMainGenerator; -import org.lflang.generator.c.CFederateGenerator; -import org.lflang.generator.c.CNetworkGenerator; -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; -import org.lflang.lf.Port; -import org.lflang.lf.Reaction; -import org.lflang.lf.Reactor; -import org.lflang.lf.ReactorDecl; -import org.lflang.lf.VarRef; -import org.lflang.lf.Variable; -import org.lflang.util.FileUtil; -import org.lflang.util.XtendUtil; - -import static extension org.lflang.ASTUtils.*; -import static extension org.lflang.ASTUtils.*; +import java.io.File +import java.nio.file.Files +import java.util.ArrayList +import java.util.HashSet +import java.util.LinkedHashMap +import java.util.LinkedHashSet +import java.util.LinkedList +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.xtext.util.CancelIndicator +import org.lflang.ASTUtils +import org.lflang.ErrorReporter +import org.lflang.FileConfig +import org.lflang.InferredType +import org.lflang.Target +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 +import org.lflang.federated.FederateInstance +import org.lflang.federated.launcher.FedCLauncher +import org.lflang.federated.serialization.FedROS2CPPSerialization +import org.lflang.federated.serialization.SupportedSerializers +import org.lflang.generator.CodeBuilder +import org.lflang.generator.DockerComposeGenerator +import org.lflang.generator.GeneratorBase +import org.lflang.generator.GeneratorResult +import org.lflang.generator.GeneratorUtils +import org.lflang.generator.IntegratedBuilder +import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.PortInstance +import org.lflang.generator.ReactionInstance +import org.lflang.generator.ReactorInstance +import org.lflang.generator.RuntimeRange +import org.lflang.generator.SendRange +import org.lflang.generator.SubContext +import org.lflang.generator.TriggerInstance +import org.lflang.lf.Action +import org.lflang.lf.ActionOrigin +import org.lflang.lf.Delay +import org.lflang.lf.Input +import org.lflang.lf.Instantiation +import org.lflang.lf.Mode +import org.lflang.lf.Model +import org.lflang.lf.Output +import org.lflang.lf.Port +import org.lflang.lf.Reaction +import org.lflang.lf.Reactor +import org.lflang.lf.ReactorDecl +import org.lflang.lf.VarRef +import org.lflang.lf.Variable +import org.lflang.util.FileUtil +import org.lflang.util.XtendUtil + +import static extension org.lflang.ASTUtils.* /** * Generator for C target. This class generates C code defining each reactor @@ -763,7 +748,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 + ); } ''') } From a38bab602a83472d2e5ab85284dbb95427e037ff Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 16:52:25 -0600 Subject: [PATCH 28/36] Updated test to test startup triggers --- org.lflang/src/lib/c/reactor-c | 2 +- .../org/lflang/generator/GeneratorBase.java | 2 +- test/C/src/modal_models/ModalStartup.lf | 65 ++++++++++++------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 8d0972f7a4..e0e3451f50 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 8d0972f7a44cee8efc7448e140e834f97f2fad73 +Subproject commit e0e3451f5068cabce0cd185e44ea65fdbed05c8e diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 0aa3d3b98d..562b0aa308 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -671,7 +671,7 @@ protected void transformStartupTriggersInModalReactors() { var timer = EcoreUtil.copy(baseTimer); timer.setName("_lf_startup_timer_for_mode_"+mode.getName()); - // Replace startup triggers + // Replace startup triggers with the timer boolean foundAtLeastOneStartupTriggerInMode = false; for (Reaction reaction : mode.getReactions()) { var hadStartupTrigger = reaction.getTriggers() diff --git a/test/C/src/modal_models/ModalStartup.lf b/test/C/src/modal_models/ModalStartup.lf index 5d772edb5b..b4324a47cd 100644 --- a/test/C/src/modal_models/ModalStartup.lf +++ b/test/C/src/modal_models/ModalStartup.lf @@ -4,7 +4,7 @@ */ target C { fast: false, - timeout: 4 sec + timeout: 4100 msec }; import TraceTesting from "util/TraceTesting.lf" @@ -13,13 +13,14 @@ reactor Modal { input next:bool output mode_switch:int - output timer1:int - output timer2:int + output startup1:int + output startup2:int + output startup3:int initial mode One { - reaction(startup) -> timer1 {= - printf("T1\n"); - SET(timer1, 1); + reaction(startup) -> startup1 {= + printf("Startup 1 at (%ld, %u).\n", get_elapsed_logical_time(), get_microstep()); + SET(startup1, 1); =} reaction(next) -> reset(Two), mode_switch {= @@ -29,9 +30,22 @@ reactor Modal { =} } mode Two { - reaction(startup) -> timer2 {= - printf("T2\n"); - SET(timer2, 1); + reaction(startup) -> startup2 {= + printf("Startup 2 at (%ld, %u).\n", get_elapsed_logical_time(), get_microstep()); + SET(startup2, 1); + =} + + reaction(next) -> continue(Three), mode_switch {= + printf("Transitioning to mode Three (continue)\n"); + SET(mode_switch, 1); + SET_MODE(Three); + =} + } + + mode Three { + reaction(startup) -> startup3 {= + printf("Startup 3 at (%ld, %u).\n", get_elapsed_logical_time(), get_microstep()); + SET(startup3, 1); =} reaction(next) -> continue(One), mode_switch {= @@ -47,21 +61,23 @@ main reactor { modal = new Modal() test = new TraceTesting( - events_size = 3, - trace_size = 77, + events_size = 4, + trace_size = 72, trace = ( 0,0,0,1,1,0,0, - 750000000,0,0,1,1,0,0, - 250000000,1,1,0,1,0,0, - 0,0,1,0,1,1,1, - 750000000,0,1,0,1,1,1, - 250000000,1,1,0,1,0,1, - 500000000,0,1,1,1,0,1, - 500000000,1,1,0,1,0,1, - 0,0,1,0,1,1,1, - 750000000,0,1,0,1,1,1, - 250000000,1,1,0,1,0,1 - ), training = true) + 0,0,1000000000, + 1,1,0,1,0,0,0, + 0,0,0,1,0,1,1, + 1,0,0,1000000000, + 1,1,0,1,0,1,0,0, + 0,0,1,0,1,0,1,1,1, + 1000000000,1,1,0, + 1,0,1,0,1, + 1000000000,1,1,0,1, + 0,1,0,1,0,0,1,0,1, + 1,1,0,1), + training = false + ) // Trigger mode change reaction(stepper) -> modal.next {= @@ -69,7 +85,8 @@ main reactor { =} modal.mode_switch, - modal.timer1, - modal.timer2 + modal.startup1, + modal.startup2, + modal.startup3 -> test.events } From 3bf7a1d1a2526b48a0e2bc9008458ff0dcc6685e Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 17:04:58 -0600 Subject: [PATCH 29/36] Ported the startup test to Python --- test/Python/src/modal_models/ModalStartup.lf | 91 ++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/Python/src/modal_models/ModalStartup.lf diff --git a/test/Python/src/modal_models/ModalStartup.lf b/test/Python/src/modal_models/ModalStartup.lf new file mode 100644 index 0000000000..ff33f9fbcb --- /dev/null +++ b/test/Python/src/modal_models/ModalStartup.lf @@ -0,0 +1,91 @@ +/* + * Modal Reactor Test. + * Test startup reactions in modes. + */ +target Python { + fast: false, + timeout: 4100 msec +}; + +import TraceTesting from "util/TraceTesting.lf" + +reactor Modal { + input next + + output mode_switch + output startup1 + output startup2 + output startup3 + + initial mode One { + reaction(startup) -> startup1 {= + print(f"Startup 1 at ({get_elapsed_logical_time()}, {get_microstep()}).") + startup1.set(1) + =} + + reaction(next) -> reset(Two), mode_switch {= + print("Transitioning to mode Two (reset)") + mode_switch.set(1) + Two.set() + =} + } + mode Two { + reaction(startup) -> startup2 {= + print(f"Startup 2 at at ({get_elapsed_logical_time()}, {get_microstep()}).") + startup2.set(1) + =} + + reaction(next) -> continue(Three), mode_switch {= + print("Transitioning to mode Three (continue)") + mode_switch.set(1) + Three.set() + =} + } + + mode Three { + reaction(startup) -> startup3 {= + print(f"Startup 3 at at ({get_elapsed_logical_time()}, {get_microstep()}).") + startup3.set(1) + =} + + reaction(next) -> continue(One), mode_switch {= + print("Transitioning to mode One (continue)") + mode_switch.set(1) + One.set() + =} + } +} + +main reactor { + timer stepper(1sec, 1sec) + + modal = new Modal() + test = new TraceTesting( + events_size = 4, + trace = ( + 0,0,0,1,1,0,0, + 0,0,1000000000, + 1,1,0,1,0,0,0, + 0,0,0,1,0,1,1, + 1,0,0,1000000000, + 1,1,0,1,0,1,0,0, + 0,0,1,0,1,0,1,1,1, + 1000000000,1,1,0, + 1,0,1,0,1, + 1000000000,1,1,0,1, + 0,1,0,1,0,0,1,0,1, + 1,1,0,1), + training = False + ) + + // Trigger mode change + reaction(stepper) -> modal.next {= + modal.next.set(True) + =} + + modal.mode_switch, + modal.startup1, + modal.startup2, + modal.startup3 + -> test.events +} From 7a890b0742f3482aaa344e95bf6952f0cb329693 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 19:54:04 -0600 Subject: [PATCH 30/36] Revert seemingly faulty strategy of replacing startup triggers with timers --- .../org/lflang/generator/GeneratorBase.java | 55 +++---------------- 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.java b/org.lflang/src/org/lflang/generator/GeneratorBase.java index 562b0aa308..64538cd901 100644 --- a/org.lflang/src/org/lflang/generator/GeneratorBase.java +++ b/org.lflang/src/org/lflang/generator/GeneratorBase.java @@ -381,7 +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); - transformStartupTriggersInModalReactors(); + generateStartupReactionsInModesIfNeeded(); enableSupportForSerializationIfApplicable(context.getCancelIndicator()); } @@ -646,52 +646,15 @@ protected String getConflictingConnectionsInModalReactorsBody(String source, Str } /** - * Transform the startup trigger in modes to a timer with an offset and a period of zero. + * Generate startup reactions in modes. * - * This allows reactions in modes with startup in their trigger to be triggered when the mode - * is entered for the first time or via a reset transition. - */ - protected void transformStartupTriggersInModalReactors() { - // Construct a timer with an offset and a period of zero - var zeroTime = LfFactory.eINSTANCE.createTime(); - zeroTime.setInterval(0); - zeroTime.setUnit("msec"); - var zeroValue = LfFactory.eINSTANCE.createValue(); - zeroValue.setTime(zeroTime); - var baseTimer = LfFactory.eINSTANCE.createTimer(); - baseTimer.setOffset(zeroValue); - baseTimer.setPeriod(zeroValue); - - // Look for reactors with modes - for (Reactor reactor : reactors) { - var reactorModes = reactor.getModes(); - if (!reactorModes.isEmpty()) { - for (Mode mode : reactorModes) { - // Create the timer with an offset and period of zero - var timer = EcoreUtil.copy(baseTimer); - timer.setName("_lf_startup_timer_for_mode_"+mode.getName()); - - // Replace startup triggers with the timer - boolean foundAtLeastOneStartupTriggerInMode = false; - for (Reaction reaction : mode.getReactions()) { - var hadStartupTrigger = reaction.getTriggers() - .removeIf(trigger -> trigger.isStartup()); - if (hadStartupTrigger) { - var timerRef = LfFactory.eINSTANCE.createVarRef(); - timerRef.setVariable(timer); - reaction.getTriggers().add(timerRef); - - foundAtLeastOneStartupTriggerInMode = true; - } - } - - if (foundAtLeastOneStartupTriggerInMode) { - // Add the timer to the mode - mode.getTimers().add(timer); - } - } - } - } + * 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 } /** From b2fc05c88584153c1008cab16cba979622dfba82 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 19:55:24 -0600 Subject: [PATCH 31/36] Added AST transformation for Python that adds startup reactions to all reactors and their modes to reset state variables --- org.lflang/src/lib/c/reactor-c | 2 +- .../generator/python/PythonGenerator.java | 8 ++ .../generator/python/PythonModeGenerator.java | 93 +++++++++++++++++++ .../python/PythonStateGenerator.java | 2 +- 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index e0e3451f50..522854333e 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit e0e3451f5068cabce0cd185e44ea65fdbed05c8e +Subproject commit 522854333e6461aeaa8322149730760e553a0381 diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index 64b89741a2..4777069c7d 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -887,6 +887,14 @@ protected void setUpParameters(LFGeneratorContext context) { targetConfig.compileAdditionalSources.add("modal_models/impl.c"); } } + + @Override + protected void generateStartupReactionsInModesIfNeeded() { + if (!hasModalReactors) { + return; + } + PythonModeGenerator.generateStartupReactionsInModesIfNeeded(reactors); + } private static String generateMacroEntry(String key, String val) { return "(" + StringUtil.addDoubleQuotes(key) + ", " + StringUtil.addDoubleQuotes(val) + ")"; diff --git a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java new file mode 100644 index 0000000000..35491de90b --- /dev/null +++ b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java @@ -0,0 +1,93 @@ +package org.lflang.generator.python; + +import java.util.List; + +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.lflang.generator.CodeBuilder; +import org.lflang.lf.LfFactory; +import org.lflang.lf.Mode; +import org.lflang.lf.Reaction; +import org.lflang.lf.Reactor; +import org.lflang.lf.TriggerRef; + +/** + * Helper class to handle modes in Python programs. + * + * @author{Soroush Bateni } + * + */ +public class PythonModeGenerator { + /** + * 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. + * + * @param reactors A list of reactors in the program, some of which could contain modes. + */ + public static void generateStartupReactionsInModesIfNeeded(List reactors) { + for (Reactor reactor : reactors) { + generateStartupReactionsInReactor(reactor); + } + } + + /** + * Generate startup reactions that reset state variables in + * - the reactor, and, + * - the modes within the reactor. + * + * @param reactor The reactor. + */ + private static void generateStartupReactionsInReactor(Reactor reactor) { + + // Create a reaction with a startup trigger + TriggerRef startupTrigger = LfFactory.eINSTANCE.createTriggerRef(); + startupTrigger.setStartup(true); + Reaction baseReaction = LfFactory.eINSTANCE.createReaction(); + baseReaction.getTriggers().add(startupTrigger); + + // Create a reaction body that resets all state variables to their initial value. + var reactionBody = LfFactory.eINSTANCE.createCode(); + CodeBuilder code = new CodeBuilder(); + for (var state: reactor.getStateVars()) { + code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + } + reactionBody.setBody(code.toString()); + baseReaction.setCode(reactionBody); + + reactor.getReactions().add(0, baseReaction); + + + var reactorModes = reactor.getModes(); + if (!reactorModes.isEmpty()) { + for (Mode mode : reactorModes) { + if (mode.getStateVars().isEmpty()) { + continue; + } + Reaction reaction = EcoreUtil.copy(baseReaction); + + // Create a reaction body that resets all state variables to their initial value. + reactionBody = LfFactory.eINSTANCE.createCode(); + code = new CodeBuilder(); + for (var state: mode.getStateVars()) { + code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + } + reactionBody.setBody(code.toString()); + reaction.setCode(reactionBody); + + try { + mode.getReactions().add(0, reaction); + } catch (IndexOutOfBoundsException e) { + // There are no scoping for state variables. + // We add this reaction for now so that it + // still resets state variables even if there + // are no reactions in this mode. + mode.getReactions().add(reaction); + } + + } + } + } +} diff --git a/org.lflang/src/org/lflang/generator/python/PythonStateGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonStateGenerator.java index 7441f53225..78745d24d5 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonStateGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonStateGenerator.java @@ -31,7 +31,7 @@ public static String generatePythonInstantiations(ReactorDecl decl) { * Handle initialization for state variable * @param state a state variable */ - private static String generatePythonInitializer(StateVar state) { + public static String generatePythonInitializer(StateVar state) { if (!ASTUtils.isInitialized(state)) { return "None"; } From 76db8db5f367261131cacc7d2506e996665ec9d3 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Sat, 12 Mar 2022 20:01:00 -0600 Subject: [PATCH 32/36] Only generate startup reactions if there are state vars --- .../generator/python/PythonModeGenerator.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java index 35491de90b..eeca3d59c5 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java @@ -48,16 +48,19 @@ private static void generateStartupReactionsInReactor(Reactor reactor) { Reaction baseReaction = LfFactory.eINSTANCE.createReaction(); baseReaction.getTriggers().add(startupTrigger); - // Create a reaction body that resets all state variables to their initial value. - var reactionBody = LfFactory.eINSTANCE.createCode(); - CodeBuilder code = new CodeBuilder(); - for (var state: reactor.getStateVars()) { - code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + if (!reactor.getStateVars().isEmpty()) { + // Create a reaction body that resets all state variables to their initial value. + var reactionBody = LfFactory.eINSTANCE.createCode(); + CodeBuilder code = new CodeBuilder(); + code.pr("# Reset the following state variables to their initial value."); + for (var state: reactor.getStateVars()) { + code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); + } + reactionBody.setBody(code.toString()); + baseReaction.setCode(reactionBody); + + reactor.getReactions().add(0, baseReaction); } - reactionBody.setBody(code.toString()); - baseReaction.setCode(reactionBody); - - reactor.getReactions().add(0, baseReaction); var reactorModes = reactor.getModes(); @@ -69,8 +72,9 @@ private static void generateStartupReactionsInReactor(Reactor reactor) { Reaction reaction = EcoreUtil.copy(baseReaction); // Create a reaction body that resets all state variables to their initial value. - reactionBody = LfFactory.eINSTANCE.createCode(); - code = new CodeBuilder(); + var reactionBody = LfFactory.eINSTANCE.createCode(); + CodeBuilder code = new CodeBuilder(); + code.pr("# Reset the following state variables to their initial value."); for (var state: mode.getStateVars()) { code.pr("self."+state.getName()+" = "+PythonStateGenerator.generatePythonInitializer(state)); } From aad8f5fd7e6ab06af892347c131edfb525c64513 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 14 Mar 2022 02:07:42 -0500 Subject: [PATCH 33/36] First working prototype for startup reactions (only for reset transitions) --- org.lflang/src/lib/c/reactor-c | 2 +- .../org/lflang/generator/c/CGenerator.xtend | 22 +++++-------------- .../generator/python/PythonModeGenerator.java | 3 ++- test/C/src/modal_models/ModalStartup.lf | 20 ++++++++--------- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 522854333e..9ea25d0d90 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 522854333e6461aeaa8322149730760e553a0381 +Subproject commit 9ea25d0d909251ebe3200b2cfd346a4ec13a2381 diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 032829c753..5aa71440c0 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -596,22 +596,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(''' @@ -1010,6 +995,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)); diff --git a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java index eeca3d59c5..dc465c357b 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonModeGenerator.java @@ -49,7 +49,8 @@ private static void generateStartupReactionsInReactor(Reactor reactor) { baseReaction.getTriggers().add(startupTrigger); if (!reactor.getStateVars().isEmpty()) { - // Create a reaction body that resets all state variables to their initial value. + // Create a reaction body that resets all state variables (that are not in a mode) + // to their initial value. var reactionBody = LfFactory.eINSTANCE.createCode(); CodeBuilder code = new CodeBuilder(); code.pr("# Reset the following state variables to their initial value."); diff --git a/test/C/src/modal_models/ModalStartup.lf b/test/C/src/modal_models/ModalStartup.lf index b4324a47cd..5244f16b01 100644 --- a/test/C/src/modal_models/ModalStartup.lf +++ b/test/C/src/modal_models/ModalStartup.lf @@ -64,18 +64,16 @@ main reactor { events_size = 4, trace_size = 72, trace = ( - 0,0,0,1,1,0,0, - 0,0,1000000000, - 1,1,0,1,0,0,0, - 0,0,0,1,0,1,1, - 1,0,0,1000000000, - 1,1,0,1,0,1,0,0, - 0,0,1,0,1,0,1,1,1, - 1000000000,1,1,0, - 1,0,1,0,1, + 0,0,0,1,1,0,0,0,0, 1000000000,1,1,0,1, - 0,1,0,1,0,0,1,0,1, - 1,1,0,1), + 0,0,0,0,0,0,1,0,1,1, + 1,0,0,1000000000,1, + 1,0,1,0,1,0,0, + 1000000000,1,1,0,1,0, + 1,0,0,1000000000,1,1, + 0,1,0,1,0,0,0,0,1,0, + 1,1,1,0,0 + ), training = false ) From 348692fe05ef1abf612b61b99862ac29dde7257d Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 14 Mar 2022 02:21:15 -0500 Subject: [PATCH 34/36] Updated test and reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- test/Python/src/modal_models/ModalStartup.lf | 21 ++++++++----------- .../MultipleOutputFeeder_2Connections.lf | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 9ea25d0d90..9445252028 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 9ea25d0d909251ebe3200b2cfd346a4ec13a2381 +Subproject commit 9445252028c51dd774d7075195d00766f90436b9 diff --git a/test/Python/src/modal_models/ModalStartup.lf b/test/Python/src/modal_models/ModalStartup.lf index ff33f9fbcb..ef8e929131 100644 --- a/test/Python/src/modal_models/ModalStartup.lf +++ b/test/Python/src/modal_models/ModalStartup.lf @@ -63,19 +63,16 @@ main reactor { test = new TraceTesting( events_size = 4, trace = ( - 0,0,0,1,1,0,0, - 0,0,1000000000, - 1,1,0,1,0,0,0, - 0,0,0,1,0,1,1, - 1,0,0,1000000000, - 1,1,0,1,0,1,0,0, - 0,0,1,0,1,0,1,1,1, - 1000000000,1,1,0, - 1,0,1,0,1, + 0,0,0,1,1,0,0,0,0, 1000000000,1,1,0,1, - 0,1,0,1,0,0,1,0,1, - 1,1,0,1), - training = False + 0,0,0,0,0,0,1,0,1,1, + 1,0,0,1000000000,1, + 1,0,1,0,1,0,0, + 1000000000,1,1,0,1,0, + 1,0,0,1000000000,1,1, + 0,1,0,1,0,0,0,0,1,0, + 1,1,1,0,0 + ), training = False ) // Trigger mode change diff --git a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf index 6a6ef990d4..ab4d032b91 100644 --- a/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf +++ b/test/Python/src/modal_models/MultipleOutputFeeder_2Connections.lf @@ -82,4 +82,4 @@ main reactor { modal.count -> test.events -} \ No newline at end of file +} From 2e46b407feb5579553179db517d36dc6c05c1282 Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 14 Mar 2022 02:21:52 -0500 Subject: [PATCH 35/36] Updated test and reactor-c --- org.lflang/src/lib/c/reactor-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index 9445252028..e61d28d7d6 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 9445252028c51dd774d7075195d00766f90436b9 +Subproject commit e61d28d7d68c682cefc740f012db8aaeb17e2914 From db6bb6760752f637e3840409b5756b353513e2bb Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Mon, 14 Mar 2022 02:49:17 -0500 Subject: [PATCH 36/36] Also handle startup reactions for history transitions --- org.lflang/src/lib/c/reactor-c | 2 +- org.lflang/src/org/lflang/generator/c/CGenerator.xtend | 8 ++++++++ test/C/src/modal_models/ModalStartup.lf | 8 ++++---- test/Python/src/modal_models/ModalStartup.lf | 8 ++++---- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/org.lflang/src/lib/c/reactor-c b/org.lflang/src/lib/c/reactor-c index e61d28d7d6..b20c870cb1 160000 --- a/org.lflang/src/lib/c/reactor-c +++ b/org.lflang/src/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit e61d28d7d68c682cefc740f012db8aaeb17e2914 +Subproject commit b20c870cb1f0f11a918348162dc884fbd01b56d9 diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend index 5aa71440c0..c377e0b882 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend @@ -934,6 +934,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); } } diff --git a/test/C/src/modal_models/ModalStartup.lf b/test/C/src/modal_models/ModalStartup.lf index 5244f16b01..4ff25a2ab8 100644 --- a/test/C/src/modal_models/ModalStartup.lf +++ b/test/C/src/modal_models/ModalStartup.lf @@ -68,11 +68,11 @@ main reactor { 1000000000,1,1,0,1, 0,0,0,0,0,0,1,0,1,1, 1,0,0,1000000000,1, - 1,0,1,0,1,0,0, + 1,0,1,0,1,0,0,0,0,1, + 0,1,0,1,1,1,1000000000, + 1,1,0,1,0,1,0,1, 1000000000,1,1,0,1,0, - 1,0,0,1000000000,1,1, - 0,1,0,1,0,0,0,0,1,0, - 1,1,1,0,0 + 1,0,1,0,0,1,0,1,1,1,0,1 ), training = false ) diff --git a/test/Python/src/modal_models/ModalStartup.lf b/test/Python/src/modal_models/ModalStartup.lf index ef8e929131..a5a42240b6 100644 --- a/test/Python/src/modal_models/ModalStartup.lf +++ b/test/Python/src/modal_models/ModalStartup.lf @@ -67,11 +67,11 @@ main reactor { 1000000000,1,1,0,1, 0,0,0,0,0,0,1,0,1,1, 1,0,0,1000000000,1, - 1,0,1,0,1,0,0, + 1,0,1,0,1,0,0,0,0,1, + 0,1,0,1,1,1,1000000000, + 1,1,0,1,0,1,0,1, 1000000000,1,1,0,1,0, - 1,0,0,1000000000,1,1, - 0,1,0,1,0,0,0,0,1,0, - 1,1,1,0,0 + 1,0,1,0,0,1,0,1,1,1,0,1 ), training = False )