From 96e38f59a182be77f5986ac28e79003848e54ec7 Mon Sep 17 00:00:00 2001 From: eal Date: Sun, 3 Jul 2022 12:47:29 -0700 Subject: [PATCH 01/35] Do not include in cycles downstream reactions --- .../generator/ReactionInstanceGraph.java | 4 +-- .../org/lflang/generator/ReactorInstance.java | 36 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java index 4b76c29ff8..72c20bb63a 100644 --- a/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java +++ b/org.lflang/src/org/lflang/generator/ReactionInstanceGraph.java @@ -32,7 +32,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import java.util.Set; import org.lflang.generator.ReactionInstance.Runtime; -import org.lflang.graph.DirectedGraph; +import org.lflang.graph.PrecedenceGraph; import org.lflang.lf.Variable; /** @@ -54,7 +54,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * @author{Marten Lohstroh } * @author{Edward A. Lee } */ -public class ReactionInstanceGraph extends DirectedGraph { +public class ReactionInstanceGraph extends PrecedenceGraph { /** * Create a new graph by traversing the maps in the named instances diff --git a/org.lflang/src/org/lflang/generator/ReactorInstance.java b/org.lflang/src/org/lflang/generator/ReactorInstance.java index c7d169ab23..32a4e40426 100644 --- a/org.lflang/src/org/lflang/generator/ReactorInstance.java +++ b/org.lflang/src/org/lflang/generator/ReactorInstance.java @@ -244,26 +244,34 @@ public void clearCaches(boolean includingRuntimes) { public Set> getCycles() { if (depth != 0) return root().getCycles(); if (cachedCycles != null) return cachedCycles; - Set reactions = new LinkedHashSet<>(); + cachedCycles = new LinkedHashSet<>(); ReactionInstanceGraph reactionRuntimes = assignLevels(); - for (ReactionInstance.Runtime runtime : reactionRuntimes.nodes()) { - reactions.add(runtime.getReaction()); - } - Set ports = new LinkedHashSet<>(); - // Need to figure out which ports are involved in the cycles. - // It may not be all ports that depend on this reaction. - for (ReactionInstance r : reactions) { - for (TriggerInstance p : r.effects) { - if (p instanceof PortInstance) { - findPaths((PortInstance)p, reactions, ports); + if (reactionRuntimes.nodes().size() > 0) { + Set reactions = new LinkedHashSet<>(); + Set ports = new LinkedHashSet<>(); + // There are cycles. But the nodes set includes not + // just the cycles, but also nodes that are downstream of the + // cycles. Use Tarjan's algorithm to get just the cycles. + var cycleNodes = reactionRuntimes.getCycles(); + for (var cycle : cycleNodes) { + for (ReactionInstance.Runtime runtime : cycle) { + reactions.add(runtime.getReaction()); + } + } + // Need to figure out which ports are involved in the cycles. + // It may not be all ports that depend on this reaction. + for (ReactionInstance r : reactions) { + for (TriggerInstance p : r.effects) { + if (p instanceof PortInstance) { + findPaths((PortInstance)p, reactions, ports); + } } } + cachedCycles.addAll(reactions); + cachedCycles.addAll(ports); } - cachedCycles = new LinkedHashSet<>(); - cachedCycles.addAll(reactions); - cachedCycles.addAll(ports); return cachedCycles; } From 92959580c45917a66a4b06d4d62c9d4d9521d313 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Tue, 5 Jul 2022 19:04:48 +0200 Subject: [PATCH 02/35] diagrams: Deactivates middle position for unconnected port labels. Prevents misplacement on self loops (#1273) --- .../org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index 000ad2f421..ccd33115a3 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -608,7 +608,8 @@ private KNode configureReactorNodeLayout(KNode node) { // Allows to freely shuffle ports on each side setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE); // Adjust port label spacing to be closer to edge but not overlap with port figure - setLayoutOption(node, CoreOptions.PORT_LABELS_PLACEMENT, EnumSet.of(PortLabelPlacement.ALWAYS_OTHER_SAME_SIDE, PortLabelPlacement.OUTSIDE, PortLabelPlacement.NEXT_TO_PORT_IF_POSSIBLE)); + // TODO: Add PortLabelPlacement.NEXT_TO_PORT_IF_POSSIBLE back into the configuration, as soon as ELK provides a fix for LF issue #1273 + setLayoutOption(node, CoreOptions.PORT_LABELS_PLACEMENT, EnumSet.of(PortLabelPlacement.ALWAYS_OTHER_SAME_SIDE, PortLabelPlacement.OUTSIDE)); setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_HORIZONTAL, 2.0); setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_VERTICAL, -3.0); // Balanced placement with straight long edges. From 9005ec4c6c628b04e76592e42aa8d6549ff8d2be Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 6 Jul 2022 16:21:08 +0200 Subject: [PATCH 03/35] modes: Fixed code generation for banks of modal reactors --- .../org/lflang/generator/c/CGenerator.java | 42 ++----- .../lflang/generator/c/CModesGenerator.java | 110 +++++++++++++++--- .../lflang/generator/c/CStateGenerator.java | 43 ++----- .../generator/c/CTriggerObjectsGenerator.java | 1 - 4 files changed, 110 insertions(+), 86 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.java b/org.lflang/src/org/lflang/generator/c/CGenerator.java index ef7cca2e35..4547d57e45 100644 --- a/org.lflang/src/org/lflang/generator/c/CGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CGenerator.java @@ -732,6 +732,8 @@ private void generateCodeForCurrentFederate( "int _lf_timer_triggers_count = 0;", "int _lf_tokens_with_ref_count_count = 0;" )); + // Add counters for modal initialization + initializeTriggerObjects.pr(CModesGenerator.generateModalInitalizationCounters(hasModalReactors)); // Create an array of arrays to store all self structs. // This is needed because connections cannot be established until @@ -809,7 +811,6 @@ private void generateCodeForCurrentFederate( federationRTIProperties, startTimeStepTokens, startTimeStepIsPresentCount, - startupReactionCount, isFederated, isFederatedAndDecentralized(), clockSyncIsOn() @@ -2060,21 +2061,15 @@ protected void generateStateVariableInitializations(ReactorInstance instance) { var mode = stateVar.eContainer() instanceof Mode ? instance.lookupModeInstance((Mode) stateVar.eContainer()) : instance.getMode(false); - // In the current concept state variables are not automatically reset. - // Instead, they need to be manually reset using a reset triggered reaction or marked as reset. - if (!stateVar.isReset()) { - mode = null; // Treat as if outside of mode - } initializeTriggerObjects.pr(CStateGenerator.generateInitializer( instance, selfRef, stateVar, mode, - types, - modalStateResetCount + types )); - if (mode != null) { - modalStateResetCount++; + if (mode != null && stateVar.isReset()) { + modalStateResetCount += currentFederate.numRuntimeInstances(instance); } } } @@ -2104,32 +2099,9 @@ private void generateSetDeadline(ReactorInstance instance) { * @param instance The reactor instance. */ private void generateModeStructure(ReactorInstance instance) { - var parentMode = instance.getMode(false); - var nameOfSelfStruct = CUtil.reactorRef(instance); - // If this instance is enclosed in another mode - if (parentMode != null) { - var parentModeRef = "&"+CUtil.reactorRef(parentMode.getParent())+"->_lf__modes["+parentMode.getParent().modes.indexOf(parentMode)+"]"; - initializeTriggerObjects.pr("// Setup relation to enclosing mode"); - - // If this reactor does not have its own modes, all reactions must be linked to enclosing mode - if (instance.modes.isEmpty()) { - int i = 0; - for (ReactionInstance reaction : instance.reactions) { - initializeTriggerObjects.pr(CUtil.reactorRef(reaction.getParent())+"->_lf__reaction_"+i+".mode = "+parentModeRef+";"); - i++; - } - } else { // Otherwise, only reactions outside modes must be linked and the mode state itself gets a parent relation - initializeTriggerObjects.pr("((self_base_t*)"+nameOfSelfStruct+")->_lf__mode_state.parent_mode = "+parentModeRef+";"); - Iterable reactionsOutsideModes = IterableExtensions.filter(instance.reactions, it -> it.getMode(true) == null); - for (ReactionInstance reaction : reactionsOutsideModes) { - initializeTriggerObjects.pr(CUtil.reactorRef(reaction.getParent())+"->_lf__reaction_"+instance.reactions.indexOf(reaction)+".mode = "+parentModeRef+";"); - } - } - } - // If this reactor has modes, register for mode change handling + CModesGenerator.generateModeStructure(instance, initializeTriggerObjects); if (!instance.modes.isEmpty()) { - initializeTriggerObjects.pr("// Register for transition handling"); - initializeTriggerObjects.pr("_lf_modal_reactor_states["+modalReactorCount+++"] = &((self_base_t*)"+nameOfSelfStruct+")->_lf__mode_state;"); + modalReactorCount += currentFederate.numRuntimeInstances(instance); } } diff --git a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java index 2a0f91ea2e..6eae920e8d 100644 --- a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java @@ -2,8 +2,11 @@ import java.util.List; +import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.lflang.ASTUtils; import org.lflang.generator.CodeBuilder; +import org.lflang.generator.ReactionInstance; +import org.lflang.generator.ReactorInstance; import org.lflang.lf.Mode; import org.lflang.lf.Reactor; @@ -78,23 +81,98 @@ public static String generateModeStatesTable( int modalReactorCount, int modalStateResetCount ) { - return String.join("\n", - (hasModalReactors ? - String.join("\n", - "// Array of pointers to mode states to be handled in _lf_handle_mode_changes().", - "reactor_mode_state_t* _lf_modal_reactor_states["+modalReactorCount+"];", - "int _lf_modal_reactor_states_size = "+modalReactorCount+";" - ) : - ""), - (hasModalReactors && modalStateResetCount > 0 ? - String.join("\n", - "// Array of reset data for state variables nested in modes. Used in _lf_handle_mode_changes().", - "mode_state_variable_reset_data_t _lf_modal_state_reset["+modalStateResetCount+"];", - "int _lf_modal_state_reset_size = "+modalStateResetCount+";" - ) : - "") - ); + if (hasModalReactors) { + return String.join("\n", + "// Array of pointers to mode states to be handled in _lf_handle_mode_changes().", + "reactor_mode_state_t* _lf_modal_reactor_states["+modalReactorCount+"];", + "int _lf_modal_reactor_states_size = "+modalReactorCount+";", + (modalStateResetCount > 0 ? + String.join("\n", + "// Array of reset data for state variables nested in modes. Used in _lf_handle_mode_changes().", + "mode_state_variable_reset_data_t _lf_modal_state_reset["+modalStateResetCount+"];", + "int _lf_modal_state_reset_size = "+modalStateResetCount+";") + : "" + )); + } + return ""; + } + + /** + * Generate counter variable declarations used for registering modal reactors. + * + * @param hasModalReactors True if there is modal model reactors, false otherwise + */ + public static String generateModalInitalizationCounters(boolean hasModalReactors) { + if (hasModalReactors) { + return String.join("\n", + "int _lf_modal_reactor_states_count = 0;", + "int _lf_modal_state_reset_count = 0;" + ); + } + return ""; } + + /** + * Generate code for modal reactor registration and hierarchy. + * + * @param instance The reactor instance. + * @param code The code builder. + */ + public static void generateModeStructure(ReactorInstance instance, CodeBuilder code) { + var parentMode = instance.getMode(false); + var nameOfSelfStruct = CUtil.reactorRef(instance); + // If this instance is enclosed in another mode + if (parentMode != null) { + var parentModeRef = "&"+CUtil.reactorRef(parentMode.getParent())+"->_lf__modes["+parentMode.getParent().modes.indexOf(parentMode)+"]"; + code.pr("// Setup relation to enclosing mode"); + + // If this reactor does not have its own modes, all reactions must be linked to enclosing mode + if (instance.modes.isEmpty()) { + int i = 0; + for (ReactionInstance reaction : instance.reactions) { + code.pr(CUtil.reactorRef(reaction.getParent())+"->_lf__reaction_"+i+".mode = "+parentModeRef+";"); + i++; + } + } else { // Otherwise, only reactions outside modes must be linked and the mode state itself gets a parent relation + code.pr("((self_base_t*)"+nameOfSelfStruct+")->_lf__mode_state.parent_mode = "+parentModeRef+";"); + Iterable reactionsOutsideModes = IterableExtensions.filter(instance.reactions, it -> it.getMode(true) == null); + for (ReactionInstance reaction : reactionsOutsideModes) { + code.pr(CUtil.reactorRef(reaction.getParent())+"->_lf__reaction_"+instance.reactions.indexOf(reaction)+".mode = "+parentModeRef+";"); + } + } + } + // If this reactor has modes, register for mode change handling + if (!instance.modes.isEmpty()) { + code.pr("// Register for transition handling"); + code.pr("_lf_modal_reactor_states[_lf_modal_reactor_states_count++] = &((self_base_t*)"+nameOfSelfStruct+")->_lf__mode_state;"); + } + } + + /** + * Generate code registering a state variable for automatic reset. + * + * @param modeRef The code to refer to the mode + * @param selfRef The code to refer to the self struct + * @param varName The variable name in the self struct + * @param source The variable that stores the initial value + * @param type The size of the initial value + */ + public static String generateStateResetStructure( + String modeRef, + String selfRef, + String varName, + String source, + String type + ) { + return String.join("\n", + "// Register for automatic reset", + "_lf_modal_state_reset[_lf_modal_state_reset_count].mode = "+modeRef+";", + "_lf_modal_state_reset[_lf_modal_state_reset_count].target = &("+selfRef+"->"+varName+");", + "_lf_modal_state_reset[_lf_modal_state_reset_count].source = &"+source+";", + "_lf_modal_state_reset[_lf_modal_state_reset_count].size = sizeof("+type+");", + "_lf_modal_state_reset_count++;" + ); + } /** * Generate code to call `_lf_process_mode_changes`. diff --git a/org.lflang/src/org/lflang/generator/c/CStateGenerator.java b/org.lflang/src/org/lflang/generator/c/CStateGenerator.java index 7cb022508c..fbe3ca6d57 100644 --- a/org.lflang/src/org/lflang/generator/c/CStateGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CStateGenerator.java @@ -44,17 +44,14 @@ public static String generateInitializer( String selfRef, StateVar stateVar, ModeInstance mode, - CTypes types, - int modalStateResetCount + CTypes types ) { var initExpr = getInitializerExpr(stateVar, instance); String baseInitializer = generateBaseInitializer(selfRef, stateVar, initExpr, types); - String modalInitializer = generateModalInitializer(instance, selfRef, stateVar, - initExpr, mode, types, - modalStateResetCount); + String modalReset = generateModalReset(instance, selfRef, stateVar, initExpr, mode, types); return String.join("\n", baseInitializer, - modalInitializer + modalReset ); } @@ -82,16 +79,15 @@ private static String generateBaseInitializer( } } - private static String generateModalInitializer( + private static String generateModalReset( ReactorInstance instance, String selfRef, StateVar stateVar, String initExpr, ModeInstance mode, - CTypes types, - int modalStateResetCount + CTypes types ) { - if (mode == null) { + if (mode == null || !stateVar.isReset()) { return ""; } var modeRef = "&"+CUtil.reactorRef(mode.getParent())+"->_lf__modes["+mode.getParent().modes.indexOf(mode)+"]"; @@ -100,8 +96,7 @@ private static String generateModalInitializer( if (ASTUtils.isOfTimeType(stateVar) || ASTUtils.isParameterized(stateVar) && stateVar.getInit().size() > 0) { - return generateModalPropertyInitializer( - modalStateResetCount, + return CModesGenerator.generateStateResetStructure( modeRef, selfRef, stateVar.getName(), initExpr, type); @@ -113,12 +108,8 @@ private static String generateModalInitializer( source, true); code.pr("{ // For scoping"); code.indent(); - code.pr(String.join("\n", - "static "+declaration+" = "+initExpr+";", - selfRef+"->"+stateVar.getName()+" = "+source+";" - )); - code.pr(generateModalPropertyInitializer( - modalStateResetCount, + code.pr("static "+declaration+" = "+initExpr+";"); + code.pr(CModesGenerator.generateStateResetStructure( modeRef, selfRef, stateVar.getName(), source, type)); @@ -128,22 +119,6 @@ private static String generateModalInitializer( } } - private static String generateModalPropertyInitializer( - int i, - String modeRef, - String selfRef, - String varName, - String source, - String type - ) { - return String.join("\n", - "_lf_modal_state_reset["+i+"].mode = "+modeRef+";", - "_lf_modal_state_reset["+i+"].target = &("+selfRef+"->"+varName+");", - "_lf_modal_state_reset["+i+"].source = &"+source+";", - "_lf_modal_state_reset["+i+"].size = sizeof("+type+");" - ); - } - /** * Return a C expression that can be used to initialize the specified * state variable within the specified parent. If the state variable diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index d55bfb514d..7f074c4c89 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -53,7 +53,6 @@ public static String generateInitializeTriggerObjects( LinkedHashMap federationRTIProperties, int startTimeStepTokens, int startTimeStepIsPresentCount, - int startupReactionCount, boolean isFederated, boolean isFederatedAndDecentralized, boolean clockSyncIsOn From 97aba331d6e93dd37512928556e2dcc370e42b91 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 6 Jul 2022 19:13:44 +0200 Subject: [PATCH 04/35] modes: Added test models for banks of modal reactors --- .../modal_models/BanksCount3ModesComplex.lf | 90 +++++++++++++++++++ .../modal_models/BanksCount3ModesSimple.lf | 40 +++++++++ .../src/modal_models/BanksModalStateReset.lf | 65 ++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 test/C/src/modal_models/BanksCount3ModesComplex.lf create mode 100644 test/C/src/modal_models/BanksCount3ModesSimple.lf create mode 100644 test/C/src/modal_models/BanksModalStateReset.lf diff --git a/test/C/src/modal_models/BanksCount3ModesComplex.lf b/test/C/src/modal_models/BanksCount3ModesComplex.lf new file mode 100644 index 0000000000..66cc6bcc66 --- /dev/null +++ b/test/C/src/modal_models/BanksCount3ModesComplex.lf @@ -0,0 +1,90 @@ +/* + * Modal Reactor Test. + * Tests cycling through modes with banks of reactors and complex nesting. + */ +target C { + fast: false, + timeout: 3 sec +}; + +import TraceTesting from "util/TraceTesting.lf" +import CounterCycle from "Count3Modes.lf" + +reactor MetaCounter { + input next:bool; + output[2] always:int; + output[2] mode1:int; + output[2] mode2:int; + output[2] never:int; + + outer_counters = new[2] CounterCycle(); + (next)+ -> outer_counters.next; + outer_counters.count -> always; + + initial mode One { + mode1_counters = new[2] CounterCycle(); + + (next)+ -> mode1_counters.next; + mode1_counters.count -> mode1; + + timer t1(500msec, 250msec); + + reaction(t1) -> reset(Two) {= + lf_set_mode(Two); + =} + } + mode Two { + mode2_counters = new[2] CounterCycle(); + + (next)+ -> mode2_counters.next; + mode2_counters.count -> mode2; + + timer t2(500msec, 250msec); + + reaction(t2) -> history(One) {= + lf_set_mode(One); + =} + } + mode Three { + mode3_counters = new[2] CounterCycle(); + + (next)+ -> mode3_counters.next; + mode3_counters.count -> never; + } +} + +main reactor { + timer stepper(0, 250msec); + counters = new[2] MetaCounter(); + test = new TraceTesting( + events_size = 16, + trace_size = 429, + trace = ( + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,0,3,0,3,0,3,0,3,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,1,1,1,1,1,1,1,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,0,1,0,1,0,1,0,1,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,2,1,2,1,2,1,2,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,2,0,2,0,2,0,2,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,0,2,0,2,0,2,0,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 + ), training = false) + + // Trigger + reaction(stepper) -> counters.next {= + for(int i = 0; i < 2; i++) { + lf_set(counters[i].next, true); + } + =} + + counters.always, + counters.mode1, + counters.mode2, + counters.never + -> test.events +} diff --git a/test/C/src/modal_models/BanksCount3ModesSimple.lf b/test/C/src/modal_models/BanksCount3ModesSimple.lf new file mode 100644 index 0000000000..572a8d0927 --- /dev/null +++ b/test/C/src/modal_models/BanksCount3ModesSimple.lf @@ -0,0 +1,40 @@ +/* + * Modal Reactor Test. + * Tests cycling through modes with banks of reactors. + */ +target C { + fast: false, + timeout: 2 sec +}; + +import TraceTesting from "util/TraceTesting.lf" +import CounterCycle from "Count3Modes.lf" + +main reactor { + timer stepper(0, 250msec); + counters = new[3] CounterCycle(); + test = new TraceTesting( + events_size = 3, + trace_size = 63, + trace = ( + 0,1,1,1,1,1,1, + 250000000,1,2,1,2,1,2, + 250000000,1,3,1,3,1,3, + 250000000,1,1,1,1,1,1, + 250000000,1,2,1,2,1,2, + 250000000,1,3,1,3,1,3, + 250000000,1,1,1,1,1,1, + 250000000,1,2,1,2,1,2, + 250000000,1,3,1,3,1,3 + ), training = false) + + // Trigger + reaction(stepper) -> counters.next {= + for(int i = 0; i < 3; i++) { + lf_set(counters[i].next, true); + } + =} + + counters.count + -> test.events +} diff --git a/test/C/src/modal_models/BanksModalStateReset.lf b/test/C/src/modal_models/BanksModalStateReset.lf new file mode 100644 index 0000000000..541dd76d01 --- /dev/null +++ b/test/C/src/modal_models/BanksModalStateReset.lf @@ -0,0 +1,65 @@ +/* + * Modal Reactor Test. + * Tests reset of state variables in modes with banks of reactors. + */ +target C { + fast: false, + timeout: 4 sec +}; + +import TraceTesting from "util/TraceTesting.lf" +import Modal as ResetReaction from "ModalStateReset.lf" +import Modal as AutoReset from "ModalStateResetAuto.lf" + +main reactor { + timer stepper(1sec, 1sec) + + reset1 = new[2] ResetReaction() + reset2 = new[2] AutoReset() + test = new TraceTesting( + events_size = 16, + trace_size = 627, + trace = ( + 0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, + 250000000,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, + 250000000,0,0,0,0,0,0,0,0,1,2,1,2,0,0,0,0, 0,0,0,0,0,0,0,0,1,2,1,2,0,0,0,0, + 250000000,0,0,0,0,0,0,0,0,1,3,1,3,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,1,3,0,0,0,0, + 250000000,1,1,1,1,1,0,1,0,1,4,1,4,0,0,0,0, 1,1,1,1,1,0,1,0,1,4,1,4,0,0,0,0, + 0,0,1,0,1,0,0,0,0,0,4,0,4,1,-2,1,-2, 0,1,0,1,0,0,0,0,0,4,0,4,1,-2,1,-2, + 250000000,0,1,0,1,0,0,0,0,0,4,0,4,1,-1,1,-1, 0,1,0,1,0,0,0,0,0,4,0,4,1,-1,1,-1, + 250000000,0,1,0,1,0,0,0,0,0,4,0,4,1,0,1,0, 0,1,0,1,0,0,0,0,0,4,0,4,1,0,1,0, + 250000000,0,1,0,1,0,0,0,0,0,4,0,4,1,1,1,1, 0,1,0,1,0,0,0,0,0,4,0,4,1,1,1,1, + 250000000,1,1,1,1,1,1,1,1,0,4,0,4,1,2,1,2, 1,1,1,1,1,1,1,1,0,4,0,4,1,2,1,2, + 250000000,0,1,0,1,0,1,0,1,1,5,1,5,0,2,0,2, 0,1,0,1,0,1,0,1,1,5,1,5,0,2,0,2, + 250000000,0,1,0,1,0,1,0,1,1,6,1,6,0,2,0,2, 0,1,0,1,0,1,0,1,1,6,1,6,0,2,0,2, + 250000000,0,1,0,1,0,1,0,1,1,7,1,7,0,2,0,2, 0,1,0,1,0,1,0,1,1,7,1,7,0,2,0,2, + 250000000,1,1,1,1,1,2,1,2,1,8,1,8,0,2,0,2, 1,1,1,1,1,2,1,2,1,8,1,8,0,2,0,2, + 0,0,1,0,1,0,2,0,2,0,8,0,8,1,-2,1,-2, 0,1,0,1,0,2,0,2,0,8,0,8,1,-2,1,-2, + 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,-1,1,-1, 0,1,0,1,0,2,0,2,0,8,0,8,1,-1,1,-1, + 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,0,1,0, 0,1,0,1,0,2,0,2,0,8,0,8,1,0,1,0, + 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,1,1,1, 0,1,0,1,0,2,0,2,0,8,0,8,1,1,1,1, + 250000000,1,1,1,1,1,3,1,3,0,8,0,8,1,2,1,2, 1,1,1,1,1,3,1,3,0,8,0,8,1,2,1,2 + ), training = false) + + // Trigger mode change (separately because of #1278) + reaction(stepper) -> reset1.next {= + for(int i = 0; i < reset1_width; i++) { + lf_set(reset1[i].next, true); + } + =} + reaction(stepper) -> reset2.next {= + for(int i = 0; i < reset2_width; i++) { + lf_set(reset2[i].next, true); + } + =} + + reset1.mode_switch, + reset1.count0, + reset1.count1, + reset1.count2, + reset2.mode_switch, + reset2.count0, + reset2.count1, + reset2.count2 + -> test.events +} From aab8a7d8ea07c53f95d898dad0c4608c6aa82780 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 6 Jul 2022 20:13:58 +0200 Subject: [PATCH 05/35] modes: Added test for reset state variables that was previously missing --- .../src/modal_models/ModalStateResetAuto.lf | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 test/Python/src/modal_models/ModalStateResetAuto.lf diff --git a/test/Python/src/modal_models/ModalStateResetAuto.lf b/test/Python/src/modal_models/ModalStateResetAuto.lf new file mode 100644 index 0000000000..de69b340fa --- /dev/null +++ b/test/Python/src/modal_models/ModalStateResetAuto.lf @@ -0,0 +1,100 @@ +/* + * 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 + + output mode_switch + output count0 + output count1 + output count2 + + state counter0(0) + + reaction(next) -> count0 {= + print(f"Counter0: {self.counter0}") + count0.set(self.counter0) + self.counter0 += 1 + =} + + initial mode One { + state counter1(0) + timer T1(0msec, 250msec) + + reaction(T1) -> count1 {= + print(f"Counter1: {self.counter1}") + count1.set(self.counter1) + self.counter1 += 1 + =} + + reaction(next) -> reset(Two), mode_switch {= + print("Transitioning to mode Two (reset)") + mode_switch.set(1) + Two.set() + =} + } + mode Two { + reset state counter2(-2) + timer T2(0msec, 250msec) + + reaction(T2) -> count2 {= + print(f"Counter2: {self.counter2}") + count2.set(self.counter2) + self.counter2 += 1 + =} + + reaction(next) -> history(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,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 {= + modal.next.set(True) + =} + + modal.mode_switch, + modal.count0, + modal.count1, + modal.count2 + -> test.events +} From 553ed1d63160e6172fe33f5920e60f5c2b7aefb9 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 6 Jul 2022 20:50:32 +0200 Subject: [PATCH 06/35] modes: Added python tests for banks of modal reactors --- .../modal_models/BanksCount3ModesComplex.lf | 88 +++++++++++++++++++ .../modal_models/BanksCount3ModesSimple.lf | 38 ++++++++ .../src/modal_models/BanksModalStateReset.lf | 62 +++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 test/Python/src/modal_models/BanksCount3ModesComplex.lf create mode 100644 test/Python/src/modal_models/BanksCount3ModesSimple.lf create mode 100644 test/Python/src/modal_models/BanksModalStateReset.lf diff --git a/test/Python/src/modal_models/BanksCount3ModesComplex.lf b/test/Python/src/modal_models/BanksCount3ModesComplex.lf new file mode 100644 index 0000000000..87fc0f5585 --- /dev/null +++ b/test/Python/src/modal_models/BanksCount3ModesComplex.lf @@ -0,0 +1,88 @@ +/* + * Modal Reactor Test. + * Tests cycling through modes with banks of reactors and complex nesting. + */ +target Python { + fast: false, + timeout: 3 sec +} + +import TraceTesting from "util/TraceTesting.lf" +import CounterCycle from "Count3Modes.lf" + +reactor MetaCounter { + input next + output[2] always + output[2] mode1 + output[2] mode2 + output[2] never + + outer_counters = new[2] CounterCycle(); + (next)+ -> outer_counters.next; + outer_counters.count -> always; + + initial mode One { + mode1_counters = new[2] CounterCycle(); + + (next)+ -> mode1_counters.next; + mode1_counters.count -> mode1; + + timer t1(500msec, 250msec); + + reaction(t1) -> reset(Two) {= + Two.set() + =} + } + mode Two { + mode2_counters = new[2] CounterCycle(); + + (next)+ -> mode2_counters.next; + mode2_counters.count -> mode2; + + timer t2(500msec, 250msec); + + reaction(t2) -> history(One) {= + One.set() + =} + } + mode Three { + mode3_counters = new[2] CounterCycle(); + + (next)+ -> mode3_counters.next; + mode3_counters.count -> never; + } +} + +main reactor { + timer stepper(0, 250msec); + counters = new[2] MetaCounter(); + test = new TraceTesting( + events_size = 16, + trace = ( + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,0,3,0,3,0,3,0,3,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,1,1,1,1,1,1,1,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,0,1,0,1,0,1,0,1,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,2,1,2,1,2,1,2,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,2,0,2,0,2,0,2,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 250000000,1,2,1,2,1,2,1,2,0,2,0,2,0,2,0,2,1,2,1,2,1,2,1,2,0,0,0,0,0,0,0,0, + 250000000,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,0,2,0,2,0,2,0,2,0,0,0,0,0,0,0,0, + 250000000,1,1,1,1,1,1,1,1,0,3,0,3,0,3,0,3,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 + ), training = False) + + // Trigger + reaction(stepper) -> counters.next {= + for i in range(len(counters)): + counters[i].next.set(True) + =} + + counters.always, + counters.mode1, + counters.mode2, + counters.never + -> test.events +} diff --git a/test/Python/src/modal_models/BanksCount3ModesSimple.lf b/test/Python/src/modal_models/BanksCount3ModesSimple.lf new file mode 100644 index 0000000000..5d93531fcf --- /dev/null +++ b/test/Python/src/modal_models/BanksCount3ModesSimple.lf @@ -0,0 +1,38 @@ +/* + * Modal Reactor Test. + * Tests cycling through modes with banks of reactors. + */ +target Python { + fast: false, + timeout: 2 sec +} + +import TraceTesting from "util/TraceTesting.lf" +import CounterCycle from "Count3Modes.lf" + +main reactor { + timer stepper(0, 250msec); + counters = new[3] CounterCycle(); + test = new TraceTesting( + events_size = 3, + trace = ( + 0,1,1,1,1,1,1, + 250000000,1,2,1,2,1,2, + 250000000,1,3,1,3,1,3, + 250000000,1,1,1,1,1,1, + 250000000,1,2,1,2,1,2, + 250000000,1,3,1,3,1,3, + 250000000,1,1,1,1,1,1, + 250000000,1,2,1,2,1,2, + 250000000,1,3,1,3,1,3 + ), training = False) + + // Trigger + reaction(stepper) -> counters.next {= + for counter in counters: + counter.next.set(True) + =} + + counters.count + -> test.events +} diff --git a/test/Python/src/modal_models/BanksModalStateReset.lf b/test/Python/src/modal_models/BanksModalStateReset.lf new file mode 100644 index 0000000000..9ab9c0c068 --- /dev/null +++ b/test/Python/src/modal_models/BanksModalStateReset.lf @@ -0,0 +1,62 @@ +/* + * Modal Reactor Test. + * Tests reset of state variables in modes with banks of reactors. + */ +target Python { + fast: false, + timeout: 4 sec +} + +import TraceTesting from "util/TraceTesting.lf" +import Modal as ResetReaction from "ModalStateReset.lf" +import Modal as AutoReset from "ModalStateResetAuto.lf" + +main reactor { + timer stepper(1sec, 1sec) + + reset1 = new[2] ResetReaction() + reset2 = new[2] AutoReset() + test = new TraceTesting( + events_size = 16, + trace = ( + 0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, + 250000000,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, + 250000000,0,0,0,0,0,0,0,0,1,2,1,2,0,0,0,0, 0,0,0,0,0,0,0,0,1,2,1,2,0,0,0,0, + 250000000,0,0,0,0,0,0,0,0,1,3,1,3,0,0,0,0, 0,0,0,0,0,0,0,0,1,3,1,3,0,0,0,0, + 250000000,1,1,1,1,1,0,1,0,1,4,1,4,0,0,0,0, 1,1,1,1,1,0,1,0,1,4,1,4,0,0,0,0, + 0,0,1,0,1,0,0,0,0,0,4,0,4,1,-2,1,-2, 0,1,0,1,0,0,0,0,0,4,0,4,1,-2,1,-2, + 250000000,0,1,0,1,0,0,0,0,0,4,0,4,1,-1,1,-1, 0,1,0,1,0,0,0,0,0,4,0,4,1,-1,1,-1, + 250000000,0,1,0,1,0,0,0,0,0,4,0,4,1,0,1,0, 0,1,0,1,0,0,0,0,0,4,0,4,1,0,1,0, + 250000000,0,1,0,1,0,0,0,0,0,4,0,4,1,1,1,1, 0,1,0,1,0,0,0,0,0,4,0,4,1,1,1,1, + 250000000,1,1,1,1,1,1,1,1,0,4,0,4,1,2,1,2, 1,1,1,1,1,1,1,1,0,4,0,4,1,2,1,2, + 250000000,0,1,0,1,0,1,0,1,1,5,1,5,0,2,0,2, 0,1,0,1,0,1,0,1,1,5,1,5,0,2,0,2, + 250000000,0,1,0,1,0,1,0,1,1,6,1,6,0,2,0,2, 0,1,0,1,0,1,0,1,1,6,1,6,0,2,0,2, + 250000000,0,1,0,1,0,1,0,1,1,7,1,7,0,2,0,2, 0,1,0,1,0,1,0,1,1,7,1,7,0,2,0,2, + 250000000,1,1,1,1,1,2,1,2,1,8,1,8,0,2,0,2, 1,1,1,1,1,2,1,2,1,8,1,8,0,2,0,2, + 0,0,1,0,1,0,2,0,2,0,8,0,8,1,-2,1,-2, 0,1,0,1,0,2,0,2,0,8,0,8,1,-2,1,-2, + 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,-1,1,-1, 0,1,0,1,0,2,0,2,0,8,0,8,1,-1,1,-1, + 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,0,1,0, 0,1,0,1,0,2,0,2,0,8,0,8,1,0,1,0, + 250000000,0,1,0,1,0,2,0,2,0,8,0,8,1,1,1,1, 0,1,0,1,0,2,0,2,0,8,0,8,1,1,1,1, + 250000000,1,1,1,1,1,3,1,3,0,8,0,8,1,2,1,2, 1,1,1,1,1,3,1,3,0,8,0,8,1,2,1,2 + ), training = False) + + // Trigger mode change (separately because of #1278) + reaction(stepper) -> reset1.next {= + for i in range(len(reset1)): + reset1[i].next.set(True) + =} + reaction(stepper) -> reset2.next {= + for i in range(len(reset2)): + reset2[i].next.set(True) + =} + + reset1.mode_switch, + reset1.count0, + reset1.count1, + reset1.count2, + reset2.mode_switch, + reset2.count0, + reset2.count1, + reset2.count2 + -> test.events +} From 6a17873694d5eb602dd2838da73e3567cb2d4ae5 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 6 Jul 2022 20:52:11 +0200 Subject: [PATCH 07/35] modes: Fixed type error in python port conversion for bank effects --- .../src/org/lflang/generator/python/PythonPortGenerator.java | 2 +- 1 file changed, 1 insertion(+), 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 db65d93783..3d69cf033e 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -195,7 +195,7 @@ public static String generatePythonListForContainedBank(String reactorName, Port "for (int i = 0; i < "+generateWidthVariable(reactorName)+"; i++) {", " if (PyList_SetItem("+reactorName+"_py_list,", " i,", - " "+generateConvertCPortToPy("self->_lf_"+reactorName+"[i]."+port.getName(), widthSpec), + " "+generateConvertCPortToPy("&self->_lf_"+reactorName+"[i]."+port.getName(), widthSpec), " ) != 0) {", " lf_print_error(\"Could not add elements to the list for "+reactorName+".\");", " if (PyErr_Occurred()) {", From f883ee844aa8281b86469bb27f96c934321e366d Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 6 Jul 2022 21:26:05 +0200 Subject: [PATCH 08/35] modes: Fixed handling of bank effect for both single and multiports in python code generation --- .../src/org/lflang/generator/python/PythonPortGenerator.java | 2 +- 1 file changed, 1 insertion(+), 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 3d69cf033e..86084fc77a 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -195,7 +195,7 @@ public static String generatePythonListForContainedBank(String reactorName, Port "for (int i = 0; i < "+generateWidthVariable(reactorName)+"; i++) {", " if (PyList_SetItem("+reactorName+"_py_list,", " i,", - " "+generateConvertCPortToPy("&self->_lf_"+reactorName+"[i]."+port.getName(), widthSpec), + " "+generateConvertCPortToPy((port.getWidthSpec() == null ? "&" : "") + "self->_lf_"+reactorName+"[i]."+port.getName(), widthSpec), " ) != 0) {", " lf_print_error(\"Could not add elements to the list for "+reactorName+".\");", " if (PyErr_Occurred()) {", From b191f301b4fb347601f0d843bdadb5fe2bcb004a Mon Sep 17 00:00:00 2001 From: Soroush Bateni Date: Wed, 6 Jul 2022 16:57:13 -0500 Subject: [PATCH 09/35] Trying a simpler solution for contained banks --- .../src/org/lflang/generator/python/PythonPortGenerator.java | 2 +- 1 file changed, 1 insertion(+), 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 86084fc77a..d9506069fc 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -195,7 +195,7 @@ public static String generatePythonListForContainedBank(String reactorName, Port "for (int i = 0; i < "+generateWidthVariable(reactorName)+"; i++) {", " if (PyList_SetItem("+reactorName+"_py_list,", " i,", - " "+generateConvertCPortToPy((port.getWidthSpec() == null ? "&" : "") + "self->_lf_"+reactorName+"[i]."+port.getName(), widthSpec), + " "+generateConvertCPortToPy(reactorName + "[i]." + port.getName(), widthSpec), " ) != 0) {", " lf_print_error(\"Could not add elements to the list for "+reactorName+".\");", " if (PyErr_Occurred()) {", From dd99aaa4b52c330f2e485e67ee33346e8043afc9 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Wed, 6 Jul 2022 15:21:18 -0700 Subject: [PATCH 10/35] [lsp] Silence spurious error messages from Pylint. --- .../lflang/generator/python/PythonGenerator.java | 3 ++- .../generator/python/PythonPortGenerator.java | 13 ++++++------- .../lflang/generator/python/PythonValidator.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java index 4ef0b3c9e3..2974d48f9b 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonGenerator.java @@ -203,7 +203,8 @@ public String generatePythonCode(FederateInstance federate, String pyModuleName) "sys.path.append(os.path.dirname(__file__))", "# List imported names, but do not use pylint's --extension-pkg-allow-list option", "# so that these names will be assumed present without having to compile and install.", - "from "+pyModuleName+" import ( # pylint: disable=no-name-in-module, import-error", + "# pylint: disable=no-name-in-module, import-error", + "from "+pyModuleName+" import (", " Tag, action_capsule_t, compare_tags, get_current_tag, get_elapsed_logical_time,", " get_elapsed_physical_time, get_logical_time, get_microstep, get_physical_time,", " get_start_time, port_capsule, request_stop, schedule_copy,", diff --git a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java index db65d93783..d1f9c5a2f6 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -132,9 +132,8 @@ public static void generateInputVariablesToSendToPythonReaction( /** Generate into the specified string builder the code to * pass local variables for sending data to an input * of a contained reaction (e.g. for a deadline violation). - * @param builder The string builder. * @param definition AST node defining the reactor within which this occurs - * @param input Input of the contained reactor. + * @param port Input of the contained reactor. */ public static String generateVariablesForSendingToContainedReactors( List pyObjects, @@ -228,23 +227,23 @@ private static String generateConvertCPortToPy(String port, String widthSpec) { * initialize local variable for port so that it can be used in the body of * the Python reaction. * @param port The port to generate code for. - * @param inits The generated code will be put in inits. */ public static String generatePythonPortVariableInReaction(VarRef port) { String containerName = port.getContainer().getName(); String variableName = port.getVariable().getName(); + String tryStatement = "try: "+containerName+" # pylint: disable=used-before-assignment"; if (port.getContainer().getWidthSpec() != null) { // It's a bank - return String.join("\n", - "try: "+containerName, + return String.join(System.lineSeparator(), + tryStatement, "except NameError: "+containerName+" = [None] * len("+containerName+"_"+variableName+")", "for i in range(len("+containerName+"_"+variableName+")):", " if "+containerName+"[i] is None: "+containerName+"[i] = Make()", " "+containerName+"[i]."+variableName+" = "+containerName+"_"+variableName+"[i]" ); } else { - return String.join("\n", - "try: "+containerName, + return String.join(System.lineSeparator(), + tryStatement, "except NameError: "+containerName+" = Make()", containerName+"."+variableName+" = "+containerName+"_"+variableName ); diff --git a/org.lflang/src/org/lflang/generator/python/PythonValidator.java b/org.lflang/src/org/lflang/generator/python/PythonValidator.java index 950322cb4e..ee4fab928b 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonValidator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonValidator.java @@ -291,7 +291,7 @@ public Strategy getOutputReportingStrategy() { e.printStackTrace(); errorReporter.reportWarning( "Failed to parse linter output. The Lingua Franca code generator is tested with Pylint " - + "version 2.12.2. Consider updating PyLint if you have an older version." + + "version 2.12.2. Consider updating Pylint if you have an older version." ); } }; From 70c505c08eacb482c9d6a311608f48c776b87387 Mon Sep 17 00:00:00 2001 From: Peter Donovan <33707478+petervdonovan@users.noreply.github.com> Date: Wed, 6 Jul 2022 18:52:02 -0700 Subject: [PATCH 11/35] Apply suggestions from code review --- .../src/org/lflang/generator/python/PythonPortGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java index d1f9c5a2f6..207d0d1602 100644 --- a/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java +++ b/org.lflang/src/org/lflang/generator/python/PythonPortGenerator.java @@ -234,7 +234,7 @@ public static String generatePythonPortVariableInReaction(VarRef port) { String tryStatement = "try: "+containerName+" # pylint: disable=used-before-assignment"; if (port.getContainer().getWidthSpec() != null) { // It's a bank - return String.join(System.lineSeparator(), + return String.join("\n", tryStatement, "except NameError: "+containerName+" = [None] * len("+containerName+"_"+variableName+")", "for i in range(len("+containerName+"_"+variableName+")):", @@ -242,7 +242,7 @@ public static String generatePythonPortVariableInReaction(VarRef port) { " "+containerName+"[i]."+variableName+" = "+containerName+"_"+variableName+"[i]" ); } else { - return String.join(System.lineSeparator(), + return String.join("\n", tryStatement, "except NameError: "+containerName+" = Make()", containerName+"."+variableName+" = "+containerName+"_"+variableName From 3cf796b6168700020bab5c535d6ad9d1b7f372b6 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Fri, 1 Jul 2022 15:12:12 -0700 Subject: [PATCH 12/35] Avoid NPE --- .../org/lflang/ui/actions/CompileActionHandler.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java b/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java index 96155bfd69..0991a012d3 100644 --- a/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java +++ b/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java @@ -134,15 +134,18 @@ public Object execute(final ExecutionEvent event) throws ExecutionException { var xtextEditor = EditorUtils.getActiveXtextEditor(event); if (xtextEditor != null) { var xtextDocument = xtextEditor.getDocument(); + var resource = xtextEditor.getResource(); if (xtextDocument != null) { // Save editor if (xtextEditor.isDirty()) { xtextEditor.doSave(new NullProgressMonitor()); + } else if (resource != null) { + // Load the resource of this editor based on its associated file (collect). + // This does not retrieve the model resource from the editor directly but creates a new one. + // => workaround for issue #746. + lfFiles.addAll(collect((IFile) resource)); } - // Load the resource of this editor based on its associated file (collect). - // This does not retrieve the model resource from the editor directly but creates a new one. - // => workaround for issue #746. - lfFiles.addAll(collect((IFile) xtextEditor.getResource())); + } } } else if (selection instanceof IStructuredSelection) { // Invoked from context menu in project explorer From 5e4bf84db2ad3674090114e85fa8fb5485bd6985 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Tue, 5 Jul 2022 22:45:24 -0700 Subject: [PATCH 13/35] Update org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java --- .../src/org/lflang/ui/actions/CompileActionHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java b/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java index 0991a012d3..3b5790882a 100644 --- a/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java +++ b/org.lflang.ui/src/org/lflang/ui/actions/CompileActionHandler.java @@ -139,7 +139,8 @@ public Object execute(final ExecutionEvent event) throws ExecutionException { // Save editor if (xtextEditor.isDirty()) { xtextEditor.doSave(new NullProgressMonitor()); - } else if (resource != null) { + } + if (resource != null) { // Load the resource of this editor based on its associated file (collect). // This does not retrieve the model resource from the editor directly but creates a new one. // => workaround for issue #746. From 0531362dc11d1910b277a3e409caefc6d0600d6c Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 29 Jun 2022 16:44:40 +0200 Subject: [PATCH 14/35] diagrams: Added adjustable spacing for reactors and improved layout configuration for reactors and modes. --- .../synthesis/LinguaFrancaSynthesis.java | 72 +++++++++++-------- .../synthesis/util/LayoutPostProcessing.java | 6 +- .../diagram/synthesis/util/ModeDiagrams.java | 41 +++++++++-- 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index ccd33115a3..5d59fa4532 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -191,6 +191,7 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis { /** Synthesis category */ public static final SynthesisOption APPEARANCE = SynthesisOption.createCategory("Appearance", true); public static final SynthesisOption EXPERIMENTAL = SynthesisOption.createCategory("Experimental", true); + public static final SynthesisOption LAYOUT = SynthesisOption.createCategory("Layout", false).setCategory(LinguaFrancaSynthesis.APPEARANCE); /** Synthesis options */ public static final SynthesisOption SHOW_ALL_REACTORS = SynthesisOption.createCheckOption("All Reactors", false); @@ -211,6 +212,8 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis { public static final SynthesisOption SHOW_STATE_VARIABLES = SynthesisOption.createCheckOption("Reactor State Variables", false).setCategory(APPEARANCE); public static final SynthesisOption REACTOR_BODY_TABLE_COLS = SynthesisOption.createRangeOption("Reactor Parameter/Variable Columns", 1, 10, 1).setCategory(APPEARANCE); + public static final SynthesisOption SPACING = SynthesisOption.createRangeOption("Spacing (%)", 0, 150, 5, 75).setCategory(LAYOUT); + /** Synthesis actions */ public static final DisplayedActionData COLLAPSE_ALL = DisplayedActionData.create(CollapseAllReactorsAction.ID, "Hide all Details"); public static final DisplayedActionData EXPAND_ALL = DisplayedActionData.create(ExpandAllReactorsAction.ID, "Show all Details"); @@ -240,8 +243,9 @@ public List getDisplayedSynthesisOptions() { REACTOR_PARAMETER_MODE, SHOW_STATE_VARIABLES, REACTOR_BODY_TABLE_COLS, - LayoutPostProcessing.LAYOUT_CATEGORY, - LayoutPostProcessing.MODEL_ORDER + LAYOUT, + LayoutPostProcessing.MODEL_ORDER, + SPACING ); } @@ -395,20 +399,7 @@ private Collection createReactorNode( allReactorNodes)); } Iterables.addAll(nodes, createUserComments(reactor, node)); - configureReactorNodeLayout(node); - - // Additional layout adjustment for main node - setLayoutOption(node, CoreOptions.ALGORITHM, LayeredOptions.ALGORITHM_ID); - setLayoutOption(node, CoreOptions.DIRECTION, Direction.RIGHT); - setLayoutOption(node, CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.MINIMUM_SIZE)); - setLayoutOption(node, LayeredOptions.NODE_PLACEMENT_BK_FIXED_ALIGNMENT, FixedAlignment.BALANCED); - setLayoutOption(node, LayeredOptions.NODE_PLACEMENT_BK_EDGE_STRAIGHTENING, EdgeStraighteningStrategy.IMPROVE_STRAIGHTNESS); - setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE, LayeredOptions.SPACING_EDGE_NODE.getDefault() * 1.1f); - setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS.getDefault() * 1.1f); - if (!getBooleanValue(SHOW_HYPERLINKS)) { - setLayoutOption(node, CoreOptions.PADDING, new ElkPadding(-1, 6, 6, 6)); - setLayoutOption(node, LayeredOptions.SPACING_COMPONENT_COMPONENT, LayeredOptions.SPACING_COMPONENT_COMPONENT.getDefault() * 0.5f); - } + configureReactorNodeLayout(node, true); _layoutPostProcessing.configureMainReactor(node); } else { ReactorInstance instance = reactorInstance; @@ -582,7 +573,7 @@ private Collection createReactorNode( } else { Iterables.addAll(nodes, createUserComments(reactor, node)); } - configureReactorNodeLayout(node); + configureReactorNodeLayout(node, false); _layoutPostProcessing.configureReactor(node); } @@ -598,13 +589,19 @@ private Collection createReactorNode( return nodes; } - private KNode configureReactorNodeLayout(KNode node) { - // Direction + public KNode configureReactorNodeLayout(KNode node, boolean main) { + // Set layered algorithm + setLayoutOption(node, CoreOptions.ALGORITHM, LayeredOptions.ALGORITHM_ID); + // Left to right layout setLayoutOption(node, CoreOptions.DIRECTION, Direction.RIGHT); // Center free floating children setLayoutOption(node, CoreOptions.CONTENT_ALIGNMENT, ContentAlignment.centerCenter()); + + // Balanced placement with straight long edges. + setLayoutOption(node, LayeredOptions.NODE_PLACEMENT_STRATEGY, NodePlacementStrategy.NETWORK_SIMPLEX); // Do not shrink nodes below content - setLayoutOption(node, CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.minimumSizeWithPorts()); + setLayoutOption(node, CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.MINIMUM_SIZE, SizeConstraint.PORTS)); + // Allows to freely shuffle ports on each side setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE); // Adjust port label spacing to be closer to edge but not overlap with port figure @@ -612,15 +609,34 @@ private KNode configureReactorNodeLayout(KNode node) { setLayoutOption(node, CoreOptions.PORT_LABELS_PLACEMENT, EnumSet.of(PortLabelPlacement.ALWAYS_OTHER_SAME_SIDE, PortLabelPlacement.OUTSIDE)); setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_HORIZONTAL, 2.0); setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_VERTICAL, -3.0); - // Balanced placement with straight long edges. - setLayoutOption(node, LayeredOptions.NODE_PLACEMENT_STRATEGY, NodePlacementStrategy.NETWORK_SIMPLEX); - if (!getBooleanValue(SHOW_HYPERLINKS)) { - setLayoutOption(node, CoreOptions.PADDING, new ElkPadding(2, 6, 6, 6)); - setLayoutOption(node, LayeredOptions.SPACING_NODE_NODE, LayeredOptions.SPACING_NODE_NODE.getDefault() * 0.75f); - setLayoutOption(node, LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS.getDefault() * 0.75f); - setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE, LayeredOptions.SPACING_EDGE_NODE.getDefault() * 0.75f); - setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS.getDefault() * 0.75f); + + // Configure spacing + if (!getBooleanValue(SHOW_HYPERLINKS)) { // Hyperlink version is more relaxed in terms of space + var factor = (double) getIntValue(SPACING) / 100; + + setLayoutOption(node, LayeredOptions.SPACING_COMPONENT_COMPONENT, LayeredOptions.SPACING_COMPONENT_COMPONENT.getDefault() * factor); + + setLayoutOption(node, LayeredOptions.SPACING_NODE_NODE, LayeredOptions.SPACING_NODE_NODE.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS.getDefault() * factor); + + setLayoutOption(node, LayeredOptions.SPACING_PORT_PORT, LayeredOptions.SPACING_PORT_PORT.getDefault() * factor); + + setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE, LayeredOptions.SPACING_EDGE_NODE.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS, LayeredOptions.SPACING_EDGE_NODE_BETWEEN_LAYERS.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_EDGE_EDGE, LayeredOptions.SPACING_EDGE_EDGE.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_EDGE_EDGE_BETWEEN_LAYERS, LayeredOptions.SPACING_EDGE_EDGE_BETWEEN_LAYERS.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_EDGE_EDGE, LayeredOptions.SPACING_EDGE_EDGE.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_EDGE_EDGE_BETWEEN_LAYERS, LayeredOptions.SPACING_EDGE_EDGE_BETWEEN_LAYERS.getDefault() * factor); + setLayoutOption(node, LayeredOptions.SPACING_EDGE_LABEL, LayeredOptions.SPACING_EDGE_LABEL.getDefault() * factor); + + // Padding for sub graph + if (main) { // Special handing for main reactors + setLayoutOption(node, CoreOptions.PADDING, new ElkPadding(-1, 6, 6, 6)); + } else { + setLayoutOption(node, CoreOptions.PADDING, new ElkPadding(2, 6, 6, 6)); + } } + return node; } diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/LayoutPostProcessing.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/LayoutPostProcessing.java index e6d171550d..d1993fa365 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/LayoutPostProcessing.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/LayoutPostProcessing.java @@ -54,10 +54,6 @@ @ViewSynthesisShared public class LayoutPostProcessing extends AbstractSynthesisExtensions { - /** The synthesis option category for layout options. */ - public static final SynthesisOption LAYOUT_CATEGORY = - SynthesisOption.createCategory("Layout", false).setCategory(LinguaFrancaSynthesis.APPEARANCE); - /** Synthesis option to control the order of nodes and edges by model order. */ public static final String MODEL_ORDER_OPTION = "Model Order"; /** Uses semi-automatic layout. */ @@ -79,7 +75,7 @@ public class LayoutPostProcessing extends AbstractSynthesisExtensions { SynthesisOption.createChoiceOption( MODEL_ORDER_OPTION, Arrays.asList(TIE_BREAKER, STRICT_REACTION_ONLY, STRICT, FULL_CONTROL), - STRICT_REACTION_ONLY).setCategory(LAYOUT_CATEGORY); + STRICT_REACTION_ONLY).setCategory(LinguaFrancaSynthesis.LAYOUT); /** * Comparator to sort KNodes based on the textual order of their linked instances. diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index 4b5eda11ac..c0cd6badaa 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -33,15 +33,18 @@ import java.util.List; import java.util.stream.Collectors; +import org.eclipse.elk.alg.layered.options.CenterEdgeLabelPlacementStrategy; +import org.eclipse.elk.alg.layered.options.EdgeStraighteningStrategy; +import org.eclipse.elk.alg.layered.options.FixedAlignment; import org.eclipse.elk.alg.layered.options.LayerConstraint; import org.eclipse.elk.alg.layered.options.LayeredOptions; +import org.eclipse.elk.alg.layered.options.NodePlacementStrategy; import org.eclipse.elk.core.math.ElkPadding; import org.eclipse.elk.core.options.CoreOptions; import org.eclipse.elk.core.options.Direction; import org.eclipse.elk.core.options.EdgeRouting; import org.eclipse.elk.core.options.PortConstraints; import org.eclipse.elk.core.options.PortSide; -import org.eclipse.elk.core.options.SizeConstraint; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.xbase.lib.Extension; import org.eclipse.xtext.xbase.lib.IterableExtensions; @@ -125,6 +128,7 @@ public class ModeDiagrams extends AbstractSynthesisExtensions { @Inject @Extension private LinguaFrancaShapeExtensions _linguaFrancaShapeExtensions; @Inject @Extension private LinguaFrancaStyleExtensions _linguaFrancaStyleExtensions; @Inject @Extension private UtilityExtensions _utilityExtensions; + @Inject @Extension private LayoutPostProcessing _layoutPostProcessing; @Extension private KRenderingFactory _kRenderingFactory = KRenderingFactory.eINSTANCE; @@ -141,11 +145,15 @@ public void handleModes(List nodes, ReactorInstance reactor) { modeNodes.put(mode, node); modeDefinitionMap.put(mode.getDefinition(), mode); + // Layout if (mode.isInitial()) { DiagramSyntheses.setLayoutOption(node, LayeredOptions.LAYERING_LAYER_CONSTRAINT, LayerConstraint.FIRST); } - DiagramSyntheses.setLayoutOption(node, LayeredOptions.CROSSING_MINIMIZATION_SEMI_INTERACTIVE, true); - DiagramSyntheses.setLayoutOption(node, CoreOptions.DIRECTION, Direction.RIGHT); + // Use general layout configuration of reactors + this.getRootSynthesis().configureReactorNodeLayout(node, false); + _layoutPostProcessing.configureReactor(node); + // Adjust for modes + DiagramSyntheses.setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FREE); var expansionState = MemorizingExpandCollapseAction.getExpansionState(mode); DiagramSyntheses.setLayoutOption(node, KlighdProperties.EXPAND, @@ -239,10 +247,31 @@ public void handleModes(List nodes, ReactorInstance reactor) { modeContainer.getChildren().addAll(modeNodes.values()); var modeContainerFigure = addModeContainerFigure(modeContainer); _kRenderingExtensions.addDoubleClickAction(modeContainerFigure, MemorizingExpandCollapseAction.ID); - DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.minimumSizeWithPorts()); - DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.EDGE_ROUTING, EdgeRouting.SPLINES); + + // Use general layout configuration of reactors + this.getRootSynthesis().configureReactorNodeLayout(modeContainer, false); + _layoutPostProcessing.configureReactor(modeContainer); + // Adjust for state machine style + // Create alternating directions to make the model more compact DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.DIRECTION, Direction.DOWN); - DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_ORDER); + // More state machine like node placement + DiagramSyntheses.setLayoutOption(modeContainer, LayeredOptions.NODE_PLACEMENT_STRATEGY, NodePlacementStrategy.BRANDES_KOEPF); + DiagramSyntheses.setLayoutOption(modeContainer, LayeredOptions.NODE_PLACEMENT_BK_FIXED_ALIGNMENT, FixedAlignment.BALANCED); + DiagramSyntheses.setLayoutOption(modeContainer, LayeredOptions.NODE_PLACEMENT_BK_EDGE_STRAIGHTENING, EdgeStraighteningStrategy.IMPROVE_STRAIGHTNESS); + // Splines + DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.EDGE_ROUTING, EdgeRouting.SPLINES); + DiagramSyntheses.setLayoutOption(modeContainer, LayeredOptions.EDGE_LABELS_CENTER_LABEL_PLACEMENT_STRATEGY, CenterEdgeLabelPlacementStrategy.TAIL_LAYER); + DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.SPACING_NODE_SELF_LOOP, 18.0); + // Unreachable states are unlikely + DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.SEPARATE_CONNECTED_COMPONENTS, false); + // Equal padding + DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.PADDING, new ElkPadding(6)); + if (reactor.modes.stream().anyMatch(m -> m.transitions.stream().anyMatch(t -> t.type == ModeTransition.HISTORY))) { + // Make additional space for history indicator + DiagramSyntheses.setLayoutOption(modeContainer, LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS, + modeContainer.getProperty(LayeredOptions.SPACING_NODE_NODE_BETWEEN_LAYERS) + + (getBooleanValue(SHOW_TRANSITION_LABELS) ? 6.0 : 10.0)); + } var modeContainerPorts = new HashMap(); for (var mode : reactor.modes) { From be58c1095d1d36b76421167b5d5a11c21d4391ed Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 7 Jul 2022 11:08:03 +0200 Subject: [PATCH 15/35] diagrams: Prevent indirectly collapsing main reactors --- .../action/MemorizingExpandCollapseAction.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/action/MemorizingExpandCollapseAction.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/action/MemorizingExpandCollapseAction.java index 87a4ce8e38..335bb887e0 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/action/MemorizingExpandCollapseAction.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/action/MemorizingExpandCollapseAction.java @@ -24,16 +24,20 @@ ***************/ package org.lflang.diagram.synthesis.action; +import java.util.WeakHashMap; + +import org.lflang.diagram.synthesis.util.InterfaceDependenciesVisualization; +import org.lflang.diagram.synthesis.util.NamedInstanceUtil; +import org.lflang.generator.NamedInstance; +import org.lflang.generator.ReactorInstance; + import com.google.common.base.Preconditions; + import de.cau.cs.kieler.klighd.IAction; import de.cau.cs.kieler.klighd.IViewer; import de.cau.cs.kieler.klighd.SynthesisOption; import de.cau.cs.kieler.klighd.ViewContext; import de.cau.cs.kieler.klighd.kgraph.KNode; -import java.util.WeakHashMap; -import org.lflang.diagram.synthesis.util.InterfaceDependenciesVisualization; -import org.lflang.diagram.synthesis.util.NamedInstanceUtil; -import org.lflang.generator.NamedInstance; /** * Action for toggling collapse/expand state of reactors that memorizes the state and @@ -108,7 +112,7 @@ public IAction.ActionResult execute(final IAction.ActionContext context) { linkedInstance = NamedInstanceUtil.getLinkedInstance(node); } - if (node == null) { + if (node == null || (linkedInstance instanceof ReactorInstance && ((ReactorInstance) linkedInstance).isMainOrFederated())) { return IAction.ActionResult.createResult(false); } else { setExpansionState(node, linkedInstance, v, !v.isExpanded(node)); // toggle From 2166a876df0fc6686d35997041241e747148e1b1 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 7 Jul 2022 11:29:16 +0200 Subject: [PATCH 16/35] diagrams: Added mirroring of connections into mode container figure --- .../diagram/synthesis/util/ModeDiagrams.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index c0cd6badaa..395a638976 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -26,6 +26,7 @@ import java.awt.Color; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -44,6 +45,7 @@ import org.eclipse.elk.core.options.Direction; import org.eclipse.elk.core.options.EdgeRouting; import org.eclipse.elk.core.options.PortConstraints; +import org.eclipse.elk.core.options.PortLabelPlacement; import org.eclipse.elk.core.options.PortSide; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.xbase.lib.Extension; @@ -372,6 +374,8 @@ public void handleModes(List nodes, ReactorInstance reactor) { label = ((Action) source).getName(); } else if (source instanceof Timer) { label = ((Timer) source).getName(); + } else if (!port.getLabels().isEmpty()) { + label = port.getLabels().get(0).getText(); } _kLabelExtensions.addOutsidePortLabel(containerPort, label, 8); @@ -405,8 +409,18 @@ public void handleModes(List nodes, ReactorInstance reactor) { _utilityExtensions.setID(dummyNode, newID); _kRenderingExtensions.addInvisibleContainerRendering(dummyNode); dummyNode.getPorts().add(copy); + // Assign layer DiagramSyntheses.setLayoutOption(dummyNode, LayeredOptions.LAYERING_LAYER_CONSTRAINT, port.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST ? LayerConstraint.FIRST : LayerConstraint.LAST); + // Configure port spacing + DiagramSyntheses.setLayoutOption(dummyNode, CoreOptions.PORT_LABELS_PLACEMENT, EnumSet.of(PortLabelPlacement.ALWAYS_OTHER_SAME_SIDE, PortLabelPlacement.OUTSIDE)); + DiagramSyntheses.setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_HORIZONTAL, 2.0); + DiagramSyntheses.setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_VERTICAL, -3.0); + // Switch port side + DiagramSyntheses.setLayoutOption(copy, CoreOptions.PORT_SIDE, + port.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST ? PortSide.EAST : PortSide.WEST); + // Place freely + DiagramSyntheses.setLayoutOption(node, LayeredOptions.CONSIDER_MODEL_ORDER_NO_MODEL_ORDER, true); modeNode.getChildren().add(dummyNode); } From d35e9e0a720fb0c7c059a99959f7201b1f43378c Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 7 Jul 2022 14:15:03 +0200 Subject: [PATCH 17/35] diagrams: Improved label placement of connection ports in modes --- .../diagram/synthesis/util/ModeDiagrams.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index 395a638976..ff0216725f 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -50,7 +50,6 @@ import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.xbase.lib.Extension; import org.eclipse.xtext.xbase.lib.IterableExtensions; -import org.eclipse.xtext.xbase.lib.ListExtensions; import org.eclipse.xtext.xbase.lib.Pair; import org.lflang.diagram.synthesis.AbstractSynthesisExtensions; import org.lflang.diagram.synthesis.LinguaFrancaSynthesis; @@ -290,7 +289,7 @@ public void handleModes(List nodes, ReactorInstance reactor) { // add transitions var representedTargets = new HashSet>(); - for (var transition : ListExtensions.reverseView(mode.transitions)) { + for (var transition : mode.transitions) { if (!representedTargets.contains(new Pair(transition.target, transition.type))) { var edge = _kEdgeExtensions.createEdge(); edge.setSource(modeNode); @@ -361,9 +360,15 @@ public void handleModes(List nodes, ReactorInstance reactor) { modeContainerPorts.put(port, containerPort); modeContainer.getPorts().add(containerPort); - _kPortExtensions.setPortSize(containerPort, 8, 4); + _kPortExtensions.setPortSize(containerPort, 8, 8); KRectangle rect = _kRenderingExtensions.addRectangle(containerPort); + _kRenderingExtensions.setPointPlacementData(rect, + _kRenderingExtensions.LEFT, 0, 0.5f, + _kRenderingExtensions.BOTTOM, 0, 0.5f, + _kRenderingExtensions.H_CENTRAL, _kRenderingExtensions.V_CENTRAL, + 0, 0, 8, 4); _kRenderingExtensions.setBackground(rect, Colors.BLACK); + _linguaFrancaStyleExtensions.boldLineSelectionStyle(rect); DiagramSyntheses.setLayoutOption(containerPort, CoreOptions.PORT_BORDER_OFFSET, -4.0); DiagramSyntheses.setLayoutOption(containerPort, CoreOptions.PORT_SIDE, sourceIsInMode ? PortSide.EAST : PortSide.WEST); @@ -377,7 +382,11 @@ public void handleModes(List nodes, ReactorInstance reactor) { } else if (!port.getLabels().isEmpty()) { label = port.getLabels().get(0).getText(); } - _kLabelExtensions.addOutsidePortLabel(containerPort, label, 8); + var portLabel = _kLabelExtensions.createLabel(containerPort); + portLabel.setText(label); + var portLabelKText = _kRenderingFactory.createKText(); + _kRenderingExtensions.setFontSize(portLabelKText, 8); + portLabel.getData().add(portLabelKText); // new connection var copy = EcoreUtil.copy(edge); @@ -414,13 +423,11 @@ public void handleModes(List nodes, ReactorInstance reactor) { port.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST ? LayerConstraint.FIRST : LayerConstraint.LAST); // Configure port spacing DiagramSyntheses.setLayoutOption(dummyNode, CoreOptions.PORT_LABELS_PLACEMENT, EnumSet.of(PortLabelPlacement.ALWAYS_OTHER_SAME_SIDE, PortLabelPlacement.OUTSIDE)); - DiagramSyntheses.setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_HORIZONTAL, 2.0); - DiagramSyntheses.setLayoutOption(node, CoreOptions.SPACING_LABEL_PORT_VERTICAL, -3.0); + // Place freely + DiagramSyntheses.setLayoutOption(dummyNode, LayeredOptions.CONSIDER_MODEL_ORDER_NO_MODEL_ORDER, true); // Switch port side DiagramSyntheses.setLayoutOption(copy, CoreOptions.PORT_SIDE, port.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST ? PortSide.EAST : PortSide.WEST); - // Place freely - DiagramSyntheses.setLayoutOption(node, LayeredOptions.CONSIDER_MODEL_ORDER_NO_MODEL_ORDER, true); modeNode.getChildren().add(dummyNode); } From 05125dbfcef8f965357e9331505b653cc3c968f6 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 7 Jul 2022 14:33:34 +0200 Subject: [PATCH 18/35] diagrams: Reduced number of port labels in mode container if instance names help identifying connections. --- .../diagram/synthesis/util/ModeDiagrams.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index ff0216725f..ef51b65d59 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -57,11 +57,13 @@ import org.lflang.diagram.synthesis.styles.LinguaFrancaShapeExtensions; import org.lflang.diagram.synthesis.styles.LinguaFrancaStyleExtensions; import org.lflang.generator.ModeInstance; +import org.lflang.generator.NamedInstance; import org.lflang.generator.ModeInstance.Transition; import org.lflang.generator.ReactorInstance; import org.lflang.lf.Action; import org.lflang.lf.Mode; import org.lflang.lf.ModeTransition; +import org.lflang.lf.Reactor; import org.lflang.lf.Timer; import com.google.common.collect.LinkedHashMultimap; @@ -381,6 +383,12 @@ public void handleModes(List nodes, ReactorInstance reactor) { label = ((Timer) source).getName(); } else if (!port.getLabels().isEmpty()) { label = port.getLabels().get(0).getText(); + if (source instanceof Reactor && getBooleanValue(LinguaFrancaSynthesis.SHOW_INSTANCE_NAMES)) { + NamedInstance linkedInstance = NamedInstanceUtil.getLinkedInstance(node); + if (linkedInstance instanceof ReactorInstance) { + label = ((ReactorInstance) linkedInstance).getName() + "." + label; + } + } } var portLabel = _kLabelExtensions.createLabel(containerPort); portLabel.setText(label); @@ -444,11 +452,17 @@ public void handleModes(List nodes, ReactorInstance reactor) { } } } - + // If mode container is unused (no ports for local connections) -> hide it if (modeContainer.getPorts().isEmpty()) { _kRenderingExtensions.setInvisible(modeContainerFigure, true); - DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.PADDING, new ElkPadding()); + DiagramSyntheses.setLayoutOption(modeContainer, CoreOptions.PADDING, new ElkPadding(2)); + } else if (getBooleanValue(LinguaFrancaSynthesis.SHOW_INSTANCE_NAMES)) { + // Remove mode container port labels of ports representing internal connections + // because their association to reactor instances is unambiguous due to instance names + for (var p : modeContainer.getPorts()) { + p.getLabels().removeIf(l -> l.getText().contains(".")); + } } nodes.add(modeContainer); From 1ae23fee64ac401d5e72eacccdbde267f09e2ec4 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 7 Jul 2022 14:58:18 +0200 Subject: [PATCH 19/35] diagrams: Fixed whitespace --- .../src/org/lflang/diagram/synthesis/util/ModeDiagrams.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java index ef51b65d59..41a91e0139 100644 --- a/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java +++ b/org.lflang.diagram/src/org/lflang/diagram/synthesis/util/ModeDiagrams.java @@ -452,7 +452,7 @@ public void handleModes(List nodes, ReactorInstance reactor) { } } } - + // If mode container is unused (no ports for local connections) -> hide it if (modeContainer.getPorts().isEmpty()) { _kRenderingExtensions.setInvisible(modeContainerFigure, true); From a12f93a5ec4c1660fca98c915a98403cfbf713f4 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 7 Jul 2022 14:58:06 +0200 Subject: [PATCH 20/35] C++: check if CMakeLists.txt exists before adding a subdirectory Closes #1171 --- .../org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index 8c8dd51f6b..04a479d58b 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -85,8 +85,10 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat |endforeach() |foreach(subdir $S{subdirs}) | if(IS_DIRECTORY "$S{PROJECT_SOURCE_DIR}/$S{subdir}") - | if(NOT $S{subdir} MATCHES "reactor-cpp-.*") - | add_subdirectory("$S{subdir}") + | if(EXISTS "$S{PROJECT_SOURCE_DIR}/$S{subdir}/CMakeLists.txt") + | if(NOT $S{subdir} MATCHES "reactor-cpp-.*") + | add_subdirectory("$S{subdir}") + | endif() | endif() | endif() |endforeach() From 08d4a5bc6ecf17c79b4c1a579c1da8fbec6dc40e Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 7 Jul 2022 15:11:59 +0200 Subject: [PATCH 21/35] use a special marker to make C++ subdir detection more robust --- .../src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt | 2 +- .../src/org/lflang/generator/cpp/CppStandaloneGenerator.kt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index 04a479d58b..7c560ad4bb 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -85,7 +85,7 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat |endforeach() |foreach(subdir $S{subdirs}) | if(IS_DIRECTORY "$S{PROJECT_SOURCE_DIR}/$S{subdir}") - | if(EXISTS "$S{PROJECT_SOURCE_DIR}/$S{subdir}/CMakeLists.txt") + | if(EXISTS "$S{PROJECT_SOURCE_DIR}/$S{subdir}/.lf-cpp-marker") | if(NOT $S{subdir} MATCHES "reactor-cpp-.*") | add_subdirectory("$S{subdir}") | endif() diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt index 7330a43f84..f81db86228 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneGenerator.kt @@ -31,9 +31,11 @@ class CppStandaloneGenerator(generator: CppGenerator) : val pkgName = fileConfig.srcGenPkgPath.fileName.toString() FileUtil.writeToFile(cmakeGenerator.generateRootCmake(pkgName), srcGenRoot.resolve("CMakeLists.txt"), true) FileUtil.writeToFile(cmakeGenerator.generateCmake(cppSources), srcGenPath.resolve("CMakeLists.txt"), true) + FileUtil.writeToFile("", srcGenPath.resolve(".lf-cpp-marker"), true) var subdir = srcGenPath.parent while (subdir != srcGenRoot) { FileUtil.writeToFile(cmakeGenerator.generateSubdirCmake(), subdir.resolve("CMakeLists.txt"), true) + FileUtil.writeToFile("", subdir.resolve(".lf-cpp-marker"), true) subdir = subdir.parent } } From bf5b320acb66fa77cc82ab3bc3da57672e747cd0 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 7 Jul 2022 15:35:54 +0200 Subject: [PATCH 22/35] include the check also for nested directories --- .../org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt index 7c560ad4bb..af8aa4cd03 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneCmakeGenerator.kt @@ -100,7 +100,9 @@ class CppStandaloneCmakeGenerator(private val targetConfig: TargetConfig, privat |file(GLOB subdirs RELATIVE "$S{CMAKE_CURRENT_SOURCE_DIR}" "$S{CMAKE_CURRENT_SOURCE_DIR}/*") |foreach(subdir $S{subdirs}) | if(IS_DIRECTORY "$S{CMAKE_CURRENT_SOURCE_DIR}/$S{subdir}") - | add_subdirectory("$S{subdir}") + | if(EXISTS "$S{CMAKE_CURRENT_SOURCE_DIR}/$S{subdir}/.lf-cpp-marker") + | add_subdirectory("$S{subdir}") + | endif() | endif() |endforeach() """.trimMargin() From 2d711d5480b6b7d79dad5eb5b224bdf03767164c Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Thu, 7 Jul 2022 16:26:02 +0200 Subject: [PATCH 23/35] Do not minimize the shadow jar Issue #1284 was caused by the mimization step of the shadowJar gradle plugin. The main problem is, that we don't import kotlin classes directly, but load them manually in order to work around the build problems in Eclipse... Hence, the minimizer thinks it can exclude the Kotlin classes. While in principle it is possible to add particular excludes, this does not work properly when we compile a second time. The reason for this is unclear, but we might as well disable the minimization. This slightly increases the jar size from 23 MB to 28 MB. Closes #1284 --- org.lflang.lfc/build.gradle | 5 ----- 1 file changed, 5 deletions(-) diff --git a/org.lflang.lfc/build.gradle b/org.lflang.lfc/build.gradle index 6c26815eca..04e334e3c5 100644 --- a/org.lflang.lfc/build.gradle +++ b/org.lflang.lfc/build.gradle @@ -35,11 +35,6 @@ task buildLfc() { shadowJar { exclude 'test/*' - minimize() { - exclude(dependency('log4j:log4j:.*')) - exclude(dependency('com.google.inject:guice:.*')) - exclude(dependency('org.lflang:org.lflang:.*')) - } transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer){ resource = 'plugin.properties' } From 12f8ec1d9285851112bd8280ee5e928860939bba Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 11 Jul 2022 09:08:33 +0200 Subject: [PATCH 24/35] Explain why minimize is not used in shadowJar --- org.lflang.lfc/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/org.lflang.lfc/build.gradle b/org.lflang.lfc/build.gradle index 04e334e3c5..5bde33be16 100644 --- a/org.lflang.lfc/build.gradle +++ b/org.lflang.lfc/build.gradle @@ -38,6 +38,11 @@ task buildLfc() { transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer){ resource = 'plugin.properties' } + // We should use minimize() here to reduce the size of the JAR, but it causes problems + // with regard to our Kotlin classes. Since we don't use imports to load them but load + // the classes manually, minimize does not see the dependency. While we can add an exclude + // rule, this does not seem to work very well and causes problems when compiling for a + // second time. Also see https://github.com/lf-lang/lingua-franca/pull/1285 } } From 3b05b243a64b8a9edf94d0a966ae33df17a8535e Mon Sep 17 00:00:00 2001 From: eal Date: Mon, 11 Jul 2022 11:20:24 -0400 Subject: [PATCH 25/35] Typo --- org.lflang/src/org/lflang/generator/PortInstance.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/PortInstance.java b/org.lflang/src/org/lflang/generator/PortInstance.java index 2841d3c854..303a9777b0 100644 --- a/org.lflang/src/org/lflang/generator/PortInstance.java +++ b/org.lflang/src/org/lflang/generator/PortInstance.java @@ -40,7 +40,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY /** * Representation of a compile-time instance of a port. - * Like {@link ReactorInstance}, one or more parents of this port + * Like {@link ReactorInstance}, if one or more parents of this port * is a bank of reactors, then there will be more than one runtime instance * corresponding to this compile-time instance. * From 2887a94eb9f908481f8b8c734e8e93fa24987bbd Mon Sep 17 00:00:00 2001 From: eal Date: Mon, 11 Jul 2022 11:21:20 -0400 Subject: [PATCH 26/35] Fixes Reactions do not consistently trigger banks #1278 --- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index d55bfb514d..7b58d97f3b 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -781,7 +781,7 @@ private static String deferredFillTriggerTable( } } } - cumulativePortWidth += port.getWidth(); + cumulativePortWidth += port.getWidth() * port.getParent().getTotalWidth(); } if (foundPort) code.endScopedBlock(); } From 3972a67e6edaccf5146feaa2947eb6a20ca02c19 Mon Sep 17 00:00:00 2001 From: eal Date: Mon, 11 Jul 2022 14:13:16 -0400 Subject: [PATCH 27/35] Added test --- test/C/src/multiport/DualBanks.lf | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/C/src/multiport/DualBanks.lf diff --git a/test/C/src/multiport/DualBanks.lf b/test/C/src/multiport/DualBanks.lf new file mode 100644 index 0000000000..8d5ec2727c --- /dev/null +++ b/test/C/src/multiport/DualBanks.lf @@ -0,0 +1,41 @@ +target C; + +reactor Base(bank_index:int(0)) { + input I:int; + state received:bool(false); + + reaction(shutdown) {= + if(!self->received) { + lf_print_error_and_exit("Bank member %d received no input.", + self->bank_index + ); + } + =} +} + +reactor Hello extends Base { + reaction(I) {= + printf("Hello %d\n", self->bank_index); + self->received = true; + =} +} +reactor World extends Base { + reaction(I) {= + printf("World %d\n", self->bank_index); + self->received = true; + =} +} + +main reactor { + hellos = new[3] Hello() + worlds = new[3] World() + + reaction(startup) -> hellos.I, worlds.I {= + for(int i = 0; i < hellos_width; i++) { + lf_set(hellos[i].I, true); + } + for(int i = 0; i < worlds_width; i++) { + lf_set(worlds[i].I, true); + } + =} +} From 55a52fa673a488efe6ba9283680bd6e5b1c6d62e Mon Sep 17 00:00:00 2001 From: eal Date: Mon, 11 Jul 2022 17:48:17 -0400 Subject: [PATCH 28/35] Second attempt at getting the index increment right --- .../src/org/lflang/generator/c/CTriggerObjectsGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index 7b58d97f3b..fd4b42074b 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -779,9 +779,9 @@ private static String deferredFillTriggerTable( code.endScopedRangeBlock(srcRange, dstRange, isFederated); multicastCount++; } + cumulativePortWidth += srcRange.width; } } - cumulativePortWidth += port.getWidth() * port.getParent().getTotalWidth(); } if (foundPort) code.endScopedBlock(); } From cbb3fce22a4e4e1a07bca6c0391487c282fdb854 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Tue, 12 Jul 2022 09:54:46 +0200 Subject: [PATCH 29/35] modes: Removed xbase dependency from c modes generator --- org.lflang/src/org/lflang/generator/c/CModesGenerator.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java index 6eae920e8d..e40404e3a5 100644 --- a/org.lflang/src/org/lflang/generator/c/CModesGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CModesGenerator.java @@ -2,7 +2,6 @@ import java.util.List; -import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.lflang.ASTUtils; import org.lflang.generator.CodeBuilder; import org.lflang.generator.ReactionInstance; @@ -135,8 +134,7 @@ public static void generateModeStructure(ReactorInstance instance, CodeBuilder c } } else { // Otherwise, only reactions outside modes must be linked and the mode state itself gets a parent relation code.pr("((self_base_t*)"+nameOfSelfStruct+")->_lf__mode_state.parent_mode = "+parentModeRef+";"); - Iterable reactionsOutsideModes = IterableExtensions.filter(instance.reactions, it -> it.getMode(true) == null); - for (ReactionInstance reaction : reactionsOutsideModes) { + for (var reaction : (Iterable) instance.reactions.stream().filter(it -> it.getMode(true) == null)::iterator) { code.pr(CUtil.reactorRef(reaction.getParent())+"->_lf__reaction_"+instance.reactions.indexOf(reaction)+".mode = "+parentModeRef+";"); } } From 32c336c2df1922f47dd527b8b32e0652bc592563 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 11 Jul 2022 09:49:42 +0200 Subject: [PATCH 30/35] Cpp: catch CLI parser exceptions and print help message Closes #1255 --- .../cpp/CppStandaloneMainGenerator.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneMainGenerator.kt b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneMainGenerator.kt index a6f84c56a1..9a48f421ed 100644 --- a/org.lflang/src/org/lflang/generator/cpp/CppStandaloneMainGenerator.kt +++ b/org.lflang/src/org/lflang/generator/cpp/CppStandaloneMainGenerator.kt @@ -79,16 +79,21 @@ class CppStandaloneMainGenerator( | ${" |"..main.parameters.joinToString("\n\n") { generateParameterParser(it) }} | - | auto result = options.parse(argc, argv); + | cxxopts::ParseResult result{}; + | bool parse_error{false}; + | try { + | result = options.parse(argc, argv); + | } catch (const cxxopts::OptionException& e) { + | reactor::log::Error() << e.what(); + | parse_error = true; + | } | - | // if parameter --help was used, print help - | if (result.count("help")) + | // if parameter --help was used or there was a parse error, print help + | if (parse_error || result.count("help")) | { - | std::cout << options.help({""}) << std::endl; - | exit(0); - | } - | - | // validate time parameters (inferredType.isTime) and the timeout parameter via the validate_time_string(val) function + | std::cout << options.help({""}); + | return parse_error ? -1 : 0; + | } | | reactor::Environment e{workers, keepalive, fast}; | From d4fc7009672c47abdcd20ebcea7834899198c04a Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Mon, 11 Jul 2022 09:50:39 +0200 Subject: [PATCH 31/35] improve formatting of time value parser errors --- org.lflang/src/lib/cpp/time_parser.hh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/lib/cpp/time_parser.hh b/org.lflang/src/lib/cpp/time_parser.hh index 2a0a8516fb..d5d6fce52b 100644 --- a/org.lflang/src/lib/cpp/time_parser.hh +++ b/org.lflang/src/lib/cpp/time_parser.hh @@ -35,6 +35,18 @@ std::stringstream &operator>>(std::stringstream& in, reactor::Duration& dur); #include #include +class argument_incorrect_type_with_reason : public cxxopts::OptionParseException +{ + public: + explicit argument_incorrect_type_with_reason( + const std::string& arg, + const std::string& reason) + : cxxopts::OptionParseException( + "Argument " + cxxopts::LQUOTE + arg + cxxopts::RQUOTE + " failed to parse (" + reason + ")" + ) + {} +}; + std::string validate_time_string(const std::string& time); @@ -61,7 +73,7 @@ std::stringstream &operator>>(std::stringstream& in, reactor::Duration& dur) { const std::string validation_msg = validate_time_string(in.str()); if (!validation_msg.empty()) { // throw cxxopts error - throw cxxopts::argument_incorrect_type(validation_msg); + throw argument_incorrect_type_with_reason(in.str(), validation_msg); } // try to read as double From 30a0cdfdbcebcbd4dd95b95f6384b7b81e22e5e4 Mon Sep 17 00:00:00 2001 From: eal Date: Tue, 12 Jul 2022 05:47:24 -0400 Subject: [PATCH 32/35] Third attempt to get indexing right. Added multiport-bank test. --- .../generator/c/CTriggerObjectsGenerator.java | 8 +++- test/C/src/multiport/DualBanksMultiport.lf | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/C/src/multiport/DualBanksMultiport.lf diff --git a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java index fd4b42074b..a8f5af627d 100644 --- a/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java +++ b/org.lflang/src/org/lflang/generator/c/CTriggerObjectsGenerator.java @@ -779,9 +779,15 @@ private static String deferredFillTriggerTable( code.endScopedRangeBlock(srcRange, dstRange, isFederated); multicastCount++; } - cumulativePortWidth += srcRange.width; } } + // If the port is an input of a contained reactor, then we have to take + // into account the bank width of the contained reactor. + if (port.getParent() != reaction.getParent()) { + cumulativePortWidth += port.getWidth() * port.getParent().getWidth(); + } else { + cumulativePortWidth += port.getWidth(); + } } if (foundPort) code.endScopedBlock(); } diff --git a/test/C/src/multiport/DualBanksMultiport.lf b/test/C/src/multiport/DualBanksMultiport.lf new file mode 100644 index 0000000000..b3300108f5 --- /dev/null +++ b/test/C/src/multiport/DualBanksMultiport.lf @@ -0,0 +1,47 @@ +target C; + +reactor Base(bank_index:int(0)) { + input[2] I:int; + state received:bool(false); + + reaction(shutdown) {= + if(!self->received) { + lf_print_error_and_exit("Bank member %d did not receive all inputs.", + self->bank_index + ); + } + =} +} + +reactor Hello extends Base { + reaction(I) {= + if (I[0]->is_present && I[1]->is_present) { + printf("Hello %d\n", self->bank_index); + self->received = true; + } + =} +} +reactor World extends Base { + reaction(I) {= + if (I[0]->is_present && I[1]->is_present) { + printf("World %d\n", self->bank_index); + self->received = true; + } + =} +} + +main reactor { + hellos = new[3] Hello() + worlds = new[3] World() + + reaction(startup) -> hellos.I, worlds.I {= + for(int i = 0; i < hellos_width; i++) { + lf_set(hellos[i].I[0], true); + lf_set(hellos[i].I[1], true); + } + for(int i = 0; i < worlds_width; i++) { + lf_set(worlds[i].I[0], true); + lf_set(worlds[i].I[1], true); + } + =} +} From 83019878d0f38abe3196e4d95255895beb34c7b4 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Fri, 8 Jul 2022 13:41:56 -0700 Subject: [PATCH 33/35] Fixed issue with multiplying non-positive parent.width in RuntimeRange --- org.lflang/src/org/lflang/generator/RuntimeRange.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/org.lflang/src/org/lflang/generator/RuntimeRange.java b/org.lflang/src/org/lflang/generator/RuntimeRange.java index fcf2582ed9..c4dcc9aac0 100644 --- a/org.lflang/src/org/lflang/generator/RuntimeRange.java +++ b/org.lflang/src/org/lflang/generator/RuntimeRange.java @@ -210,7 +210,10 @@ public RuntimeRange( int maxWidth = instance.width; // Initial value. NamedInstance parent = instance.parent; while (parent.depth > 0) { - maxWidth *= parent.width; + if (parent.width > 0) { + // Skip when width is not positive (e.g., delay reactor instance) + maxWidth *= parent.width; + } parent = parent.parent; } this.maxWidth = maxWidth; @@ -508,4 +511,4 @@ public Port(PortInstance instance, int start, int width, Set in super(instance, start, width, interleaved); } } -} \ No newline at end of file +} From 5a10005f2374d6c6f4dd7c2d89bba8cefe3d8eb3 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 11 Jul 2022 13:28:31 -0700 Subject: [PATCH 34/35] Fix the issue with non-positive parent.width in RuntimeRange by adding logic for inferring width from connections into width() function of ASTUtils.java. --- org.lflang/src/org/lflang/ASTUtils.java | 93 +++++++++++-------- .../org/lflang/generator/RuntimeRange.java | 5 +- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index d93c61c901..a989a99f03 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -1434,44 +1434,7 @@ public static int width(WidthSpec spec, List instantiations) { return 1; } if (spec.isOfVariableLength() && spec.eContainer() instanceof Instantiation) { - // We may be able to infer the width by examining the connections of - // the enclosing reactor definition. This works, for example, with - // delays between multiports or banks of reactors. - // Attempt to infer the width. - for (Connection c : ((Reactor) spec.eContainer().eContainer()).getConnections()) { - int leftWidth = 0; - int rightWidth = 0; - int leftOrRight = 0; - for (VarRef leftPort : c.getLeftPorts()) { - if (leftPort.getContainer() == spec.eContainer()) { - if (leftOrRight != 0) { - throw new InvalidSourceException("Multiple ports with variable width on a connection."); - } - // Indicate that the port is on the left. - leftOrRight = -1; - } else { - leftWidth += inferPortWidth(leftPort, c, instantiations); - } - } - for (VarRef rightPort : c.getRightPorts()) { - if (rightPort.getContainer() == spec.eContainer()) { - if (leftOrRight != 0) { - throw new InvalidSourceException("Multiple ports with variable width on a connection."); - } - // Indicate that the port is on the right. - leftOrRight = 1; - } else { - rightWidth += inferPortWidth(rightPort, c, instantiations); - } - } - if (leftOrRight < 0) { - return rightWidth - leftWidth; - } else if (leftOrRight > 0) { - return leftWidth - rightWidth; - } - } - // A connection was not found with the instantiation. - return -1; + return inferWidthFromConnections(spec, instantiations); } var result = 0; for (WidthTerm term: spec.getTerms()) { @@ -1485,7 +1448,11 @@ public static int width(WidthSpec spec, List instantiations) { } else if (term.getWidth() > 0) { result += term.getWidth(); } else { - return -1; + // If the width cannot be determined because term's width <= 0, which means the term's width + // must be inferred, try to infer the width using connections. + if (spec.eContainer() instanceof Instantiation) { + return inferWidthFromConnections(spec, instantiations); + } } } return result; @@ -1840,4 +1807,52 @@ private static LinkedHashSet superClasses(Reactor reactor, Set } return result; } + + /** + * We may be able to infer the width by examining the connections of + * the enclosing reactor definition. This works, for example, with + * delays between multiports or banks of reactors. + * Attempt to infer the width from connections and return -1 if the width cannot be inferred. + * + * @param spec The width specification or null (to return 1). + * @param instantiations The (optional) list of instantiations. + * + * @return The width, or -1 if the width could not be inferred from connections. + */ + private static int inferWidthFromConnections(WidthSpec spec, List instantiations) { + for (Connection c : ((Reactor) spec.eContainer().eContainer()).getConnections()) { + int leftWidth = 0; + int rightWidth = 0; + int leftOrRight = 0; + for (VarRef leftPort : c.getLeftPorts()) { + if (leftPort.getContainer() == spec.eContainer()) { + if (leftOrRight != 0) { + throw new InvalidSourceException("Multiple ports with variable width on a connection."); + } + // Indicate that the port is on the left. + leftOrRight = -1; + } else { + leftWidth += inferPortWidth(leftPort, c, instantiations); + } + } + for (VarRef rightPort : c.getRightPorts()) { + if (rightPort.getContainer() == spec.eContainer()) { + if (leftOrRight != 0) { + throw new InvalidSourceException("Multiple ports with variable width on a connection."); + } + // Indicate that the port is on the right. + leftOrRight = 1; + } else { + rightWidth += inferPortWidth(rightPort, c, instantiations); + } + } + if (leftOrRight < 0) { + return rightWidth - leftWidth; + } else if (leftOrRight > 0) { + return leftWidth - rightWidth; + } + } + // A connection was not found with the instantiation. + return -1; + } } diff --git a/org.lflang/src/org/lflang/generator/RuntimeRange.java b/org.lflang/src/org/lflang/generator/RuntimeRange.java index c4dcc9aac0..20ccfcd896 100644 --- a/org.lflang/src/org/lflang/generator/RuntimeRange.java +++ b/org.lflang/src/org/lflang/generator/RuntimeRange.java @@ -210,10 +210,7 @@ public RuntimeRange( int maxWidth = instance.width; // Initial value. NamedInstance parent = instance.parent; while (parent.depth > 0) { - if (parent.width > 0) { - // Skip when width is not positive (e.g., delay reactor instance) - maxWidth *= parent.width; - } + maxWidth *= parent.width; parent = parent.parent; } this.maxWidth = maxWidth; From b624dc868455558a2e57a4a76ad92e3ce1db2537 Mon Sep 17 00:00:00 2001 From: Hokeun Kim Date: Mon, 11 Jul 2022 15:15:54 -0700 Subject: [PATCH 35/35] Return -1 when the inference via connections fails. --- org.lflang/src/org/lflang/ASTUtils.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/org.lflang/src/org/lflang/ASTUtils.java b/org.lflang/src/org/lflang/ASTUtils.java index a989a99f03..6f8ca4dbfa 100644 --- a/org.lflang/src/org/lflang/ASTUtils.java +++ b/org.lflang/src/org/lflang/ASTUtils.java @@ -1451,7 +1451,12 @@ public static int width(WidthSpec spec, List instantiations) { // If the width cannot be determined because term's width <= 0, which means the term's width // must be inferred, try to infer the width using connections. if (spec.eContainer() instanceof Instantiation) { - return inferWidthFromConnections(spec, instantiations); + try { + return inferWidthFromConnections(spec, instantiations); + } catch (InvalidSourceException e) { + // If the inference fails, return -1. + return -1; + } } } }