diff --git a/core/src/main/java/org/lflang/federated/extensions/CExtension.java b/core/src/main/java/org/lflang/federated/extensions/CExtension.java index 2381b8546e..a061f2d69a 100644 --- a/core/src/main/java/org/lflang/federated/extensions/CExtension.java +++ b/core/src/main/java/org/lflang/federated/extensions/CExtension.java @@ -464,7 +464,7 @@ public String generatePortAbsentReactionBody( + receivingPortID + ", " + connection.getDstFederate().id - + ", (long long) lf_time_logical_elapsed());", + + ", lf_time_logical_elapsed());", "if (" + sendRef + " == NULL || !" + sendRef + "->is_present) {", "LF_PRINT_LOG(\"The output port is NULL or it is not present.\");", " lf_send_port_absent_to_federate(" @@ -551,15 +551,33 @@ protected String makePreamble( // that handles incoming network messages destined to the specified // port. This will only be used if there are federates. int numOfNetworkActions = federate.networkMessageActions.size(); + int numZDCNetworkActions = federate.zeroDelayCycleNetworkMessageActions.size(); code.pr( """ interval_t _lf_action_delay_table[%1$s]; lf_action_base_t* _lf_action_table[%1$s]; size_t _lf_action_table_size = %1$s; - lf_action_base_t* _lf_zero_delay_cycle_action_table[%2$s]; - size_t _lf_zero_delay_cycle_action_table_size = %2$s; """ - .formatted(numOfNetworkActions, federate.zeroDelayCycleNetworkMessageActions.size())); + .formatted(numOfNetworkActions)); + if (numZDCNetworkActions > 0) { + code.pr( + """ + lf_action_base_t* _lf_zero_delay_cycle_action_table[%1$s]; + size_t _lf_zero_delay_cycle_action_table_size = %1$s; + uint16_t _lf_zero_delay_cycle_upstream_ids[%1$s]; + bool _lf_zero_delay_cycle_upstream_disconnected[%1$s] = { false }; + """ + .formatted(numZDCNetworkActions)); + } else { + // Make sure these symbols are defined, even though only size will be used. + code.pr( + """ + lf_action_base_t** _lf_zero_delay_cycle_action_table = NULL; + size_t _lf_zero_delay_cycle_action_table_size = 0; + uint16_t* _lf_zero_delay_cycle_upstream_ids = NULL; + bool* _lf_zero_delay_cycle_upstream_disconnected = NULL; + """); + } int numOfNetworkReactions = federate.networkReceiverReactions.size(); code.pr( diff --git a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java index aa92d055b3..a7eeda782a 100644 --- a/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java +++ b/core/src/main/java/org/lflang/federated/extensions/CExtensionUtils.java @@ -36,27 +36,23 @@ public class CExtensionUtils { // Regular expression pattern for shared_ptr types. - static final Pattern sharedPointerVariable = Pattern - .compile("^(/\\*.*?\\*/)?std::shared_ptr<(?((/\\*.*?\\*/)?(\\S+))+)>$"); + static final Pattern sharedPointerVariable = + Pattern.compile("^(/\\*.*?\\*/)?std::shared_ptr<(?((/\\*.*?\\*/)?(\\S+))+)>$"); /** * Generate C code that initializes network actions. * - *

- * These network actions will be triggered by federate.c whenever a message is - * received from + *

These network actions will be triggered by federate.c whenever a message is received from * the network. * * @param federate The federate. - * @param main The main reactor that contains the federate (used to lookup - * references). + * @param main The main reactor that contains the federate (used to lookup references). */ public static String initializeTriggersForNetworkActions( FederateInstance federate, ReactorInstance main) { CodeBuilder code = new CodeBuilder(); if (!federate.networkMessageActions.isEmpty()) { var actionTableCount = 0; - var zeroDelayActionTableCount = 0; for (int i = 0; i < federate.networkMessageActions.size(); ++i) { // Find the corresponding ActionInstance. Action action = federate.networkMessageActions.get(i); @@ -79,10 +75,17 @@ public static String initializeTriggersForNetworkActions( // Set the ID of the source federate. code.pr( trigger + ".source_id = " + federate.networkMessageSourceFederate.get(i).id + "; \\"); - if (federate.zeroDelayCycleNetworkMessageActions.contains(action)) { + int j = federate.zeroDelayCycleNetworkMessageActions.indexOf(action); + if (j >= 0) { + var upstream = federate.zeroDelayCycleNetworkUpstreamFeds.get(j); + code.pr("_lf_zero_delay_cycle_upstream_ids[" + j + "] = " + upstream.id + "; \\"); + if (upstream.isTransient) { + // Transient federates are assumed to be initially disconnected. + code.pr("_lf_zero_delay_cycle_upstream_disconnected[" + j + "] = true; \\"); + } code.pr( "_lf_zero_delay_cycle_action_table[" - + zeroDelayActionTableCount++ + + j + "] = (lf_action_base_t*)&" + trigger + "; \\"); @@ -95,11 +98,8 @@ public static String initializeTriggersForNetworkActions( /** * Generate C code that holds a sorted list of STAA structs by time. * - *

- * For decentralized execution, on every logical timestep, a thread will iterate - * through each - * staa struct, wait for the designated offset time, and set the associated port - * status to absent + *

For decentralized execution, on every logical timestep, a thread will iterate through each + * staa struct, wait for the designated offset time, and set the associated port status to absent * if it isn't known. * * @param federate The federate. @@ -115,7 +115,8 @@ public static String stpStructs(FederateInstance federate) { // main reactor for each Action. for (int i = 0; i < federate.staaOffsets.size(); ++i) { // Find the corresponding ActionInstance. - List networkActions = federate.staToNetworkActionMap.get(federate.staaOffsets.get(i)); + List networkActions = + federate.staToNetworkActionMap.get(federate.staaOffsets.get(i)); code.pr("staa_lst[" + i + "] = (staa_t*) malloc(sizeof(staa_t));"); code.pr( @@ -148,8 +149,7 @@ public static String stpStructs(FederateInstance federate) { } /** - * Create a port status field variable for a network input port "input" in the - * self struct of a + * Create a port status field variable for a network input port "input" in the self struct of a * reactor. * * @param input The network input port @@ -170,23 +170,15 @@ public static String createPortStatusFieldForInput(Input input) { } /** - * Given a connection 'delay' expression, return a string that represents the - * interval_t value of + * Given a connection 'delay' expression, return a string that represents the interval_t value of * the additional delay that needs to be applied to the outgoing message. * - *

- * The returned additional delay in absence of after on network connection - * (i.e., if delay is - * passed as a null) is NEVER. This has a special meaning in C library functions - * that send network - * messages that carry timestamps (@see lf_send_tagged_message and - * lf_send_port_absent_to_federate - * in lib/core/federate.c). In this case, the sender will send its current tag - * as the timestamp of - * the outgoing message without adding a microstep delay. If the user has - * assigned an after delay - * to the network connection (that can be zero) either as a time value (e.g., - * 200 msec) or as a + *

The returned additional delay in absence of after on network connection (i.e., if delay is + * passed as a null) is NEVER. This has a special meaning in C library functions that send network + * messages that carry timestamps (@see lf_send_tagged_message and lf_send_port_absent_to_federate + * in lib/core/federate.c). In this case, the sender will send its current tag as the timestamp of + * the outgoing message without adding a microstep delay. If the user has assigned an after delay + * to the network connection (that can be zero) either as a time value (e.g., 200 msec) or as a * literal (e.g., a parameter), that delay in nsec will be returned. * * @param delay The delay associated with a connection. @@ -230,9 +222,11 @@ public static void handleCompileDefinitions( } private static void handleAdvanceMessageInterval(FederateInstance federate) { - var advanceMessageInterval = federate.targetConfig.get(CoordinationOptionsProperty.INSTANCE).advanceMessageInterval; + var advanceMessageInterval = + federate.targetConfig.get(CoordinationOptionsProperty.INSTANCE).advanceMessageInterval; if (advanceMessageInterval != null) { - federate.targetConfig + federate + .targetConfig .get(CompileDefinitionsProperty.INSTANCE) .put("ADVANCE_MESSAGE_INTERVAL", String.valueOf(advanceMessageInterval.toNanoSeconds())); } @@ -245,15 +239,12 @@ static boolean clockSyncIsOn(FederateInstance federate, RtiConfig rtiConfig) { } /** - * Initialize clock synchronization (if enabled) and its related options for a - * given federate. + * Initialize clock synchronization (if enabled) and its related options for a given federate. * - *

- * Clock synchronization can be enabled using the clock-sync target property. + *

Clock synchronization can be enabled using the clock-sync target property. * - * @see Documentation + * @see Documentation */ public static void initializeClockSynchronization( FederateInstance federate, RtiConfig rtiConfig, MessageReporter messageReporter) { @@ -280,15 +271,12 @@ public static void initializeClockSynchronization( } /** - * Initialize clock synchronization (if enabled) and its related options for a - * given federate. + * Initialize clock synchronization (if enabled) and its related options for a given federate. * - *

- * Clock synchronization can be enabled using the clock-sync target property. + *

Clock synchronization can be enabled using the clock-sync target property. * - * @see Documentation + * @see Documentation */ public static void addClockSyncCompileDefinitions(FederateInstance federate) { @@ -319,9 +307,10 @@ public static void generateCMakeInclude( FederateInstance federate, FederationFileConfig fileConfig) throws IOException { Files.createDirectories(fileConfig.getSrcPath().resolve("include")); - Path cmakeIncludePath = fileConfig - .getSrcPath() - .resolve("include" + File.separator + federate.name + "_extension.cmake"); + Path cmakeIncludePath = + fileConfig + .getSrcPath() + .resolve("include" + File.separator + federate.name + "_extension.cmake"); CodeBuilder cmakeIncludeCode = new CodeBuilder(); @@ -330,11 +319,16 @@ public static void generateCMakeInclude( "add_compile_definitions(LF_SOURCE_DIRECTORY=\"" + fileConfig.srcPath + "\")"); cmakeIncludeCode.pr( "add_compile_definitions(LF_PACKAGE_DIRECTORY=\"" + fileConfig.srcPkgPath + "\")"); + // After federates have been divided, their root package directory is different. cmakeIncludeCode.pr( - "add_compile_definitions(LF_SOURCE_GEN_DIRECTORY=\"" + fileConfig.getSrcGenPath() + "\")"); + "add_compile_definitions(LF_FED_PACKAGE_DIRECTORY=\"" + + fileConfig.srcPkgPath + + File.separator + + "fed-gen" + + File.separator + + fileConfig.name + + "\")"); cmakeIncludeCode.pr("add_compile_definitions(LF_FILE_SEPARATOR=\"" + File.separator + "\")"); - cmakeIncludeCode.pr( - "add_compile_definitions(LF_FEDERATES_BIN_DIRECTORY=\"" + fileConfig.getFedBinPath() + "\")"); try (var srcWriter = Files.newBufferedWriter(cmakeIncludePath)) { srcWriter.write(cmakeIncludeCode.getCode()); } @@ -345,8 +339,7 @@ public static void generateCMakeInclude( } /** - * Generate code that sends the neighbor structure message to the RTI. See - * {@code + * Generate code that sends the neighbor structure message to the RTI. See {@code * MSG_TYPE_NEIGHBOR_STRUCTURE} in {@code federated/net_common.h}. * * @param federate The federate that is sending its neighbor structure @@ -419,13 +412,14 @@ public static String generateFederateNeighborStructure(FederateInstance federate // Use NEVER to encode no delay at all. code.pr("candidate_tmp = NEVER;"); } else { - var delayTime = delay instanceof ParameterReference - // In that case use the default value. - ? CTypes.getInstance() - .getTargetTimeExpr( - ASTUtils.getDefaultAsTimeValue( - ((ParameterReference) delay).getParameter())) - : CTypes.getInstance().getTargetExpr(delay, InferredType.time()); + var delayTime = + delay instanceof ParameterReference + // In that case use the default value. + ? CTypes.getInstance() + .getTargetTimeExpr( + ASTUtils.getDefaultAsTimeValue( + ((ParameterReference) delay).getParameter())) + : CTypes.getInstance().getTargetExpr(delay, InferredType.time()); code.pr( String.join( @@ -482,27 +476,26 @@ public static String surroundWithIfElseFederated(String insideIf, String insideE return surroundWithIfFederated(insideIf); } else { return """ - #ifdef FEDERATED - %s - #else - %s - #endif // FEDERATED - """ + #ifdef FEDERATED + %s + #else + %s + #endif // FEDERATED + """ .formatted(insideIf, insideElse); } } /** - * Surround {@code code} with blocks to ensure that code only executes if the - * program is + * Surround {@code code} with blocks to ensure that code only executes if the program is * federated. */ public static String surroundWithIfFederated(String code) { return """ - #ifdef FEDERATED - %s - #endif // FEDERATED - """ + #ifdef FEDERATED + %s + #endif // FEDERATED + """ .formatted(code); } @@ -511,41 +504,39 @@ public static String surroundWithIfElseFederatedCentralized(String insideIf, Str return surroundWithIfFederatedCentralized(insideIf); } else { return """ - #ifdef FEDERATED_CENTRALIZED - %s - #else - %s - #endif // FEDERATED_CENTRALIZED - """ + #ifdef FEDERATED_CENTRALIZED + %s + #else + %s + #endif // FEDERATED_CENTRALIZED + """ .formatted(insideIf, insideElse); } } /** - * Surround {@code code} with blocks to ensure that code only executes if the - * program is federated + * Surround {@code code} with blocks to ensure that code only executes if the program is federated * and has a centralized coordination. */ public static String surroundWithIfFederatedCentralized(String code) { return """ - #ifdef FEDERATED_CENTRALIZED - %s - #endif // FEDERATED_CENTRALIZED - """ + #ifdef FEDERATED_CENTRALIZED + %s + #endif // FEDERATED_CENTRALIZED + """ .formatted(code); } /** - * Surround {@code code} with blocks to ensure that code only executes if the - * program is federated + * Surround {@code code} with blocks to ensure that code only executes if the program is federated * and has a decentralized coordination. */ public static String surroundWithIfFederatedDecentralized(String code) { return """ - #ifdef FEDERATED_DECENTRALIZED - %s - #endif // FEDERATED_DECENTRALIZED - """ + #ifdef FEDERATED_DECENTRALIZED + %s + #endif // FEDERATED_DECENTRALIZED + """ .formatted(code); } @@ -566,9 +557,7 @@ public static String generateSerializationIncludes(FederateInstance federate) { return code.getCode(); } - /** - * Generate cmake-include code needed for enabled serializers of the federate. - */ + /** Generate cmake-include code needed for enabled serializers of the federate. */ public static String generateSerializationCMakeExtension(FederateInstance federate) { CodeBuilder code = new CodeBuilder(); for (SupportedSerializers serializer : federate.enabledSerializers) { diff --git a/core/src/main/java/org/lflang/federated/generator/FedASTUtils.java b/core/src/main/java/org/lflang/federated/generator/FedASTUtils.java index 5973b04dfd..3355152b48 100644 --- a/core/src/main/java/org/lflang/federated/generator/FedASTUtils.java +++ b/core/src/main/java/org/lflang/federated/generator/FedASTUtils.java @@ -287,9 +287,10 @@ private static void addNetworkReceiverReactor( connection.dstFederate.networkMessageSourceFederate.add(connection.srcFederate); connection.dstFederate.networkMessageActionDelays.add(connection.getDefinition().getDelay()); if (connection.srcFederate.isInZeroDelayCycle() - && connection.getDefinition().getDelay() == null) + && connection.getDefinition().getDelay() == null) { connection.dstFederate.zeroDelayCycleNetworkMessageActions.add(networkAction); - + connection.dstFederate.zeroDelayCycleNetworkUpstreamFeds.add(connection.srcFederate); + } // Get the largest STAA for any reaction triggered by the destination port. TimeValue maxSTAA = findMaxSTAA(connection, coordination); diff --git a/core/src/main/java/org/lflang/federated/generator/FederateInstance.java b/core/src/main/java/org/lflang/federated/generator/FederateInstance.java index 7d769a731e..e443650eb2 100644 --- a/core/src/main/java/org/lflang/federated/generator/FederateInstance.java +++ b/core/src/main/java/org/lflang/federated/generator/FederateInstance.java @@ -193,6 +193,12 @@ public Instantiation getInstantiation() { */ public List zeroDelayCycleNetworkMessageActions = new ArrayList<>(); + /** + * List of upstream federates corresponding to actions in the zeroDelayCycleNetworkMessageActions + * list. + */ + public List zeroDelayCycleNetworkUpstreamFeds = new ArrayList<>(); + /** * A set of federates with which this federate has an inbound connection There will only be one * physical connection even if federate A has defined multiple physical connections to federate B. diff --git a/core/src/main/java/org/lflang/generator/c/CCompiler.java b/core/src/main/java/org/lflang/generator/c/CCompiler.java index 0883dab91b..ddd5daebf1 100644 --- a/core/src/main/java/org/lflang/generator/c/CCompiler.java +++ b/core/src/main/java/org/lflang/generator/c/CCompiler.java @@ -49,8 +49,7 @@ import org.lflang.util.LFCommand; /** - * Responsible for creating and executing the necessary CMake command to compile - * code that is + * Responsible for creating and executing the necessary CMake command to compile code that is * generated by the CGenerator. This class uses CMake to compile. * * @author Soroush Bateni @@ -67,8 +66,7 @@ public class CCompiler { MessageReporter messageReporter; /** - * Indicate whether the compiler is in C++ mode. In C++ mode, the compiler - * produces .cpp files + * Indicate whether the compiler is in C++ mode. In C++ mode, the compiler produces .cpp files * instead of .c files and uses a C++ compiler to compiler the code. */ private final boolean cppMode; @@ -79,11 +77,10 @@ public class CCompiler { /** * Create an instance of CCompiler. * - * @param targetConfig The current target configuration. - * @param fileConfig The current file configuration. + * @param targetConfig The current target configuration. + * @param fileConfig The current file configuration. * @param messageReporter Used to report errors. - * @param cppMode Whether the generated code should be compiled as if it - * were C++. + * @param cppMode Whether the generated code should be compiled as if it were C++. */ public CCompiler( TargetConfig targetConfig, @@ -100,9 +97,8 @@ public CCompiler( /** * Run the C compiler by invoking cmake and make. * - * @param generator An instance of GeneratorBase, only used to report error line - * numbers in the - * Eclipse IDE. + * @param generator An instance of GeneratorBase, only used to report error line numbers in the + * Eclipse IDE. * @return true if compilation succeeds, false otherwise. */ public boolean runCCompiler(GeneratorBase generator, LFGeneratorContext context) @@ -197,15 +193,15 @@ public boolean runCCompiler(GeneratorBase generator, LFGeneratorContext context) } /** - * Return a command to compile the specified C file using CMake. This produces a - * C-specific + * Return a command to compile the specified C file using CMake. This produces a C-specific * compile command. */ public LFCommand compileCmakeCommand() { // Set the build directory to be "build" Path buildPath = fileConfig.getSrcGenPath().resolve("build"); - LFCommand command = commandFactory.createCommand("cmake", cmakeOptions(targetConfig, fileConfig), buildPath); + LFCommand command = + commandFactory.createCommand("cmake", cmakeOptions(targetConfig, fileConfig), buildPath); if (command == null) { messageReporter .nowhere() @@ -224,7 +220,6 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f String quote = "\""; String srcPath = fileConfig.srcPath.toString(); String rootPath = fileConfig.srcPkgPath.toString(); - String binPath = fileConfig.binPath.toString(); String srcGenPath = fileConfig.getSrcGenPath().toString(); if (separator.equals("\\")) { // Windows requires escaping the backslashes. @@ -233,7 +228,6 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f srcPath = srcPath.replaceAll("\\\\", "\\\\\\\\"); rootPath = rootPath.replaceAll("\\\\", "\\\\\\\\"); srcGenPath = srcGenPath.replaceAll("\\\\", "\\\\\\\\"); - binPath = binPath.replaceAll("\\\\", "\\\\\\\\"); } arguments.addAll( List.of( @@ -247,15 +241,13 @@ private static List cmakeOptions(TargetConfig targetConfig, FileConfig f "-DLF_FILE_SEPARATOR='" + quote + separator + quote + "'")); // Add #define for source file directory. // Do not do this for federated programs because for those, the definition is - // put - // into the cmake file (and fileConfig.srcPath is the wrong directory anyway). + // put into the cmake file (and fileConfig.srcPath is the wrong directory anyway). if (!fileConfig.srcPath.toString().contains("fed-gen")) { // Do not convert to Unix path arguments.add("-DLF_SOURCE_DIRECTORY='" + quote + srcPath + quote + "'"); arguments.add("-DLF_PACKAGE_DIRECTORY='" + quote + rootPath + quote + "'"); - arguments.add("-DLF_SOURCE_GEN_DIRECTORY='" + quote + srcGenPath + quote + "'"); } else { - arguments.add("-DLF_FEDERATES_BIN_DIRECTORY=\"" + maybeQuote + binPath + maybeQuote + "\""); + arguments.add("-DLF_SOURCE_GEN_DIRECTORY='" + quote + srcGenPath + quote + "'"); } arguments.add(FileUtil.toUnixString(fileConfig.getSrcGenPath())); @@ -279,31 +271,29 @@ private String buildTypeToCmakeConfig(BuildType type) { } /** - * Return a command to build the specified C file using CMake. This produces a - * C-specific build + * Return a command to build the specified C file using CMake. This produces a C-specific build * command. * - *

- * Note: It appears that configuration and build cannot happen in one command. - * Therefore, this + *

Note: It appears that configuration and build cannot happen in one command. Therefore, this * is separated into a compile command and a build command. */ public LFCommand buildCmakeCommand() { // Set the build directory to be "build" Path buildPath = fileConfig.getSrcGenPath().resolve("build"); String cores = String.valueOf(Runtime.getRuntime().availableProcessors()); - LFCommand command = commandFactory.createCommand( - "cmake", - List.of( - "--build", - ".", - "--target", - "install", - "--parallel", - cores, - "--config", - buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE))), - buildPath); + LFCommand command = + commandFactory.createCommand( + "cmake", + List.of( + "--build", + ".", + "--target", + "install", + "--parallel", + cores, + "--config", + buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE))), + buildPath); if (command == null) { messageReporter .nowhere() @@ -316,10 +306,8 @@ public LFCommand buildCmakeCommand() { } /** - * Return a flash/emulate command using west. If board is null (defaults to - * qemu_cortex_m3) or - * qemu_* Return a flash command which runs the target as an emulation If - * ordinary target, return + * Return a flash/emulate command using west. If board is null (defaults to qemu_cortex_m3) or + * qemu_* Return a flash command which runs the target as an emulation If ordinary target, return * {@code west flash} */ public LFCommand buildWestFlashCommand(PlatformOptions options) { @@ -341,23 +329,18 @@ public LFCommand buildWestFlashCommand(PlatformOptions options) { } /** - * Check if the output produced by CMake has any known and common errors. If a - * known error is + * Check if the output produced by CMake has any known and common errors. If a known error is * detected, a specialized, more informative message is shown. * - *

- * Errors currently detected: + *

Errors currently detected: * *

* * @param CMakeOutput The captured output from CMake. - * @return true if the provided 'CMakeOutput' contains a known error. false - * otherwise. + * @return true if the provided 'CMakeOutput' contains a known error. false otherwise. */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean outputContainsKnownCMakeErrors(String CMakeOutput) { @@ -387,10 +370,8 @@ private boolean outputContainsKnownCMakeErrors(String CMakeOutput) { * Produces the filename including the target-specific extension * * @param fileName The base name of the file without any extensions - * @param cppMode Indicate whether the compiler is in C++ mode In C++ mode, the - * compiler produces - * .cpp files instead of .c files and uses a C++ compiler to - * compiler the code. + * @param cppMode Indicate whether the compiler is in C++ mode In C++ mode, the compiler produces + * .cpp files instead of .c files and uses a C++ compiler to compiler the code. */ static String getTargetFileName(String fileName, boolean cppMode, TargetConfig targetConfig) { return fileName + getFileExtension(cppMode, targetConfig); @@ -399,9 +380,8 @@ static String getTargetFileName(String fileName, boolean cppMode, TargetConfig t /** * Return the file extension of the output source files. * - * @param cppMode Whether we are building C code using a C++ compiler. - * @param targetConfig The target configuration that parameterizes the build - * process. + * @param cppMode Whether we are building C code using a C++ compiler. + * @param targetConfig The target configuration that parameterizes the build process. */ static String getFileExtension(boolean cppMode, TargetConfig targetConfig) { if (targetConfig.getOrDefault(PlatformProperty.INSTANCE).platform() == Platform.ARDUINO) { diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 75db4d6e9d..b12f3c2661 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 75db4d6e9d5cb7b31292239f20876fa933e0edf0 +Subproject commit b12f3c266161e419bfaa8890cde92f0997965bb2 diff --git a/test/C/src/federated/transient/TransientDownstreamWithTimer.lf b/test/C/src/federated/transient/TransientDownstreamWithTimer.lf index a403057e48..0b9626a362 100644 --- a/test/C/src/federated/transient/TransientDownstreamWithTimer.lf +++ b/test/C/src/federated/transient/TransientDownstreamWithTimer.lf @@ -23,14 +23,15 @@ reactor TransientExec(launch_time: time = 0, fed_instance_name: char* = "instanc // Construct the command to launch the transient federate char mid_launch_cmd[512]; sprintf(mid_launch_cmd, - "%s/federate__%s -i %s", - lf_get_federates_bin_directory(), + "%s/bin/federate__%s -i %s", + LF_FED_PACKAGE_DIRECTORY, self->fed_instance_name, lf_get_federation_id() ); lf_print("Launching federate federate__%s at physical time " PRINTF_TIME ".", self->fed_instance_name, lf_time_physical()); + lf_print("**** Launch command: %s", mid_launch_cmd); int status = system(mid_launch_cmd); @@ -110,14 +111,14 @@ reactor Down { self->count_timer++; =} - reaction(in) {= - self->count_in_mid_reactions++; - =} - reaction(join) {= self->count_join++; =} + reaction(in) {= + self->count_in_mid_reactions++; + =} + reaction(shutdown) {= // Check that the TAG has been successfully issued to Down if (self->count_timer < 5) { diff --git a/test/C/src/federated/transient/TransientDownstreamWithTwoUpstream.lf b/test/C/src/federated/transient/TransientDownstreamWithTwoUpstream.lf index 49960b05b4..745ee2727d 100644 --- a/test/C/src/federated/transient/TransientDownstreamWithTwoUpstream.lf +++ b/test/C/src/federated/transient/TransientDownstreamWithTwoUpstream.lf @@ -29,8 +29,8 @@ reactor TransientExec(launch_time: time = 0, fed_instance_name: char* = "instanc // Construct the command to launch the transient federate char mid_launch_cmd[512]; sprintf(mid_launch_cmd, - "%s/federate__%s -i %s", - lf_get_federates_bin_directory(), + "%s/bin/federate__%s -i %s", + LF_FED_PACKAGE_DIRECTORY, self->fed_instance_name, lf_get_federation_id() ); @@ -69,6 +69,10 @@ reactor Down { self->count_timer++; =} + reaction(join) {= + self->count_join++; + =} + reaction(in_mid) {= self->count_in_mid_reactions++; =} @@ -77,10 +81,6 @@ reactor Down { self->count_in_up_reactions++; =} - reaction(join) {= - self->count_join++; - =} - reaction(shutdown) {= // Check that the TAG have been successfully issued to Down if (self->count_timer < 5) { diff --git a/test/C/src/federated/transient/TransientHotSwap.lf b/test/C/src/federated/transient/TransientHotSwap.lf index a4c986d3d4..b8270af8d7 100644 --- a/test/C/src/federated/transient/TransientHotSwap.lf +++ b/test/C/src/federated/transient/TransientHotSwap.lf @@ -28,8 +28,8 @@ reactor TransientExec(launch_time: time = 0, fed_instance_name: char* = "instanc // Construct the command to launch the transient federate char mid_launch_cmd[512]; sprintf(mid_launch_cmd, - "%s/federate__%s -i %s", - lf_get_federates_bin_directory(), + "%s/bin/federate__%s -i %s", + LF_FED_PACKAGE_DIRECTORY, self->fed_instance_name, lf_get_federation_id() ); diff --git a/test/C/src/federated/transient/TransientStatePersistence.lf b/test/C/src/federated/transient/TransientStatePersistence.lf index 71f3ed2b2d..bb5c0eff68 100644 --- a/test/C/src/federated/transient/TransientStatePersistence.lf +++ b/test/C/src/federated/transient/TransientStatePersistence.lf @@ -1,10 +1,9 @@ /** * This LF program showcases and tests the persistance of the internal state of a transient federate * across executions. Using the hot swap mechanism, the transient federate `Middle` leaves and then - * joins. Whenever the state to save changes (of type `federate_state_t`), it notifies - * `Persistence`. `Middle` notifies `Persistence` also when it joins. When it joins the second time - * or after, it receives the saved state and sets it. In this, the order of the reactions is - * important. + * joins. Whenever the state (of type `federate_state_t`) changes, it notifies `Persistence`. + * `Middle` notifies `Persistence` also when it joins. When `Middle` joins the second time or after, + * it receives the saved state and sets it. In this, the order of the reactions is important. */ target C { timeout: 2900 ms @@ -13,7 +12,6 @@ target C { preamble {= #include #include - // The internal federate state to be persistent across executions typedef struct federate_state_t { char state_char; @@ -29,8 +27,8 @@ reactor TransientExec(launch_time: time = 0, fed_instance_name: char* = "instanc // Construct the command to launch the transient federate char mid_launch_cmd[512]; sprintf(mid_launch_cmd, - "%s/federate__%s -i %s", - lf_get_federates_bin_directory(), + "%s/bin/federate__%s -i %s", + LF_FED_PACKAGE_DIRECTORY, self->fed_instance_name, lf_get_federation_id() ); @@ -61,6 +59,8 @@ reactor Persistence { reaction(in_middle_join) -> out_to_middle {= if (!self->middle_first_join) { lf_set(out_to_middle, self->middle_state); + lf_print("Notifying Mid of the latest state: {%c,%d}", self->middle_state.state_char, + self->middle_state.state_count); } self->middle_first_join = false; =} @@ -68,6 +68,8 @@ reactor Persistence { reaction(in_from_middle) {= self->middle_state.state_char = in_from_middle->value.state_char; self->middle_state.state_count = in_from_middle->value.state_count; + lf_print("Latest received state: {%c,%d}", self->middle_state.state_char, + self->middle_state.state_count); =} } @@ -108,6 +110,10 @@ reactor Middle { reaction(in_from_persistence) {= self->middle_state = in_from_persistence->value; + lf_print("Received the latest state of: {%c,%d} at " PRINTF_TIME ".", + self->middle_state.state_char, + self->middle_state.state_count, + lf_time_logical_elapsed()); =} // When an input is received, the internal state is updated, and then sent to @@ -146,21 +152,21 @@ reactor Down { lf_print("Down timer count %d", self->count_timer); =} - reaction(in) {= - self->count_in_mid_reactions++; - lf_print("Down in %d", self->count_in_mid_reactions); - =} - reaction(join) {= self->count_join++; lf_print("Down count join %d", self->count_join); =} - reaction(shutdown) in {= - if(self->count_join == 2 && in->value < 4) { + reaction(in) {= + self->count_in_mid_reactions++; + lf_print("Down in %d", self->count_in_mid_reactions); + =} + + reaction(shutdown) {= + if(self->count_join == 2 && self->count_in_mid_reactions < 4) { lf_print_error_and_exit("Mid Joined twice, but the state did not persist \ across executions! state_count is %d, while is should be > then %d.", - in->value, + self->count_in_mid_reactions, 4); } =}